feat: enhance order submission flow and UI improvements

- Updated categories component to improve loading state and category display.
- Modified order section to handle payment submission with event payload.
- Refactored payment form dialog to emit order response on successful payment.
- Adjusted order response model to enforce required fields.
- Improved root component layout and added success dialog for order submission.
- Enhanced sale invoice card component to use new TSP API methods.
- Updated API routes for sale invoices to reflect TSP changes.
- Improved user interface for business activities and partners sections.
- Added card shadow styling for better visual hierarchy.
- Introduced new order submitted dialog component for post-order actions.
- Cleaned up console logs and improved code readability across components.
This commit is contained in:
2026-05-08 18:08:57 +03:30
parent ce40bd8c75
commit a138034c06
40 changed files with 375 additions and 212 deletions
@@ -1,29 +1,31 @@
<div class="flex gap-3 overflow-auto">
@if (loading()) {
@for (i of [1, 2, 3, 4, 5]; track i) {
<p-skeleton width="6rem" height="2.5rem" />
}
} @else {
@for (category of categories(); track category.id) {
<div
[class]="
'h-10 rounded-lg px-3 flex items-center gap-3 border border-surface-border cursor-pointer bg-surface-100 hover:bg-surface-200 transition-all' +
(selectedCategory() === category.id ? ' bg-surface-card' : '')
"
(click)="changeSelectedCategory(category)"
>
<span class="text-sm text-muted-color font-bold"> {{ category.name }}</span>
<div class="overflow-x-auto">
<div class="w-full overflow-visible flex gap-3">
@if (loading()) {
@for (i of [1, 2, 3, 4, 5]; track i) {
<p-skeleton width="6rem" height="2.5rem" />
}
} @else {
@for (category of categories(); track category.id) {
<div
[class]="
'flex items-center justify-center py-0.5 px-2 rounded-sm bg-surface-300 text-xs text-muted-color' +
(selectedCategory() === category.id ? ' bg-amber-400! text-white' : '')
'h-10 rounded-lg px-3 flex items-center gap-3 border border-surface-border cursor-pointer bg-surface-100 hover:bg-surface-200 transition-all shrink-0' +
(selectedCategory() === category.id ? ' bg-surface-card' : '')
"
(click)="changeSelectedCategory(category)"
>
<span>
{{ category.goods_count }}
</span>
<span class="text-sm text-muted-color font-bold"> {{ category.name }}</span>
<div
[class]="
'flex items-center justify-center py-0.5 px-2 rounded-sm bg-surface-300 text-xs text-muted-color' +
(selectedCategory() === category.id ? ' bg-amber-400! text-white' : '')
"
>
<span>
{{ category.goods_count }}
</span>
</div>
</div>
</div>
}
}
}
</div>
</div>
@@ -63,4 +63,4 @@
<pos-order-customer-dialog [(visible)]="isVisibleCustomerForm" (onSubmit)="submitCustomer()" />
<!-- <pos-pre-invoice-dialog [(visible)]="isVisiblePreInvoiceForm" /> -->
<pos-payment-form-dialog [(visible)]="isVisiblePaymentForm" (onSubmit)="submitPayment()" />
<pos-payment-form-dialog [(visible)]="isVisiblePaymentForm" (onSubmit)="submitPayment($event)" />
@@ -6,6 +6,7 @@ import { FormsModule } from '@angular/forms';
import { ButtonDirective } from 'primeng/button';
import { Card } from 'primeng/card';
import images from 'src/assets/images';
import { IPosOrderResponse } from '../../models';
import { PosLandingStore } from '../../store/main.store';
import { PosOrderCustomerDialogComponent } from '../customers/customer-dialog.component';
import { GoldPayloadOrderCardComponent } from '../payloads/gold/order-card.component';
@@ -31,7 +32,7 @@ import { POSOrderPriceInfoCardComponent } from './price-info-card.component';
export class PosOrderSectionComponent {
private readonly store = inject(PosLandingStore);
@Output() onAddMoreGoods = new EventEmitter<void>();
@Output() onSuccess = new EventEmitter<void>();
@Output() onSuccess = new EventEmitter<IPosOrderResponse>();
placeholderImage = images.placeholders.default;
@@ -84,9 +85,7 @@ export class PosOrderSectionComponent {
}
submitCustomer() {}
submitPayment() {
console.log('submitPayment');
this.onSuccess.emit();
submitPayment(invoice: IPosOrderResponse) {
this.onSuccess.emit(invoice);
}
}
@@ -0,0 +1,18 @@
<shared-dialog
[(visible)]="visible"
header="ثبت موفقیت‌آمیز"
[modal]="true"
[style]="{ width: '420px' }"
[closable]="true"
(onHide)="close()"
>
<div class="text-center pt-10 pb-14 flex flex-col items-center justify-center gap-4">
<i class="" class="pi pi-check-circle text-6xl! text-green-700"></i>
<p class="text-lg font-bold">فاکتور شما با موفقیت ایجاد شد.</p>
</div>
<div class="flex gap-2 justify-center">
<button pButton type="button" outlined (click)="printInvoice()">چاپ</button>
<button pButton type="button" outlined (click)="sendToTsp()">ارسال به سامانه مودیان</button>
<button pButton type="button" outlined (click)="openOrderDetails()">جزئیات</button>
</div>
</shared-dialog>
@@ -0,0 +1,56 @@
import { NativeBridgeService } from '@/core/services';
import { ToastService } from '@/core/services/toast.service';
import { AbstractDialog } from '@/shared/abstractClasses/abstract-dialog';
import { SharedDialogComponent } from '@/shared/components';
import { Component, inject, Input, signal } from '@angular/core';
import { ButtonDirective } from 'primeng/button';
import { finalize } from 'rxjs';
import { PosSaleInvoicesService } from '../../../saleInvoices/services/main.service';
import { IPosOrderResponse } from '../../models';
@Component({
selector: 'pos-order-submitted-dialog',
templateUrl: 'order-submitted-dialog.component.html',
imports: [SharedDialogComponent, ButtonDirective],
})
export class PosOrderSubmittedDialogComponent extends AbstractDialog {
@Input({ required: true }) invoice!: IPosOrderResponse;
private readonly nativeBridgeService = inject(NativeBridgeService);
private readonly service = inject(PosSaleInvoicesService);
private readonly toastService = inject(ToastService);
sendingLoading = signal(false);
sended = signal(false);
openOrderDetails() {
this.close();
}
sendInvoice() {
this.sendingLoading.set(true);
this.service
.sendToTsp(this.invoice.id)
.pipe(finalize(() => this.sendingLoading.set(false)))
.subscribe(() => {
this.toastService.success({ text: 'ارسال فاکتور با موفقیت انجام شد.' });
this.sended.set(true);
});
}
printInvoice() {
const printResult = this.nativeBridgeService.print({
title: 'رسید پرداخت',
items: [
{ label: 'مبلغ کل', value: String(this.invoice.code) },
// { label: 'نقدی', value: String(this.invoice.payment.cash || 0) },
// { label: 'تهاتر', value: String(this.invoice.payment.set_off || 0) },
// {
// label: 'پایانه',
// value: String((this.invoice.payment.terminals || []).reduce((acc, item) => acc + item, 0)),
// },
],
});
}
sendToTsp() {}
}
@@ -6,12 +6,12 @@ import { InputComponent, KeyValueComponent } from '@/shared/components';
import { SharedDialogComponent } from '@/shared/components/dialog/dialog.component';
import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component';
import { UikitFieldComponent } from '@/uikit';
import { Component, OnDestroy, OnInit, computed, inject, signal } from '@angular/core';
import { Component, computed, inject, signal } from '@angular/core';
import { FormControl, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms';
import { ButtonDirective } from 'primeng/button';
import { Select } from 'primeng/select';
import { catchError, throwError } from 'rxjs';
import { IPayment, TOrderPaymentTypes } from '../../models';
import { IPayment, IPosOrderResponse, TOrderPaymentTypes } from '../../models';
import { PosPaymentBridgeAbstract } from '../../services/payment-bridge.abstract';
import { PosPaymentBridgeService } from '../../services/payment-bridge.service';
import { PosLandingStore } from '../../store/main.store';
@@ -32,10 +32,7 @@ import { PosLandingStore } from '../../store/main.store';
],
providers: [{ provide: PosPaymentBridgeAbstract, useExisting: PosPaymentBridgeService }],
})
export class PosPaymentFormDialogComponent
extends AbstractFormDialog<IPayment, IPayment>
implements OnInit, OnDestroy
{
export class PosPaymentFormDialogComponent extends AbstractFormDialog<IPayment, IPosOrderResponse> {
private readonly store = inject(PosLandingStore);
private readonly paymentBridge = inject(PosPaymentBridgeAbstract);
private readonly nativeBridgeService = inject(NativeBridgeService);
@@ -72,8 +69,8 @@ export class PosPaymentFormDialogComponent
private initForm = () => {
const form = this.fb.group({
set_off: [this.initialValues?.set_off || 0],
cash: [this.initialValues?.cash || 0],
set_off: [0],
cash: [0],
terminals: this.fb.array([this.fb.control(0)]),
});
@@ -237,10 +234,7 @@ export class PosPaymentFormDialogComponent
)
.subscribe((res) => {
this.close();
this.onSubmit.emit();
this.toastServices.success({
text: 'فاکتور شما با موفقیت ایجاد شد',
});
this.onSubmit.emit(res);
});
}
}
@@ -289,7 +283,7 @@ export class PosPaymentFormDialogComponent
// }
}
override onSuccess(response: IPayment): void {}
override onSuccess(response: IPosOrderResponse): void {}
private extractAmountFromPaymentResult(payload: unknown): number | null {
if (!payload || typeof payload !== 'object') return null;