feat: update environment configurations and add gold price management module with related components and services

This commit is contained in:
2026-05-23 19:57:28 +03:30
parent 6ad1a73c16
commit 7f07bf53c2
14 changed files with 507 additions and 89 deletions
+7
View File
@@ -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
+7
View File
@@ -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
View File
@@ -42,5 +42,4 @@ testem.log
.DS_Store .DS_Store
Thumbs.db Thumbs.db
.env.*
/false
+352 -55
View File
@@ -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',
} }