feat: update environment configurations and add gold price management module with related components and services
This commit is contained in:
@@ -1,2 +1,9 @@
|
|||||||
TENANT=default
|
TENANT=default
|
||||||
DIST_DIR=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
|
||||||
|
|||||||
@@ -3,3 +3,10 @@ DIST_DIR=tis
|
|||||||
# TIS_BUILD_DATE=
|
# TIS_BUILD_DATE=
|
||||||
# TIS_APP_VERSION=
|
# TIS_APP_VERSION=
|
||||||
# TIS_BUILD_NUMBER=
|
# 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
|
||||||
|
|||||||
+1
-2
@@ -42,5 +42,4 @@ testem.log
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
Thumbs.db
|
Thumbs.db
|
||||||
|
|
||||||
|
.env.*
|
||||||
/false
|
|
||||||
|
|||||||
@@ -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)
|
# ✅ RTK-FIRST RULES (STRICT)
|
||||||
|
|
||||||
Always prefer RTK commands for repository inspection, navigation, and code understanding.
|
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:
|
Prefer:
|
||||||
|
|
||||||
```bash
|
- git status -> rtk git status
|
||||||
git status -> rtk git status
|
- git diff -> rtk git diff
|
||||||
git diff -> rtk git diff
|
- git log -> rtk git log
|
||||||
git log -> rtk git log
|
- ls -> rtk ls
|
||||||
ls -> rtk ls
|
- tree -> rtk ls
|
||||||
tree -> rtk ls
|
- cat <file> -> rtk read <file>
|
||||||
cat <file> -> rtk read <file>
|
- grep <pattern> -> rtk grep <pattern>
|
||||||
grep <pattern> -> rtk grep <pattern>
|
- rg <pattern> -> rtk grep <pattern>
|
||||||
rg <pattern> -> rtk grep <pattern>
|
- find -> rtk find
|
||||||
find -> rtk find
|
|
||||||
```
|
|
||||||
|
|
||||||
Avoid raw commands for repository inspection when RTK equivalents exist.
|
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 <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)
|
# 📂 CODE NAVIGATION WORKFLOW (MANDATORY)
|
||||||
|
|
||||||
Follow this order when working in the repository.
|
Follow this order when working in the repository.
|
||||||
|
|
||||||
## 1. Discover Structure
|
## 1. Discover Structure
|
||||||
|
|
||||||
```bash
|
bash
|
||||||
rtk ls
|
rtk ls
|
||||||
```
|
|
||||||
|
|
||||||
## 2. Search Before Opening Files
|
## 2. Search Before Opening Files
|
||||||
|
|
||||||
```bash
|
bash
|
||||||
rtk grep <symbol | class | function | DTO | service>
|
rtk grep <symbol | class | function | DTO | service>
|
||||||
```
|
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
|
|
||||||
```bash
|
bash
|
||||||
rtk grep "InputComponent"
|
rtk grep "InputComponent"
|
||||||
rtk grep "fieldControl"
|
rtk grep "fieldControl"
|
||||||
rtk grep "breadcrumbItems"
|
rtk grep "breadcrumbItems"
|
||||||
```
|
|
||||||
|
|
||||||
## 3. Read Only Necessary Files
|
## 3. Read Only Necessary Files
|
||||||
|
|
||||||
```bash
|
bash
|
||||||
rtk read <file>
|
rtk read <file>
|
||||||
```
|
|
||||||
|
|
||||||
## 4. Large File Strategy
|
## 4. Large File Strategy
|
||||||
|
|
||||||
For files larger than ~300 lines:
|
For files larger than ~300 lines:
|
||||||
|
|
||||||
```bash
|
bash
|
||||||
rtk read <file> -l aggressive
|
rtk read <file> -l aggressive
|
||||||
```
|
|
||||||
|
|
||||||
## 5. Fast Context Understanding
|
## 5. Fast Context Understanding
|
||||||
|
|
||||||
```bash
|
bash
|
||||||
rtk smart <file>
|
rtk smart <file>
|
||||||
```
|
|
||||||
|
|
||||||
Never:
|
Never:
|
||||||
- open many files blindly
|
- open many files blindly
|
||||||
@@ -101,21 +398,21 @@ Never:
|
|||||||
|
|
||||||
For repository changes use:
|
For repository changes use:
|
||||||
|
|
||||||
```bash
|
bash
|
||||||
rtk git diff
|
rtk git diff
|
||||||
```
|
|
||||||
|
|
||||||
For large diffs:
|
For large diffs:
|
||||||
|
|
||||||
```bash
|
bash
|
||||||
rtk git diff -l aggressive
|
rtk git diff -l aggressive
|
||||||
```
|
|
||||||
|
|
||||||
Avoid raw:
|
Avoid raw:
|
||||||
|
|
||||||
```bash
|
bash
|
||||||
git diff
|
git diff
|
||||||
```
|
|
||||||
|
|
||||||
unless RTK diff is unavailable.
|
unless RTK diff is unavailable.
|
||||||
|
|
||||||
@@ -125,7 +422,7 @@ unless RTK diff is unavailable.
|
|||||||
|
|
||||||
Avoid loading:
|
Avoid loading:
|
||||||
|
|
||||||
```txt
|
|
||||||
dist/
|
dist/
|
||||||
.angular/
|
.angular/
|
||||||
coverage/
|
coverage/
|
||||||
@@ -133,7 +430,7 @@ node_modules/
|
|||||||
.git/
|
.git/
|
||||||
.cache/
|
.cache/
|
||||||
.tmp/
|
.tmp/
|
||||||
```
|
|
||||||
|
|
||||||
unless explicitly required.
|
unless explicitly required.
|
||||||
|
|
||||||
@@ -169,12 +466,12 @@ unless explicitly required.
|
|||||||
|
|
||||||
Example workflow:
|
Example workflow:
|
||||||
|
|
||||||
```bash
|
bash
|
||||||
rtk index
|
rtk index
|
||||||
rtk search "InputComponent number normalization"
|
rtk search "InputComponent number normalization"
|
||||||
rtk search "fieldControl"
|
rtk search "fieldControl"
|
||||||
rtk refs "goodListConfig"
|
rtk refs "goodListConfig"
|
||||||
```
|
|
||||||
|
|
||||||
## Token Reduction Rules
|
## Token Reduction Rules
|
||||||
|
|
||||||
@@ -196,43 +493,43 @@ rtk refs "goodListConfig"
|
|||||||
|
|
||||||
### Angular Components
|
### Angular Components
|
||||||
|
|
||||||
```bash
|
bash
|
||||||
rtk search "selector: 'field-"
|
rtk search "selector: 'field-"
|
||||||
rtk search "standalone: true"
|
rtk search "standalone: true"
|
||||||
rtk search "InputComponent"
|
rtk search "InputComponent"
|
||||||
```
|
|
||||||
|
|
||||||
### Forms & Controls
|
### Forms & Controls
|
||||||
|
|
||||||
```bash
|
bash
|
||||||
rtk search "fieldControl."
|
rtk search "fieldControl."
|
||||||
rtk search "ControlConfig"
|
rtk search "ControlConfig"
|
||||||
rtk search "Validators.required"
|
rtk search "Validators.required"
|
||||||
```
|
|
||||||
|
|
||||||
### Stores & Signals
|
### Stores & Signals
|
||||||
|
|
||||||
```bash
|
bash
|
||||||
rtk search "breadcrumbItems"
|
rtk search "breadcrumbItems"
|
||||||
rtk search "computed("
|
rtk search "computed("
|
||||||
rtk search "signal("
|
rtk search "signal("
|
||||||
```
|
|
||||||
|
|
||||||
### Tenant / Docker
|
### Tenant / Docker
|
||||||
|
|
||||||
```bash
|
bash
|
||||||
rtk search "DIST_DIR"
|
rtk search "DIST_DIR"
|
||||||
rtk search "configuration tis"
|
rtk search "configuration tis"
|
||||||
rtk search "prebuild:tis"
|
rtk search "prebuild:tis"
|
||||||
```
|
|
||||||
|
|
||||||
### List Configs
|
### List Configs
|
||||||
|
|
||||||
```bash
|
bash
|
||||||
rtk search "IListConfig"
|
rtk search "IListConfig"
|
||||||
rtk search "columns:"
|
rtk search "columns:"
|
||||||
rtk search "goodListConfig"
|
rtk search "goodListConfig"
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -340,7 +637,7 @@ Additional rule:
|
|||||||
|
|
||||||
Minimal wrapper shape:
|
Minimal wrapper shape:
|
||||||
|
|
||||||
```ts
|
ts
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'field-example',
|
selector: 'field-example',
|
||||||
template: `<app-input [label]="label" [control]="control" [name]="name" type="simple" />`,
|
template: `<app-input [label]="label" [control]="control" [name]="name" type="simple" />`,
|
||||||
@@ -351,7 +648,7 @@ export class ExampleComponent {
|
|||||||
@Input() name = 'example';
|
@Input() name = 'example';
|
||||||
@Input() label = 'Example';
|
@Input() label = 'Example';
|
||||||
}
|
}
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -369,12 +666,12 @@ Add its form control factory in:
|
|||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
```ts
|
ts
|
||||||
example: (value = '', isRequired = true): ControlConfig => [
|
example: (value = '', isRequired = true): ControlConfig => [
|
||||||
value,
|
value,
|
||||||
isRequired ? [Validators.required] : [],
|
isRequired ? [Validators.required] : [],
|
||||||
],
|
],
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -416,10 +713,10 @@ Each config implements `IListConfig`:
|
|||||||
|
|
||||||
### Usage
|
### Usage
|
||||||
|
|
||||||
```ts
|
ts
|
||||||
@Input() header: IColumn[] = goodListConfig.columns;
|
@Input() header: IColumn[] = goodListConfig.columns;
|
||||||
listConfig = goodListConfig;
|
listConfig = goodListConfig;
|
||||||
```
|
|
||||||
|
|
||||||
### Rules
|
### Rules
|
||||||
|
|
||||||
@@ -435,14 +732,14 @@ listConfig = goodListConfig;
|
|||||||
- Entity stores expose `breadcrumbItems` as a computed signal.
|
- Entity stores expose `breadcrumbItems` as a computed signal.
|
||||||
- Call `store.breadcrumbItems()` in view components and extend with current page:
|
- Call `store.breadcrumbItems()` in view components and extend with current page:
|
||||||
|
|
||||||
```ts
|
ts
|
||||||
setBreadcrumb() {
|
setBreadcrumb() {
|
||||||
this.breadcrumbService.setItems([
|
this.breadcrumbService.setItems([
|
||||||
...this.store.breadcrumbItems(),
|
...this.store.breadcrumbItems(),
|
||||||
{ title: 'Current Page' },
|
{ title: 'Current Page' },
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
```
|
|
||||||
|
|
||||||
- Root page breadcrumbs are set in the store's `getData()` method once entity is loaded.
|
- Root page breadcrumbs are set in the store's `getData()` method once entity is loaded.
|
||||||
|
|
||||||
@@ -452,16 +749,16 @@ setBreadcrumb() {
|
|||||||
|
|
||||||
### TypeScript-only changes
|
### TypeScript-only changes
|
||||||
|
|
||||||
```bash
|
bash
|
||||||
pnpm -s exec tsc -p tsconfig.app.json --noEmit
|
pnpm -s exec tsc -p tsconfig.app.json --noEmit
|
||||||
```
|
|
||||||
|
|
||||||
### Docker/build changes
|
### Docker/build changes
|
||||||
|
|
||||||
```bash
|
bash
|
||||||
docker compose build app_default
|
docker compose build app_default
|
||||||
docker compose build app_tis
|
docker compose build app_tis
|
||||||
```
|
|
||||||
|
|
||||||
### Validation Strategy
|
### Validation Strategy
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<app-card-data cardTitle="قیمت پیشفرض هر گرم طلا" class="w-full">
|
||||||
|
<p-message variant="text" severity="info" icon="pi pi-info-circle">
|
||||||
|
در این بخش میتوانید قیمت پیشفرض هر گرم طلا را وارد کنید.
|
||||||
|
</p-message>
|
||||||
|
<form [formGroup]="form" class="mt-6" (submit)="submit()">
|
||||||
|
<field-unit-price [control]="form.controls.price" />
|
||||||
|
<app-form-footer-actions (onCancel)="close()" />
|
||||||
|
</form>
|
||||||
|
</app-card-data>
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export type IPosConfigGoldPriceResponse = number;
|
||||||
|
|
||||||
|
export type IPosConfigGoldPricePayload = number;
|
||||||
@@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,11 @@
|
|||||||
<div class="flex h-full w-full items-center justify-center p-4">
|
<div class="flex h-full w-full flex-col items-center justify-center gap-6 p-4">
|
||||||
|
@if (info()?.guild!.code.toLocaleLowerCase() === 'gold') {
|
||||||
|
<pos-config-gold-price-form class="w-full" (onClose)="returnToMainPage()" />
|
||||||
|
}
|
||||||
<app-card-data cardTitle="تنظیمات فاکتور" class="w-full">
|
<app-card-data cardTitle="تنظیمات فاکتور" class="w-full">
|
||||||
<p-message variant="text" severity="info" icon="pi pi-info-circle">
|
<p-message variant="text" severity="info" icon="pi pi-info-circle">
|
||||||
هریک از موارد زیر را که میخواهید در چاپ فاکتور نمایش داده شوند را انتخاب کنید
|
هریک از موارد زیر را که میخواهید در چاپ فاکتور نمایش داده شوند را انتخاب کنید
|
||||||
</p-message>
|
</p-message>
|
||||||
<pos-config-print-form class="mt-6 block w-full" (onClose)="returnToMainPage()" (onSubmit)="returnToMainPage()" />
|
<pos-config-print-form class="mt-6 block w-full" (onClose)="returnToMainPage()" />
|
||||||
</app-card-data>
|
</app-card-data>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,17 +1,27 @@
|
|||||||
|
import { PosInfoStore } from '@/domains/pos/store';
|
||||||
import { AppCardComponent } from '@/shared/components';
|
import { AppCardComponent } from '@/shared/components';
|
||||||
import { UikitFlatpickrJalaliComponent } from '@/uikit';
|
import { Component, computed, inject } from '@angular/core';
|
||||||
import { Component, inject } from '@angular/core';
|
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import { Message } from 'primeng/message';
|
import { Message } from 'primeng/message';
|
||||||
|
import { PosConfigGoldPriceFormComponent } from '../components/goldPrice/form.component';
|
||||||
import { PosConfigPrintFormComponent } from '../components/print/form.component';
|
import { PosConfigPrintFormComponent } from '../components/print/form.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'pos-config-page',
|
selector: 'pos-config-page',
|
||||||
templateUrl: './root.component.html',
|
templateUrl: './root.component.html',
|
||||||
imports: [PosConfigPrintFormComponent, AppCardComponent, Message, UikitFlatpickrJalaliComponent],
|
imports: [
|
||||||
|
PosConfigPrintFormComponent,
|
||||||
|
AppCardComponent,
|
||||||
|
Message,
|
||||||
|
PosConfigGoldPriceFormComponent,
|
||||||
|
],
|
||||||
})
|
})
|
||||||
export class PosConfigPageComponent {
|
export class PosConfigPageComponent {
|
||||||
private readonly router = inject(Router);
|
private readonly router = inject(Router);
|
||||||
|
private readonly posInfoStore = inject(PosInfoStore);
|
||||||
|
|
||||||
|
readonly info = computed(() => this.posInfoStore.entity());
|
||||||
|
|
||||||
returnToMainPage() {
|
returnToMainPage() {
|
||||||
this.router.navigateByUrl('/');
|
this.router.navigateByUrl('/');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,19 @@
|
|||||||
<div class="flex flex-col min-h-full">
|
<div class="flex min-h-full flex-col">
|
||||||
<div
|
<div class="bg-surface-card border-surface-border z-10 rounded-b-xl border-b py-4">
|
||||||
class="bg-surface-card z-10 py-4 border-b border-surface-border shadow-[0_-4px_16px_rgba(0,0,0,0.08)] rounded-b-xl"
|
<div class="mb-4 flex items-center justify-end px-4">
|
||||||
>
|
|
||||||
<div class="flex items-center justify-end mb-4 px-4">
|
|
||||||
<!-- <div class="flex items-center gap-2 text-muted-color">
|
<!-- <div class="flex items-center gap-2 text-muted-color">
|
||||||
<i class="pi pi-list"></i>
|
<i class="pi pi-list"></i>
|
||||||
<span class="text-base font-bold">لیست کالاها</span>
|
<span class="text-base font-bold">لیست کالاها</span>
|
||||||
</div> -->
|
</div> -->
|
||||||
<div class="flex items-center gap-2 w-full">
|
<div class="flex w-full items-center gap-2">
|
||||||
<app-search-input class="sm:w-auto w-full max-w-xs" (search)="onSearch($event)"></app-search-input>
|
<app-search-input class="w-full max-w-xs sm:w-auto" (search)="onSearch($event)"></app-search-input>
|
||||||
<p-divider layout="vertical" />
|
<p-divider layout="vertical" />
|
||||||
<p-selectbutton
|
<p-selectbutton
|
||||||
[options]="viewOptions"
|
[options]="viewOptions"
|
||||||
[(ngModel)]="viewType"
|
[(ngModel)]="viewType"
|
||||||
optionValue="value"
|
optionValue="value"
|
||||||
[allowEmpty]="false"
|
[allowEmpty]="false"
|
||||||
class="shrink-0 border border-surface-border bg-surface-ground"
|
class="border-surface-border bg-surface-ground shrink-0 border">
|
||||||
>
|
|
||||||
<ng-template #item let-item>
|
<ng-template #item let-item>
|
||||||
<i [class]="item.icon"></i>
|
<i [class]="item.icon"></i>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
@@ -25,13 +22,13 @@
|
|||||||
</div>
|
</div>
|
||||||
<pos-good-categories (categoryChange)="onCategoryChange($event)" />
|
<pos-good-categories (categoryChange)="onCategoryChange($event)" />
|
||||||
</div>
|
</div>
|
||||||
<div [class]="`p-4 ${!loading() && !goods()?.length ? ' grow flex items-center justify-center' : ''}`">
|
<div [class]="`p-4 ${!loading() && !goods()?.length ? ' flex grow items-center justify-center' : ''}`">
|
||||||
@if (!loading() && !goods()?.length) {
|
@if (!loading() && !goods()?.length) {
|
||||||
<div class="flex flex-col items-center gap-4 mt-10">
|
<div class="mt-10 flex flex-col items-center gap-4">
|
||||||
<i class="pi pi-box text-6xl! text-muted-color"></i>
|
<i class="pi pi-box text-muted-color text-6xl!"></i>
|
||||||
<span class="text-xl font-semibold text-muted-color">کالایی برای نمایش وجود ندارد.</span>
|
<span class="text-muted-color text-xl font-semibold">کالایی برای نمایش وجود ندارد.</span>
|
||||||
</div>
|
</div>
|
||||||
} @else if (viewType === "grid") {
|
} @else if (viewType === 'grid') {
|
||||||
<pos-good-grid-view (onAdd)="addGood($event)" />
|
<pos-good-grid-view (onAdd)="addGood($event)" />
|
||||||
} @else {
|
} @else {
|
||||||
<pos-goods-list-view (onAdd)="addGood($event)" />
|
<pos-goods-list-view (onAdd)="addGood($event)" />
|
||||||
|
|||||||
@@ -1,28 +1,27 @@
|
|||||||
<div class="grid 2xl:grid-cols-8 xl:grid-cols-6 lg:grid-cols-4 grid-cols-2 gap-3 flex-wrap">
|
<div class="grid grid-cols-2 flex-wrap gap-3 lg:grid-cols-4 xl:grid-cols-6 2xl:grid-cols-8">
|
||||||
@if (loading()) {
|
@if (loading()) {
|
||||||
@for (_ of [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15]; track $index) {
|
@for (_ of [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15]; track $index) {
|
||||||
<div class="flex-1 aspect-[0.9]">
|
<div class="aspect-[0.9] flex-1">
|
||||||
<p-skeleton width="100%" height="100%"></p-skeleton>
|
<p-skeleton width="100%" height="100%"></p-skeleton>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
} @else {
|
} @else {
|
||||||
@for (good of visibleGoods(); track good.id) {
|
@for (good of visibleGoods(); track good.id) {
|
||||||
<div class="flex-1 bg-surface-card rounded-lg p-1 shadow-xs flex flex-col">
|
<div class="bg-surface-card border-surface-border flex flex-1 flex-col rounded-lg border p-1">
|
||||||
<img
|
<img
|
||||||
[src]="good.image_url || goodPlaceholder"
|
[src]="good.image_url || goodPlaceholder"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
decoding="async"
|
decoding="async"
|
||||||
class="w-full aspect-[1.3] object-cover rounded-md shrink-0"
|
class="aspect-[1.3] w-full shrink-0 rounded-md object-cover" />
|
||||||
/>
|
<div class="mt-2 flex grow flex-col justify-between text-center">
|
||||||
<div class="mt-2 text-center grow flex flex-col justify-between">
|
<span class="flex w-full grow items-center justify-center text-center text-lg font-bold">
|
||||||
<span class="text-lg font-bold text-center grow flex items-center justify-center w-full">
|
|
||||||
{{ good.name }}
|
{{ good.name }}
|
||||||
@if (!good.is_default_guild_good) {
|
@if (!good.is_default_guild_good) {
|
||||||
<small class="text-xs text-muted-color">*</small>
|
<small class="text-muted-color text-xs">*</small>
|
||||||
}
|
}
|
||||||
</span>
|
</span>
|
||||||
<div class="flex items-center justify-between mt-1 w-full px-2 my-2 shrink-0 gap-1">
|
<div class="my-2 mt-1 flex w-full shrink-0 items-center justify-between gap-1 px-2">
|
||||||
<button pButton type="button" label="انتخاب" class="grow w-full" (click)="addProduct(good)"></button>
|
<button pButton type="button" label="انتخاب" class="w-full grow" (click)="addProduct(good)"></button>
|
||||||
<pos-good-favorite-cta [isFavorite]="good.is_favorite" [id]="good.id" />
|
<pos-good-favorite-cta [isFavorite]="good.is_favorite" [id]="good.id" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -31,7 +30,7 @@
|
|||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
@if (!loading() && hasMore()) {
|
@if (!loading() && hasMore()) {
|
||||||
<div class="flex justify-center mt-3">
|
<div class="mt-3 flex justify-center">
|
||||||
<button pButton type="button" outlined (click)="loadMore()">نمایش کالاهای بیشتر</button>
|
<button pButton type="button" outlined (click)="loadMore()">نمایش کالاهای بیشتر</button>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
import { greaterThanValidator } from '@/core/validators/greater.validator';
|
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 { AbstractForm } from '@/shared/abstractClasses';
|
||||||
import { EnumSelectComponent } from '@/shared/apiEnum/select.component';
|
import { EnumSelectComponent } from '@/shared/apiEnum/select.component';
|
||||||
import { InputComponent } from '@/shared/components';
|
import { InputComponent } from '@/shared/components';
|
||||||
import { AmountPercentageInputComponent } from '@/shared/components/amountPercentageInput/amount-percentage-input.component';
|
import { AmountPercentageInputComponent } from '@/shared/components/amountPercentageInput/amount-percentage-input.component';
|
||||||
import { TGoldKarat } from '@/shared/localEnum/constants/goldKarat';
|
import { TGoldKarat } from '@/shared/localEnum/constants/goldKarat';
|
||||||
import { formatWithCurrency } from '@/utils';
|
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 { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||||
import { FormsModule, ReactiveFormsModule, Validators } from '@angular/forms';
|
import { FormsModule, ReactiveFormsModule, Validators } from '@angular/forms';
|
||||||
import { ButtonDirective } from 'primeng/button';
|
import { ButtonDirective } from 'primeng/button';
|
||||||
@@ -47,6 +48,8 @@ export class PosGoldPayloadFormComponent extends AbstractForm<
|
|||||||
@Output() onSubmitForm = new EventEmitter<IGoldPayload>();
|
@Output() onSubmitForm = new EventEmitter<IGoldPayload>();
|
||||||
@Output() onChangeTotalAmount = new EventEmitter<number>();
|
@Output() onChangeTotalAmount = new EventEmitter<number>();
|
||||||
|
|
||||||
|
private readonly posGoldConfig = inject(PosConfigGoldPriceService);
|
||||||
|
|
||||||
readonly discountTypeItems = [
|
readonly discountTypeItems = [
|
||||||
{
|
{
|
||||||
label: 'از سود',
|
label: 'از سود',
|
||||||
@@ -66,11 +69,20 @@ export class PosGoldPayloadFormComponent extends AbstractForm<
|
|||||||
taxAmount = signal<number>(0);
|
taxAmount = signal<number>(0);
|
||||||
totalAmount = signal<number>(0);
|
totalAmount = signal<number>(0);
|
||||||
|
|
||||||
|
goldDefaultUnitPrice = signal<number>(0);
|
||||||
|
|
||||||
preparedCTAText = computed(() => (this.editMode ? 'اعمال ویرایش' : 'افزودن به لیست خرید'));
|
preparedCTAText = computed(() => (this.editMode ? 'اعمال ویرایش' : 'افزودن به لیست خرید'));
|
||||||
|
|
||||||
private readonly initialForm = () => {
|
private readonly initialForm = () => {
|
||||||
|
const defaultPrice = this.posGoldConfig.get();
|
||||||
|
|
||||||
|
this.goldDefaultUnitPrice.set(defaultPrice);
|
||||||
|
|
||||||
const form = this.fb.group({
|
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)]],
|
quantity: [this.initialValues?.quantity || 0, [Validators.required, greaterThanValidator(0)]],
|
||||||
discount: [this.initialValues?.discount || 0],
|
discount: [this.initialValues?.discount || 0],
|
||||||
discount_percentage: [0, [Validators.max(100), Validators.min(0)]],
|
discount_percentage: [0, [Validators.max(100), Validators.min(0)]],
|
||||||
|
|||||||
@@ -5,4 +5,5 @@ export enum LOCAL_STORAGE_KEYS {
|
|||||||
LAST_LOGIN_TIME = 'lastLoginTime',
|
LAST_LOGIN_TIME = 'lastLoginTime',
|
||||||
LOGIN_ATTEMPTS = 'loginAttempts',
|
LOGIN_ATTEMPTS = 'loginAttempts',
|
||||||
POS_CONFIG_PRINT = 'posConfigPrint',
|
POS_CONFIG_PRINT = 'posConfigPrint',
|
||||||
|
POS_CONFIG_GOLD_PRICE = 'posConfigGoldPrice',
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user