Initialize the user app and load home banners from the API.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,206 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { computed, ref } from 'vue'
|
||||
import { apiRequest } from '@/services/http'
|
||||
|
||||
const STORAGE_KEY = 'ft_catalog_requests'
|
||||
|
||||
export type CatalogRequestItem = {
|
||||
productId: string
|
||||
productName: string
|
||||
productCode: string
|
||||
quantity: number
|
||||
}
|
||||
|
||||
/** UI statuses (legacy) + server statuses mapped in */
|
||||
export type CatalogRequestStatus =
|
||||
| 'submitted'
|
||||
| 'reviewing'
|
||||
| 'preparing'
|
||||
| 'shipped'
|
||||
| 'delivered'
|
||||
| 'new'
|
||||
| 'approved'
|
||||
| 'ready_to_ship'
|
||||
| 'returned'
|
||||
| 'cancelled'
|
||||
|
||||
export type CatalogRequest = {
|
||||
id: string
|
||||
createdAt: string
|
||||
status: CatalogRequestStatus
|
||||
note?: string
|
||||
items: CatalogRequestItem[]
|
||||
}
|
||||
|
||||
const SERVER_TO_UI: Record<string, CatalogRequestStatus> = {
|
||||
new: 'submitted',
|
||||
approved: 'reviewing',
|
||||
ready_to_ship: 'preparing',
|
||||
delivered: 'delivered',
|
||||
returned: 'delivered',
|
||||
cancelled: 'submitted',
|
||||
}
|
||||
|
||||
function readAll(): CatalogRequest[] {
|
||||
try {
|
||||
const raw = localStorage.getItem(STORAGE_KEY)
|
||||
if (!raw) return []
|
||||
const parsed = JSON.parse(raw) as unknown
|
||||
return Array.isArray(parsed) ? (parsed as CatalogRequest[]) : []
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
function writeAll(list: CatalogRequest[]) {
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(list))
|
||||
}
|
||||
|
||||
const STATUS_LABELS: Record<string, string> = {
|
||||
submitted: 'ثبتشده',
|
||||
reviewing: 'در حال بررسی',
|
||||
preparing: 'آمادهسازی',
|
||||
shipped: 'ارسالشده',
|
||||
delivered: 'تحویلشده',
|
||||
new: 'درخواست جدید',
|
||||
approved: 'تأیید شده',
|
||||
ready_to_ship: 'آماده ارسال',
|
||||
returned: 'بازگردانده شده',
|
||||
cancelled: 'لغو شده',
|
||||
}
|
||||
|
||||
function mapServerRow(row: {
|
||||
id: string
|
||||
createdAt: string
|
||||
status: string
|
||||
note?: string
|
||||
items: Array<{
|
||||
productId: string
|
||||
productName: string
|
||||
productCode: string
|
||||
quantity: number
|
||||
}>
|
||||
}): CatalogRequest {
|
||||
return {
|
||||
id: row.id,
|
||||
createdAt: row.createdAt,
|
||||
status: SERVER_TO_UI[row.status] ?? (row.status as CatalogRequestStatus),
|
||||
note: row.note,
|
||||
items: row.items.map((i) => ({
|
||||
productId: i.productId,
|
||||
productName: i.productName,
|
||||
productCode: i.productCode,
|
||||
quantity: i.quantity,
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
export const useCatalogRequestsStore = defineStore('catalogRequests', () => {
|
||||
const requests = ref<CatalogRequest[]>(readAll())
|
||||
const useApi = ref(true)
|
||||
|
||||
const sorted = computed(() =>
|
||||
[...requests.value].sort((a, b) => b.createdAt.localeCompare(a.createdAt)),
|
||||
)
|
||||
|
||||
function statusLabel(status: CatalogRequest['status']) {
|
||||
return STATUS_LABELS[status] ?? status
|
||||
}
|
||||
|
||||
async function hydrate() {
|
||||
if (!useApi.value) {
|
||||
requests.value = readAll()
|
||||
return
|
||||
}
|
||||
try {
|
||||
const rows = await apiRequest<
|
||||
Array<{
|
||||
id: string
|
||||
createdAt: string
|
||||
status: string
|
||||
note?: string
|
||||
items: Array<{
|
||||
productId: string
|
||||
productName: string
|
||||
productCode: string
|
||||
quantity: number
|
||||
}>
|
||||
}>
|
||||
>('/catalog-requests')
|
||||
requests.value = rows.map(mapServerRow)
|
||||
writeAll(requests.value)
|
||||
} catch {
|
||||
useApi.value = false
|
||||
requests.value = readAll()
|
||||
}
|
||||
}
|
||||
|
||||
async function create(input: { items: CatalogRequestItem[]; note?: string }) {
|
||||
const items = input.items
|
||||
.filter((i) => i.productId && i.quantity > 0)
|
||||
.map((i) => ({
|
||||
...i,
|
||||
quantity: Math.max(1, Math.floor(i.quantity)),
|
||||
}))
|
||||
if (!items.length) {
|
||||
return { success: false as const, message: 'حداقل یک قلم با تعداد معتبر لازم است.' }
|
||||
}
|
||||
|
||||
if (useApi.value) {
|
||||
try {
|
||||
const row = await apiRequest<{
|
||||
id: string
|
||||
createdAt: string
|
||||
status: string
|
||||
note?: string
|
||||
items: Array<{
|
||||
productId: string
|
||||
productName: string
|
||||
productCode: string
|
||||
quantity: number
|
||||
}>
|
||||
}>('/catalog-requests', {
|
||||
method: 'POST',
|
||||
body: {
|
||||
note: input.note?.trim() || undefined,
|
||||
items: items.map((i) => ({
|
||||
productId: i.productId,
|
||||
quantity: i.quantity,
|
||||
})),
|
||||
},
|
||||
})
|
||||
const mapped = mapServerRow(row)
|
||||
requests.value = [mapped, ...requests.value.filter((r) => r.id !== mapped.id)]
|
||||
writeAll(requests.value)
|
||||
return { success: true as const, request: mapped }
|
||||
} catch (err) {
|
||||
useApi.value = false
|
||||
// fall through to local
|
||||
}
|
||||
}
|
||||
|
||||
const row: CatalogRequest = {
|
||||
id: `cr-${Date.now()}`,
|
||||
createdAt: new Date().toISOString(),
|
||||
status: 'submitted',
|
||||
note: input.note?.trim() || undefined,
|
||||
items,
|
||||
}
|
||||
requests.value = [row, ...requests.value]
|
||||
writeAll(requests.value)
|
||||
return { success: true as const, request: row }
|
||||
}
|
||||
|
||||
function getById(id: string) {
|
||||
return requests.value.find((r) => r.id === id) ?? null
|
||||
}
|
||||
|
||||
return {
|
||||
requests,
|
||||
sorted,
|
||||
statusLabel,
|
||||
create,
|
||||
getById,
|
||||
hydrate,
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user