remove unused codes and update
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
<div class="bg-white h-svh relative">
|
||||
<div class="absolute inset-0 opacity-30 blur-xs flex items-center justify-center">
|
||||
<img [src]="authVector" alt="background image" class="object-cover max-w-full max-h-full" />
|
||||
<img [src]="authVector" alt="background image" class="w-full bg-cover bg-no-repeat" />
|
||||
</div>
|
||||
<div class="flex justify-center items-center relative z-10 h-svh mx-auto bg-gray-800/80">
|
||||
<div
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
const baseUrl = '/api/v1/pos';
|
||||
|
||||
export const POS_API_ROUTES = {
|
||||
info: (posId: number) => `${baseUrl}/${posId}`,
|
||||
stock: (posId: number) => `${baseUrl}/${posId}/stock`,
|
||||
productCategories: (posId: number) => `${baseUrl}/${posId}/product-categories`,
|
||||
submitOrder: (posId: number) => `${baseUrl}/${posId}/orders/create`,
|
||||
};
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './apiRoutes';
|
||||
export * from './routes';
|
||||
@@ -1,18 +0,0 @@
|
||||
import { NamedRoutes } from '@/core';
|
||||
import { Routes } from '@angular/router';
|
||||
|
||||
export const dashboardNamedRoutes: NamedRoutes<any> = {
|
||||
POS: {
|
||||
path: '',
|
||||
loadComponent: () =>
|
||||
import('../../views/dashboard.component').then((m) => m.DashboardComponent),
|
||||
meta: {
|
||||
title: 'داشبورد',
|
||||
pagePath: () => ``,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export type TDashboardRouteNames = keyof typeof dashboardNamedRoutes;
|
||||
|
||||
export const DASHBOARD_ROUTES: Routes = Object.values(dashboardNamedRoutes);
|
||||
@@ -1,7 +0,0 @@
|
||||
<div class="">
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<!-- <app-statistics-shared-top-alert-stocks class="block h-[300px]" />
|
||||
<app-statistics-shared-top-sales class="block h-[300px]" />
|
||||
<app-statistics-shared-top-supplier-debts class="block h-[300px]" /> -->
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,33 +0,0 @@
|
||||
import { Component } from '@angular/core';
|
||||
import images from 'src/assets/images';
|
||||
|
||||
@Component({
|
||||
selector: 'app-dashboard',
|
||||
templateUrl: './dashboard.component.html',
|
||||
imports: [
|
||||
// StatisticsSharedTopAlertStocksComponent,
|
||||
// StatisticsSharedTopSalesComponent,
|
||||
// StatisticsSharedTopSupplierDebtsComponent,
|
||||
],
|
||||
})
|
||||
export class DashboardComponent {
|
||||
placeholderLogo = images.placeholders.logo;
|
||||
|
||||
now = new Date();
|
||||
|
||||
// info = this.store.info;
|
||||
// infoLoading = this.store.getInfoLoading;
|
||||
|
||||
// getInfo() {
|
||||
// this.infoLoading.set(true);
|
||||
// this.service.getInfo(Number(this.posId)).subscribe({
|
||||
// next: (res) => {
|
||||
// this.info.set(res);
|
||||
// this.infoLoading.set(false);
|
||||
// },
|
||||
// error: () => {
|
||||
// this.infoLoading.set(false);
|
||||
// },
|
||||
// });
|
||||
// }
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
<div class="flex items-center gap-2 justify-between">
|
||||
<div class="flex flex-col">
|
||||
<span class="font-bold text-lg">سفارش #{{ heldOrder.orderNumber }}</span>
|
||||
<span class="text-sm text-muted-color">
|
||||
{{ heldOrder.orderItems.length }} کالا -
|
||||
<span [appPriceMask]="heldOrder.totalAmount"></span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<button
|
||||
pButton
|
||||
size="small"
|
||||
type="button"
|
||||
label="بارگذاری"
|
||||
icon="pi pi-download"
|
||||
(click)="loadHeldOrder(heldOrder.id)"
|
||||
></button>
|
||||
<button
|
||||
pButton
|
||||
size="small"
|
||||
type="button"
|
||||
label="حذف"
|
||||
icon="pi pi-trash"
|
||||
severity="danger"
|
||||
(click)="cancelHeldOrder(heldOrder.id)"
|
||||
></button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,57 +0,0 @@
|
||||
import { ToastService } from '@/core/services/toast.service';
|
||||
import { ConfirmationDialogService } from '@/shared/components/confirmationDialog/confirmation-dialog.service';
|
||||
import { PriceMaskDirective } from '@/shared/directives';
|
||||
import { Component, EventEmitter, Input, Output, signal } from '@angular/core';
|
||||
import { ButtonDirective } from 'primeng/button';
|
||||
import { IPosHeldOrderResponse } from '../../models';
|
||||
import { PosService } from '../../services/main.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-held-order-item',
|
||||
templateUrl: './held-order-item.component.html',
|
||||
imports: [PriceMaskDirective, ButtonDirective],
|
||||
})
|
||||
export class HeldOrderItemComponent {
|
||||
@Input() heldOrder!: IPosHeldOrderResponse;
|
||||
@Input() posId!: number;
|
||||
|
||||
@Output() onCancel = new EventEmitter<number>();
|
||||
@Output() onLoad = new EventEmitter<number>();
|
||||
|
||||
constructor(
|
||||
private readonly posService: PosService,
|
||||
private readonly confirmationService: ConfirmationDialogService,
|
||||
private readonly toastService: ToastService,
|
||||
) {}
|
||||
|
||||
actionLoading = signal<boolean>(false);
|
||||
|
||||
cancelHeldOrder(heldOrderId: number) {
|
||||
this.confirmationService.confirm({
|
||||
message: `آیا از لغو سفارش شمارهی #${this.heldOrder.orderNumber} اطمینان دارید؟`,
|
||||
header: 'لغو سفارش موقت',
|
||||
acceptLabel: 'بله',
|
||||
rejectLabel: 'خیر',
|
||||
accept: () => {
|
||||
this.actionLoading.set(true);
|
||||
this.posService.cancelOrder(this.posId, heldOrderId).subscribe({
|
||||
next: () => {
|
||||
this.onCancel.emit(heldOrderId);
|
||||
this.actionLoading.set(false);
|
||||
this.toastService.success({
|
||||
text: `سفارش شمارهی #${this.heldOrder.orderNumber} با موفقیت لغو شد.`,
|
||||
});
|
||||
},
|
||||
error: () => {
|
||||
this.actionLoading.set(false);
|
||||
},
|
||||
});
|
||||
},
|
||||
reject: () => {},
|
||||
});
|
||||
}
|
||||
|
||||
loadHeldOrder(heldOrderId: number) {
|
||||
this.onLoad.emit(heldOrderId);
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
<div class="d-flex flex-col gap-4">
|
||||
<div class="flex items-center justify-between shrink-0">
|
||||
<div class="flex items-center gap-2 text-muted-color">
|
||||
<i class="pi pi-shopping-cart"></i>
|
||||
<span class="text-base font-bold">سفارشهای نگهداشته شده</span>
|
||||
</div>
|
||||
</div>
|
||||
@if (!heldOrders()?.length) {
|
||||
<div class="grow flex flex-col gap-5 justify-center items-center text-center text-muted-color">
|
||||
<span class="text-base text-muted-color pt-4">هیچ سفارش نگهداشته شدهای وجود ندارد.</span>
|
||||
</div>
|
||||
} @else {
|
||||
<div class="flex flex-col gap-2 mt-3">
|
||||
@for (heldOrder of heldOrders(); track heldOrder.id) {
|
||||
<app-held-order-item [heldOrder]="heldOrder" [posId]="posId" (onCancel)="removeHeldOrder($event)" />
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
@@ -1,41 +0,0 @@
|
||||
import { Maybe } from '@/core';
|
||||
import { Component, Input, signal } from '@angular/core';
|
||||
import { IPosHeldOrderResponse } from '../../models';
|
||||
import { PosService } from '../../services/main.service';
|
||||
import { HeldOrderItemComponent } from './held-order-item.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-held-orders',
|
||||
templateUrl: './held-orders.component.html',
|
||||
imports: [HeldOrderItemComponent],
|
||||
})
|
||||
export class HeldOrdersComponent {
|
||||
@Input() posId!: number;
|
||||
|
||||
constructor(private readonly posService: PosService) {}
|
||||
|
||||
heldOrders = signal<Maybe<IPosHeldOrderResponse[]>>(null);
|
||||
loading = signal<boolean>(false);
|
||||
|
||||
ngOnInit() {
|
||||
this.getHeldOrders();
|
||||
}
|
||||
|
||||
getHeldOrders() {
|
||||
this.loading.set(true);
|
||||
this.posService.getHeldOrders(this.posId).subscribe({
|
||||
next: (res) => {
|
||||
this.heldOrders.set(res.data);
|
||||
this.loading.set(false);
|
||||
},
|
||||
error: () => {
|
||||
this.loading.set(false);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
removeHeldOrder(orderId: number) {
|
||||
const currentOrders = this.heldOrders() || [];
|
||||
this.heldOrders.set(currentOrders.filter((order) => order.id !== orderId));
|
||||
}
|
||||
}
|
||||
@@ -1,110 +0,0 @@
|
||||
<div class="bg-surface-card w-md shrink-0 h-full overflow-hidden rounded-xl p-4 pb-0 flex flex-col">
|
||||
<div class="shrink-0">
|
||||
<app-held-orders [posId]="posId" />
|
||||
</div>
|
||||
<hr />
|
||||
<div class="shrink-0 sticky top-0">
|
||||
<!-- <customers-select-field
|
||||
[canInsert]="true"
|
||||
[value]="selectedCustomer()"
|
||||
(valueChange)="updateCustomer($event)"
|
||||
[isFullDataOptionValue]="true"
|
||||
/> -->
|
||||
</div>
|
||||
<hr />
|
||||
<div class="grow overflow-auto flex flex-col">
|
||||
<div class="flex items-center justify-between shrink-0">
|
||||
<div class="flex items-center gap-2 text-muted-color">
|
||||
<i class="pi pi-shopping-cart"></i>
|
||||
<span class="text-base font-bold">سفارشها</span>
|
||||
</div>
|
||||
<button pButton type="button" icon="pi pi-trash" outlined size="small" (click)="clearOrderList()">
|
||||
پاک کردن سفارشها
|
||||
</button>
|
||||
</div>
|
||||
@if (!inOrderProducts().length) {
|
||||
<div class="grow flex flex-col gap-5 justify-center items-center text-center text-muted-color">
|
||||
<i class="pi pi-inbox text-6xl!"></i>
|
||||
<span class="text-lg text-muted-color">هیچ سفارشی ثبت نشده است.</span>
|
||||
</div>
|
||||
} @else {
|
||||
<div class="flex flex-col gap-2 mt-3">
|
||||
@for (inOrderProduct of inOrderProducts(); track inOrderProduct.productId) {
|
||||
<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">{{ inOrderProduct.product.name }}</span>
|
||||
<span class="text-sm text-muted-color">{{ inOrderProduct.product.sku }}</span>
|
||||
</div>
|
||||
<button
|
||||
pButton
|
||||
type="button"
|
||||
icon="pi pi-trash"
|
||||
size="small"
|
||||
severity="danger"
|
||||
class="ms-auto"
|
||||
(click)="removeProductFromOrder(inOrderProduct.productId)"
|
||||
></button>
|
||||
</div>
|
||||
<div class="flex items-center gap-4 mt-auto">
|
||||
<p-inplace>
|
||||
<ng-template #display>
|
||||
<span class="font-bold text-lg shrink-0" [appPriceMask]="inOrderProduct.unitPrice"></span>
|
||||
</ng-template>
|
||||
<ng-template #content let-closeCallback="closeCallback">
|
||||
<p-input-number
|
||||
type="text"
|
||||
inputStyleClass="w-24"
|
||||
[min]="0"
|
||||
[(ngModel)]="inOrderProduct.unitPrice"
|
||||
(onBlur)="
|
||||
updateInOrderProductFee(inOrderProduct.productId, $event, inOrderProduct.unitPrice);
|
||||
closeCallback()
|
||||
"
|
||||
/>
|
||||
</ng-template>
|
||||
</p-inplace>
|
||||
<div class="ms-auto shrink-0">
|
||||
<app-uikit-counter
|
||||
[min]="1"
|
||||
[max]="inOrderProduct.maxQuantity"
|
||||
[value]="inOrderProduct.count"
|
||||
(valueChange)="updateInOrderProductQuantity(inOrderProduct.productId, $event)"
|
||||
></app-uikit-counter>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<div class="flex flex-col sticky bottom-0 py-2">
|
||||
<pos-order-price-info-card />
|
||||
<div class="grid grid-cols-2 sticky bottom-0 gap-2 pt-4">
|
||||
<button
|
||||
pButton
|
||||
type="button"
|
||||
label="ثبت موقت"
|
||||
severity="primary"
|
||||
icon="pi pi-check"
|
||||
class="w-full"
|
||||
(click)="submit()"
|
||||
></button>
|
||||
<button
|
||||
pButton
|
||||
type="button"
|
||||
label="ثبت و پرداخت"
|
||||
severity="primary"
|
||||
icon="pi pi-check"
|
||||
class="w-full"
|
||||
[attr.disabled]="true"
|
||||
(click)="submitAndPay()"
|
||||
></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,76 +0,0 @@
|
||||
// import { CustomersSelectComponent } from '@/modules/customers/components/select/select.component';
|
||||
// import { ICustomerResponse } from '@/modules/customers/models';
|
||||
import { PriceMaskDirective } from '@/shared/directives';
|
||||
import { UikitCounterComponent } from '@/uikit';
|
||||
import { Component, computed, inject } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { ButtonDirective } from 'primeng/button';
|
||||
import { InplaceModule } from 'primeng/inplace';
|
||||
import { InputNumber } from 'primeng/inputnumber';
|
||||
import images from 'src/assets/images';
|
||||
import { POSStore } from '../../store';
|
||||
import { HeldOrdersComponent } from './held-orders.component';
|
||||
import { POSOrderPriceInfoCardComponent } from './price-info-card.component';
|
||||
|
||||
@Component({
|
||||
selector: 'pos-order-card',
|
||||
templateUrl: './order-card.component.html',
|
||||
imports: [
|
||||
// CustomersSelectComponent,
|
||||
ButtonDirective,
|
||||
PriceMaskDirective,
|
||||
UikitCounterComponent,
|
||||
POSOrderPriceInfoCardComponent,
|
||||
FormsModule,
|
||||
InplaceModule,
|
||||
InputNumber,
|
||||
HeldOrdersComponent,
|
||||
],
|
||||
})
|
||||
export class PosOrderCardComponent {
|
||||
private store = inject(POSStore);
|
||||
|
||||
constructor() {
|
||||
// this.selectedCustomer.set(this.store.selectedCustomer());
|
||||
}
|
||||
|
||||
posId = this.store.posId;
|
||||
|
||||
placeholderImage = images.placeholders.default;
|
||||
|
||||
inOrderProducts = computed(() => this.store.inOrderProducts());
|
||||
|
||||
// selectedCustomer = signal<Maybe<ICustomerResponse>>(null);
|
||||
|
||||
removeProductFromOrder(productId: number) {
|
||||
this.store.removeFromInOrderProducts(productId);
|
||||
}
|
||||
|
||||
updateInOrderProductQuantity(productId: number, quantity: number) {
|
||||
this.store.updateInOrderProductQuantity(productId, quantity);
|
||||
}
|
||||
|
||||
updateInOrderProductFee(productId: number, $event: Event, prevFee: number) {
|
||||
const unitPrice = $event.target
|
||||
? parseFloat(($event.target as HTMLInputElement).ariaValueNow || prevFee.toString())
|
||||
: prevFee;
|
||||
|
||||
this.store.updateInOrderProductFee(productId, unitPrice);
|
||||
}
|
||||
|
||||
clearOrderList() {
|
||||
this.store.resetInOrderProducts();
|
||||
}
|
||||
|
||||
// updateCustomer(customer: Maybe<ICustomerResponse>) {
|
||||
// this.store.setSelectedCustomer(customer);
|
||||
// }
|
||||
|
||||
submit() {
|
||||
this.store.submitOrder();
|
||||
}
|
||||
|
||||
submitAndPay() {
|
||||
this.store.submitOrder();
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
<p-card class="border border-surface-border">
|
||||
<div class="flex flex-col gap-3">
|
||||
<div class="flex justify-between items-center">
|
||||
<span>جمع قیمت</span>
|
||||
@if (priceInfo().totalAmount == priceInfo().totalBaseAmount) {
|
||||
<span [appPriceMask]="priceInfo().totalAmount"></span>
|
||||
} @else {
|
||||
<div class="flex flex-col items-end">
|
||||
<span class="line-through text-muted-color text-xs" [appPriceMask]="priceInfo().totalBaseAmount"></span>
|
||||
<span [appPriceMask]="priceInfo().totalAmount"></span>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<div class="flex justify-between items-center">
|
||||
<span>ارزش افزوده</span>
|
||||
<span [appPriceMask]="priceInfo().taxAmount"></span>
|
||||
</div>
|
||||
<hr />
|
||||
<div class="flex justify-between items-center font-bold text-lg">
|
||||
<span>مجموع کل</span>
|
||||
<span [appPriceMask]="priceInfo().payableAmount"></span>
|
||||
</div>
|
||||
</div>
|
||||
</p-card>
|
||||
@@ -1,15 +0,0 @@
|
||||
import { PriceMaskDirective } from '@/shared/directives';
|
||||
import { Component, computed } from '@angular/core';
|
||||
import { Card } from 'primeng/card';
|
||||
import { POSStore } from '../../store';
|
||||
|
||||
@Component({
|
||||
selector: 'pos-order-price-info-card',
|
||||
templateUrl: './price-info-card.component.html',
|
||||
imports: [Card, PriceMaskDirective],
|
||||
})
|
||||
export class POSOrderPriceInfoCardComponent {
|
||||
constructor(private store: POSStore) {}
|
||||
|
||||
priceInfo = computed(() => this.store.orderPricingInfo());
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
<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 shrink-0' +
|
||||
(selectedCategory() === category.id ? ' bg-surface-card' : '')
|
||||
"
|
||||
(click)="changeSelectedCategory(category)"
|
||||
>
|
||||
<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.productCount }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
@@ -1,23 +0,0 @@
|
||||
import { Component, EventEmitter, inject, Output } from '@angular/core';
|
||||
import { Skeleton } from 'primeng/skeleton';
|
||||
import { POSStore } from '../../store';
|
||||
|
||||
@Component({
|
||||
selector: 'pos-product-categories',
|
||||
templateUrl: './categories.component.html',
|
||||
imports: [Skeleton],
|
||||
})
|
||||
export class PosProductCategoriesComponent {
|
||||
@Output() categoryChange = new EventEmitter<number>();
|
||||
|
||||
private readonly store = inject(POSStore);
|
||||
|
||||
categories = this.store.productCategories;
|
||||
loading = this.store.getProductCategoriesLoading;
|
||||
|
||||
selectedCategory = this.store.activeProductCategory;
|
||||
|
||||
changeSelectedCategory(category: { id: number; name: string }) {
|
||||
this.store.changeActiveCategory(category.id);
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
<div class="grid 2xl:grid-cols-8 xl:grid-cols-6 lg:grid-cols-4 grid-cols-2 gap-3 flex-wrap">
|
||||
@if (loading()) {
|
||||
@for (_ of [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15]; track $index) {
|
||||
<div class="flex-1 aspect-[0.9]">
|
||||
<p-skeleton width="100%" height="100%"></p-skeleton>
|
||||
</div>
|
||||
}
|
||||
} @else {
|
||||
@for (product of products(); track product.id) {
|
||||
<div class="flex-1 bg-surface-card rounded-lg p-1 shadow-xs">
|
||||
<img [src]="productPlaceholder" class="w-full aspect-[1.3] object-cover rounded-md" />
|
||||
<div class="mt-2">
|
||||
<span class="text-lg font-bold">
|
||||
{{ product.product.name }}
|
||||
<small class="text-xs text-muted-color">({{ product.quantity }})</small>
|
||||
</span>
|
||||
<div class="flex items-center justify-between mt-1">
|
||||
<span [appPriceMask]="product.product.salePrice" class="text-base font-bold"></span>
|
||||
<button pButton type="button" icon="pi pi-plus" size="small" (click)="addProduct(product.product)"></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
@@ -1,25 +0,0 @@
|
||||
import { PriceMaskDirective } from '@/shared/directives';
|
||||
import { Component, computed } from '@angular/core';
|
||||
import { ButtonDirective } from 'primeng/button';
|
||||
import { Skeleton } from 'primeng/skeleton';
|
||||
import images from 'src/assets/images';
|
||||
import { IPosProductSummary } from '../../models/types';
|
||||
import { POSStore } from '../../store';
|
||||
|
||||
@Component({
|
||||
selector: 'pos-products-grid-view',
|
||||
templateUrl: './grid-view.component.html',
|
||||
imports: [PriceMaskDirective, ButtonDirective, Skeleton],
|
||||
})
|
||||
export class PosProductsGridViewComponent {
|
||||
constructor(private store: POSStore) {}
|
||||
|
||||
products = computed(() => this.store.activatedCategoryProducts());
|
||||
loading = computed(() => this.store.getStockLoading());
|
||||
|
||||
productPlaceholder = images.placeholders.default;
|
||||
|
||||
addProduct(product: IPosProductSummary) {
|
||||
this.store.addToInOrderProducts(product.id);
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
<div class="flex flex-col gap-3 flex-wrap">
|
||||
@if (loading()) {
|
||||
@for (_ of [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15]; track $index) {
|
||||
<div class="w-100 h-12">
|
||||
<p-skeleton width="100%" height="100%"></p-skeleton>
|
||||
</div>
|
||||
}
|
||||
} @else {
|
||||
@for (product of products(); track product.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]="productPlaceholder" class="w-12 h-12 object-cover rounded-md" />
|
||||
<div class="flex flex-col gap-2">
|
||||
<span class="text-lg font-bold">
|
||||
{{ product.product.name }}
|
||||
<small class="text-xs text-muted-color">({{ product.quantity }})</small>
|
||||
</span>
|
||||
<span class="text-sm text-muted-color">
|
||||
{{ product.product.sku }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="shrink-0 flex items-center justify-between gap-3">
|
||||
<span [appPriceMask]="product.product.salePrice" class="text-base font-bold"></span>
|
||||
<button pButton type="button" icon="pi pi-plus" size="small" (click)="addProduct(product.product)"></button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
@@ -1,25 +0,0 @@
|
||||
import { PriceMaskDirective } from '@/shared/directives';
|
||||
import { Component, computed } from '@angular/core';
|
||||
import { ButtonDirective } from 'primeng/button';
|
||||
import { Skeleton } from 'primeng/skeleton';
|
||||
import images from 'src/assets/images';
|
||||
import { IPosProductSummary } from '../../models/types';
|
||||
import { POSStore } from '../../store';
|
||||
|
||||
@Component({
|
||||
selector: 'pos-products-list-view',
|
||||
templateUrl: './list-view.component.html',
|
||||
imports: [PriceMaskDirective, ButtonDirective, Skeleton],
|
||||
})
|
||||
export class PosProductsListViewComponent {
|
||||
constructor(private store: POSStore) {}
|
||||
|
||||
products = computed(() => this.store.activatedCategoryProducts());
|
||||
loading = computed(() => this.store.getStockLoading());
|
||||
|
||||
productPlaceholder = images.placeholders.default;
|
||||
|
||||
addProduct(product: IPosProductSummary) {
|
||||
this.store.addToInOrderProducts(product.id);
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
<div class="flex flex-col gap-4">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-2 text-muted-color">
|
||||
<i class="pi pi-list"></i>
|
||||
<span class="text-base font-bold">لیست کالاها</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<app-search-input (search)="onSearch($event)"></app-search-input>
|
||||
|
||||
<div class="card p-2! flex justify-center">
|
||||
<p-selectbutton [options]="viewOptions" [(ngModel)]="viewType" optionValue="value">
|
||||
<ng-template #item let-item>
|
||||
<i [class]="item.icon"></i>
|
||||
</ng-template>
|
||||
</p-selectbutton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<pos-product-categories (categoryChange)="onCategoryChange($event)" />
|
||||
|
||||
@if (viewType === "grid") {
|
||||
<pos-products-grid-view />
|
||||
} @else {
|
||||
<pos-products-list-view />
|
||||
}
|
||||
</div>
|
||||
@@ -1,55 +0,0 @@
|
||||
import { Maybe } from '@/core';
|
||||
import { SearchInputComponent } from '@/shared/components/search/search-input.component';
|
||||
import { Component, computed, inject, Input, signal } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { SelectButton } from 'primeng/selectbutton';
|
||||
import { POSStore, TViewType } from '../../store';
|
||||
import { PosProductCategoriesComponent } from './categories.component';
|
||||
import { PosProductsGridViewComponent } from './grid-view.component';
|
||||
import { PosProductsListViewComponent } from './list-view.component';
|
||||
|
||||
@Component({
|
||||
selector: 'pos-products',
|
||||
templateUrl: './products.component.html',
|
||||
imports: [
|
||||
PosProductCategoriesComponent,
|
||||
PosProductsListViewComponent,
|
||||
SelectButton,
|
||||
FormsModule,
|
||||
PosProductsGridViewComponent,
|
||||
SearchInputComponent,
|
||||
],
|
||||
})
|
||||
export class PosProductsComponent {
|
||||
@Input() activeCategory = signal<Maybe<number>>(null);
|
||||
|
||||
private readonly store = inject(POSStore);
|
||||
|
||||
constructor() {
|
||||
this.store.initial();
|
||||
}
|
||||
|
||||
viewOptions = [
|
||||
{ value: 'list', icon: 'pi pi-list' },
|
||||
{ value: 'grid', icon: 'pi pi-th-large' },
|
||||
];
|
||||
|
||||
readonly activatedCategoryProducts = computed(() => this.store.activatedCategoryProducts());
|
||||
readonly stock = computed(() => this.store.stock());
|
||||
|
||||
get viewType() {
|
||||
return this.store.viewType();
|
||||
}
|
||||
set viewType(val: TViewType) {
|
||||
this.store.updateViewType(val);
|
||||
}
|
||||
|
||||
onSearch(searchTerm: string) {
|
||||
this.store.updateSearchQuery(searchTerm);
|
||||
this.store.getStock();
|
||||
}
|
||||
|
||||
onCategoryChange(categoryId: number) {
|
||||
this.store.changeActiveCategory(categoryId);
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
const baseUrl = '/api/v1/pos';
|
||||
|
||||
export const POS_API_ROUTES = {
|
||||
info: (posId: number) => `${baseUrl}/${posId}`,
|
||||
stock: (posId: number) => `${baseUrl}/${posId}/stock`,
|
||||
productCategories: (posId: number) => `${baseUrl}/${posId}/product-categories`,
|
||||
submitOrder: (posId: number) => `${baseUrl}/${posId}/orders`,
|
||||
heldOrders: (posId: number) => `${baseUrl}/${posId}/orders/held`,
|
||||
order: (posId: number, orderId: number) => `${baseUrl}/${posId}/orders/${orderId}`,
|
||||
cancelOrder: (posId: number, orderId: number) => `${baseUrl}/${posId}/orders/${orderId}/cancel`,
|
||||
rejectOrder: (posId: number, orderId: number) => `${baseUrl}/${posId}/orders/${orderId}/reject`,
|
||||
confirmOrder: (posId: number, orderId: number) => `${baseUrl}/${posId}/orders/${orderId}/confirm`,
|
||||
};
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './apiRoutes';
|
||||
export * from './routes';
|
||||
@@ -1,17 +0,0 @@
|
||||
import { NamedRoutes } from '@/core';
|
||||
import { Routes } from '@angular/router';
|
||||
|
||||
export type TPOSRouteNames = 'POS';
|
||||
|
||||
export const posNamedRoutes: NamedRoutes<TPOSRouteNames> = {
|
||||
POS: {
|
||||
path: 'pos/:posId',
|
||||
loadComponent: () => import('../../views/pos.component').then((m) => m.POSComponent),
|
||||
meta: {
|
||||
title: 'نقاط فروش',
|
||||
pagePath: (params) => `/pos/${params.posId}`,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const POS_ROUTES: Routes = Object.values(posNamedRoutes);
|
||||
@@ -1,5 +1,4 @@
|
||||
import ISummary from '@/core/models/summary';
|
||||
import { IBankAccountSummary, IPosOrderItem, IPosProductSummary } from './types';
|
||||
|
||||
export interface IPosInfoRawResponse {
|
||||
id: number;
|
||||
@@ -12,7 +11,6 @@ export interface IPosInfoRawResponse {
|
||||
updatedAt: string;
|
||||
deletedAt: null;
|
||||
inventory: ISummary;
|
||||
bankAccount: IBankAccountSummary;
|
||||
}
|
||||
|
||||
export interface IPosInfoResponse extends IPosInfoRawResponse {}
|
||||
@@ -20,7 +18,6 @@ export interface IPosInfoResponse extends IPosInfoRawResponse {}
|
||||
export interface IPosStockRawResponse {
|
||||
id: number;
|
||||
quantity: number;
|
||||
product: IPosProductSummary;
|
||||
}
|
||||
export interface IPosStockResponse extends IPosStockRawResponse {}
|
||||
|
||||
@@ -34,7 +31,6 @@ export interface IPosProductCategoriesResponse extends IPosProductCategoriesRawR
|
||||
|
||||
export interface IPosOrderRequest {
|
||||
customerId?: number;
|
||||
items: IPosOrderItem[];
|
||||
}
|
||||
|
||||
export interface IPosOrderRawResponse {
|
||||
@@ -53,7 +49,6 @@ export interface IPosOrderRawResponse {
|
||||
lastName: string;
|
||||
};
|
||||
orderItems: {
|
||||
product: IPosProductSummary;
|
||||
count: number;
|
||||
unitPrice: number;
|
||||
totalPrice: number;
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
import ISummary from '@/core/models/summary';
|
||||
|
||||
export interface IPosProductSummary {
|
||||
id: number;
|
||||
name: string;
|
||||
sku: string;
|
||||
salePrice: string;
|
||||
categoryId: number;
|
||||
}
|
||||
|
||||
interface ICategorySummary {
|
||||
id: number;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface IPosOrderItem {
|
||||
productId: number;
|
||||
count: number;
|
||||
unitPrice: number;
|
||||
}
|
||||
|
||||
export interface IPosInOrderProduct extends IPosOrderItem {
|
||||
product: IPosProductSummary;
|
||||
maxQuantity: number;
|
||||
}
|
||||
|
||||
export interface IBankAccountSummary {
|
||||
id: number;
|
||||
name: string;
|
||||
accountNumber: string;
|
||||
branch: IBankAccountBranchSummary;
|
||||
}
|
||||
|
||||
interface IBankAccountBranchSummary extends ISummary {
|
||||
bank: ISummary;
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
import { IPaginatedResponse } from '@/core/models/service.model';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { POS_API_ROUTES } from '../constants';
|
||||
import {
|
||||
IPosInfoRawResponse,
|
||||
IPosInfoResponse,
|
||||
IPosOrderRequest,
|
||||
IPosProductCategoriesRawResponse,
|
||||
IPosProductCategoriesResponse,
|
||||
IPosStockRawResponse,
|
||||
IPosStockResponse,
|
||||
} from '../models';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class PosService {
|
||||
constructor(private http: HttpClient) {}
|
||||
|
||||
private apiRoutes = POS_API_ROUTES;
|
||||
|
||||
getInfo(posId: number): Observable<IPosInfoResponse> {
|
||||
return this.http.get<IPosInfoRawResponse>(this.apiRoutes.info(posId));
|
||||
}
|
||||
|
||||
getStock(
|
||||
posId: number,
|
||||
searchQuery: string = '',
|
||||
): Observable<IPaginatedResponse<IPosStockResponse>> {
|
||||
return this.http.get<IPaginatedResponse<IPosStockRawResponse>>(this.apiRoutes.stock(posId), {
|
||||
params: {
|
||||
q: searchQuery,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
getCategories(posId: number): Observable<IPaginatedResponse<IPosProductCategoriesResponse>> {
|
||||
return this.http.get<IPaginatedResponse<IPosProductCategoriesRawResponse>>(
|
||||
this.apiRoutes.productCategories(posId),
|
||||
);
|
||||
}
|
||||
|
||||
submitOrder(posId: number, payload: IPosOrderRequest): Observable<any> {
|
||||
return this.http.post<any>(this.apiRoutes.submitOrder(posId), payload);
|
||||
}
|
||||
|
||||
getHeldOrders(posId: number): Observable<IPaginatedResponse<any>> {
|
||||
return this.http.get<IPaginatedResponse<any>>(this.apiRoutes.heldOrders(posId));
|
||||
}
|
||||
|
||||
getOrder(posId: number, orderId: number): Observable<any> {
|
||||
return this.http.get<any>(this.apiRoutes.order(posId, orderId));
|
||||
}
|
||||
|
||||
cancelOrder(posId: number, orderId: number): Observable<any> {
|
||||
return this.http.post<any>(this.apiRoutes.cancelOrder(posId, orderId), {});
|
||||
}
|
||||
|
||||
rejectOrder(posId: number, orderId: number): Observable<any> {
|
||||
return this.http.post<any>(this.apiRoutes.rejectOrder(posId, orderId), {});
|
||||
}
|
||||
|
||||
confirmOrder(posId: number, orderId: number): Observable<any> {
|
||||
return this.http.post<any>(this.apiRoutes.confirmOrder(posId, orderId), {});
|
||||
}
|
||||
}
|
||||
@@ -1,272 +0,0 @@
|
||||
import { Maybe } from '@/core';
|
||||
// import { ICustomerResponse } from '@/modules/customers/models';
|
||||
import { computed, Inject, Injectable, InjectionToken, signal } from '@angular/core';
|
||||
import { map, of } from 'rxjs';
|
||||
import { IPosInfoResponse, IPosProductCategoriesResponse, IPosStockResponse } from '../models';
|
||||
import { IPosInOrderProduct } from '../models/types';
|
||||
import { PosService } from '../services/main.service';
|
||||
|
||||
export const POS_ID = new InjectionToken<number>('POS_ID');
|
||||
|
||||
export type TViewType = 'list' | 'grid';
|
||||
|
||||
interface IPosState {
|
||||
getStockLoading: boolean;
|
||||
stock: Maybe<IPosStockResponse[]>;
|
||||
getInfoLoading: boolean;
|
||||
info: Maybe<IPosInfoResponse>;
|
||||
getProductCategoriesLoading: boolean;
|
||||
productCategories: Maybe<IPosProductCategoriesResponse[]>;
|
||||
activeProductCategory: Maybe<number>;
|
||||
inOrderProducts: IPosInOrderProduct[];
|
||||
selectedCustomer: Maybe<any>;
|
||||
viewType: TViewType;
|
||||
searchQuery: string;
|
||||
}
|
||||
|
||||
export const INITIAL_POS_STATE: IPosState = {
|
||||
getStockLoading: true,
|
||||
stock: null,
|
||||
getInfoLoading: true,
|
||||
info: null,
|
||||
getProductCategoriesLoading: true,
|
||||
productCategories: null,
|
||||
activeProductCategory: null,
|
||||
inOrderProducts: [],
|
||||
selectedCustomer: null,
|
||||
viewType: 'grid',
|
||||
searchQuery: '',
|
||||
};
|
||||
|
||||
@Injectable({ providedIn: 'any' })
|
||||
export class POSStore {
|
||||
constructor(
|
||||
@Inject(POS_ID) posId: number,
|
||||
private service: PosService,
|
||||
) {
|
||||
this.posId = posId;
|
||||
this.initial();
|
||||
}
|
||||
|
||||
private state$ = signal<IPosState>({ ...INITIAL_POS_STATE });
|
||||
|
||||
readonly stock = computed(() => this.state$().stock);
|
||||
readonly getStockLoading = computed(() => this.state$().getStockLoading);
|
||||
readonly info = computed(() => this.state$().info);
|
||||
readonly getInfoLoading = computed(() => this.state$().getInfoLoading);
|
||||
readonly inOrderProducts = computed(() => this.state$().inOrderProducts);
|
||||
readonly productCategories = computed(() => this.state$().productCategories);
|
||||
readonly getProductCategoriesLoading = computed(() => this.state$().getProductCategoriesLoading);
|
||||
readonly activeProductCategory = computed(() => this.state$().activeProductCategory);
|
||||
readonly selectedCustomer = computed(() => this.state$().selectedCustomer);
|
||||
readonly viewType = computed(() => this.state$().viewType);
|
||||
readonly searchQuery = computed(() => this.state$().searchQuery);
|
||||
readonly activatedCategoryProducts = computed(() => {
|
||||
if (this.activeProductCategory() === 0) {
|
||||
return this.state$().stock;
|
||||
}
|
||||
return this.state$().stock?.filter(
|
||||
(s) => s.product.categoryId === this.state$().activeProductCategory,
|
||||
);
|
||||
});
|
||||
|
||||
readonly orderPricingInfo = computed(() => {
|
||||
const totalAmount = this.inOrderProducts().reduce(
|
||||
(acc, curr) => acc + curr.unitPrice * curr.count,
|
||||
0,
|
||||
);
|
||||
const taxAmount = totalAmount * 0.1;
|
||||
return {
|
||||
totalItems: this.inOrderProducts().reduce((acc, curr) => acc + curr.count, 0),
|
||||
totalBaseAmount: this.inOrderProducts().reduce(
|
||||
(acc, curr) => acc + parseFloat(curr.product.salePrice) * curr.count,
|
||||
0,
|
||||
),
|
||||
totalAmount,
|
||||
taxAmount,
|
||||
payableAmount: totalAmount + taxAmount,
|
||||
};
|
||||
});
|
||||
|
||||
posId!: number;
|
||||
|
||||
setState(partial: Partial<IPosState>) {
|
||||
this.state$.set({ ...this.state$(), ...partial });
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.state$.set({ ...INITIAL_POS_STATE });
|
||||
}
|
||||
|
||||
initial() {
|
||||
this.getInfo();
|
||||
this.getStock();
|
||||
this.getProductCategories();
|
||||
}
|
||||
|
||||
fillInitial(data: Partial<IPosState>) {
|
||||
this.state$.set({ ...INITIAL_POS_STATE, ...data });
|
||||
}
|
||||
|
||||
getInfo() {
|
||||
this.setState({ getInfoLoading: true });
|
||||
this.service.getInfo(this.posId).subscribe({
|
||||
next: (res) => {
|
||||
this.setState({ info: res, getInfoLoading: false });
|
||||
},
|
||||
error: () => {
|
||||
this.setState({ getInfoLoading: false });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
getStock() {
|
||||
this.setState({ getStockLoading: true });
|
||||
this.service.getStock(this.posId, this.state$().searchQuery).subscribe({
|
||||
next: (res) => {
|
||||
this.setState({ stock: res.data, getStockLoading: false });
|
||||
},
|
||||
error: () => {
|
||||
this.setState({ getStockLoading: false });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
getProductCategories() {
|
||||
this.setState({ getProductCategoriesLoading: true });
|
||||
this.service
|
||||
.getCategories(this.posId)
|
||||
.pipe(
|
||||
map((res) => {
|
||||
return {
|
||||
data: [
|
||||
{
|
||||
id: 0,
|
||||
name: 'همه',
|
||||
totalQuantity: res.data.reduce((acc, curr) => acc + curr.totalQuantity, 0),
|
||||
productCount: res.data.reduce((acc, curr) => acc + curr.productCount, 0),
|
||||
},
|
||||
...res.data,
|
||||
],
|
||||
meta: res.meta,
|
||||
};
|
||||
}),
|
||||
)
|
||||
.subscribe({
|
||||
next: (res) => {
|
||||
this.setState({
|
||||
productCategories: res.data,
|
||||
getProductCategoriesLoading: false,
|
||||
activeProductCategory: res.data[0]?.id,
|
||||
});
|
||||
},
|
||||
error: () => {
|
||||
this.setState({ getProductCategoriesLoading: false });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
changeActiveCategory(categoryId: number) {
|
||||
this.setState({ activeProductCategory: categoryId });
|
||||
}
|
||||
|
||||
addToInOrderProducts(productId: number) {
|
||||
const productStock = this.state$().stock?.find((s) => s.product.id === productId);
|
||||
if (!productStock) return;
|
||||
const { product } = productStock;
|
||||
|
||||
if (this.state$().inOrderProducts.some((p) => p.productId === productId)) {
|
||||
this.updateInOrderProductQuantity(
|
||||
productId,
|
||||
this.state$().inOrderProducts.find((p) => p.productId === productId)!.count + 1,
|
||||
);
|
||||
} else {
|
||||
this.setState({
|
||||
inOrderProducts: [
|
||||
...this.state$().inOrderProducts,
|
||||
{
|
||||
product,
|
||||
productId: product.id,
|
||||
count: 1,
|
||||
unitPrice: parseFloat(product.salePrice),
|
||||
maxQuantity: productStock.quantity,
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
removeFromInOrderProducts(productId: number) {
|
||||
this.setState({
|
||||
inOrderProducts: this.state$().inOrderProducts.filter((p) => p.productId !== productId),
|
||||
});
|
||||
}
|
||||
|
||||
updateInOrderProductQuantity(productId: number, quantity: number) {
|
||||
const inOrderProducts = this.state$().inOrderProducts;
|
||||
|
||||
if (quantity < 1) {
|
||||
this.removeFromInOrderProducts(productId);
|
||||
} else if (inOrderProducts.findIndex((p) => p.productId === productId) === -1) {
|
||||
this.addToInOrderProducts(productId);
|
||||
} else {
|
||||
const updatedProducts = inOrderProducts.map((p) => {
|
||||
if (p.productId === productId) {
|
||||
return { ...p, count: Math.min(quantity, p.maxQuantity) };
|
||||
}
|
||||
return p;
|
||||
});
|
||||
this.setState({ inOrderProducts: updatedProducts });
|
||||
}
|
||||
}
|
||||
|
||||
updateInOrderProductFee(productId: number, unitPrice: number) {
|
||||
const inOrderProducts = this.state$().inOrderProducts;
|
||||
|
||||
if (inOrderProducts.some((p) => p.productId === productId)) {
|
||||
const updatedProducts = inOrderProducts.map((p) => {
|
||||
if (p.productId === productId) {
|
||||
return { ...p, unitPrice: unitPrice };
|
||||
}
|
||||
return p;
|
||||
});
|
||||
this.setState({ inOrderProducts: updatedProducts });
|
||||
}
|
||||
}
|
||||
|
||||
updateViewType(viewType: TViewType) {
|
||||
this.setState({ viewType });
|
||||
}
|
||||
updateSearchQuery(searchQuery: string) {
|
||||
this.setState({ searchQuery });
|
||||
}
|
||||
|
||||
resetInOrderProducts() {
|
||||
this.setState({ inOrderProducts: [] });
|
||||
}
|
||||
|
||||
setSelectedCustomer(customer: Maybe<any>) {
|
||||
this.setState({ selectedCustomer: customer });
|
||||
}
|
||||
|
||||
submitOrder() {
|
||||
const orderPayload = {
|
||||
customer_id: this.selectedCustomer()?.id,
|
||||
items: this.inOrderProducts()?.map((item) => ({
|
||||
productId: item.productId,
|
||||
count: item.count,
|
||||
unitPrice: item.unitPrice,
|
||||
})),
|
||||
};
|
||||
return this.service.submitOrder(this.posId, orderPayload).subscribe({
|
||||
next: (res) => {
|
||||
this.resetInOrderProducts();
|
||||
this.setSelectedCustomer(null);
|
||||
return of(res);
|
||||
},
|
||||
error: (err) => {
|
||||
return of(err);
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
<div class="w-svw h-svh bg-surface-ground flex flex-col gap-4 p-4">
|
||||
<div class="w-full flex items-center gap-4 shrink-0 justify-between">
|
||||
<div class="flex gap-2 items-center">
|
||||
<div class="w-10 h-10">
|
||||
<img [src]="placeholderLogo" alt="Logo" class="w-full h-full object-cover rounded-md bg-surface-card" />
|
||||
</div>
|
||||
<span class="text-lg font-bold">{{ info()?.name }} - شعبه {{ info()?.inventory?.name }}</span>
|
||||
</div>
|
||||
<div class="flex gap-2 items-center">
|
||||
<div class="bg-surface-card py-1 px-3 rounded-md shadow-sm">
|
||||
<span [jalaliDate]="now" jalaliFormat="dddd YYYY/MM/DD" class="text-base"></span>
|
||||
</div>
|
||||
<div class="bg-surface-card py-1 px-3 rounded-md shadow-sm">
|
||||
<span class="text-base"> فروشنده شمارهی ۱ </span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex gap-4 grow overflow-hidden">
|
||||
<div class="grow h-full overflow-auto">
|
||||
<pos-products />
|
||||
</div>
|
||||
<div class="shrink-0 h-full">
|
||||
<pos-order-card />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,44 +0,0 @@
|
||||
import { JalaliDateDirective } from '@/shared/directives';
|
||||
import { Component, inject } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import images from 'src/assets/images';
|
||||
import { PosOrderCardComponent } from '../components/order/order-card.component';
|
||||
import { PosProductsComponent } from '../components/products/products.component';
|
||||
import { POSStore, POS_ID } from '../store';
|
||||
|
||||
@Component({
|
||||
selector: 'app-pos',
|
||||
templateUrl: './pos.component.html',
|
||||
imports: [JalaliDateDirective, PosProductsComponent, PosOrderCardComponent],
|
||||
providers: [
|
||||
{
|
||||
provide: POS_ID,
|
||||
useFactory: (route: ActivatedRoute) => Number(route.snapshot.params['posId']),
|
||||
deps: [ActivatedRoute],
|
||||
},
|
||||
POSStore,
|
||||
],
|
||||
})
|
||||
export class POSComponent {
|
||||
private readonly store = inject(POSStore);
|
||||
|
||||
placeholderLogo = images.placeholders.logo;
|
||||
|
||||
now = new Date();
|
||||
|
||||
info = this.store.info;
|
||||
infoLoading = this.store.getInfoLoading;
|
||||
|
||||
// getInfo() {
|
||||
// this.infoLoading.set(true);
|
||||
// this.service.getInfo(Number(this.posId)).subscribe({
|
||||
// next: (res) => {
|
||||
// this.info.set(res);
|
||||
// this.infoLoading.set(false);
|
||||
// },
|
||||
// error: () => {
|
||||
// this.infoLoading.set(false);
|
||||
// },
|
||||
// });
|
||||
// }
|
||||
}
|
||||
Reference in New Issue
Block a user