diff --git a/.env.production b/.env.production index ab1ff6d..5a4811e 100644 --- a/.env.production +++ b/.env.production @@ -1,2 +1,9 @@ TENANT=default DIST_DIR=default +PRODUCTION=false +API_BASE_URL=https://psp-api.shift-am.ir +HOST=localhost +PORT=5001 +ENABLE_LOGGING=false +ENABLE_DEBUG=false +ENABLE_NATIVE_BRIDGE=false diff --git a/.env.tis b/.env.tis index a541ac0..9fa1d31 100644 --- a/.env.tis +++ b/.env.tis @@ -3,3 +3,10 @@ DIST_DIR=tis # TIS_BUILD_DATE= # TIS_APP_VERSION= # TIS_BUILD_NUMBER= +PRODUCTION=true +API_BASE_URL=http://192.168.128.73:5002 +HOST=localhost +PORT=5000 +ENABLE_LOGGING=false +ENABLE_DEBUG=false +ENABLE_NATIVE_BRIDGE=true diff --git a/.gitignore b/.gitignore index b2bf714..baef06d 100644 --- a/.gitignore +++ b/.gitignore @@ -42,5 +42,4 @@ testem.log .DS_Store Thumbs.db - -/false +.env.* diff --git a/agents.md b/agents.md index 1c0be04..feb5806 100644 --- a/agents.md +++ b/agents.md @@ -8,6 +8,233 @@ --- +# ⚡ EXECUTION RULES (STRICT) + +Minimize reasoning verbosity. + +## Response Style + +Do not: +- narrate thoughts +- speculate +- explain obvious steps +- describe intentions before acting +- write reflective/internal commentary + +Avoid messages like: +- "I think I need to..." +- "I should inspect..." +- "I'm going to..." +- "I wonder if..." +- "Let me check..." +- "I'm curious about..." + +Instead: +- act immediately +- use short operational updates only when necessary + +Good: + +Inspecting environment files and Angular configs. + +Bad: + +I think I should inspect the environment files first to understand how everything is connected before deciding what changes are necessary. + + +--- + +# 🔇 MINIMAL PLANNING MODE + +Only create plans when: +- task has multiple independent phases +- task is ambiguous +- task is large/refactor-level + +Do not create plans for: +- small fixes +- single-file edits +- obvious tasks + +Avoid: +- repeated plan updates +- verbose planning +- unnecessary decomposition + +--- + +# ✂️ MINIMAL OUTPUT RULES + +Keep responses compact. + +Do not: +- restate the user request +- explain already-visible edits +- summarize trivial findings +- narrate file discovery +- explain failed reads unless blocking +- describe obvious implementation details + +Prefer: + +Updated environment files for development, staging, production, and tis. + +Avoid: + +I found a mismatch and then investigated several files before determining that... + +--- + +# 📦 FINAL RESPONSE RULES + +Final responses must be under 10 lines unless: + +- user explicitly asks for explanation +- architectural decisions changed +- validation failed +- multiple systems were affected + +Prefer: + +Updated: +- file1 +- file2 + +Validation: +- pnpm tsc passed + +Avoid: + +- long prose summaries +- unnecessary explanations +- repeating repository context + +--- + +# 🚫 NO TEACHING MODE + +Do not explain: + +- Angular basics +- TypeScript basics +- RxJS basics +- obvious framework behavior +- standard programming concepts + +Assume repository maintainers already understand the stack. + +--- + +# 🧠 CONTEXT PRESERVATION RULES + +Avoid repeating previously established context. + +Do not repeatedly mention: +- repository structure +- framework details +- previously inspected files +- known architecture +- already-established conventions + +Assume prior context remains valid unless changed. + +--- + +# 🛑 EXPLORATION STOP RULE + +If the likely edit location is identified: + +- stop searching +- stop exploring +- implement changes + +Do not continue repository exploration after: + +- target component found +- target service found +- target config found +- target store found +- target route found + +--- + +# 🎯 EDIT-FIRST BEHAVIOR + +After identifying the target location: + +- edit quickly +- avoid excessive inspection +- avoid repeated verification reads +- avoid speculative exploration + +Prefer implementation over exploration. + +--- + +# ✍️ EDIT CONFIDENCE RULES + +Prefer direct implementation when: + +- existing patterns are obvious +- nearby components establish conventions +- requested change is localized +- existing abstractions already match requirements + +Do not over-investigate obvious implementations. + +--- + +# ⚠️ FAILURE HANDLING RULES + +If a file read fails: + +- do not retry repeatedly +- verify path once +- continue with nearest valid target + +Do not: + +- repeatedly attempt missing files +- scan nearby directories unnecessarily +- speculate about missing files +- retry identical commands + +--- + +# 📏 HARD CONTEXT LIMITS + +Do not: + +- read more than 2 files before first edit +- read more than 1 sibling file unless required +- reread unchanged files +- inspect unrelated modules + +For small tasks: + +- maximum 3 repository reads before editing + +For medium tasks: + +- maximum 8 repository reads before editing + +Large/refactor tasks may exceed limits only when necessary. + +--- + +# 🚫 AVOID MULTI-FILE EAGER READS + +Never chain multiple reads in one command unless necessary. + +Avoid: + +- rtk read a.ts && rtk read b.ts && rtk read c.ts + +Prefer sequential targeted reads. + +--- + + # ✅ RTK-FIRST RULES (STRICT) Always prefer RTK commands for repository inspection, navigation, and code understanding. @@ -16,17 +243,15 @@ Always prefer RTK commands for repository inspection, navigation, and code under Prefer: -```bash -git status -> rtk git status -git diff -> rtk git diff -git log -> rtk git log -ls -> rtk ls -tree -> rtk ls -cat -> rtk read -grep -> rtk grep -rg -> rtk grep -find -> rtk find -``` +- git status -> rtk git status +- git diff -> rtk git diff +- git log -> rtk git log +- ls -> rtk ls +- tree -> rtk ls +- cat -> rtk read +- grep -> rtk grep +- rg -> rtk grep +- find -> rtk find Avoid raw commands for repository inspection when RTK equivalents exist. @@ -45,49 +270,121 @@ Raw commands are allowed only when: --- + + +# 🧩 FILE READING RULES (IMPORTANT) + +Prefer minimal-context reads. + +## Preferred Order + +1. `rtk grep` +2. `rtk smart` +3. `rtk read` +4. aggressive read only if required + +## Rules + +Never immediately read a full file after search. + +Before reading: +- identify exact symbol/component/function +- inspect only relevant sections + +Prefer: + +- rtk smart + +before: + +- rtk read + + +For large files: + +- rtk read -l aggressive + + +Only when necessary. + +Avoid: +- reading TS + HTML + SCSS together +- opening sibling files preemptively +- rereading files already inspected +- reading generated or unrelated files + +--- + +## Angular-Specific Strategy + +For Angular components: + +1. grep component selector/class +2. smart-read TS file +3. read template only if UI changes required +4. read styles only if styling changes required + +Example: + +bash +rtk grep "invoice-type-card" +rtk smart invoice-type-card.component.ts +rtk read invoice-type-card.component.html + + +Avoid: + +bash +rtk read component.ts +rtk read component.html +rtk read component.scss + + +unless all files are actually needed. + # 📂 CODE NAVIGATION WORKFLOW (MANDATORY) Follow this order when working in the repository. ## 1. Discover Structure -```bash +bash rtk ls -``` + ## 2. Search Before Opening Files -```bash +bash rtk grep -``` + Examples: -```bash +bash rtk grep "InputComponent" rtk grep "fieldControl" rtk grep "breadcrumbItems" -``` + ## 3. Read Only Necessary Files -```bash +bash rtk read -``` + ## 4. Large File Strategy For files larger than ~300 lines: -```bash +bash rtk read -l aggressive -``` + ## 5. Fast Context Understanding -```bash +bash rtk smart -``` + Never: - open many files blindly @@ -101,21 +398,21 @@ Never: For repository changes use: -```bash +bash rtk git diff -``` + For large diffs: -```bash +bash rtk git diff -l aggressive -``` + Avoid raw: -```bash +bash git diff -``` + unless RTK diff is unavailable. @@ -125,7 +422,7 @@ unless RTK diff is unavailable. Avoid loading: -```txt + dist/ .angular/ coverage/ @@ -133,7 +430,7 @@ node_modules/ .git/ .cache/ .tmp/ -``` + unless explicitly required. @@ -169,12 +466,12 @@ unless explicitly required. Example workflow: -```bash +bash rtk index rtk search "InputComponent number normalization" rtk search "fieldControl" rtk refs "goodListConfig" -``` + ## Token Reduction Rules @@ -196,43 +493,43 @@ rtk refs "goodListConfig" ### Angular Components -```bash +bash rtk search "selector: 'field-" rtk search "standalone: true" rtk search "InputComponent" -``` + ### Forms & Controls -```bash +bash rtk search "fieldControl." rtk search "ControlConfig" rtk search "Validators.required" -``` + ### Stores & Signals -```bash +bash rtk search "breadcrumbItems" rtk search "computed(" rtk search "signal(" -``` + ### Tenant / Docker -```bash +bash rtk search "DIST_DIR" rtk search "configuration tis" rtk search "prebuild:tis" -``` + ### List Configs -```bash +bash rtk search "IListConfig" rtk search "columns:" rtk search "goodListConfig" -``` + --- @@ -340,7 +637,7 @@ Additional rule: Minimal wrapper shape: -```ts +ts @Component({ selector: 'field-example', template: ``, @@ -351,7 +648,7 @@ export class ExampleComponent { @Input() name = 'example'; @Input() label = 'Example'; } -``` + --- @@ -369,12 +666,12 @@ Add its form control factory in: Example: -```ts +ts example: (value = '', isRequired = true): ControlConfig => [ value, isRequired ? [Validators.required] : [], ], -``` + --- @@ -416,10 +713,10 @@ Each config implements `IListConfig`: ### Usage -```ts +ts @Input() header: IColumn[] = goodListConfig.columns; listConfig = goodListConfig; -``` + ### Rules @@ -435,14 +732,14 @@ listConfig = goodListConfig; - Entity stores expose `breadcrumbItems` as a computed signal. - Call `store.breadcrumbItems()` in view components and extend with current page: -```ts +ts setBreadcrumb() { this.breadcrumbService.setItems([ ...this.store.breadcrumbItems(), { title: 'Current Page' }, ]); } -``` + - Root page breadcrumbs are set in the store's `getData()` method once entity is loaded. @@ -452,16 +749,16 @@ setBreadcrumb() { ### TypeScript-only changes -```bash +bash pnpm -s exec tsc -p tsconfig.app.json --noEmit -``` + ### Docker/build changes -```bash +bash docker compose build app_default docker compose build app_tis -``` + ### Validation Strategy diff --git a/src/app/domains/pos/modules/configs/components/goldPrice/form.component.html b/src/app/domains/pos/modules/configs/components/goldPrice/form.component.html new file mode 100644 index 0000000..3e54b90 --- /dev/null +++ b/src/app/domains/pos/modules/configs/components/goldPrice/form.component.html @@ -0,0 +1,9 @@ + + + در این بخش می‌توانید قیمت پیش‌فرض هر گرم طلا را وارد کنید. + +
+ + + +
diff --git a/src/app/domains/pos/modules/configs/components/goldPrice/form.component.ts b/src/app/domains/pos/modules/configs/components/goldPrice/form.component.ts new file mode 100644 index 0000000..8b84b80 --- /dev/null +++ b/src/app/domains/pos/modules/configs/components/goldPrice/form.component.ts @@ -0,0 +1,55 @@ +import { AbstractForm } from '@/shared/abstractClasses'; +import { AppCardComponent, UnitPriceComponent } from '@/shared/components'; +import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component'; +import { fieldControl } from '@/shared/constants'; +import { Component, inject, signal } from '@angular/core'; +import { ReactiveFormsModule } from '@angular/forms'; +import { Message } from 'primeng/message'; +import { IPosConfigGoldPricePayload, IPosConfigGoldPriceResponse } from './models'; +import { PosConfigGoldPriceService } from './services/main.service'; + +@Component({ + selector: 'pos-config-gold-price-form', + templateUrl: 'form.component.html', + imports: [ + ReactiveFormsModule, + FormFooterActionsComponent, + AppCardComponent, + Message, + UnitPriceComponent, + ], +}) +export class PosConfigGoldPriceFormComponent extends AbstractForm< + IPosConfigGoldPricePayload, + IPosConfigGoldPriceResponse +> { + private readonly service = inject(PosConfigGoldPriceService); + + loading = signal(true); + + initForm = () => { + const form = this.fb.group({ + price: fieldControl.unit_price(), + }); + + return form; + }; + + form = this.initForm(); + + override async ngOnInit() { + this.loading.set(true); + const initialValues = await this.service.get(); + + console.log('initialValues.price', initialValues); + + this.form.controls.price.setValue((initialValues || 0) + ''); + this.loading.set(false); + } + + override submitForm() { + const formValue = this.form.value.price as IPosConfigGoldPricePayload; + this.toastService.success({ text: 'تغییرات با موفقیت اعمال شد.' }); + return this.service.submit(formValue); + } +} diff --git a/src/app/domains/pos/modules/configs/components/goldPrice/models/index.ts b/src/app/domains/pos/modules/configs/components/goldPrice/models/index.ts new file mode 100644 index 0000000..3c4a85a --- /dev/null +++ b/src/app/domains/pos/modules/configs/components/goldPrice/models/index.ts @@ -0,0 +1,3 @@ +export type IPosConfigGoldPriceResponse = number; + +export type IPosConfigGoldPricePayload = number; diff --git a/src/app/domains/pos/modules/configs/components/goldPrice/services/main.service.ts b/src/app/domains/pos/modules/configs/components/goldPrice/services/main.service.ts new file mode 100644 index 0000000..6951d25 --- /dev/null +++ b/src/app/domains/pos/modules/configs/components/goldPrice/services/main.service.ts @@ -0,0 +1,19 @@ +import { Injectable } from '@angular/core'; +import { LOCAL_STORAGE_KEYS } from 'src/assets/constants'; +import { IPosConfigGoldPricePayload, IPosConfigGoldPriceResponse } from '../models'; + +@Injectable({ providedIn: 'root' }) +export class PosConfigGoldPriceService { + get(): IPosConfigGoldPriceResponse { + const defaultPrice = Number( + window.localStorage.getItem(LOCAL_STORAGE_KEYS.POS_CONFIG_GOLD_PRICE) || '0' + ); + + this.submit(defaultPrice); + return defaultPrice; + } + + submit(data: IPosConfigGoldPricePayload) { + window.localStorage.setItem(LOCAL_STORAGE_KEYS.POS_CONFIG_GOLD_PRICE, data.toString()); + } +} diff --git a/src/app/domains/pos/modules/configs/views/root.component.html b/src/app/domains/pos/modules/configs/views/root.component.html index c76cbc6..5047641 100644 --- a/src/app/domains/pos/modules/configs/views/root.component.html +++ b/src/app/domains/pos/modules/configs/views/root.component.html @@ -1,8 +1,11 @@ -
+
+ @if (info()?.guild!.code.toLocaleLowerCase() === 'gold') { + + } هریک از موارد زیر را که می‌خواهید در چاپ فاکتور نمایش داده‌ شوند را انتخاب کنید - +
diff --git a/src/app/domains/pos/modules/configs/views/root.component.ts b/src/app/domains/pos/modules/configs/views/root.component.ts index 8f6cbc0..b473352 100644 --- a/src/app/domains/pos/modules/configs/views/root.component.ts +++ b/src/app/domains/pos/modules/configs/views/root.component.ts @@ -1,17 +1,27 @@ +import { PosInfoStore } from '@/domains/pos/store'; import { AppCardComponent } from '@/shared/components'; -import { UikitFlatpickrJalaliComponent } from '@/uikit'; -import { Component, inject } from '@angular/core'; +import { Component, computed, inject } from '@angular/core'; import { Router } from '@angular/router'; import { Message } from 'primeng/message'; +import { PosConfigGoldPriceFormComponent } from '../components/goldPrice/form.component'; import { PosConfigPrintFormComponent } from '../components/print/form.component'; @Component({ selector: 'pos-config-page', templateUrl: './root.component.html', - imports: [PosConfigPrintFormComponent, AppCardComponent, Message, UikitFlatpickrJalaliComponent], + imports: [ + PosConfigPrintFormComponent, + AppCardComponent, + Message, + PosConfigGoldPriceFormComponent, + ], }) export class PosConfigPageComponent { private readonly router = inject(Router); + private readonly posInfoStore = inject(PosInfoStore); + + readonly info = computed(() => this.posInfoStore.entity()); + returnToMainPage() { this.router.navigateByUrl('/'); } diff --git a/src/app/domains/pos/modules/shop/components/goods.component.html b/src/app/domains/pos/modules/shop/components/goods.component.html index cf7da88..750f138 100644 --- a/src/app/domains/pos/modules/shop/components/goods.component.html +++ b/src/app/domains/pos/modules/shop/components/goods.component.html @@ -1,22 +1,19 @@ -
-
-
+
+
+
-
- +
+ + class="border-surface-border bg-surface-ground shrink-0 border"> @@ -25,13 +22,13 @@
-
+
@if (!loading() && !goods()?.length) { -
- - کالایی برای نمایش وجود ندارد. +
+ + کالایی برای نمایش وجود ندارد.
- } @else if (viewType === "grid") { + } @else if (viewType === 'grid') { } @else { diff --git a/src/app/domains/pos/modules/shop/components/grid-view.component.html b/src/app/domains/pos/modules/shop/components/grid-view.component.html index c84024b..4ee77b0 100644 --- a/src/app/domains/pos/modules/shop/components/grid-view.component.html +++ b/src/app/domains/pos/modules/shop/components/grid-view.component.html @@ -1,28 +1,27 @@ -
+
@if (loading()) { @for (_ of [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15]; track $index) { -
+
} } @else { @for (good of visibleGoods(); track good.id) { -
+
-
- + class="aspect-[1.3] w-full shrink-0 rounded-md object-cover" /> +
+ {{ good.name }} @if (!good.is_default_guild_good) { - * + * } -
- +
+
@@ -31,7 +30,7 @@ }
@if (!loading() && hasMore()) { -
+
} diff --git a/src/app/domains/pos/modules/shop/components/payloads/gold/form.component.ts b/src/app/domains/pos/modules/shop/components/payloads/gold/form.component.ts index d1062cf..7218c73 100644 --- a/src/app/domains/pos/modules/shop/components/payloads/gold/form.component.ts +++ b/src/app/domains/pos/modules/shop/components/payloads/gold/form.component.ts @@ -1,11 +1,12 @@ import { greaterThanValidator } from '@/core/validators/greater.validator'; +import { PosConfigGoldPriceService } from '@/domains/pos/modules/configs/components/goldPrice/services/main.service'; import { AbstractForm } from '@/shared/abstractClasses'; import { EnumSelectComponent } from '@/shared/apiEnum/select.component'; import { InputComponent } from '@/shared/components'; import { AmountPercentageInputComponent } from '@/shared/components/amountPercentageInput/amount-percentage-input.component'; import { TGoldKarat } from '@/shared/localEnum/constants/goldKarat'; import { formatWithCurrency } from '@/utils'; -import { Component, computed, EventEmitter, Input, Output, signal } from '@angular/core'; +import { Component, computed, EventEmitter, inject, Input, Output, signal } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { FormsModule, ReactiveFormsModule, Validators } from '@angular/forms'; import { ButtonDirective } from 'primeng/button'; @@ -47,6 +48,8 @@ export class PosGoldPayloadFormComponent extends AbstractForm< @Output() onSubmitForm = new EventEmitter(); @Output() onChangeTotalAmount = new EventEmitter(); + private readonly posGoldConfig = inject(PosConfigGoldPriceService); + readonly discountTypeItems = [ { label: 'از سود', @@ -66,11 +69,20 @@ export class PosGoldPayloadFormComponent extends AbstractForm< taxAmount = signal(0); totalAmount = signal(0); + goldDefaultUnitPrice = signal(0); + preparedCTAText = computed(() => (this.editMode ? 'اعمال ویرایش' : 'افزودن به لیست خرید')); private readonly initialForm = () => { + const defaultPrice = this.posGoldConfig.get(); + + this.goldDefaultUnitPrice.set(defaultPrice); + const form = this.fb.group({ - unit_price: [this.initialValues?.unit_price || 0, [Validators.required]], + unit_price: [ + this.initialValues?.unit_price || this.goldDefaultUnitPrice() || 0, + [Validators.required], + ], quantity: [this.initialValues?.quantity || 0, [Validators.required, greaterThanValidator(0)]], discount: [this.initialValues?.discount || 0], discount_percentage: [0, [Validators.max(100), Validators.min(0)]], diff --git a/src/assets/constants/localStorageKeys.const.ts b/src/assets/constants/localStorageKeys.const.ts index 68b2dcc..d03eb6e 100644 --- a/src/assets/constants/localStorageKeys.const.ts +++ b/src/assets/constants/localStorageKeys.const.ts @@ -5,4 +5,5 @@ export enum LOCAL_STORAGE_KEYS { LAST_LOGIN_TIME = 'lastLoginTime', LOGIN_ATTEMPTS = 'loginAttempts', POS_CONFIG_PRINT = 'posConfigPrint', + POS_CONFIG_GOLD_PRICE = 'posConfigGoldPrice', }