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('POS_ID'); export type TViewType = 'list' | 'grid'; interface IPosState { getStockLoading: boolean; stock: Maybe; getInfoLoading: boolean; info: Maybe; getProductCategoriesLoading: boolean; productCategories: Maybe; activeProductCategory: Maybe; inOrderProducts: IPosInOrderProduct[]; selectedCustomer: Maybe; 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({ ...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) { this.state$.set({ ...this.state$(), ...partial }); } reset() { this.state$.set({ ...INITIAL_POS_STATE }); } initial() { this.getInfo(); this.getStock(); this.getProductCategories(); } fillInitial(data: Partial) { 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) { 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); }, }); } }