From e027b891735d20ee041db8298aabd38feb54a96f Mon Sep 17 00:00:00 2001 From: ahasani194 Date: Sat, 18 Apr 2026 12:14:39 +0330 Subject: [PATCH] fix ui issues and upload image for guild good --- .../components/invoices/single.component.html | 2 +- .../license-info-dialog.component.ts | 14 +-- .../consumer/layouts/layout.component.ts | 14 +-- src/app/domains/consumer/models/io.d.ts | 22 ++--- .../customers/models/saleInvoices.io.d.ts | 50 ++++++++--- .../views/saleInvoices/single.component.html | 2 +- .../latest-Invoices.component.html | 4 + .../latest-Invoices.component.ts | 13 ++- src/app/domains/pos/models/good.io.ts | 1 + .../components/grid-view.component.html | 2 +- .../components/list-view.component.html | 2 +- .../order/order-card-template.component.html | 46 ++++++---- .../order/order-card-template.component.ts | 5 +- .../payloads/gold/form.component.html | 8 ++ .../payloads/gold/form.component.ts | 90 +++++++++++-------- .../payloads/standard/form.component.html | 13 +-- .../payloads/standard/form.component.ts | 14 ++- .../pos/modules/landing/models/types.ts | 1 + .../pos/modules/landing/store/main.store.ts | 29 +++--- .../consumers/views/single.component.ts | 18 +--- .../components/goods/form.component.html | 1 + .../guilds/components/goods/form.component.ts | 23 ++++- .../modules/guilds/services/goods.service.ts | 6 +- .../shared/abstractClasses/abstract-form.ts | 4 + .../amount-percentage-input.component.ts | 43 +++++++-- .../components/input/input.component.ts | 35 ++++---- .../shared/components/uploadFile/model.d.ts | 2 +- .../uploadFile/upload-file.component.ts | 6 +- src/app/utils/enum-translator.utils.ts | 20 +++++ src/app/utils/form-data.util.ts | 25 ++++++ src/app/utils/index.ts | 3 + src/app/utils/license-status.utils.ts | 12 +++ 32 files changed, 353 insertions(+), 177 deletions(-) create mode 100644 src/app/utils/enum-translator.utils.ts create mode 100644 src/app/utils/form-data.util.ts create mode 100644 src/app/utils/license-status.utils.ts diff --git a/src/app/domains/consumer/components/invoices/single.component.html b/src/app/domains/consumer/components/invoices/single.component.html index fc5b7d7..cdb57a2 100644 --- a/src/app/domains/consumer/components/invoices/single.component.html +++ b/src/app/domains/consumer/components/invoices/single.component.html @@ -31,7 +31,7 @@ - + @if (variant !== "customer") { diff --git a/src/app/domains/consumer/components/license-info-dialog.component.ts b/src/app/domains/consumer/components/license-info-dialog.component.ts index 9eac3bf..3899fd2 100644 --- a/src/app/domains/consumer/components/license-info-dialog.component.ts +++ b/src/app/domains/consumer/components/license-info-dialog.component.ts @@ -1,6 +1,6 @@ import { AbstractDialog } from '@/shared/abstractClasses/abstract-dialog'; import { KeyValueComponent } from '@/shared/components'; -import { licenseStatus } from '@/shared/localEnum/constants/licenseStatus'; +import { getLicenseStatus } from '@/utils'; import { Component, computed, inject } from '@angular/core'; import { Dialog } from 'primeng/dialog'; import { Message } from 'primeng/message'; @@ -14,15 +14,7 @@ import { ConsumerStore } from '../store/main.store'; export class ConsumerLicenseInfoDialogComponent extends AbstractDialog { private readonly store = inject(ConsumerStore); - license = computed(() => this.store.entity()?.license); + license = computed(() => this.store.entity()?.license_info); - licenseStatus = computed(() => { - if (!this.store.entity()?.license?.expires_at) { - return null; - } - if (new Date().getTime() > new Date(this.store.entity()?.license?.expires_at || '').getTime()) { - return licenseStatus.EXPIRED; - } - return licenseStatus.ACTIVE; - }); + licenseStatus = computed(() => getLicenseStatus(getLicenseStatus(this.license()?.expires_at))); } diff --git a/src/app/domains/consumer/layouts/layout.component.ts b/src/app/domains/consumer/layouts/layout.component.ts index 5ab9694..45d1628 100644 --- a/src/app/domains/consumer/layouts/layout.component.ts +++ b/src/app/domains/consumer/layouts/layout.component.ts @@ -1,5 +1,5 @@ import { LayoutService } from '@/layout/service/layout.service'; -import { licenseStatus } from '@/shared/localEnum/constants/licenseStatus'; +import { getLicenseStatus } from '@/utils'; import { Component, computed, inject, signal, TemplateRef, ViewChild } from '@angular/core'; import { RouterOutlet } from '@angular/router'; import { Button, ButtonIcon } from 'primeng/button'; @@ -20,15 +20,9 @@ export class LayoutComponent { visibleLicenseInfo = signal(false); - licenseStatus = computed(() => { - if (!this.store.entity()?.license?.expires_at) { - return null; - } - if (new Date().getTime() > new Date(this.store.entity()?.license?.expires_at || '').getTime()) { - return licenseStatus.EXPIRED; - } - return licenseStatus.ACTIVE; - }); + licenseStatus = computed(() => + getLicenseStatus(getLicenseStatus(this.store.entity()?.license_info?.expires_at)), + ); ngOnInit() { this.layoutService.setMenuItems(CONSUMER_MENU_ITEMS); diff --git a/src/app/domains/consumer/models/io.d.ts b/src/app/domains/consumer/models/io.d.ts index cc2bfac..578bdd1 100644 --- a/src/app/domains/consumer/models/io.d.ts +++ b/src/app/domains/consumer/models/io.d.ts @@ -1,17 +1,19 @@ -import ISummary from '@/core/models/summary'; -import { TLicenseStatus } from '@/shared/localEnum/constants/licenseStatus'; - export interface IConsumerInfoRawResponse { id: string; first_name: string; last_name: string; - full_name: string; mobile_number: string; - license: { - starts_at: string; - expires_at: string; - status: TLicenseStatus; - partner?: ISummary; - }; + status: string; + fullname: string; + license_info: LicenseInfo; } export interface IConsumerInfoResponse extends IConsumerInfoRawResponse {} + +interface LicenseInfo { + id: string; + starts_at: string; + expires_at: string; + partner: { + name: string; + }; +} diff --git a/src/app/domains/consumer/modules/customers/models/saleInvoices.io.d.ts b/src/app/domains/consumer/modules/customers/models/saleInvoices.io.d.ts index 13141e4..2b89ba9 100644 --- a/src/app/domains/consumer/modules/customers/models/saleInvoices.io.d.ts +++ b/src/app/domains/consumer/modules/customers/models/saleInvoices.io.d.ts @@ -1,22 +1,25 @@ import ISummary from '@/core/models/summary'; +import { CustomerType } from '@/shared/localEnum/constants/customerTypes'; +import { CustomerIndividual, CustomerLegal } from './io'; export interface ICustomerSaleInvoicesRawResponse { id: string; code: string; - invoice_date: string; - notes?: string; total_amount: string; + invoice_date: string; + consumer_account: ConsumerAccount; pos: Pos; - account: ConsumerAccount; + items: SaleInvoiceItem[]; + payments: Payment[]; + unknown_customer?: UnknownCustomer; + customer?: Customer; + notes?: string; created_at: string; - items_count: number; - payments: Payments[]; } export interface ICustomerSaleInvoicesResponse extends ICustomerSaleInvoicesRawResponse {} interface ConsumerAccount { role: string; - user: User; account: Account; } @@ -24,11 +27,6 @@ interface Account { username: string; } -interface User { - first_name: string; - last_name: string; -} - interface Pos extends ISummary { complex: Complex; } @@ -37,8 +35,36 @@ interface Complex extends ISummary { business_activity: ISummary; } -interface Payments { +interface Payment { amount: string; paid_at: string; payment_method: string; } + +interface SaleInvoiceItem { + id: string; + good: ISummary; +} + +interface Customer { + id: string; + type: CustomerType; + customer_legal?: CustomerLegal; + customer_individual?: CustomerIndividual; +} + +// interface CustomerIndividual { +// id: string; +// first_name: string; +// last_name: string; +// } + +// interface CustomerLegal { +// id: string; +// name: string; +// } + +interface UnknownCustomer { + name: string; + national_id: string; +} diff --git a/src/app/domains/consumer/modules/customers/views/saleInvoices/single.component.html b/src/app/domains/consumer/modules/customers/views/saleInvoices/single.component.html index b692211..5310703 100644 --- a/src/app/domains/consumer/modules/customers/views/saleInvoices/single.component.html +++ b/src/app/domains/consumer/modules/customers/views/saleInvoices/single.component.html @@ -31,7 +31,7 @@ - + diff --git a/src/app/domains/consumer/modules/dashboard/components/statistics/latestInvoices/latest-Invoices.component.html b/src/app/domains/consumer/modules/dashboard/components/statistics/latestInvoices/latest-Invoices.component.html index be2712e..7634913 100644 --- a/src/app/domains/consumer/modules/dashboard/components/statistics/latestInvoices/latest-Invoices.component.html +++ b/src/app/domains/consumer/modules/dashboard/components/statistics/latestInvoices/latest-Invoices.component.html @@ -10,4 +10,8 @@ تمامی فاکتورها + + + + diff --git a/src/app/domains/consumer/modules/dashboard/components/statistics/latestInvoices/latest-Invoices.component.ts b/src/app/domains/consumer/modules/dashboard/components/statistics/latestInvoices/latest-Invoices.component.ts index 5454242..0855231 100644 --- a/src/app/domains/consumer/modules/dashboard/components/statistics/latestInvoices/latest-Invoices.component.ts +++ b/src/app/domains/consumer/modules/dashboard/components/statistics/latestInvoices/latest-Invoices.component.ts @@ -1,8 +1,9 @@ import { consumerSaleInvoicesNamedRoutes } from '@/domains/consumer/modules/saleInvoices/constants'; import { AbstractList } from '@/shared/abstractClasses'; import { PageDataListComponent } from '@/shared/components/pageDataList/page-data-list.component'; -import { Component, inject } from '@angular/core'; +import { Component, inject, TemplateRef, ViewChild } from '@angular/core'; import { Router, RouterLink } from '@angular/router'; +import { Badge } from 'primeng/badge'; import { ButtonDirective } from 'primeng/button'; import { IStatisticsSaleInvoicesResponse } from '../../../models'; import { CustomerStatisticsService } from '../../../services/main.service'; @@ -10,9 +11,11 @@ import { CustomerStatisticsService } from '../../../services/main.service'; @Component({ selector: 'consumer-statistics-latest-Invoices', templateUrl: './latest-Invoices.component.html', - imports: [PageDataListComponent, ButtonDirective, RouterLink], + imports: [PageDataListComponent, ButtonDirective, RouterLink, Badge], }) export class ConsumerStatisticsLatestInvoicesComponent extends AbstractList { + @ViewChild('status', { static: true }) status!: TemplateRef; + private readonly service = inject(CustomerStatisticsService); private readonly router = inject(Router); @@ -22,11 +25,7 @@ export class ConsumerStatisticsLatestInvoicesComponent extends AbstractList - +
{{ good.name }} diff --git a/src/app/domains/pos/modules/landing/components/list-view.component.html b/src/app/domains/pos/modules/landing/components/list-view.component.html index e700d65..1b0c217 100644 --- a/src/app/domains/pos/modules/landing/components/list-view.component.html +++ b/src/app/domains/pos/modules/landing/components/list-view.component.html @@ -9,7 +9,7 @@ @for (good of goods(); track good.id) {
- +
{{ good.name }} diff --git a/src/app/domains/pos/modules/landing/components/order/order-card-template.component.html b/src/app/domains/pos/modules/landing/components/order/order-card-template.component.html index 14f4c55..6ebcb42 100644 --- a/src/app/domains/pos/modules/landing/components/order/order-card-template.component.html +++ b/src/app/domains/pos/modules/landing/components/order/order-card-template.component.html @@ -1,11 +1,33 @@
-
- -
-
-
- {{ inOrderGood.good.name }} - {{ inOrderGood.good.sku }} +
+ +
+
+
+
+
+ + {{ inOrderGood.good.name }} + +
+ ({{ inOrderGood.good.sku }}) +
+
+ + {{ inOrderGood.quantity }} {{ enumTranslator(inOrderGood.good.unit_type) }} + +
+ @if (!inOrderGood.discount_amount) { + + } @else { +
+ + +
+ }
-
-
- -
-
diff --git a/src/app/domains/pos/modules/landing/components/order/order-card-template.component.ts b/src/app/domains/pos/modules/landing/components/order/order-card-template.component.ts index 36a12d9..32bae4f 100644 --- a/src/app/domains/pos/modules/landing/components/order/order-card-template.component.ts +++ b/src/app/domains/pos/modules/landing/components/order/order-card-template.component.ts @@ -1,3 +1,5 @@ +import { PriceMaskDirective } from '@/shared/directives'; +import { enumTranslator } from '@/utils'; import { Component, inject, Input, signal } from '@angular/core'; import { ButtonDirective } from 'primeng/button'; import images from 'src/assets/images'; @@ -8,7 +10,7 @@ import { PayloadFormDialogComponent } from '../payload-form.component'; @Component({ selector: 'pos-order-card-template', templateUrl: './order-card-template.component.html', - imports: [ButtonDirective, PayloadFormDialogComponent], + imports: [ButtonDirective, PayloadFormDialogComponent, PriceMaskDirective], }) export class OrderCardTemplateComponent { @Input({ required: true }) inOrderGood!: IPosInOrderGood; @@ -18,6 +20,7 @@ export class OrderCardTemplateComponent { visible = signal(false); placeholderImage = images.placeholders.default; + enumTranslator = (key: string) => enumTranslator(key); removeGoodFromOrder() { this.store.removeFromInOrderGoods(this.inOrderGood.id); diff --git a/src/app/domains/pos/modules/landing/components/payloads/gold/form.component.html b/src/app/domains/pos/modules/landing/components/payloads/gold/form.component.html index b4bfca5..1aad815 100644 --- a/src/app/domains/pos/modules/landing/components/payloads/gold/form.component.html +++ b/src/app/domains/pos/modules/landing/components/payloads/gold/form.component.html @@ -7,6 +7,8 @@ [percentageControl]="form.controls.payload.controls.wages_percentage" [amountControl]="form.controls.payload.controls.wages_amount" [baseAmount]="unitWithQuantity()" + [minAmount]="0" + [maxAmount]="unitWithQuantity()" name="wages" label="اجرت" /> @@ -14,6 +16,8 @@ [percentageControl]="form.controls.payload.controls.commission_percentage" [amountControl]="form.controls.payload.controls.commission_amount" [baseAmount]="unitWithQuantity()" + [minAmount]="0" + [maxAmount]="unitWithQuantity()" name="commission" label="حق‌العمل" /> @@ -21,6 +25,8 @@ [percentageControl]="form.controls.payload.controls.profit_percentage" [amountControl]="form.controls.payload.controls.profit_amount" [baseAmount]="totalAmountBeforeProfit()" + [minAmount]="0" + [maxAmount]="totalAmountBeforeProfit()" name="profit" label="سود" /> @@ -29,6 +35,8 @@ [percentageControl]="form.controls.discount_percentage" [amountControl]="form.controls.discount_amount" [baseAmount]="baseTotalAmount()" + [minAmount]="0" + [maxAmount]="baseTotalAmount()" name="discount" label="تخفیف" /> diff --git a/src/app/domains/pos/modules/landing/components/payloads/gold/form.component.ts b/src/app/domains/pos/modules/landing/components/payloads/gold/form.component.ts index e3eecb6..8baaa88 100644 --- a/src/app/domains/pos/modules/landing/components/payloads/gold/form.component.ts +++ b/src/app/domains/pos/modules/landing/components/payloads/gold/form.component.ts @@ -4,7 +4,7 @@ import { InputComponent } from '@/shared/components'; import { AmountPercentageInputComponent } from '@/shared/components/amountPercentageInput/amount-percentage-input.component'; import { TGoldKarat } from '@/shared/localEnum/constants/goldKarat'; import { formatWithCurrency } from '@/utils'; -import { Component, computed, EventEmitter, Output } from '@angular/core'; +import { Component, EventEmitter, Output, signal } from '@angular/core'; import { ReactiveFormsModule, Validators } from '@angular/forms'; import { Button } from 'primeng/button'; import { IGoldPayload, IPosOrderItem } from '../../../models'; @@ -29,30 +29,54 @@ export class PosGoldPayloadFormComponent extends AbstractForm< @Output() onSubmitForm = new EventEmitter(); @Output() onChangeTotalAmount = new EventEmitter(); + unitWithQuantity = signal(0); + totalAmountBeforeProfit = signal(0); + baseTotalAmount = signal(0); + taxAmount = signal(0); + totalAmount = signal(0); + private readonly initialForm = () => { const form = this.fb.group({ - unit_price: [this.initialValues?.unit_price || 200_000_000, [Validators.required]], - quantity: [this.initialValues?.quantity || 2, [Validators.required]], + unit_price: [this.initialValues?.unit_price || 0, [Validators.required]], + quantity: [this.initialValues?.quantity || 0, [Validators.required]], discount_amount: [this.initialValues?.discount || 0], - discount_percentage: [this.initialValues?.discount || 0], + discount_percentage: [0, [Validators.max(100), Validators.min(0)]], payload: this.fb.group({ commission_amount: [this.initialValues?.payload?.commission || 0, [Validators.required]], - commission_percentage: [ - this.initialValues?.payload?.commission || 10, - [Validators.required], - ], + commission_percentage: [0, [Validators.required, Validators.max(100), Validators.min(0)]], wages_amount: [this.initialValues?.payload?.wages || 0, [Validators.required]], - wages_percentage: [this.initialValues?.payload?.wages || 10, [Validators.required]], + wages_percentage: [0, [Validators.required, Validators.max(100), Validators.min(0)]], profit_amount: [this.initialValues?.payload?.profit || 0, [Validators.required]], - profit_percentage: [this.initialValues?.payload?.profit || 10, [Validators.required]], + profit_percentage: [0, [Validators.required, Validators.max(100), Validators.min(0)]], karat: [this.initialValues?.payload?.karat || '', [Validators.required]], }), }); + form.valueChanges.subscribe((value) => { + this.updateCalculateAmount(value as any); + }); + + form.setValue({ + unit_price: 200_000_000, + quantity: 2, + discount_amount: 0, + discount_percentage: 0, + payload: { + commission_amount: 0, + commission_percentage: 10, + wages_amount: 0, + wages_percentage: 10, + profit_amount: 0, + profit_percentage: 10, + karat: '18', + }, + }); + return form; }; form = this.initialForm(); + override submitForm(payload: IPosOrderItem) { this.onSubmit.emit({ ...payload, @@ -68,36 +92,28 @@ export class PosGoldPayloadFormComponent extends AbstractForm< }); } - unitWithQuantity = computed( - () => (this.form.value.unit_price ?? 0) * (this.form.value.quantity ?? 0), - ); + updateCalculateAmount(payload: Partial>) { + const commissionAmount = Number(payload.payload?.commission_amount ?? '0'); + const wageAmount = Number(payload.payload?.wages ?? '0'); + const discountAmount = Number(payload.discount_amount ?? '0'); + const profitAmount = Number(payload.payload?.profit_amount ?? '0'); + const unitPrice = Number(payload.unit_price ?? '0'); + const quantity = Number(payload.quantity ?? '0'); - totalAmountBeforeProfit = computed(() => { - const commissionAmount = Number(this.form.value.payload?.commission_amount ?? '0'); - const wageAmount = Number(this.form.value.payload?.wages_amount ?? '0'); - return this.unitWithQuantity() + wageAmount + commissionAmount; - }); + const unitWithQuantity = unitPrice * quantity; + const totalAmountBeforeProfit = unitWithQuantity + wageAmount + commissionAmount; + const baseTotalAmount = totalAmountBeforeProfit + profitAmount; + const taxAmount = (profitAmount + commissionAmount + wageAmount - discountAmount) * 0.1; + const totalAmount = baseTotalAmount - discountAmount + taxAmount; - baseTotalAmount = computed(() => { - const commissionAmount = Number(this.form.value.payload?.commission_amount ?? '0'); - const wageAmount = Number(this.form.value.payload?.wages_amount ?? '0'); - const profitAmount = Number(this.form.value.payload?.profit_amount ?? '0'); + this.unitWithQuantity.set(unitWithQuantity); + this.totalAmountBeforeProfit.set(totalAmountBeforeProfit); + this.baseTotalAmount.set(baseTotalAmount); + this.taxAmount.set(taxAmount); + this.totalAmount.set(totalAmount); - return this.unitWithQuantity() + profitAmount + commissionAmount + wageAmount; - }); - - taxAmount = computed(() => { - const commissionAmount = Number(this.form.value.payload?.commission_amount ?? '0'); - const wageAmount = Number(this.form.value.payload?.wages_amount ?? '0'); - const profitAmount = Number(this.form.value.payload?.profit_amount ?? '0'); - const discountAmount = Number(this.form.value.discount_amount ?? '0'); - return (profitAmount + commissionAmount + wageAmount - discountAmount) * 0.1; - }); - - totalAmount = computed(() => { - const discountAmount = Number(this.form.value.discount_amount ?? '0'); - return this.baseTotalAmount() - discountAmount + this.taxAmount(); - }); + this.onChangeTotalAmount.emit(totalAmount); + } toAmountFormat(amount: number) { if (!amount) { diff --git a/src/app/domains/pos/modules/landing/components/payloads/standard/form.component.html b/src/app/domains/pos/modules/landing/components/payloads/standard/form.component.html index cb1e84e..cf1c5e8 100644 --- a/src/app/domains/pos/modules/landing/components/payloads/standard/form.component.html +++ b/src/app/domains/pos/modules/landing/components/payloads/standard/form.component.html @@ -9,15 +9,16 @@ [min]="0" /> - +
, @@ -23,7 +30,8 @@ export class PosStandardPayloadFormComponent extends AbstractForm< const form = this.fb.group({ unit_price: [this.initialValues?.unit_price || 0, [Validators.required]], quantity: [this.initialValues?.quantity || 0, [Validators.required]], - discount: [this.initialValues?.discount || ''], + discount_amount: [this.initialValues?.discount || 0, [Validators.min(0)]], + discount_percentage: [0, [Validators.min(0), Validators.max(100)]], }); form.valueChanges.subscribe((value) => { @@ -60,7 +68,7 @@ export class PosStandardPayloadFormComponent extends AbstractForm< updateCalculateAmount(value: Partial>) { const baseTotalAmount = (value.unit_price ?? 0) * (value.quantity ?? 0); - const discountAmount = (baseTotalAmount * Number(value.discount || '0')) / 100; + const discountAmount = Number(value.discount_amount ?? '0'); const baseTotalAmountWithoutTax = baseTotalAmount - discountAmount; const taxAmount = baseTotalAmountWithoutTax * 0.1; diff --git a/src/app/domains/pos/modules/landing/models/types.ts b/src/app/domains/pos/modules/landing/models/types.ts index 6bf2b50..b0008ec 100644 --- a/src/app/domains/pos/modules/landing/models/types.ts +++ b/src/app/domains/pos/modules/landing/models/types.ts @@ -11,6 +11,7 @@ export interface IPosOrderItem { good_id?: string; service_id?: string; notes?: string; + image_url?: string; payload: T; base_total_amount: number; diff --git a/src/app/domains/pos/modules/landing/store/main.store.ts b/src/app/domains/pos/modules/landing/store/main.store.ts index 0bf3db7..b9c997f 100644 --- a/src/app/domains/pos/modules/landing/store/main.store.ts +++ b/src/app/domains/pos/modules/landing/store/main.store.ts @@ -233,19 +233,22 @@ export class PosLandingStore { }), map((_res) => { - // this.setState({ - // inOrderGoods: [], - // customerDetails: { - // type: CustomerType.UNKNOWN, - // }, - // invoiceDate: nowJalali(JALALI_DATE_FORMATS.NUMERIC), - // payments: { - // cash: 0, - // set_off: 0, - // terminal: 0, - // }, - // orderNote: '', - // }); + if (_res) { + this.setState({ + inOrderGoods: [], + customerDetails: { + type: CustomerType.UNKNOWN, + }, + invoiceDate: nowJalali(JALALI_DATE_FORMATS.NUMERIC), + payments: { + cash: 0, + set_off: 0, + terminal: 0, + }, + orderNote: '', + }); + } + return _res; }), ); diff --git a/src/app/domains/superAdmin/modules/consumers/views/single.component.ts b/src/app/domains/superAdmin/modules/consumers/views/single.component.ts index b0ff717..2760d17 100644 --- a/src/app/domains/superAdmin/modules/consumers/views/single.component.ts +++ b/src/app/domains/superAdmin/modules/consumers/views/single.component.ts @@ -1,7 +1,7 @@ import { BreadcrumbService } from '@/core/services'; import { AppCardComponent, KeyValueComponent } from '@/shared/components'; import { JalaliDateDirective } from '@/shared/directives'; -import { licenseStatus } from '@/shared/localEnum/constants/licenseStatus'; +import { getLicenseStatus } from '@/utils'; import { Component, computed, effect, inject, signal } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { Button } from 'primeng/button'; @@ -38,19 +38,9 @@ export class ConsumerComponent { readonly loading = computed(() => this.store.loading()); readonly consumer = computed(() => this.store.entity()); readonly license = computed(() => this.store.entity()?.license_info); - readonly licenseStatus = computed(() => { - console.log(this.store.entity()?.license_info?.expires_at); - - if (!this.store.entity()?.license_info?.expires_at) { - return null; - } - if ( - new Date().getTime() > new Date(this.store.entity()?.license_info?.expires_at || '').getTime() - ) { - return licenseStatus.EXPIRED; - } - return licenseStatus.ACTIVE; - }); + readonly licenseStatus = computed(() => + getLicenseStatus(this.store.entity()?.license_info?.expires_at), + ); constructor() { effect(() => { diff --git a/src/app/domains/superAdmin/modules/guilds/components/goods/form.component.html b/src/app/domains/superAdmin/modules/guilds/components/goods/form.component.html index 79f3957..5208e21 100644 --- a/src/app/domains/superAdmin/modules/guilds/components/goods/form.component.html +++ b/src/app/domains/superAdmin/modules/guilds/components/goods/form.component.html @@ -1,5 +1,6 @@
+ >(null); + form = this.fb.group({ name: [this.initialValues?.name || '', [Validators.required]], sku: [this.initialValues?.sku || '', [Validators.required]], @@ -44,10 +51,20 @@ export class GuildGoodFormComponent extends AbstractFormDialog< return `${this.editMode ? 'ویرایش' : 'ایجاد'} کالا`; } + changeFile(payload: onSelectFileArgs) { + this.goodImage.set(payload.file); + } + + override ngOnChanges(): void { + this.form.patchValue(this.initialValues || {}); + this.form.controls.category_id.setValue(this.initialValues?.category.id || ''); + } + override submitForm(payload: IGuildGoodRequest) { + const formData = buildFormData(this.form, { image: this.goodImage() }); if (this.editMode) { - return this.service.update(this.guildId, this.categoryId!, payload); + return this.service.update(this.guildId, this.categoryId!, formData); } - return this.service.create(this.guildId, payload); + return this.service.create(this.guildId, formData); } } diff --git a/src/app/domains/superAdmin/modules/guilds/services/goods.service.ts b/src/app/domains/superAdmin/modules/guilds/services/goods.service.ts index d697fa4..bc9b7ba 100644 --- a/src/app/domains/superAdmin/modules/guilds/services/goods.service.ts +++ b/src/app/domains/superAdmin/modules/guilds/services/goods.service.ts @@ -3,7 +3,7 @@ import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { GUILD_GOODS_API_ROUTES } from '../constants/apiRoutes/goods'; -import { IGuildGoodRequest, IGuildGoodsRawResponse, IGuildGoodsResponse } from '../models'; +import { IGuildGoodsRawResponse, IGuildGoodsResponse } from '../models'; @Injectable({ providedIn: 'root' }) export class GuildGoodsService { @@ -18,11 +18,11 @@ export class GuildGoodsService { return this.http.get(this.apiRoutes.single(guildId, id)); } - create(guildId: string, data: IGuildGoodRequest): Observable { + create(guildId: string, data: FormData): Observable { return this.http.post(this.apiRoutes.list(guildId), data); } - update(guildId: string, id: string, data: IGuildGoodRequest): Observable { + update(guildId: string, id: string, data: FormData): Observable { return this.http.patch(this.apiRoutes.single(guildId, id), data); } } diff --git a/src/app/shared/abstractClasses/abstract-form.ts b/src/app/shared/abstractClasses/abstract-form.ts index 61ce2da..f1ba607 100644 --- a/src/app/shared/abstractClasses/abstract-form.ts +++ b/src/app/shared/abstractClasses/abstract-form.ts @@ -36,6 +36,10 @@ export abstract class AbstractForm< return payload; } + ngOnInit() { + this.form.patchValue(this.initialValues ?? {}); + } + ngOnChanges() { this.form.patchValue(this.initialValues ?? {}); } diff --git a/src/app/shared/components/amountPercentageInput/amount-percentage-input.component.ts b/src/app/shared/components/amountPercentageInput/amount-percentage-input.component.ts index c631aea..9e157be 100644 --- a/src/app/shared/components/amountPercentageInput/amount-percentage-input.component.ts +++ b/src/app/shared/components/amountPercentageInput/amount-percentage-input.component.ts @@ -79,14 +79,27 @@ export class AmountPercentageInputComponent { onInput($event: Event, isPriceType: boolean = false) { // @ts-ignore let value = $event.target.value as string; + // @ts-ignore + const isDotInput = $event.data === '.'; + + if (isDotInput) { + return; + } + if (isPriceType) { value = value.replace(/,/g, ''); } - this.modifyControlsData(value); + const newValueToSet = this.modifyControlsData(value); + + if (newValueToSet !== null) { + // @ts-ignore + $event.target.value = newValueToSet; + } } private modifyControlsData(value: string) { + let newValueToSet: Maybe = null; let newValue = parseFloat(value); const isPercentageType = this.selectedType.value === 'percentage'; @@ -96,7 +109,9 @@ export class AmountPercentageInputComponent { const maxValidator = max < newValue; const minValidator = min > newValue; - if (minValidator || maxValidator) { + const notValid = minValidator || maxValidator; + + if (notValid) { if (minValidator) { this.toastService.warn({ text: `مقدار فیلد باید بیشتر از ${min} باشد.`, @@ -116,16 +131,23 @@ export class AmountPercentageInputComponent { if (isPercentageType) { const amountValue = (this.baseAmount * newValue) / 100; + newValueToSet = newValue + ''; this.amountControl.setValue(amountValue); + if (notValid) { + this.percentageControl.setValue(newValue); + } } else { const percentageValue = (newValue / this.baseAmount) * 100; - + newValueToSet = newValue + ''; this.percentageControl.setValue(percentageValue); this.preparedLabel.set(`${this.label} (${percentageValue} درصد)`); - // @ts-ignore - // $event.target.value = newValue; + if (notValid) { + this.amountControl.setValue(newValue); + } } this.preparedLabel.set(this.prepareLabel(isPercentageType)); + + return newValue; } private prepareLabel(isPercentageType: boolean) { @@ -145,4 +167,15 @@ export class AmountPercentageInputComponent { this.preparedLabel.set(this.prepareLabel(value === 'percentage')); }); } + + ngOnChanges() { + const isPercentageType = this.selectedType.value === 'percentage'; + this.modifyControlsData( + isPercentageType ? this.percentageControl.value : this.amountControl.value, + ); + + this.selectedType.valueChanges.subscribe((value) => { + this.preparedLabel.set(this.prepareLabel(value === 'percentage')); + }); + } } diff --git a/src/app/shared/components/input/input.component.ts b/src/app/shared/components/input/input.component.ts index ce48179..74896b2 100644 --- a/src/app/shared/components/input/input.component.ts +++ b/src/app/shared/components/input/input.component.ts @@ -189,26 +189,27 @@ export class InputComponent { onInput($event: Event, isPriceFormat?: boolean) { // @ts-ignore let value = $event.target.value as string; + // @ts-ignore + const isDotInput = $event.data === '.'; if (isPriceFormat) { value = value.replace(/,/g, ''); } - if (this.inputMode === 'numeric') { - let newValue = parseFloat(value); - + let replacedTargetValue: Maybe = null; + if (this.inputMode === 'numeric' && !isDotInput) { + let newValue = parseFloat(value || '0'); const minValidator = (this.min || this.min === 0) && parseFloat(value) < this.min!; const maxValidator = (this.max || this.max === 0) && parseFloat(value) > this.max; - if (minValidator || maxValidator) { if (minValidator) { this.toastService.warn({ - text: `مقدار فیلد باید بیشتر از ${this.min} باشد.`, + text: `مقدار ${this.label} نمی‌تواند بیشتر از ${this.min} باشد.`, }); } if (maxValidator) { this.toastService.warn({ - text: `مقدار فیلد باید کمتر از ${this.max} باشد.`, + text: `مقدار ${this.label} نمی‌تواند کمتر از ${this.max} باشد.`, }); } @@ -217,23 +218,23 @@ export class InputComponent { newValue = 0; } - if (isPriceFormat) { - // @ts-ignore - $event.target.value = newValue.toLocaleString('en-US'); - } else { - // @ts-ignore - $event.target.value = newValue; - } - } - if (!['nationalId', 'phone', 'postalCode', 'mobile'].includes(this.type)) + replacedTargetValue = isPriceFormat ? newValue.toLocaleString('en-US') : newValue; this.control.setValue(newValue); + } + if (!['nationalId', 'phone', 'postalCode', 'mobile'].includes(this.type)) { + this.control.setValue(newValue); + } } - if (this.preparedMaxLength && this.preparedMaxLength < value.length) { let newValue = value.slice(0, value.length - 1); // @ts-ignore - $event.target.value = newValue; + replacedTargetValue = newValue; this.control.setValue(newValue); } + + if (replacedTargetValue !== null) { + // @ts-ignore + this.$event.value = replacedTargetValue; + } } } diff --git a/src/app/shared/components/uploadFile/model.d.ts b/src/app/shared/components/uploadFile/model.d.ts index 0308646..20eff13 100644 --- a/src/app/shared/components/uploadFile/model.d.ts +++ b/src/app/shared/components/uploadFile/model.d.ts @@ -1,4 +1,4 @@ -export interface onSelectArgs { +export interface onSelectFileArgs { data: FormData; file: File; } diff --git a/src/app/shared/components/uploadFile/upload-file.component.ts b/src/app/shared/components/uploadFile/upload-file.component.ts index 31c18cf..75dd7e2 100644 --- a/src/app/shared/components/uploadFile/upload-file.component.ts +++ b/src/app/shared/components/uploadFile/upload-file.component.ts @@ -1,7 +1,7 @@ import { Maybe } from '@/core'; import { Component, EventEmitter, Input, Output } from '@angular/core'; import { FileSelectEvent, FileUpload } from 'primeng/fileupload'; -import { onSelectArgs } from './model'; +import { onSelectFileArgs } from './model'; @Component({ selector: 'app-shared-upload-file', @@ -13,11 +13,11 @@ export class SharedUploadFileComponent { @Input() loading: boolean = false; @Input() name: string = 'file'; @Input() accept: string = 'image/*'; - @Input() maxSize: Maybe = 5 * 1024; + @Input() maxSize: Maybe = 5 * 1024 * 1024; @Input() error: Maybe = null; @Input() hint: Maybe = null; - @Output() onSelect = new EventEmitter(); + @Output() onSelect = new EventEmitter(); onSelectedFile($event: FileSelectEvent) { const file = $event.files?.[0]; diff --git a/src/app/utils/enum-translator.utils.ts b/src/app/utils/enum-translator.utils.ts new file mode 100644 index 0000000..a9163e9 --- /dev/null +++ b/src/app/utils/enum-translator.utils.ts @@ -0,0 +1,20 @@ +import { Maybe } from '@/core'; +import { apiEnumTranslates } from '@/shared/apiEnum/constants'; + +const translates = { + COUNT: 'تعداد', + GRAM: 'گرم', + KILOGRAM: 'کیلوگرم', + METER: 'متر', + MILLILITER: 'میلی‌لیتر', + LITER: 'لیتر', + HOUR: 'ساعت', +}; + +export function enumTranslator(key: Maybe): string { + if (!key) { + return ''; + } + // @ts-ignore + return apiEnumTranslates[key] || translates[key] || key; +} diff --git a/src/app/utils/form-data.util.ts b/src/app/utils/form-data.util.ts new file mode 100644 index 0000000..5fe239f --- /dev/null +++ b/src/app/utils/form-data.util.ts @@ -0,0 +1,25 @@ +export function buildFormData( + formGroup: any, + fileProperties: { [key: string]: File | null } = {}, +): FormData { + const formData = new FormData(); + + console.log(formGroup); + console.log(formGroup.controls); + + Object.keys(formGroup.controls).forEach((key) => { + const control = formGroup.get(key); + if (control && control.value !== null && control.value !== undefined) { + formData.append(key, control.value); + } + }); + + Object.keys(fileProperties).forEach((key) => { + const file = fileProperties[key]; + if (file) { + formData.append(key, file, file.name); + } + }); + + return formData; +} diff --git a/src/app/utils/index.ts b/src/app/utils/index.ts index f5abda6..2baa572 100644 --- a/src/app/utils/index.ts +++ b/src/app/utils/index.ts @@ -1,6 +1,9 @@ export * from './currency'; export * from './date-formatter.utils'; +export * from './enum-translator.utils'; +export * from './form-data.util'; export * from './good-unit-types.utils'; +export * from './license-status.utils'; export * from './name'; export * from './page-params.utils'; export * from './price-mask.utils'; diff --git a/src/app/utils/license-status.utils.ts b/src/app/utils/license-status.utils.ts new file mode 100644 index 0000000..68c96db --- /dev/null +++ b/src/app/utils/license-status.utils.ts @@ -0,0 +1,12 @@ +import { Maybe } from '@/core'; +import { licenseStatus } from '@/shared/localEnum/constants/licenseStatus'; + +export function getLicenseStatus(licenseExpiresAt?: Maybe) { + if (!licenseExpiresAt) { + return null; + } + if (new Date().getTime() > new Date(licenseExpiresAt || '').getTime()) { + return licenseStatus.EXPIRED; + } + return licenseStatus.ACTIVE; +}