feat: paginate customer orders and polish the dashboard hero
Add paged orders API and infinite scroll, short order numbers, and align the guest invite card with the logged-in hero shell. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -20,3 +20,19 @@ export function jdate(val: string | undefined | null): string {
|
||||
}
|
||||
return val as any
|
||||
}
|
||||
|
||||
/** Jalali date + time for status timelines and similar detail surfaces. */
|
||||
export function jdatetime(val: string | undefined | null): string {
|
||||
if (val) {
|
||||
return moment(val).format('jYYYY/jMM/jDD HH:mm')
|
||||
}
|
||||
return val as any
|
||||
}
|
||||
|
||||
/** Compact timeline stamp — e.g. «25 مهر، 11:19» (reference card style). */
|
||||
export function jtimelineWhen(val: string | undefined | null): string {
|
||||
if (val) {
|
||||
return moment(val).format('jD jMMMM، HH:mm')
|
||||
}
|
||||
return val as any
|
||||
}
|
||||
|
||||
@@ -1,59 +1,96 @@
|
||||
// Persian labels ported from the legacy Laravel Order::$STS enum (henza_server
|
||||
// app/Models/Order.php) — the authoritative source, not henza_pwa's own
|
||||
// Orders.vue filter-tab labels, which used a slightly different, inconsistent
|
||||
// set (its key=0 was labeled "لغو شده"/cancelled, while the actual backend
|
||||
// model's sts=0 means "معلق"/suspended — legacy itself was inconsistent here).
|
||||
// Our OrderStatus enum cleanly separates SUSPENDED and CANCELLED, so mapped
|
||||
// directly, one label per real status.
|
||||
// Persian labels for the real OrderStatus enum (apps/api prisma schema).
|
||||
// One label per status — shared by filter chips, list cards, and detail.
|
||||
export const ORDER_STATUS_LABELS: Record<string, string> = {
|
||||
SUSPENDED: 'معلق',
|
||||
PLACED: 'ثبت سفارش',
|
||||
ADMIN_APPROVED: 'تایید مدیر سیستم',
|
||||
SOURCING: 'درحال تامین',
|
||||
PREPARING: 'درحال آماده سازی',
|
||||
DELIVERED_TO_AGENT: 'تحویل مسئول فروش',
|
||||
ADMIN_APPROVED: 'تایید مدیر',
|
||||
SOURCING: 'در حال تامین',
|
||||
PREPARING: 'آمادهسازی',
|
||||
DELIVERED_TO_AGENT: 'تحویل نماینده',
|
||||
DELIVERED_TO_CUSTOMER: 'تحویل مشتری',
|
||||
CANCELLED: 'لغو شده',
|
||||
};
|
||||
}
|
||||
|
||||
/** Short blurbs under timeline titles (reference card style). */
|
||||
export const ORDER_STATUS_BLURBS: Record<string, string> = {
|
||||
SUSPENDED: 'سفارش بهدلیل سقف اعتبار معلق شد',
|
||||
PLACED: 'سفارش ثبت و دریافت شد',
|
||||
ADMIN_APPROVED: 'سفارش توسط مدیر تایید شد',
|
||||
SOURCING: 'کالا در حال تامین است',
|
||||
PREPARING: 'سفارش در حال آمادهسازی است',
|
||||
DELIVERED_TO_AGENT: 'تحویل به نماینده فروش انجام شد',
|
||||
DELIVERED_TO_CUSTOMER: 'سفارش به مشتری تحویل شد',
|
||||
CANCELLED: 'سفارش لغو شده است',
|
||||
}
|
||||
|
||||
/** Line icons for the timeline nodes (vuesax-linear set). */
|
||||
export const ORDER_STATUS_ICONS: Record<string, string> = {
|
||||
SUSPENDED: 'vuesax-linear:bag-timer',
|
||||
PLACED: 'vuesax-linear:bag-tick',
|
||||
ADMIN_APPROVED: 'vuesax-linear:verify',
|
||||
SOURCING: 'vuesax-linear:box-search',
|
||||
PREPARING: 'vuesax-linear:box-1',
|
||||
DELIVERED_TO_AGENT: 'vuesax-linear:truck-time',
|
||||
DELIVERED_TO_CUSTOMER: 'vuesax-linear:location-tick',
|
||||
CANCELLED: 'vuesax-linear:bag-cross',
|
||||
}
|
||||
|
||||
/** Happy-path pipeline shown in the detail stepper (excludes terminal SUSPENDED / CANCELLED). */
|
||||
export const ORDER_PIPELINE = [
|
||||
'PLACED',
|
||||
'ADMIN_APPROVED',
|
||||
'SOURCING',
|
||||
'PREPARING',
|
||||
'DELIVERED_TO_AGENT',
|
||||
'DELIVERED_TO_CUSTOMER',
|
||||
] as const
|
||||
|
||||
export type OrderPipelineStatus = (typeof ORDER_PIPELINE)[number]
|
||||
|
||||
/** Step titles = system status labels, in pipeline order. */
|
||||
export const ORDER_STEPS = ORDER_PIPELINE.map((status) => ({
|
||||
status,
|
||||
title: ORDER_STATUS_LABELS[status]!,
|
||||
}))
|
||||
|
||||
// Ported from legacy order.vue/Orders.vue's setColor() — legacy only ever
|
||||
// colored sts 1,3,4,5,6 (never sts=0 or sts=2), an existing gap preserved here
|
||||
// for the directly-analogous statuses (ADMIN_APPROVED, SUSPENDED, CANCELLED
|
||||
// stay uncolored, same as legacy's untouched sts=0/2).
|
||||
//
|
||||
// Backgrounds are the font colour at 12% alpha (the trailing 1F), not the
|
||||
// opaque pale tints they used to be: a translucent wash reads the same on a
|
||||
// white card and stays a soft tint on a dark one, where #EFF6FF & co. were
|
||||
// glaring light patches.
|
||||
export const ORDER_STATUS_COLORS: Record<string, { font: string; background: string }> = {
|
||||
SUSPENDED: { font: '#A16207', background: '#A162071F' },
|
||||
PLACED: { font: '#3B82F6', background: '#3B82F61F' },
|
||||
ADMIN_APPROVED: { font: '#6366F1', background: '#6366F11F' },
|
||||
SOURCING: { font: '#EAB308', background: '#EAB3081F' },
|
||||
PREPARING: { font: '#F97316', background: '#F973161F' },
|
||||
DELIVERED_TO_AGENT: { font: '#06B6D4', background: '#06B6D41F' },
|
||||
DELIVERED_TO_CUSTOMER: { font: '#22C55E', background: '#22C55E1F' },
|
||||
};
|
||||
CANCELLED: { font: '#DC2626', background: '#DC26261F' },
|
||||
}
|
||||
|
||||
// Ported from legacy Orders.vue's 4-step tracker (`steps`/`setCurrentStep`).
|
||||
// Legacy's ambiguous sts=0 (suspended vs cancelled) is resolved here since our
|
||||
// enum keeps them distinct; SUSPENDED is grouped with the first step (not yet
|
||||
// progressing), matching the spirit of legacy's grouping of early statuses.
|
||||
export const ORDER_STEPS = ['ثبت سفارش', 'آماده سازی', 'تحویل مشتری', 'لغو شده'];
|
||||
const FALLBACK_COLOR = { font: '#64748B', background: '#64748B1F' }
|
||||
const FALLBACK_ICON = 'vuesax-linear:receipt-text'
|
||||
|
||||
export function orderStatusColor(status: string) {
|
||||
return ORDER_STATUS_COLORS[status] ?? FALLBACK_COLOR
|
||||
}
|
||||
|
||||
export function orderStatusLabel(status: string) {
|
||||
return ORDER_STATUS_LABELS[status] ?? status
|
||||
}
|
||||
|
||||
export function orderStatusBlurb(status: string) {
|
||||
return ORDER_STATUS_BLURBS[status] ?? ''
|
||||
}
|
||||
|
||||
export function orderStatusIcon(status: string) {
|
||||
return ORDER_STATUS_ICONS[status] ?? FALLBACK_ICON
|
||||
}
|
||||
|
||||
/**
|
||||
* Index into ORDER_PIPELINE for the active step.
|
||||
* CANCELLED → -1 (no pipeline progress).
|
||||
* SUSPENDED → 0 (stuck at placement / first step).
|
||||
*/
|
||||
export function orderStepIndex(status: string): number {
|
||||
switch (status) {
|
||||
case 'PLACED':
|
||||
case 'ADMIN_APPROVED':
|
||||
case 'SUSPENDED':
|
||||
return 0;
|
||||
case 'SOURCING':
|
||||
case 'PREPARING':
|
||||
return 1;
|
||||
case 'DELIVERED_TO_AGENT':
|
||||
case 'DELIVERED_TO_CUSTOMER':
|
||||
return 2;
|
||||
case 'CANCELLED':
|
||||
return 3;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
if (status === 'CANCELLED') return -1
|
||||
if (status === 'SUSPENDED') return 0
|
||||
const i = (ORDER_PIPELINE as readonly string[]).indexOf(status)
|
||||
return i >= 0 ? i : 0
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user