some ui fix and debug input field, separate price field
This commit is contained in:
@@ -202,6 +202,7 @@ Never output:
|
|||||||
- "I wonder..."
|
- "I wonder..."
|
||||||
- "Maybe..."
|
- "Maybe..."
|
||||||
- implementation deliberation
|
- implementation deliberation
|
||||||
|
- "It sounds..."
|
||||||
|
|
||||||
Never explain alternative approaches unless requested.
|
Never explain alternative approaches unless requested.
|
||||||
|
|
||||||
|
|||||||
Generated
+1
-32
@@ -44,9 +44,6 @@ importers:
|
|||||||
'@tailwindcss/postcss':
|
'@tailwindcss/postcss':
|
||||||
specifier: ^4.2.3
|
specifier: ^4.2.3
|
||||||
version: 4.2.3
|
version: 4.2.3
|
||||||
'@zoomit/dayjs-jalali-plugin':
|
|
||||||
specifier: ^0.1.11
|
|
||||||
version: 0.1.11
|
|
||||||
angularx-qrcode:
|
angularx-qrcode:
|
||||||
specifier: 20.0.0
|
specifier: 20.0.0
|
||||||
version: 20.0.0(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2))
|
version: 20.0.0(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2))
|
||||||
@@ -56,12 +53,6 @@ importers:
|
|||||||
dayjs:
|
dayjs:
|
||||||
specifier: ^1.11.20
|
specifier: ^1.11.20
|
||||||
version: 1.11.20
|
version: 1.11.20
|
||||||
flatpickr:
|
|
||||||
specifier: ^4.6.13
|
|
||||||
version: 4.6.13
|
|
||||||
flatpickr-wrap:
|
|
||||||
specifier: ^1.0.0
|
|
||||||
version: 1.0.0
|
|
||||||
jalaliday:
|
jalaliday:
|
||||||
specifier: ^3.1.1
|
specifier: ^3.1.1
|
||||||
version: 3.1.1(dayjs@1.11.20)
|
version: 3.1.1(dayjs@1.11.20)
|
||||||
@@ -2817,9 +2808,6 @@ packages:
|
|||||||
'@yarnpkg/lockfile@1.1.0':
|
'@yarnpkg/lockfile@1.1.0':
|
||||||
resolution: {integrity: sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==}
|
resolution: {integrity: sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==}
|
||||||
|
|
||||||
'@zoomit/dayjs-jalali-plugin@0.1.11':
|
|
||||||
resolution: {integrity: sha512-xqbrBPRoBhbaAWeLbqq9QERZwj+h2KuJMehpOQrlT4y980oA+rdiF/YzXlzVaIvc845UYDrsTp8IDwttWyV/xg==}
|
|
||||||
|
|
||||||
abbrev@4.0.0:
|
abbrev@4.0.0:
|
||||||
resolution: {integrity: sha512-a1wflyaL0tHtJSmLSOVybYhy22vRih4eduhhrkcjgrWGnRfrZtovJ2FRjxuTtkkj47O/baf0R86QU5OuYpz8fA==}
|
resolution: {integrity: sha512-a1wflyaL0tHtJSmLSOVybYhy22vRih4eduhhrkcjgrWGnRfrZtovJ2FRjxuTtkkj47O/baf0R86QU5OuYpz8fA==}
|
||||||
engines: {node: ^20.17.0 || >=22.9.0}
|
engines: {node: ^20.17.0 || >=22.9.0}
|
||||||
@@ -3956,12 +3944,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==}
|
resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
flatpickr-wrap@1.0.0:
|
|
||||||
resolution: {integrity: sha512-ctthNBZyDJe1P2fpLF7N7ivwDQGkWAAr7R5BLZ2BSlz9O+w4NEc68M7FNvK/UPUPNj3FQ2pGV8UNZV/9WBU9/Q==}
|
|
||||||
|
|
||||||
flatpickr@4.6.13:
|
|
||||||
resolution: {integrity: sha512-97PMG/aywoYpB4IvbvUJi0RQi8vearvU0oov1WW3k0WZPBMrTQVqekSX5CjSG/M4Q3i6A/0FKXC7RyAoAUUSPw==}
|
|
||||||
|
|
||||||
flatted@3.4.2:
|
flatted@3.4.2:
|
||||||
resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==}
|
resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==}
|
||||||
|
|
||||||
@@ -4229,7 +4211,7 @@ packages:
|
|||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
iconv-lite@0.7.2:
|
iconv-lite@0.7.2:
|
||||||
resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==}
|
resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==, tarball: https://hub.megan.ir/repository/npm/iconv-lite/-/iconv-lite-0.7.2.tgz}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
icss-utils@5.1.0:
|
icss-utils@5.1.0:
|
||||||
@@ -5085,9 +5067,6 @@ packages:
|
|||||||
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
native-date-adapter@1.0.3:
|
|
||||||
resolution: {integrity: sha512-WXA6496dHy79BS35CWbeBha1A1QV5LjezaTA5bFWV3LXrwiRT9YXE5LJc+SSEgbN7QHz++xJnjhMHZAXMKIqAg==}
|
|
||||||
|
|
||||||
natural-compare@1.4.0:
|
natural-compare@1.4.0:
|
||||||
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
|
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
|
||||||
|
|
||||||
@@ -9450,8 +9429,6 @@ snapshots:
|
|||||||
|
|
||||||
'@yarnpkg/lockfile@1.1.0': {}
|
'@yarnpkg/lockfile@1.1.0': {}
|
||||||
|
|
||||||
'@zoomit/dayjs-jalali-plugin@0.1.11': {}
|
|
||||||
|
|
||||||
abbrev@4.0.0: {}
|
abbrev@4.0.0: {}
|
||||||
|
|
||||||
abort-controller@3.0.0:
|
abort-controller@3.0.0:
|
||||||
@@ -10884,12 +10861,6 @@ snapshots:
|
|||||||
|
|
||||||
flat@5.0.2: {}
|
flat@5.0.2: {}
|
||||||
|
|
||||||
flatpickr-wrap@1.0.0:
|
|
||||||
dependencies:
|
|
||||||
native-date-adapter: 1.0.3
|
|
||||||
|
|
||||||
flatpickr@4.6.13: {}
|
|
||||||
|
|
||||||
flatted@3.4.2: {}
|
flatted@3.4.2: {}
|
||||||
|
|
||||||
follow-redirects@1.16.0(debug@4.4.3):
|
follow-redirects@1.16.0(debug@4.4.3):
|
||||||
@@ -12106,8 +12077,6 @@ snapshots:
|
|||||||
|
|
||||||
nanoid@3.3.11: {}
|
nanoid@3.3.11: {}
|
||||||
|
|
||||||
native-date-adapter@1.0.3: {}
|
|
||||||
|
|
||||||
natural-compare@1.4.0: {}
|
natural-compare@1.4.0: {}
|
||||||
|
|
||||||
needle@3.5.0:
|
needle@3.5.0:
|
||||||
|
|||||||
@@ -10,10 +10,18 @@ interface IToast extends Pick<ToastMessageOptions, 'sticky' | 'life'> {
|
|||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
})
|
})
|
||||||
export class ToastService {
|
export class ToastService {
|
||||||
|
private readonly dedupeWindowMs = 2000;
|
||||||
|
private readonly recentMessages = new Map<string, number>();
|
||||||
|
|
||||||
constructor(private messageService: MessageService) {}
|
constructor(private messageService: MessageService) {}
|
||||||
|
|
||||||
add(message: ToastMessageOptions) {
|
add(message: ToastMessageOptions) {
|
||||||
if (!message.detail) return;
|
if (!message.detail) return;
|
||||||
|
const dedupeKey = `${message.severity ?? ''}|${message.summary ?? ''}|${message.detail}`;
|
||||||
|
const now = Date.now();
|
||||||
|
const lastShownAt = this.recentMessages.get(dedupeKey);
|
||||||
|
if (lastShownAt && now - lastShownAt < this.dedupeWindowMs) return;
|
||||||
|
this.recentMessages.set(dedupeKey, now);
|
||||||
this.messageService.add(message);
|
this.messageService.add(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
<app-input
|
<app-price-input
|
||||||
[control]="goldPrice"
|
[control]="goldPrice"
|
||||||
label="قیمت پیشفرض هر گرم طلا"
|
label="قیمت پیشفرض هر گرم طلا"
|
||||||
name="goldBasePrice"
|
name="goldBasePrice"
|
||||||
type="price"
|
|
||||||
[min]="0"
|
[min]="0"
|
||||||
hint="در این بخش میتوانید قیمت پیشفرض هر گرم طلا را وارد کنید." />
|
hint="در این بخش میتوانید قیمت پیشفرض هر گرم طلا را وارد کنید." />
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { InputComponent } from '@/shared/components';
|
import { PriceInputComponent } from '@/shared/components/input/price-input.component';
|
||||||
import { Component, inject } from '@angular/core';
|
import { Component, inject } from '@angular/core';
|
||||||
import { FormBuilder, ReactiveFormsModule } from '@angular/forms';
|
import { FormBuilder, ReactiveFormsModule } from '@angular/forms';
|
||||||
import { debounce, timer } from 'rxjs';
|
import { debounce, timer } from 'rxjs';
|
||||||
@@ -7,7 +7,7 @@ import { PosConfigGoldPriceService } from './services/main.service';
|
|||||||
@Component({
|
@Component({
|
||||||
selector: 'pos-config-gold-price-form',
|
selector: 'pos-config-gold-price-form',
|
||||||
templateUrl: 'form.component.html',
|
templateUrl: 'form.component.html',
|
||||||
imports: [ReactiveFormsModule, InputComponent],
|
imports: [ReactiveFormsModule, PriceInputComponent],
|
||||||
})
|
})
|
||||||
export class PosConfigGoldPriceFormComponent {
|
export class PosConfigGoldPriceFormComponent {
|
||||||
private readonly service = inject(PosConfigGoldPriceService);
|
private readonly service = inject(PosConfigGoldPriceService);
|
||||||
|
|||||||
@@ -48,8 +48,8 @@
|
|||||||
</div> -->
|
</div> -->
|
||||||
|
|
||||||
<div class="grid grid-cols-2 gap-2">
|
<div class="grid grid-cols-2 gap-2">
|
||||||
<app-input label="از مبلغ" [control]="form.controls.total_amount_from" name="total_amount_from" type="price" />
|
<app-price-input label="از مبلغ" [control]="form.controls.total_amount_from" name="total_amount_from" />
|
||||||
<app-input label="تا مبلغ" [control]="form.controls.total_amount_to" name="total_amount_to" type="price" />
|
<app-price-input label="تا مبلغ" [control]="form.controls.total_amount_to" name="total_amount_to" />
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<div class="bg-surface-card mt-auto shrink-0 pb-4">
|
<div class="bg-surface-card mt-auto shrink-0 pb-4">
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { Maybe } from '@/core';
|
|||||||
import { EnumSelectComponent } from '@/shared/apiEnum/select.component';
|
import { EnumSelectComponent } from '@/shared/apiEnum/select.component';
|
||||||
import { TspProviderResponseStatus } from '@/shared/catalog';
|
import { TspProviderResponseStatus } from '@/shared/catalog';
|
||||||
import { InputComponent } from '@/shared/components';
|
import { InputComponent } from '@/shared/components';
|
||||||
|
import { PriceInputComponent } from '@/shared/components/input/price-input.component';
|
||||||
import { DatepickerComponent } from '@/uikit';
|
import { DatepickerComponent } from '@/uikit';
|
||||||
import { nowGregorian } from '@/utils';
|
import { nowGregorian } from '@/utils';
|
||||||
import {
|
import {
|
||||||
@@ -32,6 +33,7 @@ import { IPosSaleInvoicesFilterDto } from '../models';
|
|||||||
EnumSelectComponent,
|
EnumSelectComponent,
|
||||||
InputComponent,
|
InputComponent,
|
||||||
DatepickerComponent,
|
DatepickerComponent,
|
||||||
|
PriceInputComponent,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class PosSaleInvoicesFilterDrawerComponent implements OnChanges {
|
export class PosSaleInvoicesFilterDrawerComponent implements OnChanges {
|
||||||
|
|||||||
@@ -54,4 +54,4 @@
|
|||||||
[visible]="filterVisible()"
|
[visible]="filterVisible()"
|
||||||
[value]="filters()"
|
[value]="filters()"
|
||||||
(visibleChange)="filterVisible.set($event)"
|
(visibleChange)="filterVisible.set($event)"
|
||||||
(apply)="onFilterApply($event)" />
|
(apply)="changeFilter($event)" />
|
||||||
|
|||||||
@@ -112,6 +112,12 @@ export class PosSaleInvoiceListComponent {
|
|||||||
this.filterVisible.update((v) => !v);
|
this.filterVisible.update((v) => !v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
changeFilter(query: IPosSaleInvoicesFilterDto) {
|
||||||
|
const { page, ...rest } = query;
|
||||||
|
this.resetPage();
|
||||||
|
this.onFilterApply(rest);
|
||||||
|
}
|
||||||
|
|
||||||
onFilterApply(query: IPosSaleInvoicesFilterDto) {
|
onFilterApply(query: IPosSaleInvoicesFilterDto) {
|
||||||
this.filters.set(query);
|
this.filters.set(query);
|
||||||
this.syncFiltersToQueryParams();
|
this.syncFiltersToQueryParams();
|
||||||
@@ -119,6 +125,7 @@ export class PosSaleInvoiceListComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
removeFilter(key: keyof IPosSaleInvoicesFilterDto) {
|
removeFilter(key: keyof IPosSaleInvoicesFilterDto) {
|
||||||
|
this.resetPage();
|
||||||
const next = { ...this.filters() };
|
const next = { ...this.filters() };
|
||||||
delete next[key];
|
delete next[key];
|
||||||
this.filters.set(next);
|
this.filters.set(next);
|
||||||
@@ -126,6 +133,10 @@ export class PosSaleInvoiceListComponent {
|
|||||||
this.getData();
|
this.getData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resetPage() {
|
||||||
|
this.filters.update((val) => ({ ...val, page: 1 }));
|
||||||
|
}
|
||||||
|
|
||||||
toSinglePage(item: IPosSaleInvoicesResponse) {
|
toSinglePage(item: IPosSaleInvoicesResponse) {
|
||||||
this.router.navigateByUrl(posSaleInvoicesNamedRoutes.saleInvoice.meta.pagePath!(item.id));
|
this.router.navigateByUrl(posSaleInvoicesNamedRoutes.saleInvoice.meta.pagePath!(item.id));
|
||||||
}
|
}
|
||||||
@@ -160,7 +171,6 @@ export class PosSaleInvoiceListComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private syncFiltersToQueryParams() {
|
private syncFiltersToQueryParams() {
|
||||||
this.filters.update((val) => ({ ...val, page: 1 }));
|
|
||||||
const queryParams = Object.entries(this.filters()).reduce<Record<string, string>>(
|
const queryParams = Object.entries(this.filters()).reduce<Record<string, string>>(
|
||||||
(acc, [key, value]) => {
|
(acc, [key, value]) => {
|
||||||
if (value !== undefined && value !== null && value !== '') {
|
if (value !== undefined && value !== null && value !== '') {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<form [formGroup]="form" (submit)="submit()">
|
<form [formGroup]="form" (submit)="submit()">
|
||||||
<app-input [control]="form.controls.name" name="name" label="نام مشتری" />
|
<app-input [control]="form.controls.name" name="name" label="نام مشتری" />
|
||||||
<app-input [control]="form.controls.national_id" name="national_id" label="شماره ملی" type="nationalId" />
|
<app-input [control]="form.controls.national_id" name="national_id" label="شماره ملی" type="nationalId" />
|
||||||
<app-form-footer-actions submitLabel="ثبت بدون اطلاعات خریدار و ادامه" (onCancel)="close()" />
|
<app-form-footer-actions submitLabel="ثبت مشتری و ادامه" (onCancel)="close()" />
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -36,7 +36,7 @@
|
|||||||
(onChange)="changePayByTerminalSteps($event.value)" />
|
(onChange)="changePayByTerminalSteps($event.value)" />
|
||||||
</uikit-field>
|
</uikit-field>
|
||||||
@for (terminalControl of terminalControls; track $index) {
|
@for (terminalControl of terminalControls; track $index) {
|
||||||
<app-input
|
<app-price-input
|
||||||
[control]="terminalControl"
|
[control]="terminalControl"
|
||||||
[name]="'terminal_' + $index"
|
[name]="'terminal_' + $index"
|
||||||
[label]="$index === 0 ? 'پرداخت با پوز' : 'پرداخت با پوز - مرحله ' + ($index + 1)"
|
[label]="$index === 0 ? 'پرداخت با پوز' : 'پرداخت با پوز - مرحله ' + ($index + 1)"
|
||||||
@@ -53,30 +53,29 @@
|
|||||||
باقیمانده مبلغ
|
باقیمانده مبلغ
|
||||||
</button>
|
</button>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</app-input>
|
</app-price-input>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
<form [formGroup]="form" (submit)="submit()">
|
<form [formGroup]="form" (submit)="submit()">
|
||||||
<app-input
|
<app-price-input
|
||||||
[control]="form.controls.cash"
|
[control]="form.controls.cash"
|
||||||
name="cash"
|
name="cash"
|
||||||
label="نقدی/ کارتخوان دیگر/ کارت به کارت"
|
label="نقدی/ کارتخوان دیگر/ کارت به کارت"
|
||||||
type="price"
|
|
||||||
[max]="cashMax()">
|
[max]="cashMax()">
|
||||||
<ng-template #suffixTemp>
|
<ng-template #suffixTemp>
|
||||||
<button pButton size="small" type="button" [disabled]="!remainedAmount()" (click)="fillRemained('CASH')">
|
<button pButton size="small" type="button" [disabled]="!remainedAmount()" (click)="fillRemained('CASH')">
|
||||||
باقیمانده مبلغ
|
باقیمانده مبلغ
|
||||||
</button>
|
</button>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</app-input>
|
</app-price-input>
|
||||||
@if (isGoldGuild()) {
|
@if (isGoldGuild()) {
|
||||||
<app-input [control]="form.controls.set_off" name="setOff" label="تهاتر" type="price" [max]="setOffMax()">
|
<app-price-input [control]="form.controls.set_off" name="setOff" label="تهاتر" [max]="setOffMax()">
|
||||||
<ng-template #suffixTemp>
|
<ng-template #suffixTemp>
|
||||||
<button pButton size="small" type="button" [disabled]="!remainedAmount()" (click)="fillRemained('SET_OFF')">
|
<button pButton size="small" type="button" [disabled]="!remainedAmount()" (click)="fillRemained('SET_OFF')">
|
||||||
باقیمانده مبلغ
|
باقیمانده مبلغ
|
||||||
</button>
|
</button>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</app-input>
|
</app-price-input>
|
||||||
}
|
}
|
||||||
|
|
||||||
<app-form-footer-actions
|
<app-form-footer-actions
|
||||||
|
|||||||
@@ -3,9 +3,10 @@ import { NativeBridgeService } from '@/core/services';
|
|||||||
import { ToastService } from '@/core/services/toast.service';
|
import { ToastService } from '@/core/services/toast.service';
|
||||||
import { PosInfoStore } from '@/domains/pos/store';
|
import { PosInfoStore } from '@/domains/pos/store';
|
||||||
import { AbstractFormDialog } from '@/shared/abstractClasses';
|
import { AbstractFormDialog } from '@/shared/abstractClasses';
|
||||||
import { InputComponent, KeyValueComponent } from '@/shared/components';
|
import { KeyValueComponent } from '@/shared/components';
|
||||||
import { SharedLightBottomsheetComponent } from '@/shared/components/dialog/light-bottomsheet.component';
|
import { SharedLightBottomsheetComponent } from '@/shared/components/dialog/light-bottomsheet.component';
|
||||||
import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component';
|
import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component';
|
||||||
|
import { PriceInputComponent } from '@/shared/components/input/price-input.component';
|
||||||
import { UikitFieldComponent, UikitLabelComponent } from '@/uikit';
|
import { UikitFieldComponent, UikitLabelComponent } from '@/uikit';
|
||||||
import { Component, computed, inject, signal } from '@angular/core';
|
import { Component, computed, inject, signal } from '@angular/core';
|
||||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||||
@@ -31,7 +32,6 @@ import { PosLandingStore } from '../../store/main.store';
|
|||||||
imports: [
|
imports: [
|
||||||
FormsModule,
|
FormsModule,
|
||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
InputComponent,
|
|
||||||
FormFooterActionsComponent,
|
FormFooterActionsComponent,
|
||||||
KeyValueComponent,
|
KeyValueComponent,
|
||||||
ButtonDirective,
|
ButtonDirective,
|
||||||
@@ -40,6 +40,7 @@ import { PosLandingStore } from '../../store/main.store';
|
|||||||
UikitFieldComponent,
|
UikitFieldComponent,
|
||||||
SelectButton,
|
SelectButton,
|
||||||
UikitLabelComponent,
|
UikitLabelComponent,
|
||||||
|
PriceInputComponent,
|
||||||
],
|
],
|
||||||
providers: [{ provide: PosPaymentBridgeAbstract, useExisting: PosPaymentBridgeService }],
|
providers: [{ provide: PosPaymentBridgeAbstract, useExisting: PosPaymentBridgeService }],
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -7,7 +7,12 @@
|
|||||||
}
|
}
|
||||||
} @else {
|
} @else {
|
||||||
@for (good of visibleGoods(); track good.id) {
|
@for (good of visibleGoods(); track good.id) {
|
||||||
<div class="bg-surface-card border-surface-border flex flex-1 flex-col rounded-lg border p-1">
|
<div class="bg-surface-card border-surface-border relative flex flex-1 flex-col rounded-lg border p-1">
|
||||||
|
@if (!good.is_default_guild_good) {
|
||||||
|
<div class="absolute inset-e-2 top-2">
|
||||||
|
<p-tag severity="contrast" value="شخصی" />
|
||||||
|
</div>
|
||||||
|
}
|
||||||
<img
|
<img
|
||||||
[src]="good.image_url || goodPlaceholder"
|
[src]="good.image_url || goodPlaceholder"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
@@ -16,9 +21,6 @@
|
|||||||
<div class="mt-2 flex grow flex-col justify-between text-center">
|
<div class="mt-2 flex grow flex-col justify-between text-center">
|
||||||
<span class="flex w-full grow items-center justify-center text-center text-lg font-bold">
|
<span class="flex w-full grow items-center justify-center text-center text-lg font-bold">
|
||||||
{{ good.name }}
|
{{ good.name }}
|
||||||
@if (!good.is_default_guild_good) {
|
|
||||||
<small class="text-muted-color text-xs">*</small>
|
|
||||||
}
|
|
||||||
</span>
|
</span>
|
||||||
<div class="my-2 mt-1 flex w-full shrink-0 items-center justify-between gap-1 px-2">
|
<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="w-full grow" (click)="addProduct(good)"></button>
|
<button pButton type="button" label="انتخاب" class="w-full grow" (click)="addProduct(good)"></button>
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { ButtonDirective } from 'primeng/button';
|
import { ButtonDirective } from 'primeng/button';
|
||||||
import { Skeleton } from 'primeng/skeleton';
|
import { Skeleton } from 'primeng/skeleton';
|
||||||
|
import { Tag } from 'primeng/tag';
|
||||||
import images from 'src/assets/images';
|
import images from 'src/assets/images';
|
||||||
import { PosLandingStore } from '../../store/main.store';
|
import { PosLandingStore } from '../../store/main.store';
|
||||||
import { FavoriteCTAComponent } from './favorite-CTA.component';
|
import { FavoriteCTAComponent } from './favorite-CTA.component';
|
||||||
@@ -19,7 +20,7 @@ import { FavoriteCTAComponent } from './favorite-CTA.component';
|
|||||||
selector: 'pos-good-grid-view',
|
selector: 'pos-good-grid-view',
|
||||||
templateUrl: './grid-view.component.html',
|
templateUrl: './grid-view.component.html',
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
imports: [ButtonDirective, Skeleton, FavoriteCTAComponent],
|
imports: [ButtonDirective, Skeleton, FavoriteCTAComponent, Tag],
|
||||||
})
|
})
|
||||||
export class PosGoodsGridViewComponent {
|
export class PosGoodsGridViewComponent {
|
||||||
@Output() onAdd = new EventEmitter<IGoodResponse>();
|
@Output() onAdd = new EventEmitter<IGoodResponse>();
|
||||||
|
|||||||
@@ -17,9 +17,9 @@
|
|||||||
<div class="flex flex-col gap-2">
|
<div class="flex flex-col gap-2">
|
||||||
<span class="text-base font-bold">
|
<span class="text-base font-bold">
|
||||||
{{ good.name }}
|
{{ good.name }}
|
||||||
@if (!good.is_default_guild_good) {
|
<!-- @if (!good.is_default_guild_good) {
|
||||||
<small class="text-muted-color text-xs">*</small>
|
<p-tag severity="contrast" value="شخصی" />
|
||||||
}
|
} -->
|
||||||
</span>
|
</span>
|
||||||
<span class="text-muted-color text-sm">
|
<span class="text-muted-color text-sm">
|
||||||
{{ good.sku.code }}
|
{{ good.sku.code }}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { ButtonDirective } from 'primeng/button';
|
import { ButtonDirective } from 'primeng/button';
|
||||||
import { Skeleton } from 'primeng/skeleton';
|
import { Skeleton } from 'primeng/skeleton';
|
||||||
|
import { Tag } from 'primeng/tag';
|
||||||
import images from 'src/assets/images';
|
import images from 'src/assets/images';
|
||||||
import { PosLandingStore } from '../../store/main.store';
|
import { PosLandingStore } from '../../store/main.store';
|
||||||
import { FavoriteCTAComponent } from './favorite-CTA.component';
|
import { FavoriteCTAComponent } from './favorite-CTA.component';
|
||||||
@@ -19,7 +20,7 @@ import { FavoriteCTAComponent } from './favorite-CTA.component';
|
|||||||
selector: 'pos-goods-list-view',
|
selector: 'pos-goods-list-view',
|
||||||
templateUrl: './list-view.component.html',
|
templateUrl: './list-view.component.html',
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
imports: [ButtonDirective, Skeleton, FavoriteCTAComponent],
|
imports: [ButtonDirective, Skeleton, FavoriteCTAComponent, Tag],
|
||||||
})
|
})
|
||||||
export class PosGoodsListViewComponent {
|
export class PosGoodsListViewComponent {
|
||||||
private readonly store = inject(PosLandingStore);
|
private readonly store = inject(PosLandingStore);
|
||||||
|
|||||||
+7
-11
@@ -3,10 +3,9 @@
|
|||||||
[name]="name"
|
[name]="name"
|
||||||
[control]="selectedType.value === 'amount' ? amountControl : percentageControl"
|
[control]="selectedType.value === 'amount' ? amountControl : percentageControl"
|
||||||
[showLabel]="!!label"
|
[showLabel]="!!label"
|
||||||
[showErrors]="showErrors"
|
[showErrors]="showErrors">
|
||||||
>
|
|
||||||
<ng-template #labelView>
|
<ng-template #labelView>
|
||||||
<div class="flex gap-1 items-center justify-between">
|
<div class="flex items-center justify-between gap-1">
|
||||||
<uikit-label [name]="name">
|
<uikit-label [name]="name">
|
||||||
{{ preparedLabel() }}
|
{{ preparedLabel() }}
|
||||||
</uikit-label>
|
</uikit-label>
|
||||||
@@ -17,7 +16,7 @@
|
|||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
<p-inputgroup>
|
<p-inputgroup>
|
||||||
@if (selectedType.value === "amount") {
|
@if (selectedType.value === 'amount') {
|
||||||
<p-inputnumber
|
<p-inputnumber
|
||||||
#amountInput
|
#amountInput
|
||||||
[attr.id]="name"
|
[attr.id]="name"
|
||||||
@@ -25,11 +24,11 @@
|
|||||||
[attr.name]="name"
|
[attr.name]="name"
|
||||||
[attr.placeholder]="placeholder"
|
[attr.placeholder]="placeholder"
|
||||||
[attr.autocomplete]="autocomplete"
|
[attr.autocomplete]="autocomplete"
|
||||||
|
[attr.inputmode]="'numeric'"
|
||||||
[inputStyle]="{ direction: 'ltr' }"
|
[inputStyle]="{ direction: 'ltr' }"
|
||||||
[inputStyleClass]="` w-full hasSuffix text-left ${size === 'large' ? 'p-inputtext-lg' : size === 'small' ? 'p-inputtext-sm' : ''}`"
|
[inputStyleClass]="` w-full hasSuffix text-left ${size === 'large' ? 'p-inputtext-lg' : size === 'small' ? 'p-inputtext-sm' : ''}`"
|
||||||
[required]="required"
|
[required]="required"
|
||||||
[invalid]="amountControl.invalid && (amountControl.touched || amountControl.dirty)"
|
[invalid]="amountControl.invalid && (amountControl.touched || amountControl.dirty)" />
|
||||||
/>
|
|
||||||
} @else {
|
} @else {
|
||||||
<input
|
<input
|
||||||
#percentageInput
|
#percentageInput
|
||||||
@@ -39,7 +38,6 @@
|
|||||||
[attr.name]="name"
|
[attr.name]="name"
|
||||||
[attr.placeholder]="placeholder"
|
[attr.placeholder]="placeholder"
|
||||||
[attr.inputmode]="'numeric'"
|
[attr.inputmode]="'numeric'"
|
||||||
[attr.type]="'number'"
|
|
||||||
dir="ltr"
|
dir="ltr"
|
||||||
[attr.autocomplete]="autocomplete"
|
[attr.autocomplete]="autocomplete"
|
||||||
[classList]="
|
[classList]="
|
||||||
@@ -49,8 +47,7 @@
|
|||||||
].join(' ')
|
].join(' ')
|
||||||
"
|
"
|
||||||
[required]="required"
|
[required]="required"
|
||||||
[invalid]="percentageControl.invalid && (percentageControl.touched || percentageControl.dirty)"
|
[invalid]="percentageControl.invalid && (percentageControl.touched || percentageControl.dirty)" />
|
||||||
/>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
<p-inputgroup-addon class="py-0!">
|
<p-inputgroup-addon class="py-0!">
|
||||||
@@ -60,8 +57,7 @@
|
|||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
optionValue="value"
|
optionValue="value"
|
||||||
size="small"
|
size="small"
|
||||||
class="border-none! shadow-none! w-full!"
|
class="w-full! border-none! shadow-none!" />
|
||||||
/>
|
|
||||||
</p-inputgroup-addon>
|
</p-inputgroup-addon>
|
||||||
</p-inputgroup>
|
</p-inputgroup>
|
||||||
@if (hint) {
|
@if (hint) {
|
||||||
|
|||||||
+27
-5
@@ -97,10 +97,32 @@ export class AmountPercentageInputComponent {
|
|||||||
private lastValidAmount = 0;
|
private lastValidAmount = 0;
|
||||||
private lastValidPercentage = 0;
|
private lastValidPercentage = 0;
|
||||||
|
|
||||||
private parseValue(value: Maybe<any>) {
|
private toEnglishDigits(value: string): string {
|
||||||
const normalized = `${value ?? ''}`.replace(/,/g, '');
|
return value
|
||||||
const parsed = parseFloat(normalized);
|
.replace(/[۰-۹]/g, (digit) => String(digit.charCodeAt(0) - 1776))
|
||||||
return Number.isFinite(parsed) ? parsed : 0;
|
.replace(/[٠-٩]/g, (digit) => String(digit.charCodeAt(0) - 1632));
|
||||||
|
}
|
||||||
|
|
||||||
|
private sanitizeNumericValue(value: string, allowDecimal: boolean): string {
|
||||||
|
const normalized = this.toEnglishDigits(value).replace(/,/g, '');
|
||||||
|
|
||||||
|
const cleaned = normalized.replace(allowDecimal ? /[^0-9.]/g : /[^0-9]/g, '');
|
||||||
|
if (!allowDecimal) return cleaned;
|
||||||
|
|
||||||
|
const [integerPart = '', ...decimalParts] = cleaned.split('.');
|
||||||
|
const mergedDecimals = decimalParts.join('');
|
||||||
|
return decimalParts.length ? `${integerPart}.${mergedDecimals}` : integerPart;
|
||||||
|
}
|
||||||
|
|
||||||
|
private parseValue(value: Maybe<string>) {
|
||||||
|
const cleanedValue = this.sanitizeNumericValue(
|
||||||
|
value || '',
|
||||||
|
this.selectedType.value === 'percentage'
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log('cleaned', cleanedValue);
|
||||||
|
|
||||||
|
return Number.isFinite(cleanedValue) ? Number(cleanedValue) : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private getRangeLabel(value: number, isPercentageType: boolean) {
|
private getRangeLabel(value: number, isPercentageType: boolean) {
|
||||||
@@ -115,7 +137,7 @@ export class AmountPercentageInputComponent {
|
|||||||
value: number,
|
value: number,
|
||||||
isPercentageType: boolean,
|
isPercentageType: boolean,
|
||||||
min = 0,
|
min = 0,
|
||||||
max = Number.MAX_SAFE_INTEGER,
|
max = Number.MAX_SAFE_INTEGER
|
||||||
) {
|
) {
|
||||||
if (value < min) {
|
if (value < min) {
|
||||||
this.toastService.warn({
|
this.toastService.warn({
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
import { Component, Input } from '@angular/core';
|
|
||||||
import { FormControl, ReactiveFormsModule } from '@angular/forms';
|
|
||||||
import { InputComponent } from '../input/input.component';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'field-cash',
|
|
||||||
template: `<app-input label="نقدی" [control]="control" [name]="name" type="price" />`,
|
|
||||||
imports: [ReactiveFormsModule, InputComponent],
|
|
||||||
})
|
|
||||||
export class CashComponent {
|
|
||||||
@Input({ required: true }) control = new FormControl<string>('');
|
|
||||||
@Input() name = 'cash';
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
export * from './address.component';
|
export * from './address.component';
|
||||||
export * from './branch_code.component';
|
export * from './branch_code.component';
|
||||||
export * from './cash.component';
|
|
||||||
export * from './code.component';
|
export * from './code.component';
|
||||||
export * from './company_name.component';
|
export * from './company_name.component';
|
||||||
export * from './description.component';
|
export * from './description.component';
|
||||||
@@ -28,10 +27,7 @@ export * from './quantity.component';
|
|||||||
export * from './registration_code.component';
|
export * from './registration_code.component';
|
||||||
export * from './registration_number.component';
|
export * from './registration_number.component';
|
||||||
export * from './serial_number.component';
|
export * from './serial_number.component';
|
||||||
export * from './set_off.component';
|
|
||||||
export * from './sku.component';
|
export * from './sku.component';
|
||||||
export * from './sku_type.component';
|
export * from './sku_type.component';
|
||||||
export * from './terminal.component';
|
|
||||||
export * from './unit_price.component';
|
|
||||||
export * from './username.component';
|
export * from './username.component';
|
||||||
export * from './vat.component';
|
export * from './vat.component';
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
import { Component, Input } from '@angular/core';
|
|
||||||
import { FormControl, ReactiveFormsModule } from '@angular/forms';
|
|
||||||
import { InputComponent } from '../input/input.component';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'field-set-off',
|
|
||||||
template: `<app-input label="تهاتر" [control]="control" [name]="name" type="price" />`,
|
|
||||||
imports: [ReactiveFormsModule, InputComponent],
|
|
||||||
})
|
|
||||||
export class SetOffComponent {
|
|
||||||
@Input({ required: true }) control = new FormControl<string>('');
|
|
||||||
@Input() name = 'set_off';
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
import { Component, Input } from '@angular/core';
|
|
||||||
import { FormControl, ReactiveFormsModule } from '@angular/forms';
|
|
||||||
import { InputComponent } from '../input/input.component';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'field-terminal',
|
|
||||||
template: `<app-input label="پرداخت با پوز" [control]="control" [name]="name" type="price" />`,
|
|
||||||
imports: [ReactiveFormsModule, InputComponent],
|
|
||||||
})
|
|
||||||
export class TerminalComponent {
|
|
||||||
@Input({ required: true }) control = new FormControl<string>('');
|
|
||||||
@Input() name = 'terminal';
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
import { Component, Input } from '@angular/core';
|
|
||||||
import { FormControl, ReactiveFormsModule } from '@angular/forms';
|
|
||||||
import { InputComponent } from '../input/input.component';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'field-unit-price',
|
|
||||||
template: `<app-input
|
|
||||||
label="مبلغ واحد"
|
|
||||||
[control]="control"
|
|
||||||
[name]="name"
|
|
||||||
[hint]="hint"
|
|
||||||
type="price"
|
|
||||||
/>`,
|
|
||||||
imports: [ReactiveFormsModule, InputComponent],
|
|
||||||
})
|
|
||||||
export class UnitPriceComponent {
|
|
||||||
@Input({ required: true }) control = new FormControl<string>('');
|
|
||||||
@Input() name = 'unit_price';
|
|
||||||
@Input() hint = '';
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<div class="flex justify-end gap-2">
|
<div class="flex justify-end gap-2">
|
||||||
<button pButton [label]="cancelLabel" severity="secondary" (click)="cancel()"></button>
|
<button pButton [label]="cancelLabel" severity="secondary" type="button" (click)="cancel()"></button>
|
||||||
<button
|
<button
|
||||||
pButton
|
pButton
|
||||||
[label]="submitLabel"
|
[label]="submitLabel"
|
||||||
|
|||||||
@@ -17,24 +17,6 @@
|
|||||||
<p-toggleSwitch [attr.disabled]="disabled" [formControl]="control" />
|
<p-toggleSwitch [attr.disabled]="disabled" [formControl]="control" />
|
||||||
} @else {
|
} @else {
|
||||||
<p-inputgroup>
|
<p-inputgroup>
|
||||||
<!-- [minlength]="minlength || null" -->
|
|
||||||
@if (type === 'price') {
|
|
||||||
<p-inputnumber
|
|
||||||
[attr.id]="name"
|
|
||||||
[formControl]="control"
|
|
||||||
[maxlength]="preparedMaxLength || null"
|
|
||||||
[attr.name]="name"
|
|
||||||
[attr.placeholder]="preparedPlaceholder"
|
|
||||||
[attr.type]="htmlType"
|
|
||||||
[attr.autocomplete]="autocomplete"
|
|
||||||
[attr.disabled]="disabled"
|
|
||||||
[class]="`w-full ${size === 'large' ? 'p-inputtext-lg' : size === 'small' ? 'p-inputtext-sm' : ''}`"
|
|
||||||
[required]="isRequired"
|
|
||||||
[invalid]="control.invalid && (control.touched || control.dirty)"
|
|
||||||
(onInput)="onPriceInput($event)"
|
|
||||||
(onBlur)="blur.emit()" />
|
|
||||||
<!-- (input)="onInput($event)" -->
|
|
||||||
} @else {
|
|
||||||
<input
|
<input
|
||||||
pInputText
|
pInputText
|
||||||
[attr.id]="name"
|
[attr.id]="name"
|
||||||
@@ -43,7 +25,6 @@
|
|||||||
[attr.placeholder]="preparedPlaceholder"
|
[attr.placeholder]="preparedPlaceholder"
|
||||||
[attr.inputmode]="inputMode"
|
[attr.inputmode]="inputMode"
|
||||||
[attr.maxlength]="preparedMaxLength || null"
|
[attr.maxlength]="preparedMaxLength || null"
|
||||||
[attr.type]="htmlType"
|
|
||||||
[attr.disabled]="disabled"
|
[attr.disabled]="disabled"
|
||||||
[attr.autocomplete]="autocomplete"
|
[attr.autocomplete]="autocomplete"
|
||||||
[classList]="
|
[classList]="
|
||||||
@@ -58,7 +39,6 @@
|
|||||||
[invalid]="control.invalid && (control.touched || control.dirty)"
|
[invalid]="control.invalid && (control.touched || control.dirty)"
|
||||||
(onBlur)="blur.emit()"
|
(onBlur)="blur.emit()"
|
||||||
(input)="onInput($event)" />
|
(input)="onInput($event)" />
|
||||||
}
|
|
||||||
@if (suffixTemp) {
|
@if (suffixTemp) {
|
||||||
<ng-container [ngTemplateOutlet]="suffixTemp" />
|
<ng-container [ngTemplateOutlet]="suffixTemp" />
|
||||||
} @else if (preparedSuffix()) {
|
} @else if (preparedSuffix()) {
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import { FormControl, ReactiveFormsModule, Validators } from '@angular/forms';
|
|||||||
import { InputGroupModule } from 'primeng/inputgroup';
|
import { InputGroupModule } from 'primeng/inputgroup';
|
||||||
import { InputGroupAddon } from 'primeng/inputgroupaddon';
|
import { InputGroupAddon } from 'primeng/inputgroupaddon';
|
||||||
import { InputMaskModule } from 'primeng/inputmask';
|
import { InputMaskModule } from 'primeng/inputmask';
|
||||||
import { InputNumberInputEvent, InputNumberModule } from 'primeng/inputnumber';
|
import { InputNumberModule } from 'primeng/inputnumber';
|
||||||
import { InputTextModule } from 'primeng/inputtext';
|
import { InputTextModule } from 'primeng/inputtext';
|
||||||
import { KeyFilterModule } from 'primeng/keyfilter';
|
import { KeyFilterModule } from 'primeng/keyfilter';
|
||||||
import { ToggleSwitch } from 'primeng/toggleswitch';
|
import { ToggleSwitch } from 'primeng/toggleswitch';
|
||||||
@@ -51,8 +51,7 @@ export class InputComponent {
|
|||||||
| 'email'
|
| 'email'
|
||||||
| 'checkbox'
|
| 'checkbox'
|
||||||
| 'switch'
|
| 'switch'
|
||||||
| 'number'
|
| 'number' = 'simple';
|
||||||
| 'price' = 'simple';
|
|
||||||
@Input() control!: FormControl<Maybe<any>>;
|
@Input() control!: FormControl<Maybe<any>>;
|
||||||
@Input() name!: string;
|
@Input() name!: string;
|
||||||
@Input() label: string = '';
|
@Input() label: string = '';
|
||||||
@@ -80,17 +79,8 @@ export class InputComponent {
|
|||||||
|
|
||||||
private readonly toastService = inject(ToastService);
|
private readonly toastService = inject(ToastService);
|
||||||
|
|
||||||
// onInput(ev: Event) {
|
|
||||||
// const v = (ev.target as HTMLInputElement).value;
|
|
||||||
|
|
||||||
// const newLocal = v || v == '0';
|
|
||||||
// // this.valueChange.emit(this.type === 'price' && newLocal ? parseFloat(v) : v);
|
|
||||||
// }
|
|
||||||
|
|
||||||
preparedSuffix = computed(() => {
|
preparedSuffix = computed(() => {
|
||||||
if (this.type === 'price') {
|
return this.suffix;
|
||||||
return 'ریال';
|
|
||||||
} else return this.suffix;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
get preparedPlaceholder(): string | null {
|
get preparedPlaceholder(): string | null {
|
||||||
@@ -106,30 +96,34 @@ export class InputComponent {
|
|||||||
return 'شماره تلفن';
|
return 'شماره تلفن';
|
||||||
case 'email':
|
case 'email':
|
||||||
return 'آدرس ایمیل خود را وارد کنید';
|
return 'آدرس ایمیل خود را وارد کنید';
|
||||||
case 'price':
|
|
||||||
return 'مبلغ';
|
|
||||||
default:
|
default:
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get inputMode(): string | null {
|
get inputMode(): string {
|
||||||
switch (this.type) {
|
switch (this.type) {
|
||||||
case 'price':
|
|
||||||
return 'decimal';
|
|
||||||
case 'number':
|
case 'number':
|
||||||
case 'mobile':
|
return 'decimal';
|
||||||
case 'phone':
|
|
||||||
case 'postalCode':
|
case 'postalCode':
|
||||||
case 'nationalId':
|
case 'nationalId':
|
||||||
return 'numeric';
|
return 'numeric';
|
||||||
|
case 'mobile':
|
||||||
|
case 'phone':
|
||||||
|
return 'tel';
|
||||||
|
case 'email':
|
||||||
|
return 'email';
|
||||||
|
case 'checkbox':
|
||||||
|
return 'checkbox';
|
||||||
|
case 'switch':
|
||||||
|
return 'radio';
|
||||||
default:
|
default:
|
||||||
return 'text';
|
return 'text';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get isNumeric(): boolean {
|
get isNumeric(): boolean {
|
||||||
return this.inputMode === 'numeric';
|
return ['number', 'decimal'].includes(this.inputMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
get preparedMaxLength(): number | null {
|
get preparedMaxLength(): number | null {
|
||||||
@@ -153,7 +147,6 @@ export class InputComponent {
|
|||||||
switch (this.type) {
|
switch (this.type) {
|
||||||
case 'mobile':
|
case 'mobile':
|
||||||
case 'phone':
|
case 'phone':
|
||||||
case 'price':
|
|
||||||
case 'number':
|
case 'number':
|
||||||
inputClass.push('ltrInput');
|
inputClass.push('ltrInput');
|
||||||
break;
|
break;
|
||||||
@@ -174,14 +167,12 @@ export class InputComponent {
|
|||||||
switch (this.type) {
|
switch (this.type) {
|
||||||
case 'email':
|
case 'email':
|
||||||
return 'email';
|
return 'email';
|
||||||
case 'mobile':
|
// case 'mobile':
|
||||||
case 'phone':
|
// case 'phone':
|
||||||
case 'postalCode':
|
// case 'postalCode':
|
||||||
case 'nationalId':
|
// case 'nationalId':
|
||||||
return 'number';
|
|
||||||
case 'price':
|
|
||||||
case 'number':
|
case 'number':
|
||||||
return 'text';
|
return 'number';
|
||||||
case 'checkbox':
|
case 'checkbox':
|
||||||
return 'checkbox';
|
return 'checkbox';
|
||||||
case 'switch':
|
case 'switch':
|
||||||
@@ -195,10 +186,6 @@ export class InputComponent {
|
|||||||
return this.required || this.control.hasValidator(Validators.required);
|
return this.required || this.control.hasValidator(Validators.required);
|
||||||
}
|
}
|
||||||
|
|
||||||
onPriceInput($event: InputNumberInputEvent) {
|
|
||||||
this.onInput($event.originalEvent, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private toEnglishDigits(value: string): string {
|
private toEnglishDigits(value: string): string {
|
||||||
return value
|
return value
|
||||||
.replace(/[۰-۹]/g, (digit) => String(digit.charCodeAt(0) - 1776))
|
.replace(/[۰-۹]/g, (digit) => String(digit.charCodeAt(0) - 1776))
|
||||||
@@ -207,6 +194,7 @@ export class InputComponent {
|
|||||||
|
|
||||||
private sanitizeNumericValue(value: string, allowDecimal: boolean): string {
|
private sanitizeNumericValue(value: string, allowDecimal: boolean): string {
|
||||||
const normalized = this.toEnglishDigits(value).replace(/,/g, '');
|
const normalized = this.toEnglishDigits(value).replace(/,/g, '');
|
||||||
|
|
||||||
const cleaned = normalized.replace(allowDecimal ? /[^0-9.]/g : /[^0-9]/g, '');
|
const cleaned = normalized.replace(allowDecimal ? /[^0-9.]/g : /[^0-9]/g, '');
|
||||||
if (!allowDecimal) return cleaned;
|
if (!allowDecimal) return cleaned;
|
||||||
|
|
||||||
@@ -245,76 +233,82 @@ export class InputComponent {
|
|||||||
return value.replace(/^0+(?=\d)/, '');
|
return value.replace(/^0+(?=\d)/, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
onInput($event: Event, isPriceFormat?: boolean) {
|
private enforceMaxLength(value: string): string {
|
||||||
if (this.type !== 'price' && this.type !== 'number') {
|
if (!this.preparedMaxLength || value.length <= this.preparedMaxLength) return value;
|
||||||
return;
|
return value.slice(0, this.preparedMaxLength);
|
||||||
}
|
|
||||||
const target = $event.target as HTMLInputElement | null;
|
|
||||||
if (!target) return;
|
|
||||||
|
|
||||||
let value = target.value ?? '';
|
|
||||||
const allowDecimal = this.type === 'price' || this.type === 'number';
|
|
||||||
|
|
||||||
if (this.inputMode === 'numeric' || this.inputMode === 'decimal' || isPriceFormat) {
|
|
||||||
value = this.sanitizeNumericValue(value, allowDecimal);
|
|
||||||
value = this.applyFixed(value);
|
|
||||||
value = this.normalizeLeadingZeros(value, allowDecimal);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.preparedMaxLength && value.length > this.preparedMaxLength) {
|
private parseNumericValue(value: string): number {
|
||||||
value = value.slice(0, this.preparedMaxLength);
|
return Number.isNaN(parseFloat(value)) ? 0 : parseFloat(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
let numericValue = Number.isNaN(parseFloat(value)) ? 0 : parseFloat(value);
|
private isOutOfRange(numericValue: number): { min: boolean; max: boolean } {
|
||||||
const minValidator = (this.min || this.min === 0) && numericValue < this.min!;
|
return {
|
||||||
const maxValidator = (this.max || this.max === 0) && numericValue > this.max!;
|
min: !!((this.min || this.min === 0) && numericValue < this.min),
|
||||||
if (
|
max: !!((this.max || this.max === 0) && numericValue > this.max),
|
||||||
(this.inputMode === 'numeric' || this.inputMode === 'decimal') &&
|
};
|
||||||
value !== '' &&
|
}
|
||||||
(minValidator || maxValidator)
|
|
||||||
) {
|
private notifyRangeError(range: { min: boolean; max: boolean }) {
|
||||||
if (maxValidator) {
|
if (range.max) {
|
||||||
this.toastService.warn({
|
this.toastService.warn({
|
||||||
text: `مقدار ${this.label} نمیتواند بیشتر از ${this.max} باشد.`,
|
text: `مقدار ${this.label} نمیتواند بیشتر از ${this.max} باشد.`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (minValidator) {
|
if (range.min) {
|
||||||
this.toastService.warn({
|
this.toastService.warn({
|
||||||
text: `مقدار ${this.label} نمیتواند کمتر از ${this.min} باشد.`,
|
text: `مقدار ${this.label} نمیتواند کمتر از ${this.min} باشد.`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private isIdentifierField(): boolean {
|
||||||
|
return ['mobile', 'phone', 'postalCode', 'nationalId'].includes(this.type);
|
||||||
|
}
|
||||||
|
|
||||||
|
private writeControlAndViewValue(target: HTMLInputElement, value: string, numericValue: number) {
|
||||||
|
const isIdentifierField = this.isIdentifierField();
|
||||||
|
const controlValue = isIdentifierField ? value : value === '' ? '' : numericValue;
|
||||||
|
this.control.setValue(controlValue);
|
||||||
|
target.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
onInput($event: Event) {
|
||||||
|
const target = $event.target as HTMLInputElement | null;
|
||||||
|
if (!target) return;
|
||||||
|
|
||||||
|
let value = target.value ?? '';
|
||||||
|
const allowDecimal = this.type === 'number';
|
||||||
|
const isNumericMode = this.isNumeric;
|
||||||
|
|
||||||
|
if (isNumericMode) {
|
||||||
|
value = this.sanitizeNumericValue(value, allowDecimal);
|
||||||
|
value = this.applyFixed(value);
|
||||||
|
value = this.normalizeLeadingZeros(value, allowDecimal);
|
||||||
|
|
||||||
|
value = this.enforceMaxLength(value);
|
||||||
|
|
||||||
|
let numericValue = this.parseNumericValue(value);
|
||||||
|
const range = this.isOutOfRange(numericValue);
|
||||||
|
if (value !== '' && (range.min || range.max)) {
|
||||||
|
this.notifyRangeError(range);
|
||||||
value = this.lastValidNumericValue;
|
value = this.lastValidNumericValue;
|
||||||
numericValue = Number.isNaN(parseFloat(value)) ? 0 : parseFloat(value);
|
numericValue = this.parseNumericValue(value);
|
||||||
|
this.writeControlAndViewValue(target, value, numericValue);
|
||||||
// const isIdentifierField = ['mobile', 'phone', 'postalCode', 'nationalId'].includes(this.type);
|
|
||||||
const isIdentifierField = !this.numericValue;
|
|
||||||
const restoredControlValue = isIdentifierField ? value : value === '' ? '' : numericValue;
|
|
||||||
|
|
||||||
this.control.setValue(restoredControlValue);
|
|
||||||
target.value = isPriceFormat && value !== '' ? numericValue.toLocaleString('en-US') : value;
|
|
||||||
return;
|
return;
|
||||||
} else if (this.inputMode === 'numeric' || this.inputMode === 'decimal') {
|
} else {
|
||||||
this.lastValidNumericValue = value;
|
this.lastValidNumericValue = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isIdentifierField = ['mobile', 'phone', 'postalCode', 'nationalId'].includes(this.type);
|
this.writeControlAndViewValue(target, value, numericValue);
|
||||||
const controlValue = isIdentifierField ? value : value === '' ? '' : numericValue;
|
if (this.valueChange.observed) {
|
||||||
|
this.valueChange.emit(this.isIdentifierField() ? value : numericValue);
|
||||||
this.control.setValue(controlValue);
|
}
|
||||||
|
|
||||||
const viewValue = isPriceFormat && value !== '' ? numericValue.toLocaleString('en-US') : value;
|
|
||||||
|
|
||||||
target.value = viewValue;
|
|
||||||
if (
|
|
||||||
(this.inputMode === 'numeric' || this.inputMode === 'decimal') &&
|
|
||||||
this.valueChange.observed
|
|
||||||
) {
|
|
||||||
this.valueChange.emit(isIdentifierField ? value : numericValue);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
const allowDecimal = this.type === 'price' || this.type === 'number';
|
const allowDecimal = this.type === 'number';
|
||||||
const initial = this.sanitizeNumericValue(`${this.control?.value ?? ''}`, allowDecimal);
|
const initial = this.sanitizeNumericValue(`${this.control?.value ?? ''}`, allowDecimal);
|
||||||
this.lastValidNumericValue = initial;
|
this.lastValidNumericValue = initial;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<uikit-field [label]="label" [name]="name" [control]="control" [showLabel]="!!label" [showErrors]="showErrors">
|
||||||
|
<p-inputgroup>
|
||||||
|
<p-inputnumber
|
||||||
|
[attr.id]="name"
|
||||||
|
[formControl]="control"
|
||||||
|
[attr.name]="name"
|
||||||
|
[attr.placeholder]="preparedPlaceholder"
|
||||||
|
[attr.type]="'text'"
|
||||||
|
[attr.autocomplete]="autocomplete"
|
||||||
|
[attr.disabled]="disabled"
|
||||||
|
[class]="`ltrInput rtlPlaceholder hasSuffix w-full ${sizeClass}`"
|
||||||
|
inputStyleClass="hasSuffix"
|
||||||
|
[required]="isRequired"
|
||||||
|
[invalid]="control.invalid && (control.touched || control.dirty)"
|
||||||
|
(onInput)="onInput($event)"
|
||||||
|
(onBlur)="blur.emit()" />
|
||||||
|
@if (suffixTemp) {
|
||||||
|
<ng-container [ngTemplateOutlet]="suffixTemp" />
|
||||||
|
} @else {
|
||||||
|
<p-inputgroup-addon [ngClass]="{ 'border-red-400!': (control.touched || control.dirty) && control.invalid }">
|
||||||
|
ریال
|
||||||
|
</p-inputgroup-addon>
|
||||||
|
}
|
||||||
|
</p-inputgroup>
|
||||||
|
@if (hint) {
|
||||||
|
<small [attr.id]="name + '-help'" class="text-muted-color">{{ hint }}</small>
|
||||||
|
}
|
||||||
|
</uikit-field>
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
import { Maybe } from '@/core';
|
||||||
|
import { ToastService } from '@/core/services/toast.service';
|
||||||
|
import { UikitFieldComponent } from '@/uikit/uikit-field.component';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import {
|
||||||
|
Component,
|
||||||
|
ContentChild,
|
||||||
|
EventEmitter,
|
||||||
|
inject,
|
||||||
|
Input,
|
||||||
|
Output,
|
||||||
|
TemplateRef,
|
||||||
|
} from '@angular/core';
|
||||||
|
import { FormControl, ReactiveFormsModule, Validators } from '@angular/forms';
|
||||||
|
import { InputGroupModule } from 'primeng/inputgroup';
|
||||||
|
import { InputGroupAddon } from 'primeng/inputgroupaddon';
|
||||||
|
import { InputNumberInputEvent, InputNumberModule } from 'primeng/inputnumber';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-price-input',
|
||||||
|
standalone: true,
|
||||||
|
imports: [
|
||||||
|
ReactiveFormsModule,
|
||||||
|
InputGroupModule,
|
||||||
|
InputGroupAddon,
|
||||||
|
InputNumberModule,
|
||||||
|
UikitFieldComponent,
|
||||||
|
CommonModule,
|
||||||
|
],
|
||||||
|
templateUrl: './price-input.component.html',
|
||||||
|
})
|
||||||
|
export class PriceInputComponent {
|
||||||
|
@Input() control!: FormControl<Maybe<any>>;
|
||||||
|
@Input() name!: string;
|
||||||
|
@Input() label = '';
|
||||||
|
@Input() placeholder?: string;
|
||||||
|
@Input() disabled = false;
|
||||||
|
@Input() size?: 'small' | 'large';
|
||||||
|
@Input() autocomplete: string = 'off';
|
||||||
|
@Input() showErrors = true;
|
||||||
|
@Input() hint?: string;
|
||||||
|
@Input() min?: number = 0;
|
||||||
|
@Input() max?: number;
|
||||||
|
@Input() required = false;
|
||||||
|
@Output() inputEvent = new EventEmitter<InputNumberInputEvent>();
|
||||||
|
@Output() blur = new EventEmitter<void>();
|
||||||
|
|
||||||
|
@ContentChild('suffixTemp', { static: true }) suffixTemp!: TemplateRef<any> | null;
|
||||||
|
|
||||||
|
private readonly toastService = inject(ToastService);
|
||||||
|
|
||||||
|
get preparedPlaceholder(): string {
|
||||||
|
return this.placeholder || 'مبلغ';
|
||||||
|
}
|
||||||
|
|
||||||
|
get isRequired(): boolean {
|
||||||
|
return this.required || this.control.hasValidator(Validators.required);
|
||||||
|
}
|
||||||
|
|
||||||
|
get sizeClass(): string {
|
||||||
|
return this.size === 'large' ? 'p-inputtext-lg' : this.size === 'small' ? 'p-inputtext-sm' : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
onInput(event: InputNumberInputEvent) {
|
||||||
|
console.log('event', event);
|
||||||
|
|
||||||
|
const eventValue = Number(event.value ?? 0);
|
||||||
|
const min = this.min ?? undefined;
|
||||||
|
const max = this.max ?? undefined;
|
||||||
|
|
||||||
|
const minValidator = min !== undefined && eventValue < min;
|
||||||
|
const maxValidator = max !== undefined && eventValue > max;
|
||||||
|
|
||||||
|
if (maxValidator) {
|
||||||
|
this.toastService.warn({
|
||||||
|
text: `مقدار ${this.label} نمیتواند بیشتر از ${this.max!.toLocaleString('en-US')} باشد.`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (minValidator) {
|
||||||
|
this.toastService.warn({
|
||||||
|
text: `مقدار ${this.label} نمیتواند کمتر از ${this.min!.toLocaleString('en-US')} باشد.`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (minValidator || maxValidator) {
|
||||||
|
const normalizedValue = parseFloat(
|
||||||
|
eventValue.toString().slice(0, eventValue.toString().length - 1) || '0'
|
||||||
|
);
|
||||||
|
|
||||||
|
this.control.setValue(normalizedValue);
|
||||||
|
// @ts-ignore
|
||||||
|
event.originalEvent.target.value = normalizedValue
|
||||||
|
? normalizedValue.toLocaleString('en-US')
|
||||||
|
: normalizedValue;
|
||||||
|
} else {
|
||||||
|
// @ts-ignore
|
||||||
|
event.originalEvent.target.value = eventValue
|
||||||
|
? eventValue.toLocaleString('en-US')
|
||||||
|
: eventValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.inputEvent.emit({
|
||||||
|
...event,
|
||||||
|
value: eventValue,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<form [formGroup]="form">
|
<form [formGroup]="form">
|
||||||
<app-input [control]="form.controls.unit_price" name="unit_price" label="مبلغ واحد" type="price" />
|
<app-price-input [control]="form.controls.unit_price" name="unit_price" label="مبلغ واحد" />
|
||||||
<app-input [control]="form.controls.quantity" name="quantity" label="مقدار" type="number" suffix="گرم" />
|
<app-input [control]="form.controls.quantity" name="quantity" label="مقدار" type="number" suffix="گرم" />
|
||||||
<app-enum-select [control]="form.controls.payload.controls.karat" type="goldKarat" name="karat" />
|
<app-enum-select [control]="form.controls.payload.controls.karat" type="goldKarat" name="karat" />
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ 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';
|
||||||
import { SelectButton, SelectButtonChangeEvent } from 'primeng/selectbutton';
|
import { SelectButton, SelectButtonChangeEvent } from 'primeng/selectbutton';
|
||||||
|
import { PriceInputComponent } from '../input/price-input.component';
|
||||||
import goldAmountCalculationUtil from './gold-amount-calculation.util';
|
import goldAmountCalculationUtil from './gold-amount-calculation.util';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@@ -31,6 +32,7 @@ import goldAmountCalculationUtil from './gold-amount-calculation.util';
|
|||||||
AmountPercentageInputComponent,
|
AmountPercentageInputComponent,
|
||||||
ButtonDirective,
|
ButtonDirective,
|
||||||
CalculatedAmountCardComponent,
|
CalculatedAmountCardComponent,
|
||||||
|
PriceInputComponent,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class SharedGoldPayloadFormComponent extends AbstractForm<
|
export class SharedGoldPayloadFormComponent extends AbstractForm<
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<form [formGroup]="form" (submit)="submit()">
|
<form [formGroup]="form" (submit)="submit()">
|
||||||
<app-input [control]="form.controls.unit_price" name="unit_price" label="مبلغ واحد" type="price" />
|
<app-price-input [control]="form.controls.unit_price" name="unit_price" label="مبلغ واحد" />
|
||||||
<app-input
|
<app-input
|
||||||
[control]="form.controls.quantity"
|
[control]="form.controls.quantity"
|
||||||
name="quantity"
|
name="quantity"
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|||||||
import { ReactiveFormsModule, Validators } from '@angular/forms';
|
import { ReactiveFormsModule, Validators } from '@angular/forms';
|
||||||
import { ButtonDirective } from 'primeng/button';
|
import { ButtonDirective } from 'primeng/button';
|
||||||
import { IPosOrderItem } from '../../../domains/pos/modules/shop/models';
|
import { IPosOrderItem } from '../../../domains/pos/modules/shop/models';
|
||||||
|
import { PriceInputComponent } from '../input/price-input.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'shared-standard-payload-form',
|
selector: 'shared-standard-payload-form',
|
||||||
@@ -22,6 +23,7 @@ import { IPosOrderItem } from '../../../domains/pos/modules/shop/models';
|
|||||||
AmountPercentageInputComponent,
|
AmountPercentageInputComponent,
|
||||||
ButtonDirective,
|
ButtonDirective,
|
||||||
CalculatedAmountCardComponent,
|
CalculatedAmountCardComponent,
|
||||||
|
PriceInputComponent,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class SharedStandardPayloadFormComponent extends AbstractForm<
|
export class SharedStandardPayloadFormComponent extends AbstractForm<
|
||||||
|
|||||||
@@ -17,19 +17,12 @@
|
|||||||
<button
|
<button
|
||||||
pButton
|
pButton
|
||||||
type="button"
|
type="button"
|
||||||
|
size="large"
|
||||||
[label]="seasonItem.label"
|
[label]="seasonItem.label"
|
||||||
[severity]="seasonItem.severity"
|
[severity]="seasonItem.severity"
|
||||||
outlined
|
outlined
|
||||||
(click)="selectedSeasonDraft.set(seasonItem.key)"></button>
|
(click)="selectSeason(seasonItem)"></button>
|
||||||
}
|
}
|
||||||
<!-- [disabled]="selectedYear <= selectedYearDraft() && selectedSeason <= selectedSeasonDraft()" -->
|
<!-- [disabled]="selectedYear <= selectedYearDraft() && selectedSeason <= selectedSeasonDraft()" -->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mt-4">
|
|
||||||
<app-form-footer-actions
|
|
||||||
submitLabel="تایید"
|
|
||||||
cancelLabel="انصراف"
|
|
||||||
(onCancel)="visibleChange.emit(false)"
|
|
||||||
(onSubmit)="onSubmit()" />
|
|
||||||
</div>
|
|
||||||
</shared-light-bottomsheet>
|
</shared-light-bottomsheet>
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import { Component, computed, EventEmitter, Input, Output, signal } from '@angul
|
|||||||
import { FormsModule } from '@angular/forms';
|
import { FormsModule } from '@angular/forms';
|
||||||
import { ButtonDirective, ButtonSeverity } from 'primeng/button';
|
import { ButtonDirective, ButtonSeverity } from 'primeng/button';
|
||||||
import { SharedLightBottomsheetComponent } from '../dialog/light-bottomsheet.component';
|
import { SharedLightBottomsheetComponent } from '../dialog/light-bottomsheet.component';
|
||||||
import { FormFooterActionsComponent } from '../formFooterActions/form-footer-actions.component';
|
|
||||||
|
|
||||||
type SeasonKey = 0 | 1 | 2 | 3;
|
type SeasonKey = 0 | 1 | 2 | 3;
|
||||||
|
|
||||||
@@ -22,7 +21,6 @@ interface SeasonItem {
|
|||||||
CommonModule,
|
CommonModule,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
SharedLightBottomsheetComponent,
|
SharedLightBottomsheetComponent,
|
||||||
FormFooterActionsComponent,
|
|
||||||
ButtonDirective,
|
ButtonDirective,
|
||||||
UikitLabelComponent,
|
UikitLabelComponent,
|
||||||
],
|
],
|
||||||
@@ -40,10 +38,11 @@ export class SeasonPickerDialogComponent extends AbstractDialog {
|
|||||||
|
|
||||||
isSeasonDisabled = computed(() => this.selectedYear >= this.selectedYearDraft());
|
isSeasonDisabled = computed(() => this.selectedYear >= this.selectedYearDraft());
|
||||||
|
|
||||||
onSubmit(): void {
|
selectSeason(season: SeasonItem) {
|
||||||
|
this.selectedSeasonDraft.set(season.key);
|
||||||
this.submit.emit({
|
this.submit.emit({
|
||||||
year: this.selectedYearDraft(),
|
year: this.selectedYearDraft(),
|
||||||
season: this.selectedSeasonDraft(),
|
season: season.key,
|
||||||
});
|
});
|
||||||
this.close();
|
this.close();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user