From 6de0af5056adc083d223567c8b4a3a25de5ef24d Mon Sep 17 00:00:00 2001 From: LiusDev Date: Wed, 26 Aug 2026 15:52:20 +0700 Subject: [PATCH] fix(admin): guard dashboard widgets against incomplete API payloads MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On a fresh/empty store (zero orders, zero low-stock products, zero past-due subscriptions), every plugin API route this data flows through gets a response whose "empty" fields (empty object/array, zero count) come back missing entirely rather than present-but-empty — observed as `{"data":{}}` for admin/widgets/revenue-snapshot instead of `{"data":{"sevenDay":{},"thirtyDay":{}}}`. None of the five admin dashboard widgets guarded against this, so whichever widget rendered first threw synchronously: - RevenueSnapshot: `Object.entries(data.sevenDay)` — TypeError: Cannot convert undefined or null to object - LowStockAlerts / RecentOrders: `rows.length` — TypeError: Cannot read properties of undefined (reading 'length') - FailedSubscriptions: `data.items.map` when `data.count` was also missing (so the `count === 0` short-circuit never triggered) - PendingReviews: `count` rendered as `undefined` instead of `0` Because the error boundary wraps the whole admin app, this reliably white-screened the entire /_emdash/admin dashboard on any brand new DashCommerce store — reproduced locally on a freshly seeded db and on a Cloudflare Workers deploy, both with zero real orders yet. Default each field to its empty value ({}/[]/0) so every widget renders its normal empty state instead of crashing the page. Co-Authored-By: Claude Sonnet 5 --- packages/core/src/admin/widgets/FailedSubscriptions.tsx | 4 ++-- packages/core/src/admin/widgets/LowStockAlerts.tsx | 2 +- packages/core/src/admin/widgets/PendingReviews.tsx | 2 +- packages/core/src/admin/widgets/RecentOrders.tsx | 2 +- packages/core/src/admin/widgets/RevenueSnapshot.tsx | 8 ++++---- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/core/src/admin/widgets/FailedSubscriptions.tsx b/packages/core/src/admin/widgets/FailedSubscriptions.tsx index b4d51f9..50090e3 100644 --- a/packages/core/src/admin/widgets/FailedSubscriptions.tsx +++ b/packages/core/src/admin/widgets/FailedSubscriptions.tsx @@ -18,7 +18,7 @@ export function FailedSubscriptions() { if (!data) return ; - if (data.count === 0) { + if ((data.count ?? 0) === 0) { return

All subscriptions current ✓

; } @@ -28,7 +28,7 @@ export function FailedSubscriptions() { {data.count} past-due
    - {data.items.map((s) => ( + {(data.items ?? []).map((s) => (
  • / {s.intervalCount}{" "} {s.interval} diff --git a/packages/core/src/admin/widgets/LowStockAlerts.tsx b/packages/core/src/admin/widgets/LowStockAlerts.tsx index 1045504..1b9813c 100644 --- a/packages/core/src/admin/widgets/LowStockAlerts.tsx +++ b/packages/core/src/admin/widgets/LowStockAlerts.tsx @@ -15,7 +15,7 @@ export function LowStockAlerts() { useEffect(() => { api.get<{ items: Row[] }>("admin/widgets/low-stock-alerts").then((r) => - setRows(r.items), + setRows(r.items ?? []), ); }, [api]); diff --git a/packages/core/src/admin/widgets/PendingReviews.tsx b/packages/core/src/admin/widgets/PendingReviews.tsx index 8288489..7219edf 100644 --- a/packages/core/src/admin/widgets/PendingReviews.tsx +++ b/packages/core/src/admin/widgets/PendingReviews.tsx @@ -8,7 +8,7 @@ export function PendingReviews() { useEffect(() => { api .get<{ count: number }>("admin/widgets/pending-reviews") - .then((r) => setCount(r.count)); + .then((r) => setCount(r.count ?? 0)); }, [api]); if (count === null) return ; diff --git a/packages/core/src/admin/widgets/RecentOrders.tsx b/packages/core/src/admin/widgets/RecentOrders.tsx index d528e0f..e55f29c 100644 --- a/packages/core/src/admin/widgets/RecentOrders.tsx +++ b/packages/core/src/admin/widgets/RecentOrders.tsx @@ -19,7 +19,7 @@ export function RecentOrders() { useEffect(() => { api.get<{ items: Row[] }>("admin/widgets/recent-orders").then((r) => - setRows(r.items), + setRows(r.items ?? []), ); }, [api]); diff --git a/packages/core/src/admin/widgets/RevenueSnapshot.tsx b/packages/core/src/admin/widgets/RevenueSnapshot.tsx index 2d09648..9fda137 100644 --- a/packages/core/src/admin/widgets/RevenueSnapshot.tsx +++ b/packages/core/src/admin/widgets/RevenueSnapshot.tsx @@ -22,9 +22,9 @@ export function RevenueSnapshot() {
    Last 7 days
    - {Object.entries(data.sevenDay).length === 0 + {Object.entries(data.sevenDay ?? {}).length === 0 ? "—" - : Object.entries(data.sevenDay) + : Object.entries(data.sevenDay ?? {}) .map(([c, v]) => `${c} ${(v as number) / 100}`) .join(" · ")}
    @@ -32,9 +32,9 @@ export function RevenueSnapshot() {
    Last 30 days
    - {Object.entries(data.thirtyDay).length === 0 + {Object.entries(data.thirtyDay ?? {}).length === 0 ? "—" - : Object.entries(data.thirtyDay) + : Object.entries(data.thirtyDay ?? {}) .map(([c, v]) => `${c} ${(v as number) / 100}`) .join(" · ")}