feat(admin): add v2 dashboard with stat cards and today's stops
This commit is contained in:
@@ -28,6 +28,30 @@ export type DashboardSummary = {
|
||||
active_products: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* Mobile-dashboard summary shape. Returned by the `get_dashboard_summary`
|
||||
* RPC (migration 0091) and consumed by `/admin/v2` to power the four
|
||||
* stat cards + the 7-day order chart.
|
||||
*
|
||||
* - `revenue_today` is in CENTS (not dollars) — divide by 100 for display.
|
||||
* - `orders_last_7_days` is oldest → today (7 elements, includes today).
|
||||
*/
|
||||
export type MobileDashboardSummary = {
|
||||
orders_today: number;
|
||||
revenue_today: number;
|
||||
pending_fulfillment: number;
|
||||
stops_today: number;
|
||||
orders_last_7_days: Array<{ date: string; count: number }>;
|
||||
};
|
||||
|
||||
const EMPTY_MOBILE_DASHBOARD: MobileDashboardSummary = {
|
||||
orders_today: 0,
|
||||
revenue_today: 0,
|
||||
pending_fulfillment: 0,
|
||||
stops_today: 0,
|
||||
orders_last_7_days: [],
|
||||
};
|
||||
|
||||
// ── Dashboard Stats Actions ─────────────────────────────────────────────────
|
||||
|
||||
export async function getDashboardStats(): Promise<DashboardStats> {
|
||||
@@ -276,6 +300,48 @@ export async function getDashboardSummary(): Promise<DashboardSummary> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the mobile dashboard summary for a single brand.
|
||||
*
|
||||
* Different name from the existing `getDashboardSummary()` (which is the
|
||||
* legacy 4-field shape used by the v1 dashboard) so both can coexist.
|
||||
* Returns safe fallbacks on any error — the v2 page renders a 0s
|
||||
* dashboard rather than crashing the shell.
|
||||
*/
|
||||
export async function getMobileDashboardSummary(
|
||||
brandId: string,
|
||||
): Promise<MobileDashboardSummary> {
|
||||
try {
|
||||
const adminUser = await getAdminUser();
|
||||
if (!adminUser) {
|
||||
return EMPTY_MOBILE_DASHBOARD;
|
||||
}
|
||||
const { rows } = await pool.query<{ data: MobileDashboardSummary }>(
|
||||
"SELECT get_dashboard_summary($1::uuid) AS data",
|
||||
[brandId],
|
||||
);
|
||||
const data = rows[0]?.data;
|
||||
if (!data) {
|
||||
return EMPTY_MOBILE_DASHBOARD;
|
||||
}
|
||||
return {
|
||||
orders_today: Number(data.orders_today ?? 0),
|
||||
revenue_today: Number(data.revenue_today ?? 0),
|
||||
pending_fulfillment: Number(data.pending_fulfillment ?? 0),
|
||||
stops_today: Number(data.stops_today ?? 0),
|
||||
orders_last_7_days: Array.isArray(data.orders_last_7_days)
|
||||
? data.orders_last_7_days.map((d) => ({
|
||||
date: String(d?.date ?? ""),
|
||||
count: Number(d?.count ?? 0),
|
||||
}))
|
||||
: [],
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch mobile dashboard summary:", error);
|
||||
return EMPTY_MOBILE_DASHBOARD;
|
||||
}
|
||||
}
|
||||
|
||||
// ── Helpers ────────────────────────────────────────────────────────────────────
|
||||
|
||||
function formatTimeAgo(dateString: string): string {
|
||||
|
||||
Reference in New Issue
Block a user