Files
fabric_trade_user/src/stores/cart.ts
T

313 lines
8.8 KiB
TypeScript

import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import type { Cart, CartItem, Company, Product } from '@/types'
import { generateId } from '@/utils/format'
import { validateMeterage } from '@/utils/validation'
import { getItem, setItem, STORAGE_KEYS } from '@/services/storage'
import { getCompanyById } from '@/data/mockData'
const DEFAULT_MAX_METERAGE = 5000
function loadCarts(): Cart[] {
return getItem<Cart[]>(STORAGE_KEYS.carts, [])
}
function saveCarts(carts: Cart[]) {
setItem(STORAGE_KEYS.carts, carts)
}
function loadActiveCartId(): string | null {
return getItem<string | null>(STORAGE_KEYS.activeCartId, null)
}
function saveActiveCartId(id: string | null) {
setItem(STORAGE_KEYS.activeCartId, id)
}
function getCartTotalMeterage(cart: Cart): number {
return cart.items.reduce((sum, item) => sum + item.meterage, 0)
}
function companyMeterage(cart: Cart, companyId: string): number {
return cart.items
.filter((item) => item.companyId === companyId)
.reduce((sum, item) => sum + item.meterage, 0)
}
function uniqueCompanyNames(items: CartItem[]): string {
const names = [...new Set(items.map((item) => item.companyName).filter(Boolean))]
return names.join('، ')
}
function syncCartCompanyMeta(cart: Cart) {
if (cart.items.length === 0) return
const first = cart.items[0]!
cart.companyId = first.companyId
cart.companyName = uniqueCompanyNames(cart.items) || first.companyName
}
function migrateCartItems(carts: Cart[]): Cart[] {
return carts.map((cart) => ({
...cart,
items: cart.items.map((item) => ({
...item,
companyId: item.companyId || cart.companyId,
companyName: item.companyName || cart.companyName,
})),
}))
}
export const useCartStore = defineStore('cart', () => {
const carts = ref<Cart[]>(migrateCartItems(loadCarts()))
const activeCartId = ref<string | null>(loadActiveCartId())
const selectedCompanyId = ref<string | null>(null)
const activeCart = computed(() => carts.value.find((c) => c.id === activeCartId.value) ?? null)
const totalMeterage = computed(
() => activeCart.value?.items.reduce((sum, i) => sum + i.meterage, 0) ?? 0,
)
const itemCount = computed(() => activeCart.value?.items.length ?? 0)
const companyNamesLabel = computed(() => {
if (!activeCart.value?.items.length) return ''
return uniqueCompanyNames(activeCart.value.items)
})
const maxMeterage = computed(() => {
if (!activeCart.value?.items.length) {
const selected = selectedCompanyId.value
? getCompanyById(selectedCompanyId.value)
: null
return selected?.maxMeteragePerInvoice ?? DEFAULT_MAX_METERAGE
}
const companyIds = [...new Set(activeCart.value.items.map((item) => item.companyId))]
return companyIds.reduce((sum, id) => {
const company = getCompanyById(id)
return sum + (company?.maxMeteragePerInvoice ?? DEFAULT_MAX_METERAGE)
}, 0)
})
const remainingCapacity = computed(() => Math.max(0, maxMeterage.value - totalMeterage.value))
const isFull = computed(() => totalMeterage.value >= maxMeterage.value)
function persist() {
saveCarts(carts.value)
saveActiveCartId(activeCartId.value)
}
function createEmptyCart(company?: Company): Cart {
const cart: Cart = {
id: generateId(),
companyId: company?.id ?? '',
companyName: company?.name ?? '',
items: [],
}
carts.value.push(cart)
activeCartId.value = cart.id
if (company) selectedCompanyId.value = company.id
persist()
return cart
}
function getOrCreateCart(company?: Company): Cart {
if (activeCart.value) return activeCart.value
const withItems = carts.value.find((cart) => cart.items.length > 0)
if (withItems) {
activeCartId.value = withItems.id
selectedCompanyId.value = selectedCompanyId.value ?? withItems.companyId
persist()
return withItems
}
const open = carts.value.find(
(cart) => getCartTotalMeterage(cart) < DEFAULT_MAX_METERAGE,
)
if (open) {
activeCartId.value = open.id
if (company) selectedCompanyId.value = company.id
persist()
return open
}
return createEmptyCart(company)
}
/** @deprecated Prefer getOrCreateCart / selectCompany — kept for callers. */
function initCartForCompany(company: Company): Cart {
selectedCompanyId.value = company.id
const cart = getOrCreateCart(company)
if (!cart.companyId) {
cart.companyId = company.id
cart.companyName = company.name
persist()
}
return cart
}
function selectCompany(company: Company): void {
selectedCompanyId.value = company.id
getOrCreateCart(company)
}
function createNewCartForSameCompany(): Cart | null {
const company = selectedCompanyId.value
? getCompanyById(selectedCompanyId.value)
: activeCart.value?.companyId
? getCompanyById(activeCart.value.companyId)
: undefined
return createEmptyCart(company)
}
function addItem(
product: Product,
catalogCode: string,
meterage: number,
): { success: boolean; message?: string } {
const company = getCompanyById(product.companyId)
if (!company) {
return { success: false, message: 'شرکت محصول یافت نشد.' }
}
const cart = getOrCreateCart(company)
selectedCompanyId.value = company.id
const companyMax = company.maxMeteragePerInvoice
const currentCompanyMeterage = companyMeterage(cart, company.id)
const existing = cart.items.find((i) => i.productId === product.id)
const previousMeterage = existing?.meterage ?? 0
const validation = validateMeterage(
meterage,
product,
currentCompanyMeterage,
companyMax,
previousMeterage,
)
if (!validation.valid) {
return { success: false, message: validation.message }
}
if (existing) {
existing.meterage = meterage
existing.catalogCode = catalogCode
existing.companyId = company.id
existing.companyName = company.name
} else {
cart.items.push({
id: generateId(),
productId: product.id,
catalogCode,
productName: product.name,
productCode: product.code,
color: product.color,
meterage,
unitPrice: product.basePrice,
companyId: company.id,
companyName: company.name,
})
}
syncCartCompanyMeta(cart)
persist()
return { success: true }
}
function updateItemMeterage(
itemId: string,
meterage: number,
product: Product,
): { success: boolean; message?: string } {
if (!activeCart.value) return { success: false, message: 'سبد خرید یافت نشد.' }
const item = activeCart.value.items.find((i) => i.id === itemId)
if (!item) return { success: false, message: 'آیتم یافت نشد.' }
const company = getCompanyById(item.companyId || product.companyId)
const companyMax = company?.maxMeteragePerInvoice ?? DEFAULT_MAX_METERAGE
const currentCompanyMeterage = companyMeterage(activeCart.value, item.companyId)
const validation = validateMeterage(
meterage,
product,
currentCompanyMeterage,
companyMax,
item.meterage,
)
if (!validation.valid) {
return { success: false, message: validation.message }
}
item.meterage = meterage
persist()
return { success: true }
}
function removeItem(itemId: string) {
if (!activeCart.value) return
activeCart.value.items = activeCart.value.items.filter((i) => i.id !== itemId)
syncCartCompanyMeta(activeCart.value)
persist()
}
function setNotes(notes: string) {
if (!activeCart.value) return
activeCart.value.notes = notes
persist()
}
function clearActiveCart() {
if (!activeCart.value) return
activeCart.value.items = []
activeCart.value.notes = undefined
persist()
}
function ensureActiveCartForCartPage() {
if (activeCart.value?.items.length) return
const cartWithItems = carts.value.find((cart) => cart.items.length > 0)
if (!cartWithItems) return
activeCartId.value = cartWithItems.id
selectedCompanyId.value = selectedCompanyId.value || cartWithItems.companyId
persist()
}
function hydrate() {
carts.value = migrateCartItems(loadCarts())
activeCartId.value = loadActiveCartId()
if (activeCart.value) {
syncCartCompanyMeta(activeCart.value)
selectedCompanyId.value = selectedCompanyId.value || activeCart.value.companyId
persist()
}
}
return {
carts,
activeCartId,
selectedCompanyId,
activeCart,
totalMeterage,
itemCount,
companyNamesLabel,
maxMeterage,
remainingCapacity,
isFull,
initCartForCompany,
selectCompany,
createNewCartForSameCompany,
addItem,
updateItemMeterage,
removeItem,
setNotes,
clearActiveCart,
ensureActiveCartForCartPage,
hydrate,
}
})