feat: update app version and build details in ngsw-config.json

fix: add baseZIndex to p-toast component in app.component.ts

feat: enhance pagination support in saleInvoices list component

refactor: modify saleInvoices service to accept pagination query

feat: update API routes for saleInvoices to support pagination

fix: adjust layout for consumer accounts in partner module

style: improve layout for consumer business activities in partner module

fix: update license info display in dashboard component

fix: ensure price info card displays default values for discount and tax

refactor: clean up gold payload form component

fix: adjust root component layout for mobile view

refactor: unify consumer account list configuration across modules

chore: remove deprecated partner consumer account list config

chore: remove deprecated superAdmin consumer account list config

feat: add new consumer account list configuration

style: add summary list styling in customize.scss

chore: switch API base URL for TIS environment
This commit is contained in:
2026-05-16 14:49:22 +03:30
parent 4f76056ac0
commit 8c98e53427
29 changed files with 219 additions and 172 deletions
@@ -28,9 +28,7 @@
[inputStyleClass]="` w-full hasSuffix text-left ${size === 'large' ? 'p-inputtext-lg' : size === 'small' ? 'p-inputtext-sm' : ''}`"
[required]="required"
[invalid]="amountControl.invalid && (amountControl.touched || amountControl.dirty)"
(onInput)="onPriceInput($event)"
/>
<!-- (input)="onInput($event)" -->
} @else {
<input
pInputText
@@ -50,7 +48,6 @@
"
[required]="required"
[invalid]="percentageControl.invalid && (percentageControl.touched || percentageControl.dirty)"
(input)="onInput($event)"
/>
}
@@ -19,7 +19,7 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { FormControl, ReactiveFormsModule } from '@angular/forms';
import { InputGroup } from 'primeng/inputgroup';
import { InputGroupAddon } from 'primeng/inputgroupaddon';
import { InputNumberInputEvent, InputNumberModule } from 'primeng/inputnumber';
import { InputNumberModule } from 'primeng/inputnumber';
import { InputText } from 'primeng/inputtext';
import { Select } from 'primeng/select';
@@ -90,83 +90,85 @@ export class AmountPercentageInputComponent {
});
preparedLabel = signal(this.label);
private lastValidAmount = 0;
private lastValidPercentage = 0;
onPriceInput($event: InputNumberInputEvent) {
this.onInput($event.originalEvent, true);
private parseValue(value: Maybe<any>) {
const normalized = `${value ?? ''}`.replace(/,/g, '');
const parsed = parseFloat(normalized);
return Number.isFinite(parsed) ? parsed : 0;
}
onInput($event: Event, isPriceType: boolean = false) {
// @ts-ignore
let value = $event.target.value as string;
// @ts-ignore
const isDotInput = $event.data === '.';
private getRangeLabel(value: number, isPercentageType: boolean) {
return isPercentageType ? `${value}%` : formatWithCurrency(value);
}
if (isDotInput) {
private isInRange(value: number, min = 0, max = Number.MAX_SAFE_INTEGER) {
return value >= min && value <= max;
}
private showOutOfRangeToast(value: number, isPercentageType: boolean, min = 0, max = Number.MAX_SAFE_INTEGER) {
if (value < min) {
this.toastService.warn({
text: `مقدار ${this.label} باید بیشتر از ${this.getRangeLabel(min, isPercentageType)} باشد.`,
});
}
if (value > max) {
this.toastService.warn({
text: `مقدار ${this.label} باید کمتر از ${this.getRangeLabel(max, isPercentageType)} باشد.`,
});
}
}
private getAmountRange() {
const min = this.minAmount ?? 0;
const maxFromInput = this.maxAmount;
const max = maxFromInput ?? (this.baseAmount > 0 ? this.baseAmount : Number.MAX_SAFE_INTEGER);
return { min, max };
}
private getPercentageRange() {
const min = this.minPercentage ?? 0;
const max = this.maxPercentage ?? 100;
return { min, max };
}
private syncFromPercentage(rawValue: Maybe<any>) {
const { min, max } = this.getPercentageRange();
const percentageValue = this.parseValue(rawValue);
if (!this.isInRange(percentageValue, min, max)) {
this.showOutOfRangeToast(percentageValue, true, min, max);
this.percentageControl.setValue(this.lastValidPercentage, { emitEvent: false });
return;
}
if (isPriceType) {
value = value.replace(/,/g, '');
}
this.lastValidPercentage = percentageValue;
const amountValue = (this.baseAmount * percentageValue) / 100;
this.lastValidAmount = Number.isFinite(amountValue) ? amountValue : 0;
const newValueToSet = this.modifyControlsData(value);
if (newValueToSet !== null) {
// @ts-ignore
$event.target.value = newValueToSet;
}
this.percentageControl.setValue(this.lastValidPercentage, { emitEvent: false });
this.amountControl.setValue(this.lastValidAmount, { emitEvent: false });
this.preparedLabel.set(this.prepareLabel(true));
}
private modifyControlsData(value: string) {
let newValueToSet: Maybe<string> = null;
let newValue = parseFloat(value);
const isPercentageType = this.selectedType.value === 'percentage';
const min = (isPercentageType ? this.minPercentage : this.minAmount) || 0;
const max = (isPercentageType ? this.maxPercentage : this.maxAmount) || Number.MAX_SAFE_INTEGER;
const maxValidator = max < newValue;
const minValidator = min > newValue;
const notValid = minValidator || maxValidator;
if (notValid) {
if (minValidator) {
this.toastService.warn({
text: `مقدار ${this.label} باید بیشتر از ${formatWithCurrency(min)} باشد.`,
});
}
if (maxValidator) {
this.toastService.warn({
text: `مقدار ${this.label} باید کمتر از ${formatWithCurrency(max)} باشد.`,
});
}
newValue = parseFloat(value.slice(0, value.length - 1));
if (newValue < 0 || isNaN(newValue)) {
newValue = 0;
}
private syncFromAmount(rawValue: Maybe<any>) {
const { min, max } = this.getAmountRange();
const amountValue = this.parseValue(rawValue);
if (!this.isInRange(amountValue, min, max)) {
this.showOutOfRangeToast(amountValue, false, min, max);
this.amountControl.setValue(this.lastValidAmount, { emitEvent: false });
return;
}
if (isPercentageType) {
const amountValue = (this.baseAmount * newValue) / 100;
newValueToSet = newValue + '';
this.amountControl.setValue(isNaN(amountValue) ? 0 : amountValue, { emitEvent: false });
if (notValid) {
this.percentageControl.setValue(newValue, { emitEvent: false });
}
} else {
const percentageValue = ((newValue / this.baseAmount) * 100).toFixed(2);
newValueToSet = newValue + '';
this.percentageControl.setValue(percentageValue, { emitEvent: false });
this.preparedLabel.set(`${this.label} (${percentageValue} درصد)`);
if (notValid) {
this.amountControl.setValue(newValue, { emitEvent: false });
}
}
this.preparedLabel.set(this.prepareLabel(isPercentageType));
this.lastValidAmount = amountValue;
const percentageValue = this.baseAmount
? ((amountValue / this.baseAmount) * 100).toFixed(2)
: '0';
this.lastValidPercentage = this.parseValue(percentageValue);
return newValue;
this.amountControl.setValue(this.lastValidAmount, { emitEvent: false });
this.percentageControl.setValue(percentageValue, { emitEvent: false });
this.preparedLabel.set(this.prepareLabel(false));
}
private prepareLabel(isPercentageType: boolean) {
@@ -177,25 +179,44 @@ export class AmountPercentageInputComponent {
}
ngOnInit() {
const amountValue = Number(this.amountControl.value || 0);
const percentageValue = Number(this.percentageControl.value || 0);
const isPercentageType = percentageValue > 0 && amountValue <= 0;
console.log(amountValue, percentageValue, isPercentageType);
this.preparedLabel.set(this.prepareLabel(isPercentageType));
const isPercentageType = this.defaultType === 'percentage';
this.selectedType.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((value) => {
this.preparedLabel.set(this.prepareLabel(value === 'percentage'));
});
this.selectedType.setValue(isPercentageType ? 'percentage' : 'amount');
this.percentageControl.valueChanges
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((value) => {
if (this.selectedType.value === 'percentage') {
this.syncFromPercentage(value);
}
});
this.amountControl.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((value) => {
if (this.selectedType.value === 'amount') {
this.syncFromAmount(value);
}
});
this.selectedType.setValue(isPercentageType ? 'percentage' : 'amount', { emitEvent: false });
this.lastValidAmount = this.parseValue(this.amountControl.value);
this.lastValidPercentage = this.parseValue(this.percentageControl.value);
if (isPercentageType) {
this.syncFromPercentage(this.percentageControl.value);
} else {
this.syncFromAmount(this.amountControl.value);
}
}
ngOnChanges() {
const amountValue = Number(this.amountControl.value || 0);
const percentageValue = Number(this.percentageControl.value || 0);
const isPercentageType = percentageValue > 0 && amountValue <= 0;
const isPercentageType = this.selectedType.value === 'percentage';
if (isPercentageType) {
this.syncFromPercentage(this.percentageControl.value);
} else {
this.syncFromAmount(this.amountControl.value);
}
this.preparedLabel.set(this.prepareLabel(isPercentageType));
}
}
@@ -58,7 +58,7 @@
}
@if (showIndex) {
<td>
{{ i * (currentPage || 1) + 1 }}
{{ i + 1 + (currentPage && perPage ? (currentPage - 1) * perPage : 0) }}
</td>
}
@for (col of columns; track col.field) {
@@ -18,8 +18,8 @@ import { SkeletonModule } from 'primeng/skeleton';
import { TableModule } from 'primeng/table';
import { KeyValueComponent } from '../key-value.component/key-value.component';
import { TableActionRowComponent } from '../table-action-row.component';
import { PageDataValueComponent } from './page-data-value.component';
import { IColumn } from './page-data-list.component';
import { PageDataValueComponent } from './page-data-value.component';
@Component({
selector: 'app-page-data-list-table-view',
@@ -50,6 +50,7 @@ export class AppPageDataListTableView<I = any> {
@Input() emptyPlaceholderDescription?: string = '';
@Input() emptyPlaceholderCtaLabel?: string;
@Input() currentPage?: number = 1;
@Input() perPage?: number = 1;
@Input() showEdit: boolean = false;
@Input() showDelete: boolean = false;
@Input() showDetails: boolean = false;
@@ -76,6 +76,7 @@
[emptyPlaceholderCtaLabel]="emptyPlaceholderCtaLabel || addNewCtaLabel"
[addNewCtaLabel]="addNewCtaLabel"
[currentPage]="currentPage"
[perPage]="perPage"
[showEdit]="showEdit"
[showDelete]="showDelete"
[showDetails]="showDetails"
@@ -84,9 +85,11 @@
[expandable]="expandable"
[expandableItemKey]="expandableItemKey"
[expandColumns]="expandColumns"
[showPaginator]="showPaginator"
(onEdit)="edit($event)"
(onDelete)="remove($event)"
(onDetails)="details($event)"
(onChangePage)="onPage($event)"
>
@if (pageTitle || showAdd || filter || showRefresh || moreActions) {
<ng-template #captionBox>
@@ -176,6 +176,8 @@ export class PageDataListComponent<I = any> {
};
onPage = ($event: any) => {
console.log('$event', $event);
this.onChangePage.emit($event);
};