fix ui issues and upload image for guild good

This commit is contained in:
2026-04-18 12:14:39 +03:30
parent ac23d47b79
commit e027b89173
32 changed files with 353 additions and 177 deletions
+1
View File
@@ -9,6 +9,7 @@ export interface IGoodRawResponse {
unit_type: UnitType;
pricing_model: string;
description?: string;
image_url: string;
is_default_guild_good: boolean;
created_at: string;
updated_at: string;
@@ -8,7 +8,7 @@
} @else {
@for (good of goods(); track good.id) {
<div class="flex-1 bg-surface-card rounded-lg p-1 shadow-xs">
<img [src]="goodPlaceholder" class="w-full aspect-[1.3] object-cover rounded-md" />
<img [src]="good.image_url || goodPlaceholder" class="w-full aspect-[1.3] object-cover rounded-md" />
<div class="mt-2">
<span class="text-lg font-bold">
{{ good.name }}
@@ -9,7 +9,7 @@
@for (good of goods(); track good.id) {
<div class="flex items-center bg-surface-card rounded-lg p-1 shadow-xs gap-2">
<div class="grow flex items-center gap-2">
<img [src]="goodPlaceholder" class="w-12 h-12 object-cover rounded-md" />
<img [src]="good.image_url || goodPlaceholder" class="w-12 h-12 object-cover rounded-md" />
<div class="flex flex-col gap-2">
<span class="text-lg font-bold">
{{ good.name }}
@@ -1,11 +1,33 @@
<div class="border border-surface-border rounded-xl p-2 shadow-sm">
<div class="flex gap-3 items-stretch">
<img [src]="placeholderImage" class="w-24 h-24 rounded-md bg-surface-100 object-cover shrink-0" />
<div class="flex flex-col grow justify-between">
<div class="flex items-center">
<div class="flex flex-col grow">
<span class="font-bold text-lg">{{ inOrderGood.good.name }}</span>
<span class="text-sm text-muted-color">{{ inOrderGood.good.sku }}</span>
<div class="flex gap-3 items-center">
<img
[src]="inOrderGood.good.image_url || placeholderImage"
class="w-24 h-24 rounded-md bg-surface-100 object-cover shrink-0 border border-surface-border"
/>
<div class="flex flex-col grow justify-between overflow-hidden">
<div class="flex items-stretch grow gap-4">
<div class="flex flex-col justify-between gap-2 grow overflow-hidden py-1">
<div class="shrink-0 flex flex-col w-full overflow-hidden">
<div class="flex w-full overflow-hidden gap-1 items-center">
<span class="font-bold text-lg text-ellipsis text-nowrap overflow-hidden">
{{ inOrderGood.good.name }}
</span>
<div class="grow">
<span class="text-sm text-muted-color">({{ inOrderGood.good.sku }})</span>
</div>
</div>
<span class="text-sm text-ellipsis text-nowrap overflow-hidden">
{{ inOrderGood.quantity }} {{ enumTranslator(inOrderGood.good.unit_type) }}
</span>
</div>
@if (!inOrderGood.discount_amount) {
<span [appPriceMask]="inOrderGood.total_amount"></span>
} @else {
<div class="flex flex-col">
<span class="line-through text-muted-color text-xs" [appPriceMask]="inOrderGood.base_total_amount"></span>
<span [appPriceMask]="inOrderGood.base_total_amount - inOrderGood.discount_amount"></span>
</div>
}
</div>
<div class="flex shrink-0 gap-1">
<button
@@ -28,16 +50,6 @@
></button>
</div>
</div>
<div class="flex items-center gap-4 mt-auto">
<div class="ms-auto shrink-0">
<!-- <app-uikit-counter
[min]="1"
[max]="inOrderGood.maxQuantity"
[value]="inOrderGood.count"
(valueChange)="updateInOrderGoodQuantity(inOrderGood.goodId, $event)"
></app-uikit-counter> -->
</div>
</div>
</div>
</div>
</div>
@@ -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);
@@ -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="تخفیف"
/>
@@ -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<IGoldPayload>();
@Output() onChangeTotalAmount = new EventEmitter<number>();
unitWithQuantity = signal<number>(0);
totalAmountBeforeProfit = signal<number>(0);
baseTotalAmount = signal<number>(0);
taxAmount = signal<number>(0);
totalAmount = signal<number>(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<IPosOrderItem<any>>) {
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) {
@@ -9,15 +9,16 @@
[min]="0"
/>
<app-input
[control]="form.controls.discount"
<app-amount-percentage-input
[percentageControl]="form.controls.discount_percentage"
[amountControl]="form.controls.discount_amount"
[baseAmount]="baseTotalAmount()"
[minAmount]="0"
[maxAmount]="baseTotalAmount()"
name="discount"
label="تخفیف"
type="number"
suffix="درصد"
[min]="0"
[max]="100"
/>
<hr />
<div class="flex flex-col gap-4 justify-center w-full">
<pos-form-dialog-amount-card-template
@@ -1,5 +1,6 @@
import { AbstractForm } from '@/shared/abstractClasses';
import { InputComponent } from '@/shared/components';
import { AmountPercentageInputComponent } from '@/shared/components/amountPercentageInput/amount-percentage-input.component';
import { formatWithCurrency, getGoodUnitTypeProperties, UnitType } from '@/utils';
import { Component, computed, EventEmitter, Input, Output, signal } from '@angular/core';
import { ReactiveFormsModule, Validators } from '@angular/forms';
@@ -10,7 +11,13 @@ import { PosFormDialogAmountCardTemplateComponent } from '../form-dialog-amount-
@Component({
selector: 'pos-standard-payload-form',
templateUrl: './form.component.html',
imports: [ReactiveFormsModule, InputComponent, Button, PosFormDialogAmountCardTemplateComponent],
imports: [
ReactiveFormsModule,
InputComponent,
Button,
PosFormDialogAmountCardTemplateComponent,
AmountPercentageInputComponent,
],
})
export class PosStandardPayloadFormComponent extends AbstractForm<
IPosOrderItem<IStandardPayload>,
@@ -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<IPosOrderItem<IStandardPayload>>) {
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;
@@ -11,6 +11,7 @@ export interface IPosOrderItem<T = TPosOrderGoodPayload> {
good_id?: string;
service_id?: string;
notes?: string;
image_url?: string;
payload: T;
base_total_amount: number;
@@ -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;
}),
);