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:
s.esmaeili
2026-10-17 13:11:42 +03:30
co-authored by Cursor
parent f550172567
commit afb068c643
89 changed files with 4518 additions and 1325 deletions
+4 -1
View File
@@ -7,7 +7,10 @@ export function useCachedReads() {
const cache = useQueryCache()
return {
me: (o?: FetchOptions) => cache.fetch(KEYS.profile, api.getMe, o),
myOrders: (o?: FetchOptions) => cache.fetch(KEYS.orders, api.getMyOrders, o),
// Orders are paged (infinite scroll). The list screen calls api.getMyOrders
// directly; this helper only covers a rare full-first-page peek/invalidate.
myOrders: (o?: FetchOptions) =>
cache.fetch(KEYS.orders, () => api.getMyOrders({ page: 1 }).then((p) => p.result), o),
accountingBalance: (o?: FetchOptions) => cache.fetch(KEYS.balance, api.getAccountingBalance, o),
myLedgerEntries: (o?: FetchOptions) => cache.fetch(KEYS.ledger, api.getMyLedgerEntries, o),
goldPrice: (o?: FetchOptions) => cache.fetch(KEYS.goldPrice, () => api.getLatestGoldPrice(), o),
+36 -1
View File
@@ -241,6 +241,14 @@ export interface OrderItemDetail {
} | null;
}
export interface OrderStatusEvent {
id: string;
fromStatus: string | null;
toStatus: string;
note: string | null;
createdAt: string;
}
export interface OrderDetail {
id: string;
orderNumber: string;
@@ -252,6 +260,25 @@ export interface OrderDetail {
deliveredAt: string | null;
createdAt: string;
items: OrderItemDetail[];
/** Chronological status changes (oldest first). May be empty on older rows. */
statusEvents?: OrderStatusEvent[];
}
/** One page of GET /customer/me/orders — same envelope as the report endpoints. */
export interface OrdersPage {
result: OrderDetail[];
count: number;
page: number;
limit: number;
}
export interface OrdersQuery {
page?: number;
limit?: number;
/** OrderStatus, or omit / ALL for every status. */
status?: string;
/** Substring of orderNumber. */
q?: string;
}
export interface CustomerDetail {
@@ -525,7 +552,15 @@ export const api = {
getMe: () => request<CustomerDetail>('/customer/me'),
/** D27 §12 — this customer's balance, read from the accounting software on every call. */
getAccountingBalance: () => request<AccountingBalance>('/customer/me/accounting-balance'),
getMyOrders: () => request<OrderDetail[]>('/customer/me/orders'),
getMyOrders: (query: OrdersQuery = {}) => {
const params = new URLSearchParams();
if (query.page) params.set('page', String(query.page));
if (query.limit) params.set('limit', String(query.limit));
if (query.status && query.status !== 'ALL') params.set('status', query.status);
if (query.q?.trim()) params.set('q', query.q.trim());
const qs = params.toString();
return request<OrdersPage>(`/customer/me/orders${qs ? `?${qs}` : ''}`);
},
getMyLedgerEntries: () => request<LedgerEntrySummary[]>('/customer/me/ledger-entries'),
/** The making charge the server will charge for these variants (MON-08). */
getWageQuotes: (variantIds: string[]) =>