From eb671d5949ce56cdbb3989f005e40adb5c386d16 Mon Sep 17 00:00:00 2001 From: ahasani194 Date: Sun, 24 May 2026 19:40:02 +0330 Subject: [PATCH] feat: enhance form components with validation and dynamic properties; update payment handling and UI interactions --- agents.md | 973 +++--------------- .../core/services/native-bridge.service.ts | 2 +- src/app/core/validators/greater.validator.ts | 2 + .../payloads/gold/form.component.html | 22 +- .../payloads/gold/form.component.ts | 2 +- .../payloads/standard/form.component.ts | 8 +- .../payment/form-dialog.component.html | 43 +- .../payment/form-dialog.component.ts | 45 +- .../components/views/goods.component.html | 2 +- .../domains/pos/modules/shop/models/io.d.ts | 1 + .../pos/modules/shop/store/main.store.ts | 17 +- .../modules/shop/views/root.component.html | 8 +- .../shared/abstractClasses/abstract-form.ts | 2 +- .../dialog/light-bottomsheet.component.ts | 10 +- .../components/fields/quantity.component.ts | 11 +- 15 files changed, 283 insertions(+), 865 deletions(-) diff --git a/agents.md b/agents.md index feb5806..fd33199 100644 --- a/agents.md +++ b/agents.md @@ -1,98 +1,188 @@ # AGENT.md -## Purpose +## Scope -- This file defines repository-specific instructions for coding agents. -- Scope is the full repo unless a deeper `AGENT.md` overrides it. -- Use RTK to reduce token usage, avoid repeated context transfer, and preserve repository knowledge across sessions. +Applies to the full repository unless overridden by a deeper `AGENT.md`. + +Stack: +- Angular 20 +- Standalone components +- pnpm +- Docker +- RTK --- -# ⚡ EXECUTION RULES (STRICT) +# EXECUTION RULES -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 +- Keep responses short. +- Do not narrate thoughts. +- Do not explain obvious steps. +- Do not create plans for simple tasks. +- Prefer implementation over exploration. 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 +- “I think…” +- “Let me check…” +- “I should inspect…” +- “I’m going to…” Prefer: - -Updated environment files for development, staging, production, and tis. - -Avoid: - -I found a mismatch and then investigated several files before determining that... +- “Updated environment files.” +- “Applied tenant config fix.” --- -# 📦 FINAL RESPONSE RULES +# RTK RULES (MANDATORY) -Final responses must be under 10 lines unless: +Always prefer RTK commands. -- user explicitly asks for explanation -- architectural decisions changed +Use: + +- `rtk ls` +- `rtk grep` +- `rtk smart` +- `rtk read` +- `rtk git diff` +- `rtk git status` + +Avoid raw: +- `cat` +- `grep` +- `rg` +- `tree` +- `git diff` +- recursive `find` + +Use raw shell only for: +- builds +- runtime/debugging +- Docker +- pnpm +- commands RTK cannot perform + +--- + +# FILE READING POLICY + +Preferred order: + +1. `rtk grep` +2. `rtk smart` +3. `rtk read` + +Rules: +- Search before reading. +- Read only required files. +- Do not read TS/HTML/SCSS together unless required. +- Stop exploring once edit location is clear. +- Avoid rereading unchanged files. + +Large files: +- `rtk read -l aggressive` + +--- + +# ANGULAR WORKFLOW + +For components: + +1. `rtk grep ""` +2. `rtk smart component.ts` +3. Read template only if UI changes are required +4. Read styles only if styling changes are required + +Avoid broad module inspection. + +--- + +# TOKEN RULES + +Do not: +- dump large files +- scan unrelated folders +- inspect generated directories +- perform repeated searches +- over-explain edits + +Avoid: +- `dist/` +- `.angular/` +- `coverage/` +- `node_modules/` +- `.git/` + +--- + +# CHANGE POLICY + +- Keep changes minimal and scoped. +- Reuse existing patterns. +- Avoid unrelated refactors. +- Preserve tenant separation. +- Prefer root-cause fixes. + +--- + +# PROJECT RULES + +## Tenant Builds + +- `default` → `dist/production` +- `tis` → `dist/tis` + +Keep Docker `DIST_DIR` aligned with Angular output. + +Do not use Angular `fileReplacements` for static assets. + +--- + +## Input Component + +File: +- `src/app/shared/components/input/input.component.ts` + +For: +- `type="number"` +- `type="price"` + +Requirements: +- normalize Persian/Arabic digits +- allow only digits and `.` +- support fixed precision +- keep identifier fields string-safe + +--- + +## Shared Field Rules + +When creating fields: +- use shared field wrappers +- reuse `app-input` +- register exports in shared indexes +- keep control names consistent + +--- + +# VALIDATION + +TypeScript: +- `pnpm -s exec tsc -p tsconfig.app.json --noEmit` + +Docker: +- `docker compose build app_default` +- `docker compose build app_tis` + +Validate only impacted areas when possible. + +--- + +# FINAL RESPONSE RULES + +Keep final responses under 10 lines unless: - validation failed -- multiple systems were affected +- architecture changed +- user requested explanation Prefer: @@ -101,729 +191,4 @@ Updated: - 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. - -## Command Rewrite Rules - -Prefer: - -- 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. - -Do not: -- use raw `cat` for large files -- use raw `grep`/`rg` across the repository -- use recursive `find` blindly -- inspect large diffs with raw `git diff` -- scan directories manually before searching - -Raw commands are allowed only when: -- RTK cannot perform the operation -- executing builds/tests/runtime commands -- debugging environment/runtime issues -- working outside indexed repository scope - ---- - - - -# 🧩 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 -rtk ls - - -## 2. Search Before Opening Files - -bash -rtk grep - - -Examples: - -bash -rtk grep "InputComponent" -rtk grep "fieldControl" -rtk grep "breadcrumbItems" - - -## 3. Read Only Necessary Files - -bash -rtk read - - -## 4. Large File Strategy - -For files larger than ~300 lines: - -bash -rtk read -l aggressive - - -## 5. Fast Context Understanding - -bash -rtk smart - - -Never: -- open many files blindly -- read entire modules without searching first -- inspect unrelated folders -- load generated directories into context - ---- - -# 🧠 DIFF INSPECTION RULES - -For repository changes use: - -bash -rtk git diff - - -For large diffs: - -bash -rtk git diff -l aggressive - - -Avoid raw: - -bash -git diff - - -unless RTK diff is unavailable. - ---- - -# 🚫 LOW-VALUE PATHS - -Avoid loading: - - -dist/ -.angular/ -coverage/ -node_modules/ -.git/ -.cache/ -.tmp/ - - -unless explicitly required. - ---- - -# RTK Usage Rules - -## Required RTK Usage - -- Always use RTK for: - - repository indexing - - semantic code search - - symbol lookup - - dependency tracing - - architectural context - - change impact analysis - -- Prefer RTK context retrieval over: - - reading large files entirely - - repeatedly scanning unchanged directories - - re-sending full file contents - - broad grep operations - -- Retrieve only the minimal relevant context before making changes. - -## Recommended RTK Workflow - -1. Index repository -2. Search related symbols/files/components -3. Retrieve minimal context -4. Apply scoped changes -5. Validate impacted areas only - -Example workflow: - -bash -rtk index -rtk search "InputComponent number normalization" -rtk search "fieldControl" -rtk refs "goodListConfig" - - -## Token Reduction Rules - -- Never load entire large files unless required. -- Never inspect generated folders (`dist`, `.angular`, coverage, etc.). -- Prefer symbol-level retrieval over file-level retrieval. -- Reuse previously retrieved RTK context whenever possible. -- Avoid duplicate searches for the same symbols within one task. -- Use targeted searches: - - component name - - selector - - signal/store name - - config constant - - Angular route - - injected service - - form control key - -## Suggested RTK Queries - -### Angular Components - -bash -rtk search "selector: 'field-" -rtk search "standalone: true" -rtk search "InputComponent" - - -### Forms & Controls - -bash -rtk search "fieldControl." -rtk search "ControlConfig" -rtk search "Validators.required" - - -### Stores & Signals - -bash -rtk search "breadcrumbItems" -rtk search "computed(" -rtk search "signal(" - - -### Tenant / Docker - -bash -rtk search "DIST_DIR" -rtk search "configuration tis" -rtk search "prebuild:tis" - - -### List Configs - -bash -rtk search "IListConfig" -rtk search "columns:" -rtk search "goodListConfig" - - ---- - -## Stack Context - -- Frontend: Angular 20 standalone app. -- Package manager: `pnpm`. -- Deployment: Docker / Docker Compose with tenant-specific services. -- AI-assisted repository navigation: RTK. - -### Current service mapping expectation - -- `app_default` on host port `8090` -- `app_tis` on host port `8091` - ---- - -## Tenant Build Rules - -- `default` tenant currently builds via `ng build` and outputs to `dist/production`. -- `tis` tenant builds via `ng build --configuration tis` and outputs to `dist/tis`. -- `prebuild:tis` is tenant-scoped and should keep using scripts under `scripts/tis/*`. -- Keep Docker `DIST_DIR` aligned with actual Angular output path. -- Do not assume `default` Angular configuration is usable unless verified (it may reference missing replacements). -- Do not use Angular `fileReplacements` for static assets (`.png`, `.jpg`, etc.); use tenant public assets or prebuild copy scripts. - ---- - -## Tenant PWA Rules - -- Keep tenant manifest files under tenant public assets (e.g. `public-tis/favicon/site.webmanifest`). -- Ensure `manifest` `id`, `start_url`, and `scope` match actual deployment path: - - root deploy: `/` - - subpath deploy example: `/tis/` -- Keep `` path and branding config `manifestPath` aligned. - ---- - -## Input Component Rules - -File: -- `src/app/shared/components/input/input.component.ts` - -For: -- `type === 'number'` -- `type === 'price'` - -Requirements: -- Normalize Persian/Arabic digits to English digits. -- Allow only digits and `.`. -- Support `fixed` precision formatting when provided. - -Additional rule: -- Keep behavior for identifier fields (`mobile`, `phone`, `postalCode`, `nationalId`) string-safe. - ---- - -## Change Policy - -- Keep changes minimal and scoped to user request. -- Prefer root-cause fixes over temporary workarounds. -- Avoid unrelated refactors. -- Reuse existing patterns and naming conventions. -- Use RTK to understand existing patterns before introducing new ones. - ---- - -## Do / Don't - -### Do - -- Follow existing field wrapper style in: - - `src/app/shared/components/fields/*.component.ts` -- Reuse `app-input` and set only required props: - - `type` - - `label` - - `name` - - constraints -- Register every new field in: - - `src/app/shared/components/fields/index.ts` - - `src/app/shared/constants/fields/index.ts` -- Keep control keys consistent across: - - form group - - field component `name` - - `fieldControl` key -- Use RTK searches before creating new abstractions. - -### Don't - -- Don't add one-off field patterns when an existing field component can be reused. -- Don't use invalid Angular file replacements for directories or empty paths. -- Don't change tenant output directories without updating Docker `DIST_DIR`. -- Don't scan unrelated folders/files when RTK can retrieve exact symbols. -- Don't load full files if RTK symbol extraction is sufficient. - ---- - -## How To Create Form Fields - -- Create a wrapper component in: - - `src/app/shared/components/fields` -- Use the same pattern as existing files like: - - `src/app/shared/components/fields/name.component.ts` - - `src/app/shared/components/fields/unit_price.component.ts` - -Minimal wrapper shape: - -ts -@Component({ - selector: 'field-example', - template: ``, - imports: [ReactiveFormsModule, InputComponent], -}) -export class ExampleComponent { - @Input({ required: true }) control = new FormControl(''); - @Input() name = 'example'; - @Input() label = 'Example'; -} - - ---- - -## Register New Fields - -Export the component from: -- `src/app/shared/components/fields/index.ts` - -Add its form control factory in: -- `src/app/shared/constants/fields/index.ts` - -`fieldControl` entry shape: -- key must match form control name -- return tuple: `[defaultValue, validators]` - -Example: - -ts -example: (value = '', isRequired = true): ControlConfig => [ - value, - isRequired ? [Validators.required] : [], -], - - ---- - -## Using Fields In Forms - -- In form group builders, use: - - `fieldControl.(initialValue, isRequired)` -- In templates, render matching wrapper component and pass matching control: - - `` -- For numeric/price behavior: - - use `app-input` - - use `type="number"` or `type="price"` - - optional `[fixed]` - ---- - -## List Component Configs - -Centralized list metadata is stored in: -- `src/app/shared/constants/list-configs/` - -Never store configs inside domain modules. - -### Structure - -Structure by data type, not domain: -- `good-list.const.ts` -- `sku-list.const.ts` -- `category-list.const.ts` - -### Requirements - -Each config implements `IListConfig`: -- `pageTitle` -- `addNewCtaLabel` -- `emptyPlaceholderTitle` -- `emptyPlaceholderDescription` -- `columns` - -### Usage - -ts -@Input() header: IColumn[] = goodListConfig.columns; -listConfig = goodListConfig; - - -### Rules - -- Any domain needing a list config imports from: - - `@/shared/constants/list-configs` -- No cross-domain dependencies. -- Do not duplicate configs. - ---- - -## Breadcrumb Usage in Stores & Views - -- Entity stores expose `breadcrumbItems` as a computed signal. -- Call `store.breadcrumbItems()` in view components and extend with current page: - -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. - ---- - -## Validation Checklist - -### TypeScript-only changes - -bash -pnpm -s exec tsc -p tsconfig.app.json --noEmit - - -### Docker/build changes - -bash -docker compose build app_default -docker compose build app_tis - - -### Validation Strategy - -- Start with targeted validation. -- Run broader checks only if needed. -- Validate only impacted tenants/features when possible. - ---- - -## Communication Expectations - -- Report exactly which files changed and why. -- Call out assumptions and discovered config mismatches. -- If validation is blocked: - - explain why - - provide next command -- Mention RTK searches/symbols used when relevant to implementation decisions. - ---- - -## Preferred Investigation Order - -Before editing code: - -1. RTK symbol search -2. Existing implementation discovery -3. Shared abstraction verification -4. Minimal scoped change -5. Targeted validation - -Avoid: -- broad exploratory reads -- repeated file dumping -- unnecessary context expansion - ---- - -## Angular Architecture Preferences - -- Prefer standalone components. -- Prefer signals/computed over legacy observable-only state when consistent with current architecture. -- Keep forms typed. -- Keep reusable UI in `shared`. -- Keep domain logic outside presentation components. -- Preserve existing tenant separation model. - ---- - -## Performance & AI Efficiency - -- Optimize for: - - minimal token usage - - minimal file reads - - scoped context retrieval - - reusable architectural understanding - -Prefer: -- RTK symbol search -- targeted reads -- incremental inspection - -Avoid: -- dumping full files -- repeated repository scans -- repeated searches for the same symbols -- unnecessary architectural exploration - -- RTK should be the primary repository understanding tool. +- tsc passed diff --git a/src/app/core/services/native-bridge.service.ts b/src/app/core/services/native-bridge.service.ts index 6be1a2d..94cc0b4 100644 --- a/src/app/core/services/native-bridge.service.ts +++ b/src/app/core/services/native-bridge.service.ts @@ -87,7 +87,7 @@ export class NativeBridgeService { window.NativeBridge.pay(request.amount, request.id || ''); return { success: true }; } catch (error) { - this.toastService.info({ text: (error as Error).message }); + // this.toastService.info({ text: (error as Error).message }); return { success: false, error: (error as Error).message }; } // const fn = window.NativeBridge.pay(123, 'test'); diff --git a/src/app/core/validators/greater.validator.ts b/src/app/core/validators/greater.validator.ts index 1de3bf7..8fb481d 100644 --- a/src/app/core/validators/greater.validator.ts +++ b/src/app/core/validators/greater.validator.ts @@ -11,6 +11,8 @@ export function greaterThanValidator(minValue: number): ValidatorFn { return null; } + console.log('v', v); + const numericValue = typeof v === 'number' ? v : Number(v); if (Number.isNaN(numericValue)) { return { greaterThan: 'مقدار باید عددی باشد' }; diff --git a/src/app/domains/pos/modules/shop/components/payloads/gold/form.component.html b/src/app/domains/pos/modules/shop/components/payloads/gold/form.component.html index 8d2deff..86bdce3 100644 --- a/src/app/domains/pos/modules/shop/components/payloads/gold/form.component.html +++ b/src/app/domains/pos/modules/shop/components/payloads/gold/form.component.html @@ -11,8 +11,7 @@ [minAmount]="0" [maxAmount]="unitWithQuantity()" name="wages" - label="اجرت" - /> + label="اجرت" /> {{ initialValues?.payload?.profit }} + label="حق‌العمل" /> + label="سود" /> + label="تخفیف"> + (onChange)="changeDiscountCalculation($event)" />
-
+
- + [taxAmount]="taxAmount()" /> +
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 6b4ff67..d6d229d 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 @@ -84,7 +84,7 @@ export class PosGoldPayloadFormComponent extends AbstractForm< const form = this.fb.group({ unit_price: [ this.initialValues?.unit_price || this.goldDefaultUnitPrice() || 0, - [Validators.required], + [Validators.required, greaterThanValidator(0)], ], quantity: [this.initialValues?.quantity || 0, [Validators.required, greaterThanValidator(0)]], discount: [this.initialValues?.discount || 0], diff --git a/src/app/domains/pos/modules/shop/components/payloads/standard/form.component.ts b/src/app/domains/pos/modules/shop/components/payloads/standard/form.component.ts index 8ed4a5e..cf343a7 100644 --- a/src/app/domains/pos/modules/shop/components/payloads/standard/form.component.ts +++ b/src/app/domains/pos/modules/shop/components/payloads/standard/form.component.ts @@ -1,4 +1,5 @@ import ISummary from '@/core/models/summary'; +import { greaterThanValidator } from '@/core/validators'; import { AbstractForm } from '@/shared/abstractClasses'; import { InputComponent } from '@/shared/components'; import { AmountPercentageInputComponent } from '@/shared/components/amountPercentageInput/amount-percentage-input.component'; @@ -32,8 +33,11 @@ export class PosStandardPayloadFormComponent extends AbstractForm< private readonly initialForm = () => { const form = this.fb.group({ - unit_price: [this.initialValues?.unit_price || 0, [Validators.required]], - quantity: [this.initialValues?.quantity || 0, [Validators.required]], + unit_price: [ + this.initialValues?.unit_price || 0, + [Validators.required, greaterThanValidator(0)], + ], + quantity: [this.initialValues?.quantity || 0, [Validators.required, greaterThanValidator(0)]], discount_amount: [this.initialValues?.discount || 0, [Validators.min(0)]], discount_percentage: [0, [Validators.min(0), Validators.max(100)]], }); diff --git a/src/app/domains/pos/modules/shop/components/payment/form-dialog.component.html b/src/app/domains/pos/modules/shop/components/payment/form-dialog.component.html index 0936793..e83aabd 100644 --- a/src/app/domains/pos/modules/shop/components/payment/form-dialog.component.html +++ b/src/app/domains/pos/modules/shop/components/payment/form-dialog.component.html @@ -4,8 +4,7 @@ [modal]="true" [style]="{ 'max-height': '90svh', height: 'auto' }" [closable]="true" - (onHide)="close()" -> + (onHide)="close()">
@@ -14,6 +13,15 @@
+
+ نوع تسویه‌حساب + +
+ (onChange)="changePayByTerminalSteps($event.value)" /> @for (terminalControl of terminalControls; track $index) { + [disabled]="payByTerminalSteps()[$index].payed"> @@ -55,27 +60,27 @@ name="cash" label="نقدی/ کارت‌خوان دیگر/ کارت به کارت" type="price" - [max]="cashMax()" - > + [max]="cashMax()"> - - - - - + @if (isGoldGuild()) { + + + + + + } + (onCancel)="close()" />
diff --git a/src/app/domains/pos/modules/shop/components/payment/form-dialog.component.ts b/src/app/domains/pos/modules/shop/components/payment/form-dialog.component.ts index 5d6f746..a3c1125 100644 --- a/src/app/domains/pos/modules/shop/components/payment/form-dialog.component.ts +++ b/src/app/domains/pos/modules/shop/components/payment/form-dialog.component.ts @@ -1,16 +1,18 @@ import { Maybe } from '@/core'; import { NativeBridgeService } from '@/core/services'; import { ToastService } from '@/core/services/toast.service'; +import { PosInfoStore } from '@/domains/pos/store'; import { AbstractFormDialog } from '@/shared/abstractClasses'; import { InputComponent, KeyValueComponent } from '@/shared/components'; import { SharedLightBottomsheetComponent } from '@/shared/components/dialog/light-bottomsheet.component'; import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component'; -import { UikitFieldComponent } from '@/uikit'; +import { UikitFieldComponent, UikitLabelComponent } from '@/uikit'; import { Component, computed, inject, signal } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { FormControl, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms'; import { ButtonDirective } from 'primeng/button'; import { Select } from 'primeng/select'; +import { SelectButton } from 'primeng/selectbutton'; import { catchError, throwError } from 'rxjs'; import { IPayment, @@ -36,15 +38,35 @@ import { PosLandingStore } from '../../store/main.store'; SharedLightBottomsheetComponent, Select, UikitFieldComponent, + SelectButton, + UikitLabelComponent, ], providers: [{ provide: PosPaymentBridgeAbstract, useExisting: PosPaymentBridgeService }], }) export class PosPaymentFormDialogComponent extends AbstractFormDialog { private readonly store = inject(PosLandingStore); + private readonly posInfo = inject(PosInfoStore); private readonly paymentBridge = inject(PosPaymentBridgeAbstract); private readonly nativeBridgeService = inject(NativeBridgeService); private readonly toastServices = inject(ToastService); + readonly settlementTypeItems = [ + { + label: 'نقدی', + value: 'CASH', + }, + { + label: 'نسیه', + value: 'CREDIT', + }, + { + label: 'نقدی / نسیه', + value: 'MIXED', + }, + ]; + + readonly selectedSettlementType = signal<'CASH' | 'CREDIT' | 'MIXED'>('CASH'); + readonly totalAmount = computed(() => this.store.orderPricingInfo().totalAmount); readonly remainedAmount = signal(this.totalAmount()); @@ -52,7 +74,8 @@ export class PosPaymentFormDialogComponent extends AbstractFormDialog this.store.submitOrderLoading()); + readonly submitOrderLoading = computed(() => this.store.submitOrderLoading()); + readonly isGoldGuild = computed(() => this.posInfo.entity()?.guild.code.toLowerCase() === 'gold'); payByTerminalSteps = signal( Array.from({ length: 10 }, (_, i) => ({ @@ -260,12 +283,16 @@ export class PosPaymentFormDialogComponent extends AbstractFormDialog 0) { - return this.toastServices.warn({ - text: 'فاکتور تسویه نشده است. لطفا مبلغ پرداخت را کامل کنید..', - }); + if (this.selectedSettlementType() === 'CASH') { + if (this.remainedAmount() > 0) { + return this.toastServices.warn({ + text: 'فاکتور تسویه نشده است. لطفا مبلغ پرداخت را کامل کنید..', + }); + } } + this.store.setSettlementType(this.selectedSettlementType()); + const rawPayment = this.form.getRawValue(); const terminalPayments = this.payByTerminalSteps() @@ -292,9 +319,9 @@ export class PosPaymentFormDialogComponent extends AbstractFormDialog - @if (showPayloadForm() && selectedGoodToAdd(); as selectedGood) { + @if (selectedGoodToAdd(); as selectedGood) { s.category.id === this.state$().activeGoodCategory); }); readonly filteredGoods = computed(() => - this.activatedCategoryGoods()?.filter((good) => good.name.includes(this.state$().searchQuery)), + this.activatedCategoryGoods()?.filter((good) => good.name.includes(this.state$().searchQuery)) ); readonly customer = computed(() => this.state$().customerDetails); @@ -251,6 +253,10 @@ export class PosLandingStore { this.setState({ payments }); } + setSettlementType(settlement_type: 'CASH' | 'CREDIT' | 'MIXED') { + this.setState({ settlement_type }); + } + submitOrder() { this.setState({ submitOrderLoading: true }); const orderPayload: IPosOrderRequest = { @@ -264,12 +270,13 @@ export class PosLandingStore { })), invoice_date: new Date().toISOString(), payments: this.payments(), + settlement_type: this.state$().settlement_type, total_amount: this.orderPricingInfo().totalAmount, }; - this.toastServices.info({ - text: `payment submitted: ${this.payments().terminals[0]?.customer_card_no || ''}`, - }); + // this.toastServices.info({ + // text: `payment submitted: ${this.payments().terminals[0]?.customer_card_no || ''}`, + // }); let res: Maybe = null; @@ -297,7 +304,7 @@ export class PosLandingStore { } return _res; - }), + }) ); } } diff --git a/src/app/domains/pos/modules/shop/views/root.component.html b/src/app/domains/pos/modules/shop/views/root.component.html index cdc5606..639084c 100644 --- a/src/app/domains/pos/modules/shop/views/root.component.html +++ b/src/app/domains/pos/modules/shop/views/root.component.html @@ -33,11 +33,9 @@
} - @if (showInvoiceBottomSheet()) { - - - - } + + + @if (isVisiblePaymentForm()) { diff --git a/src/app/shared/abstractClasses/abstract-form.ts b/src/app/shared/abstractClasses/abstract-form.ts index 7468ea6..e4a23a3 100644 --- a/src/app/shared/abstractClasses/abstract-form.ts +++ b/src/app/shared/abstractClasses/abstract-form.ts @@ -63,7 +63,7 @@ export abstract class AbstractForm< finalize(() => { this.submitLoading.set(false); // this.form.enable(); - }), + }) ) .subscribe((res) => { this.onSuccess(res); diff --git a/src/app/shared/components/dialog/light-bottomsheet.component.ts b/src/app/shared/components/dialog/light-bottomsheet.component.ts index a054657..91ac5ec 100644 --- a/src/app/shared/components/dialog/light-bottomsheet.component.ts +++ b/src/app/shared/components/dialog/light-bottomsheet.component.ts @@ -132,12 +132,16 @@ export class SharedLightBottomsheetComponent implements OnInit, OnDestroy, OnCha this.syncZIndex(); } if (changes['visible']) { + console.log('changed', this.visible); + this.toggleBodyScrollLock(this.visible); } } ngOnDestroy() { this.toggleBodyScrollLock(false); + console.log('destroyed'); + this.removeDrawer(); } @@ -167,6 +171,8 @@ export class SharedLightBottomsheetComponent implements OnInit, OnDestroy, OnCha } private toggleBodyScrollLock(locked: boolean) { + console.log('locked', locked); + const body = this.document.body; if (!body) return; @@ -182,8 +188,8 @@ export class SharedLightBottomsheetComponent implements OnInit, OnDestroy, OnCha return; } - this.renderer.setStyle(body, 'overflow', this.previousBodyOverflow); - this.renderer.setStyle(body, 'touch-action', this.previousBodyTouchAction); + this.renderer.removeStyle(body, 'overflow'); + this.renderer.removeStyle(body, 'touch-action'); this.previousBodyOverflow = ''; this.previousBodyTouchAction = ''; } diff --git a/src/app/shared/components/fields/quantity.component.ts b/src/app/shared/components/fields/quantity.component.ts index 9e739cb..4966087 100644 --- a/src/app/shared/components/fields/quantity.component.ts +++ b/src/app/shared/components/fields/quantity.component.ts @@ -4,10 +4,19 @@ import { InputComponent } from '../input/input.component'; @Component({ selector: 'field-quantity', - template: ``, + template: ``, imports: [ReactiveFormsModule, InputComponent], }) export class QuantityComponent { @Input({ required: true }) control = new FormControl(''); + @Input() min = 0; + @Input() max = undefined; @Input() name = 'quantity'; }