Allow mixed-company cart, add delete confirmations, and fix profile settings routes.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+133
-81
@@ -6,6 +6,8 @@ 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, [])
|
||||
}
|
||||
@@ -26,16 +28,37 @@ function getCartTotalMeterage(cart: Cart): number {
|
||||
return cart.items.reduce((sum, item) => sum + item.meterage, 0)
|
||||
}
|
||||
|
||||
function findOpenCartForCompany(carts: Cart[], company: Company): Cart | undefined {
|
||||
return carts.find(
|
||||
(cart) =>
|
||||
cart.companyId === company.id &&
|
||||
getCartTotalMeterage(cart) < company.maxMeteragePerInvoice,
|
||||
)
|
||||
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[]>(loadCarts())
|
||||
const carts = ref<Cart[]>(migrateCartItems(loadCarts()))
|
||||
const activeCartId = ref<string | null>(loadActiveCartId())
|
||||
const selectedCompanyId = ref<string | null>(null)
|
||||
|
||||
@@ -47,10 +70,24 @@ export const useCartStore = defineStore('cart', () => {
|
||||
|
||||
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) return 5000
|
||||
const company = getCompanyById(activeCart.value.companyId)
|
||||
return company?.maxMeteragePerInvoice ?? 5000
|
||||
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))
|
||||
@@ -62,60 +99,68 @@ export const useCartStore = defineStore('cart', () => {
|
||||
saveActiveCartId(activeCartId.value)
|
||||
}
|
||||
|
||||
function initCartForCompany(company: Company): Cart {
|
||||
const existing = findOpenCartForCompany(carts.value, company)
|
||||
if (existing) {
|
||||
activeCartId.value = existing.id
|
||||
selectedCompanyId.value = company.id
|
||||
persist()
|
||||
return existing
|
||||
}
|
||||
|
||||
function createEmptyCart(company?: Company): Cart {
|
||||
const cart: Cart = {
|
||||
id: generateId(),
|
||||
companyId: company.id,
|
||||
companyName: company.name,
|
||||
companyId: company?.id ?? '',
|
||||
companyName: company?.name ?? '',
|
||||
items: [],
|
||||
}
|
||||
carts.value.push(cart)
|
||||
activeCartId.value = cart.id
|
||||
selectedCompanyId.value = company.id
|
||||
if (company) selectedCompanyId.value = company.id
|
||||
persist()
|
||||
return cart
|
||||
}
|
||||
|
||||
function switchCompanyWarning(currentCompanyName: string, newCompanyName: string): string {
|
||||
return `سبد خرید فعلی مربوط به ${currentCompanyName} است. برای خرید از ${newCompanyName} باید سبد خرید جداگانهای ایجاد کنید.`
|
||||
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)
|
||||
}
|
||||
|
||||
function canSwitchToCompany(companyId: string): { allowed: boolean; message?: string } {
|
||||
if (!activeCart.value || activeCart.value.items.length === 0) {
|
||||
return { allowed: true }
|
||||
}
|
||||
if (activeCart.value.companyId === companyId) {
|
||||
return { allowed: true }
|
||||
}
|
||||
const newCompany = getCompanyById(companyId)
|
||||
return {
|
||||
allowed: false,
|
||||
message: switchCompanyWarning(activeCart.value.companyName, newCompany?.name ?? ''),
|
||||
/** @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 {
|
||||
if (!activeCart.value) return null
|
||||
const company = getCompanyById(activeCart.value.companyId)
|
||||
if (!company) return null
|
||||
const cart: Cart = {
|
||||
id: generateId(),
|
||||
companyId: company.id,
|
||||
companyName: company.name,
|
||||
items: [],
|
||||
}
|
||||
carts.value.push(cart)
|
||||
activeCartId.value = cart.id
|
||||
persist()
|
||||
return cart
|
||||
const company = selectedCompanyId.value
|
||||
? getCompanyById(selectedCompanyId.value)
|
||||
: activeCart.value?.companyId
|
||||
? getCompanyById(activeCart.value.companyId)
|
||||
: undefined
|
||||
return createEmptyCart(company)
|
||||
}
|
||||
|
||||
function addItem(
|
||||
@@ -123,40 +168,37 @@ export const useCartStore = defineStore('cart', () => {
|
||||
catalogCode: string,
|
||||
meterage: number,
|
||||
): { success: boolean; message?: string } {
|
||||
if (!activeCart.value) {
|
||||
return { success: false, message: 'ابتدا شرکت را انتخاب کنید.' }
|
||||
const company = getCompanyById(product.companyId)
|
||||
if (!company) {
|
||||
return { success: false, message: 'شرکت محصول یافت نشد.' }
|
||||
}
|
||||
|
||||
if (activeCart.value.companyId !== product.companyId) {
|
||||
const currentCompany = activeCart.value.companyName
|
||||
const newCompany = getCompanyById(product.companyId)
|
||||
return {
|
||||
success: false,
|
||||
message: switchCompanyWarning(currentCompany, newCompany?.name ?? ''),
|
||||
}
|
||||
}
|
||||
const cart = getOrCreateCart(company)
|
||||
selectedCompanyId.value = company.id
|
||||
|
||||
const validation = validateMeterage(meterage, product, totalMeterage.value, maxMeterage.value)
|
||||
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 }
|
||||
}
|
||||
|
||||
const existingIndex = activeCart.value.items.findIndex((i) => i.productId === product.id)
|
||||
if (existingIndex >= 0) {
|
||||
const existing = activeCart.value.items[existingIndex]!
|
||||
const revalidation = validateMeterage(
|
||||
meterage,
|
||||
product,
|
||||
totalMeterage.value,
|
||||
maxMeterage.value,
|
||||
existing.meterage,
|
||||
)
|
||||
if (!revalidation.valid) {
|
||||
return { success: false, message: revalidation.message }
|
||||
}
|
||||
if (existing) {
|
||||
existing.meterage = meterage
|
||||
existing.catalogCode = catalogCode
|
||||
existing.companyId = company.id
|
||||
existing.companyName = company.name
|
||||
} else {
|
||||
const item: CartItem = {
|
||||
cart.items.push({
|
||||
id: generateId(),
|
||||
productId: product.id,
|
||||
catalogCode,
|
||||
@@ -165,10 +207,12 @@ export const useCartStore = defineStore('cart', () => {
|
||||
color: product.color,
|
||||
meterage,
|
||||
unitPrice: product.basePrice,
|
||||
}
|
||||
activeCart.value.items.push(item)
|
||||
companyId: company.id,
|
||||
companyName: company.name,
|
||||
})
|
||||
}
|
||||
|
||||
syncCartCompanyMeta(cart)
|
||||
persist()
|
||||
return { success: true }
|
||||
}
|
||||
@@ -182,11 +226,15 @@ export const useCartStore = defineStore('cart', () => {
|
||||
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,
|
||||
totalMeterage.value,
|
||||
maxMeterage.value,
|
||||
currentCompanyMeterage,
|
||||
companyMax,
|
||||
item.meterage,
|
||||
)
|
||||
if (!validation.valid) {
|
||||
@@ -201,6 +249,7 @@ export const useCartStore = defineStore('cart', () => {
|
||||
function removeItem(itemId: string) {
|
||||
if (!activeCart.value) return
|
||||
activeCart.value.items = activeCart.value.items.filter((i) => i.id !== itemId)
|
||||
syncCartCompanyMeta(activeCart.value)
|
||||
persist()
|
||||
}
|
||||
|
||||
@@ -224,15 +273,17 @@ export const useCartStore = defineStore('cart', () => {
|
||||
if (!cartWithItems) return
|
||||
|
||||
activeCartId.value = cartWithItems.id
|
||||
selectedCompanyId.value = cartWithItems.companyId
|
||||
selectedCompanyId.value = selectedCompanyId.value || cartWithItems.companyId
|
||||
persist()
|
||||
}
|
||||
|
||||
function hydrate() {
|
||||
carts.value = loadCarts()
|
||||
carts.value = migrateCartItems(loadCarts())
|
||||
activeCartId.value = loadActiveCartId()
|
||||
if (activeCart.value) {
|
||||
selectedCompanyId.value = activeCart.value.companyId
|
||||
syncCartCompanyMeta(activeCart.value)
|
||||
selectedCompanyId.value = selectedCompanyId.value || activeCart.value.companyId
|
||||
persist()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -243,11 +294,12 @@ export const useCartStore = defineStore('cart', () => {
|
||||
activeCart,
|
||||
totalMeterage,
|
||||
itemCount,
|
||||
companyNamesLabel,
|
||||
maxMeterage,
|
||||
remainingCapacity,
|
||||
isFull,
|
||||
initCartForCompany,
|
||||
canSwitchToCompany,
|
||||
selectCompany,
|
||||
createNewCartForSameCompany,
|
||||
addItem,
|
||||
updateItemMeterage,
|
||||
|
||||
Reference in New Issue
Block a user