fix some bugs

This commit is contained in:
2026-08-06 17:12:57 +03:30
parent 7eef5e64b3
commit 72ff454c90
103 changed files with 6506 additions and 1573 deletions
+17 -3
View File
@@ -7,7 +7,11 @@ import {
sendOtp,
verifyOtp,
updateProfile as authUpdateProfile,
fetchMe,
} from '@/services/authService'
import { useCartStore } from './cart'
import { useOrderStore } from './order'
import { useCatalogStore } from './catalog'
export const useAuthStore = defineStore('auth', () => {
const user = ref<User | null>(getCurrentUser())
@@ -42,6 +46,11 @@ export const useAuthStore = defineStore('auth', () => {
loading.value = false
if (result.success && result.user) {
user.value = result.user
await Promise.all([
useCatalogStore().loadCompanies(),
useCartStore().hydrate(),
useOrderStore().hydrate(),
])
} else {
error.value = result.message
}
@@ -51,17 +60,22 @@ export const useAuthStore = defineStore('auth', () => {
function logout() {
authLogout()
user.value = null
useCartStore().resetLocal()
useOrderStore().resetLocal()
}
function updateProfile(data: Partial<Pick<User, 'firstName' | 'lastName' | 'nationalId'>>) {
async function updateProfile(data: Partial<Pick<User, 'firstName' | 'lastName' | 'nationalId'>>) {
if (!user.value) return null
const updated = authUpdateProfile(user.value.id, data)
const updated = await authUpdateProfile(data)
if (updated) user.value = updated
return updated
}
function hydrate() {
async function hydrate() {
user.value = getCurrentUser()
if (!user.value) return
const me = await fetchMe()
user.value = me
}
return {
+135 -166
View File
@@ -1,31 +1,22 @@
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import type { Cart, CartItem, Company, Product } from '@/types'
import { generateId } from '@/utils/format'
import type { Cart, Company, Product } from '@/types'
import { validateMeterage } from '@/utils/validation'
import { getItem, setItem, STORAGE_KEYS } from '@/services/storage'
import { getCompanyById } from '@/data/mockData'
import { apiRequest, ApiError } from '@/services/http'
import { getAccessToken } from '@/services/http'
import { useCatalogStore } from './catalog'
const DEFAULT_MAX_METERAGE = 5000
function loadCarts(): Cart[] {
return getItem<Cart[]>(STORAGE_KEYS.carts, [])
type ApiCart = Cart & { totalMeterage?: number; itemCount?: number }
function emptyCart(): Cart {
return { id: 'local', companyId: '', companyName: '', items: [] }
}
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 uniqueCompanyNames(items: Cart['items']): string {
const names = [...new Set(items.map((item) => item.companyName).filter(Boolean))]
return names.join('، ')
}
function companyMeterage(cart: Cart, companyId: string): number {
@@ -34,35 +25,13 @@ function companyMeterage(cart: Cart, companyId: string): number {
.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 carts = ref<Cart[]>([emptyCart()])
const activeCartId = ref<string | null>('local')
const selectedCompanyId = ref<string | null>(null)
const syncing = ref(false)
const activeCart = computed(() => carts.value.find((c) => c.id === activeCartId.value) ?? null)
const activeCart = computed(() => carts.value.find((c) => c.id === activeCartId.value) ?? carts.value[0] ?? null)
const totalMeterage = computed(
() => activeCart.value?.items.reduce((sum, i) => sum + i.meterage, 0) ?? 0,
@@ -75,107 +44,101 @@ export const useCartStore = defineStore('cart', () => {
return uniqueCompanyNames(activeCart.value.items)
})
const catalogStore = useCatalogStore()
const maxMeterage = computed(() => {
if (!activeCart.value?.items.length) {
const selected = selectedCompanyId.value
? getCompanyById(selectedCompanyId.value)
? catalogStore.getCompany(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)
const company = catalogStore.getCompany(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 {
function applyServerCart(serverCart: ApiCart) {
const cart: Cart = {
id: generateId(),
companyId: company?.id ?? '',
companyName: company?.name ?? '',
items: [],
id: serverCart.id,
companyId: serverCart.companyId || '',
companyName: serverCart.companyName || uniqueCompanyNames(serverCart.items),
items: serverCart.items ?? [],
notes: serverCart.notes,
}
carts.value.push(cart)
carts.value = [cart]
activeCartId.value = cart.id
if (company) selectedCompanyId.value = company.id
persist()
return cart
if (cart.companyId) selectedCompanyId.value = cart.companyId
}
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
async function hydrate() {
if (!getAccessToken()) {
resetLocal()
return
}
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
syncing.value = true
try {
const serverCart = await apiRequest<ApiCart>('/carts')
applyServerCart(serverCart)
} catch {
resetLocal()
} finally {
syncing.value = false
}
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 resetLocal() {
carts.value = [emptyCart()]
activeCartId.value = carts.value[0]!.id
selectedCompanyId.value = null
}
function selectCompany(company: Company): void {
selectedCompanyId.value = company.id
getOrCreateCart(company)
if (activeCart.value && !activeCart.value.items.length) {
activeCart.value.companyId = company.id
activeCart.value.companyName = company.name
}
}
function createNewCartForSameCompany(): Cart | null {
const company = selectedCompanyId.value
? getCompanyById(selectedCompanyId.value)
: activeCart.value?.companyId
? getCompanyById(activeCart.value.companyId)
: undefined
return createEmptyCart(company)
function initCartForCompany(company: Company): Cart {
selectCompany(company)
return activeCart.value ?? emptyCart()
}
function addItem(
function getOrCreateCart(company?: Company): Cart {
if (company) selectCompany(company)
return activeCart.value ?? emptyCart()
}
async function createNewCartForSameCompany(): Promise<Cart | null> {
try {
const serverCart = await apiRequest<ApiCart>('/carts', { method: 'DELETE' })
applyServerCart(serverCart)
return activeCart.value
} catch (e) {
console.error(e)
return null
}
}
async function addItem(
product: Product,
catalogCode: string,
meterage: number,
): { success: boolean; message?: string } {
const company = getCompanyById(product.companyId)
): Promise<{ success: boolean; message?: string }> {
const company = catalogStore.getCompany(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)
@@ -192,41 +155,40 @@ export const useCartStore = defineStore('cart', () => {
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,
})
try {
let serverCart: ApiCart
if (existing) {
serverCart = await apiRequest<ApiCart>(`/carts/items/${existing.id}`, {
method: 'PATCH',
body: { meterage },
})
} else {
serverCart = await apiRequest<ApiCart>('/carts/items', {
method: 'POST',
body: { productId: product.id, meterage },
})
}
applyServerCart(serverCart)
selectedCompanyId.value = company.id
return { success: true }
} catch (e) {
return {
success: false,
message: e instanceof ApiError ? e.message : 'افزودن به سبد ناموفق بود.',
}
}
syncCartCompanyMeta(cart)
persist()
return { success: true }
}
function updateItemMeterage(
async function updateItemMeterage(
itemId: string,
meterage: number,
product: Product,
): { success: boolean; message?: string } {
): Promise<{ 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 company = catalogStore.getCompany(item.companyId || product.companyId)
const companyMax = company?.maxMeteragePerInvoice ?? DEFAULT_MAX_METERAGE
const currentCompanyMeterage = companyMeterage(activeCart.value, item.companyId)
@@ -241,56 +203,62 @@ export const useCartStore = defineStore('cart', () => {
return { success: false, message: validation.message }
}
item.meterage = meterage
persist()
return { success: true }
try {
const serverCart = await apiRequest<ApiCart>(`/carts/items/${itemId}`, {
method: 'PATCH',
body: { meterage },
})
applyServerCart(serverCart)
return { success: true }
} catch (e) {
return {
success: false,
message: e instanceof ApiError ? e.message : 'به‌روزرسانی متراژ ناموفق بود.',
}
}
}
function removeItem(itemId: string) {
if (!activeCart.value) return
activeCart.value.items = activeCart.value.items.filter((i) => i.id !== itemId)
syncCartCompanyMeta(activeCart.value)
persist()
async function removeItem(itemId: string) {
try {
const serverCart = await apiRequest<ApiCart>(`/carts/items/${itemId}`, {
method: 'DELETE',
})
applyServerCart(serverCart)
} catch (e) {
console.error(e)
}
}
function setNotes(notes: string) {
if (!activeCart.value) return
activeCart.value.notes = notes
persist()
async function setNotes(notes: string) {
try {
const serverCart = await apiRequest<ApiCart>('/carts/notes', {
method: 'PATCH',
body: { notes },
})
applyServerCart(serverCart)
} catch (e) {
console.error(e)
}
}
function clearActiveCart() {
if (!activeCart.value) return
activeCart.value.items = []
activeCart.value.notes = undefined
persist()
async function clearActiveCart() {
try {
const serverCart = await apiRequest<ApiCart>('/carts', { method: 'DELETE' })
applyServerCart(serverCart)
} catch {
resetLocal()
}
}
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()
}
// server keeps a single cart
}
return {
carts,
activeCartId,
selectedCompanyId,
syncing,
activeCart,
totalMeterage,
itemCount,
@@ -308,5 +276,6 @@ export const useCartStore = defineStore('cart', () => {
clearActiveCart,
ensureActiveCartForCartPage,
hydrate,
resetLocal,
}
})
+81 -18
View File
@@ -1,38 +1,86 @@
import { defineStore } from 'pinia'
import { ref } from 'vue'
import {
companies as mockCompanies,
getCatalogsByCompany,
getProductsByCatalog,
getCatalogById,
getProductById,
getCompanyById,
} from '@/data/mockData'
import type { Catalog, Company, Product } from '@/types'
import { apiRequest } from '@/services/http'
import type { BankAccount, Catalog, Company, Product } from '@/types'
export const useCatalogStore = defineStore('catalog', () => {
const companies = ref<Company[]>(mockCompanies.filter((c) => c.isActive))
const companies = ref<Company[]>([])
const catalogs = ref<Catalog[]>([])
const products = ref<Product[]>([])
const bankAccounts = ref<BankAccount[]>([])
const searchQuery = ref('')
const loading = ref(false)
const loaded = ref(false)
async function loadCompanies(force = false) {
if (loaded.value && !force) return companies.value
loading.value = true
try {
const list = await apiRequest<Company[]>('/companies', {
query: { activeOnly: true },
})
companies.value = list
.filter((c) => c.isActive)
.sort((a, b) => a.displayOrder - b.displayOrder)
loaded.value = true
} finally {
loading.value = false
}
return companies.value
}
function getActiveCompanies(): Company[] {
return [...companies.value].sort((a, b) => a.displayOrder - b.displayOrder)
}
function getCompany(id: string): Company | undefined {
return getCompanyById(id)
return companies.value.find((c) => c.id === id)
}
async function loadCatalogs(companyId: string) {
const list = await apiRequest<Catalog[]>('/catalogs', {
query: { companyId, activeOnly: true },
})
catalogs.value = [
...catalogs.value.filter((c) => c.companyId !== companyId),
...list,
]
return list
}
async function loadProducts(catalogId: string, color?: string) {
const list = await apiRequest<Product[]>('/products', {
query: {
catalogId,
color: color && color !== 'all' ? color : undefined,
activeOnly: true,
},
})
const others = products.value.filter((p) => p.catalogId !== catalogId)
products.value = [...others, ...list]
return list
}
async function loadBankAccounts(companyId: string) {
const list = await apiRequest<BankAccount[]>(`/companies/${companyId}/bank-accounts`)
bankAccounts.value = [
...bankAccounts.value.filter((b) => b.companyId !== companyId),
...list.filter((b) => b.isActive),
]
return bankAccounts.value.filter((b) => b.companyId === companyId)
}
function fetchCatalogs(companyId: string, query = ''): Catalog[] {
let list = getCatalogsByCompany(companyId)
let list = catalogs.value.filter((c) => c.companyId === companyId && c.isActive)
if (query.trim()) {
const q = query.trim()
list = list.filter((c) => c.code.includes(q) || c.name.includes(q))
}
return list
return [...list].sort((a, b) => a.displayOrder - b.displayOrder)
}
function fetchProducts(catalogId: string, colorFilter?: string): Product[] {
let list = getProductsByCatalog(catalogId)
let list = products.value.filter((p) => p.catalogId === catalogId && p.isActive)
if (colorFilter && colorFilter !== 'all') {
list = list.filter((p) => p.color.includes(colorFilter))
}
@@ -40,21 +88,35 @@ export const useCatalogStore = defineStore('catalog', () => {
}
function getCatalog(id: string) {
return getCatalogById(id)
return catalogs.value.find((c) => c.id === id)
}
function getProduct(id: string) {
return getProductById(id)
return products.value.find((p) => p.id === id)
}
function getUniqueColors(catalogId: string): string[] {
const products = getProductsByCatalog(catalogId)
return [...new Set(products.map((p) => p.color))]
return [...new Set(fetchProducts(catalogId).map((p) => p.color))]
}
function getBankAccountsByCompany(companyId: string): BankAccount[] {
return bankAccounts.value
.filter((b) => b.companyId === companyId && b.isActive)
.sort((a, b) => a.displayOrder - b.displayOrder)
}
return {
companies,
catalogs,
products,
bankAccounts,
searchQuery,
loading,
loaded,
loadCompanies,
loadCatalogs,
loadProducts,
loadBankAccounts,
getActiveCompanies,
getCompany,
fetchCatalogs,
@@ -62,5 +124,6 @@ export const useCatalogStore = defineStore('catalog', () => {
getCatalog,
getProduct,
getUniqueColors,
getBankAccountsByCompany,
}
})
+121 -154
View File
@@ -5,87 +5,29 @@ import type {
CheckData,
CashPaymentReceipt,
Order,
OrderItem,
OrderStatus,
OrderStatusHistoryEntry,
SettlementType,
CheckRegistrationMode,
} from '@/types'
import { generateId, generateOrderNumber } from '@/utils/format'
import { generateId } from '@/utils/format'
import { calculateCheckRass } from '@/utils/checkRass'
import { getItem, setItem, STORAGE_KEYS } from '@/services/storage'
import { getCompanyById, getBankAccountsByCompany } from '@/data/mockData'
function loadOrders(): Order[] {
return getItem<Order[]>(STORAGE_KEYS.orders, [])
}
function saveOrders(orders: Order[]) {
setItem(STORAGE_KEYS.orders, orders)
}
function createStatusEntry(
from: OrderStatus | null,
to: OrderStatus,
note?: string,
): OrderStatusHistoryEntry {
return {
id: generateId(),
fromStatus: from,
toStatus: to,
changedAt: new Date().toISOString(),
changedBy: 'customer',
note,
}
}
function cartToOrderItems(cart: Cart): OrderItem[] {
return cart.items.map((item) => ({
id: generateId(),
productId: item.productId,
catalogCode: item.catalogCode,
productName: item.productName,
productCode: item.productCode,
color: item.color,
requestedMeterage: item.meterage,
unitPrice: item.unitPrice,
notes: item.notes,
}))
}
function computeCashExpiry(companyId: string): string {
const company = getCompanyById(companyId)
const now = new Date()
if (company?.cashInvoiceValidUntilHour) {
const expiry = new Date(now)
expiry.setHours(company.cashInvoiceValidUntilHour, 0, 0, 0)
if (expiry <= now) {
expiry.setDate(expiry.getDate() + 1)
}
return expiry.toISOString()
}
const hours = company?.cashInvoiceValidityHours ?? 8
return new Date(now.getTime() + hours * 60 * 60 * 1000).toISOString()
}
import { apiRequest, ApiError, getAccessToken } from '@/services/http'
import { useCatalogStore } from './catalog'
export const useOrderStore = defineStore('order', () => {
const orders = ref<Order[]>(loadOrders())
const orders = ref<Order[]>([])
const draftChecks = ref<CheckData[]>([])
const draftSettlementType = ref<SettlementType | null>(null)
const draftCheckMode = ref<CheckRegistrationMode | null>(null)
const checkoutCartId = ref<string | null>(null)
const loading = ref(false)
const userOrders = computed(
() => (userId: string) =>
orders.value
.filter((o) => o.userId === userId)
.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()),
() => (_userId: string) =>
[...orders.value].sort(
(a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime(),
),
)
function persist() {
saveOrders(orders.value)
}
function getOrderById(id: string): Order | undefined {
return orders.value.find((o) => o.id === id)
}
@@ -120,108 +62,131 @@ export const useOrderStore = defineStore('order', () => {
return calculateCheckRass(draftChecks.value, baseDate ?? new Date())
}
function submitOrder(
userId: string,
cart: Cart,
settlementType: SettlementType,
checks?: CheckData[],
checkMode?: CheckRegistrationMode,
): Order {
const items = cartToOrderItems(cart)
const totalMeterage = items.reduce((s, i) => s + i.requestedMeterage, 0)
const now = new Date().toISOString()
let status: OrderStatus = 'awaiting_review'
let checkRassDays: number | undefined
let checkRassDate: string | undefined
let cashInvoiceExpiresAt: string | undefined
if (settlementType === 'check' && checks && checks.length > 0) {
const rass = calculateCheckRass(checks, new Date())
checkRassDays = rass.rassDays
checkRassDate = rass.rassDate ?? undefined
status = 'awaiting_review'
async function hydrate() {
if (!getAccessToken()) {
orders.value = []
return
}
if (settlementType === 'cash') {
cashInvoiceExpiresAt = computeCashExpiry(cart.companyId)
status = 'awaiting_review'
loading.value = true
try {
orders.value = await apiRequest<Order[]>('/orders')
} catch {
orders.value = []
} finally {
loading.value = false
}
}
const order: Order = {
id: generateId(),
orderNumber: generateOrderNumber(),
userId,
companyId: cart.companyId,
companyName: cart.companyName,
items,
totalMeterage,
settlementType,
checkMode,
checks: checks ? [...checks] : undefined,
checkRassDays,
checkRassDate,
status,
notes: cart.notes,
cashInvoiceExpiresAt,
statusHistory: [
createStatusEntry(null, 'submitted', 'سفارش ثبت شد'),
createStatusEntry('submitted', status, 'ارسال برای بررسی مدیریت'),
],
createdAt: now,
updatedAt: now,
}
orders.value.unshift(order)
persist()
function resetLocal() {
orders.value = []
draftChecks.value = []
draftSettlementType.value = null
draftCheckMode.value = null
checkoutCartId.value = null
return order
}
function submitCashReceipt(
async function fetchOrder(id: string): Promise<Order | null> {
try {
const order = await apiRequest<Order>(`/orders/${id}`)
const idx = orders.value.findIndex((o) => o.id === id)
if (idx >= 0) orders.value[idx] = order
else orders.value.unshift(order)
return order
} catch {
return null
}
}
async function submitOrder(
_userId: string,
cart: Cart,
settlementType: SettlementType,
checks?: CheckData[],
checkMode?: CheckRegistrationMode,
): Promise<Order> {
if (!cart.companyId) {
throw new Error('شرکت سبد مشخص نیست.')
}
if (!cart.items.length) {
throw new Error('سبد خرید خالی است.')
}
const body = {
companyId: cart.companyId,
settlementType,
checkMode: settlementType === 'check' ? (checkMode ?? 'full') : undefined,
notes: cart.notes,
items: cart.items.map((item) => ({
productId: item.productId,
meterage: item.meterage,
notes: item.notes,
})),
checks:
settlementType === 'check' && checks
? checks.map(({ id: _id, ...rest }) => rest)
: undefined,
}
try {
const order = await apiRequest<Order>('/orders', {
method: 'POST',
body,
})
orders.value.unshift(order)
draftChecks.value = []
draftSettlementType.value = null
draftCheckMode.value = null
checkoutCartId.value = null
return order
} catch (e) {
throw new Error(e instanceof ApiError ? e.message : 'ثبت سفارش ناموفق بود.')
}
}
async function submitCashReceipt(
orderId: string,
receipt: Omit<CashPaymentReceipt, 'id' | 'submittedAt'>,
) {
const order = getOrderById(orderId)
if (!order) return null
const fullReceipt: CashPaymentReceipt = {
...receipt,
id: generateId(),
submittedAt: new Date().toISOString(),
try {
const order = await apiRequest<Order>(`/orders/${orderId}/cash-receipt`, {
method: 'POST',
body: {
amount: receipt.amount,
transferDate: receipt.transferDate,
transferTime: receipt.transferTime,
trackingNumber: receipt.trackingNumber,
destinationAccountId: receipt.destinationAccountId,
receiptImage: receipt.receiptImage,
notes: receipt.notes,
},
})
const idx = orders.value.findIndex((o) => o.id === orderId)
if (idx >= 0) orders.value[idx] = order
else orders.value.unshift(order)
return order
} catch (e) {
throw new Error(e instanceof ApiError ? e.message : 'ثبت رسید ناموفق بود.')
}
const prevStatus = order.status
order.cashReceipt = fullReceipt
order.status = 'awaiting_cash_confirmation'
order.statusHistory.push(
createStatusEntry(prevStatus, 'awaiting_cash_confirmation', 'رسید پرداخت ثبت شد'),
)
order.updatedAt = new Date().toISOString()
persist()
return order
}
function updateOrderChecks(orderId: string, checks: CheckData[]) {
const order = getOrderById(orderId)
if (!order) return null
const rass = calculateCheckRass(checks, new Date(order.createdAt))
order.checks = checks
order.checkRassDays = rass.rassDays
order.checkRassDate = rass.rassDate ?? undefined
order.updatedAt = new Date().toISOString()
persist()
return order
async function updateOrderChecks(orderId: string, checks: CheckData[]) {
try {
const order = await apiRequest<Order>(`/orders/${orderId}/checks`, {
method: 'PATCH',
body: {
checks: checks.map(({ id: _id, ...rest }) => rest),
},
})
const idx = orders.value.findIndex((o) => o.id === orderId)
if (idx >= 0) orders.value[idx] = order
return order
} catch {
return null
}
}
function hydrate() {
orders.value = loadOrders()
async function getBankAccountsForCompany(companyId: string) {
return useCatalogStore().loadBankAccounts(companyId)
}
return {
@@ -230,8 +195,8 @@ export const useOrderStore = defineStore('order', () => {
draftSettlementType,
draftCheckMode,
checkoutCartId,
loading,
userOrders,
persist,
getOrderById,
startCheckout,
setCheckMode,
@@ -243,6 +208,8 @@ export const useOrderStore = defineStore('order', () => {
submitCashReceipt,
updateOrderChecks,
hydrate,
getBankAccountsForCompany: getBankAccountsByCompany,
resetLocal,
fetchOrder,
getBankAccountsForCompany,
}
})
+34
View File
@@ -0,0 +1,34 @@
import { defineStore } from 'pinia'
import { computed, watchEffect } from 'vue'
import { usePreferredDark, useStorage } from '@vueuse/core'
export type ThemePreference = 'light' | 'dark' | 'system'
export const useThemeStore = defineStore('theme', () => {
const preference = useStorage<ThemePreference>('ft_theme', 'system')
const prefersDark = usePreferredDark()
const resolved = computed<'light' | 'dark'>(() => {
if (preference.value === 'system') return prefersDark.value ? 'dark' : 'light'
return preference.value
})
const isDark = computed(() => resolved.value === 'dark')
watchEffect(() => {
const root = document.documentElement
root.classList.toggle('dark', isDark.value)
root.style.colorScheme = resolved.value
})
function setPreference(value: ThemePreference) {
preference.value = value
}
return {
preference,
resolved,
isDark,
setPreference,
}
})