feat(admin): add v2 dashboard with stat cards and today's stops

This commit is contained in:
Tyler
2026-06-17 15:19:32 -06:00
parent d913ca194e
commit afd96b93e1
2 changed files with 441 additions and 0 deletions
+66
View File
@@ -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 {