feat: enhance form components with validation and dynamic properties; update payment handling and UI interactions
This commit is contained in:
@@ -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 <file> -l aggressive`
|
||||
|
||||
---
|
||||
|
||||
# ANGULAR WORKFLOW
|
||||
|
||||
For components:
|
||||
|
||||
1. `rtk grep "<component-name>"`
|
||||
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 <file> -> rtk read <file>
|
||||
- grep <pattern> -> rtk grep <pattern>
|
||||
- rg <pattern> -> rtk grep <pattern>
|
||||
- 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 <file>
|
||||
|
||||
before:
|
||||
|
||||
- rtk read <file>
|
||||
|
||||
|
||||
For large files:
|
||||
|
||||
- rtk read <file> -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 <symbol | class | function | DTO | service>
|
||||
|
||||
|
||||
Examples:
|
||||
|
||||
bash
|
||||
rtk grep "InputComponent"
|
||||
rtk grep "fieldControl"
|
||||
rtk grep "breadcrumbItems"
|
||||
|
||||
|
||||
## 3. Read Only Necessary Files
|
||||
|
||||
bash
|
||||
rtk read <file>
|
||||
|
||||
|
||||
## 4. Large File Strategy
|
||||
|
||||
For files larger than ~300 lines:
|
||||
|
||||
bash
|
||||
rtk read <file> -l aggressive
|
||||
|
||||
|
||||
## 5. Fast Context Understanding
|
||||
|
||||
bash
|
||||
rtk smart <file>
|
||||
|
||||
|
||||
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 `<link rel="manifest">` 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: `<app-input [label]="label" [control]="control" [name]="name" type="simple" />`,
|
||||
imports: [ReactiveFormsModule, InputComponent],
|
||||
})
|
||||
export class ExampleComponent {
|
||||
@Input({ required: true }) control = new FormControl<string>('');
|
||||
@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.<key>(initialValue, isRequired)`
|
||||
- In templates, render matching wrapper component and pass matching control:
|
||||
- `<field-example [control]="form.controls.example" />`
|
||||
- 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
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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: 'مقدار باید عددی باشد' };
|
||||
|
||||
@@ -11,8 +11,7 @@
|
||||
[minAmount]="0"
|
||||
[maxAmount]="unitWithQuantity()"
|
||||
name="wages"
|
||||
label="اجرت"
|
||||
/>
|
||||
label="اجرت" />
|
||||
|
||||
{{ initialValues?.payload?.profit }}
|
||||
<app-amount-percentage-input
|
||||
@@ -22,8 +21,7 @@
|
||||
[minAmount]="0"
|
||||
[maxAmount]="unitWithQuantity()"
|
||||
name="commission"
|
||||
label="حقالعمل"
|
||||
/>
|
||||
label="حقالعمل" />
|
||||
<app-amount-percentage-input
|
||||
[percentageControl]="form.controls.payload.controls.profit_percentage"
|
||||
[amountControl]="form.controls.payload.controls.profit"
|
||||
@@ -31,8 +29,7 @@
|
||||
[minAmount]="0"
|
||||
[maxAmount]="totalAmountBeforeProfit()"
|
||||
name="profit"
|
||||
label="سود"
|
||||
/>
|
||||
label="سود" />
|
||||
</form>
|
||||
|
||||
<app-amount-percentage-input
|
||||
@@ -42,8 +39,7 @@
|
||||
[minAmount]="0"
|
||||
[maxAmount]="baseAmountForDiscountCalculation()"
|
||||
name="discount"
|
||||
label="تخفیف"
|
||||
>
|
||||
label="تخفیف">
|
||||
<ng-template #labelSuffix>
|
||||
<p-selectButton
|
||||
[options]="discountTypeItems"
|
||||
@@ -51,19 +47,17 @@
|
||||
[allowEmpty]="false"
|
||||
optionLabel="label"
|
||||
optionValue="value"
|
||||
(onChange)="changeDiscountCalculation($event)"
|
||||
/>
|
||||
(onChange)="changeDiscountCalculation($event)" />
|
||||
</ng-template>
|
||||
</app-amount-percentage-input>
|
||||
|
||||
<hr />
|
||||
<div class="flex flex-col gap-4 justify-center w-full">
|
||||
<div class="flex w-full flex-col justify-center gap-4">
|
||||
<pos-form-dialog-amount-card-template
|
||||
[totalAmount]="totalAmount()"
|
||||
[baseTotalAmount]="baseTotalAmount()"
|
||||
[discountAmount]="form.controls.discount.value || 0"
|
||||
[taxAmount]="taxAmount()"
|
||||
/>
|
||||
<button pButton class="sm:w-auto w-full" (click)="submit()">{{ preparedCTAText() }}</button>
|
||||
[taxAmount]="taxAmount()" />
|
||||
<button pButton class="w-full sm:w-auto" (click)="submit()">{{ preparedCTAText() }}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -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],
|
||||
|
||||
@@ -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)]],
|
||||
});
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
[modal]="true"
|
||||
[style]="{ 'max-height': '90svh', height: 'auto' }"
|
||||
[closable]="true"
|
||||
(onHide)="close()"
|
||||
>
|
||||
(onHide)="close()">
|
||||
<div class="form-group">
|
||||
<form [formGroup]="form" (submit)="submit()">
|
||||
<app-key-value label="مبلغ قابل پرداخت" [value]="totalAmount().toString()" type="price" />
|
||||
@@ -14,6 +13,15 @@
|
||||
</form>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="flex items-center justify-between gap-3">
|
||||
<uikit-label> نوع تسویهحساب </uikit-label>
|
||||
<p-selectButton
|
||||
[options]="settlementTypeItems"
|
||||
[(ngModel)]="selectedSettlementType"
|
||||
[allowEmpty]="false"
|
||||
optionLabel="label"
|
||||
optionValue="value" />
|
||||
</div>
|
||||
<uikit-field label="تعداد مراحل پرداخت با پوز" name="pay_by_terminal_steps">
|
||||
<p-select
|
||||
[options]="payByTerminalSteps()"
|
||||
@@ -23,8 +31,7 @@
|
||||
optionValue="value"
|
||||
[disabled]="!remainedAmount()"
|
||||
class="w-full"
|
||||
(onChange)="changePayByTerminalSteps($event.value)"
|
||||
/>
|
||||
(onChange)="changePayByTerminalSteps($event.value)" />
|
||||
</uikit-field>
|
||||
@for (terminalControl of terminalControls; track $index) {
|
||||
<app-input
|
||||
@@ -33,16 +40,14 @@
|
||||
[label]="$index === 0 ? 'پرداخت با پوز' : 'پرداخت با پوز - مرحله ' + ($index + 1)"
|
||||
type="price"
|
||||
[max]="terminalsMax()[$index]"
|
||||
[disabled]="payByTerminalSteps()[$index].payed"
|
||||
>
|
||||
[disabled]="payByTerminalSteps()[$index].payed">
|
||||
<ng-template #suffixTemp>
|
||||
<button
|
||||
pButton
|
||||
size="small"
|
||||
type="button"
|
||||
[disabled]="!remainedAmount() || payByTerminalSteps()[$index].payed"
|
||||
(click)="fillTerminalRemained($index)"
|
||||
>
|
||||
(click)="fillTerminalRemained($index)">
|
||||
باقیمانده مبلغ
|
||||
</button>
|
||||
</ng-template>
|
||||
@@ -55,27 +60,27 @@
|
||||
name="cash"
|
||||
label="نقدی/ کارتخوان دیگر/ کارت به کارت"
|
||||
type="price"
|
||||
[max]="cashMax()"
|
||||
>
|
||||
[max]="cashMax()">
|
||||
<ng-template #suffixTemp>
|
||||
<button pButton size="small" type="button" [disabled]="!remainedAmount()" (click)="fillRemained('CASH')">
|
||||
باقیمانده مبلغ
|
||||
</button>
|
||||
</ng-template>
|
||||
</app-input>
|
||||
<app-input [control]="form.controls.set_off" name="setOff" label="تهاتر" type="price" [max]="setOffMax()">
|
||||
<ng-template #suffixTemp>
|
||||
<button pButton size="small" type="button" [disabled]="!remainedAmount()" (click)="fillRemained('SET_OFF')">
|
||||
باقیمانده مبلغ
|
||||
</button>
|
||||
</ng-template>
|
||||
</app-input>
|
||||
@if (isGoldGuild()) {
|
||||
<app-input [control]="form.controls.set_off" name="setOff" label="تهاتر" type="price" [max]="setOffMax()">
|
||||
<ng-template #suffixTemp>
|
||||
<button pButton size="small" type="button" [disabled]="!remainedAmount()" (click)="fillRemained('SET_OFF')">
|
||||
باقیمانده مبلغ
|
||||
</button>
|
||||
</ng-template>
|
||||
</app-input>
|
||||
}
|
||||
|
||||
<app-form-footer-actions
|
||||
submitLabel="ثبت اطلاعات پرداخت و ادامه"
|
||||
[loading]="submitOrderLoading()"
|
||||
(onCancel)="close()"
|
||||
/>
|
||||
(onCancel)="close()" />
|
||||
</form>
|
||||
</div>
|
||||
</shared-light-bottomsheet>
|
||||
|
||||
@@ -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<IPayment, IPosOrderResponse> {
|
||||
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<IPayment,
|
||||
cashMax = signal(this.remainedAmount());
|
||||
terminalsMax = signal([this.remainedAmount()]);
|
||||
|
||||
submitOrderLoading = computed(() => 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<IPayment,
|
||||
}
|
||||
|
||||
override submitForm() {
|
||||
if (this.remainedAmount() > 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<IPayment,
|
||||
const terminalPaymentIsDone = this.checkTerminalPayments();
|
||||
|
||||
if (terminalPaymentIsDone) {
|
||||
this.toastService.info({
|
||||
text: `terminalPayments[0].customer_card_no: ${payment.terminals[0]?.customer_card_no || ''}`,
|
||||
});
|
||||
// this.toastService.info({
|
||||
// text: `terminalPayments[0].customer_card_no: ${payment.terminals[0]?.customer_card_no || ''}`,
|
||||
// });
|
||||
this.store
|
||||
.submitOrder()
|
||||
.pipe(
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
}
|
||||
</div>
|
||||
|
||||
@if (showPayloadForm() && selectedGoodToAdd(); as selectedGood) {
|
||||
@if (selectedGoodToAdd(); as selectedGood) {
|
||||
<pos-payload-form-dialog
|
||||
[(visible)]="showPayloadForm"
|
||||
[good]="selectedGood"
|
||||
|
||||
@@ -8,6 +8,7 @@ import { IPosOrderItem } from './types';
|
||||
export interface IPosOrderRequest {
|
||||
total_amount: number;
|
||||
invoice_date: string;
|
||||
settlement_type: 'CASH' | 'CREDIT' | 'MIXED';
|
||||
payments: IPayment;
|
||||
items: IPosOrderItem[];
|
||||
notes?: string;
|
||||
|
||||
@@ -29,6 +29,7 @@ interface IPosLandingState {
|
||||
};
|
||||
invoiceDate: string;
|
||||
orderNote?: string;
|
||||
settlement_type: 'CASH' | 'CREDIT' | 'MIXED';
|
||||
payments: IPayment;
|
||||
submitOrderLoading: boolean;
|
||||
}
|
||||
@@ -42,6 +43,7 @@ export const INITIAL_POS_STATE: IPosLandingState = {
|
||||
inOrderGoods: [],
|
||||
viewType: 'grid',
|
||||
searchQuery: '',
|
||||
settlement_type: 'CASH',
|
||||
customerDetails: {
|
||||
type: CustomerType.UNKNOWN,
|
||||
},
|
||||
@@ -81,7 +83,7 @@ export class PosLandingStore {
|
||||
return this.state$().goods?.filter((s) => 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<IPosOrderResponse> = null;
|
||||
|
||||
@@ -297,7 +304,7 @@ export class PosLandingStore {
|
||||
}
|
||||
|
||||
return _res;
|
||||
}),
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,11 +33,9 @@
|
||||
</div>
|
||||
</button>
|
||||
}
|
||||
@if (showInvoiceBottomSheet()) {
|
||||
<shared-light-bottomsheet [(visible)]="showInvoiceBottomSheet" [modal]="true" [closable]="true" header="فاکتور">
|
||||
<pos-order-section (onAddMoreGoods)="closeInvoiceBottomSheet()" (onSubmit)="submitOrder()" />
|
||||
</shared-light-bottomsheet>
|
||||
}
|
||||
<shared-light-bottomsheet [(visible)]="showInvoiceBottomSheet" [closable]="true" header="جزییات فاکتور">
|
||||
<pos-order-section (onAddMoreGoods)="closeInvoiceBottomSheet()" (onSubmit)="submitOrder()" />
|
||||
</shared-light-bottomsheet>
|
||||
|
||||
@if (isVisiblePaymentForm()) {
|
||||
<pos-payment-form-dialog [(visible)]="isVisiblePaymentForm" (onSubmit)="submitPayment($event)" />
|
||||
|
||||
@@ -63,7 +63,7 @@ export abstract class AbstractForm<
|
||||
finalize(() => {
|
||||
this.submitLoading.set(false);
|
||||
// this.form.enable();
|
||||
}),
|
||||
})
|
||||
)
|
||||
.subscribe((res) => {
|
||||
this.onSuccess(res);
|
||||
|
||||
@@ -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 = '';
|
||||
}
|
||||
|
||||
@@ -4,10 +4,19 @@ import { InputComponent } from '../input/input.component';
|
||||
|
||||
@Component({
|
||||
selector: 'field-quantity',
|
||||
template: `<app-input label="مقدار" [control]="control" [name]="name" type="number" />`,
|
||||
template: `<app-input
|
||||
label="مقدار"
|
||||
[control]="control"
|
||||
[name]="name"
|
||||
type="number"
|
||||
[min]="min"
|
||||
[max]="max"
|
||||
/>`,
|
||||
imports: [ReactiveFormsModule, InputComponent],
|
||||
})
|
||||
export class QuantityComponent {
|
||||
@Input({ required: true }) control = new FormControl<string>('');
|
||||
@Input() min = 0;
|
||||
@Input() max = undefined;
|
||||
@Input() name = 'quantity';
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user