Allow mixed-company cart, add delete confirmations, and fix profile settings routes.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-05 14:07:42 +02:00
co-authored by Cursor
parent fd3e5e5126
commit 7eef5e64b3
20 changed files with 802 additions and 145 deletions
+133 -81
View File
@@ -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,