Fix shipping + dashboard + analytics queries against new schema
Deploy to route.crispygoat.com / deploy (push) Successful in 4m48s
Deploy to route.crispygoat.com / deploy (push) Successful in 4m48s
The /admin/shipping page was 500-ing with 'column c.name does not exist' because the getShippingOrders query in src/actions/shipping.ts was still using legacy column names on the new-schema customers table (name, email, phone) — the actual columns are first_name, last_name, primary_email, primary_phone. The dashboard and analytics pages were logging the same kind of errors but catching them, so the UI just showed zeroed stats instead of crashing. Same fix: - customers: name → first_name || ' ' || last_name - customers: email → primary_email - customers: phone → primary_phone - orders: subtotal → total_cents / 100 - orders: created_at → placed_at - orders: customer_name → join customers and concat first/last These are the same kind of fixes that landed in migration 0041 for the command-center RPCs. The application-layer queries just hadn't been updated.
This commit is contained in:
@@ -284,7 +284,7 @@ export async function getRecentOrders(limit: number = 10): Promise<RecentOrder[]
|
||||
}>(
|
||||
`SELECT
|
||||
o.id::text AS id,
|
||||
c.name AS customer_name,
|
||||
TRIM(COALESCE(c.first_name, '') || ' ' || COALESCE(c.last_name, '')) AS customer_name,
|
||||
o.total_cents::float / 100.0 AS subtotal,
|
||||
o.status,
|
||||
o.placed_at::text AS created_at,
|
||||
|
||||
+42
-32
@@ -42,24 +42,22 @@ export async function getDashboardStats(): Promise<DashboardStats> {
|
||||
const startOfDay = new Date(today.getFullYear(), today.getMonth(), today.getDate());
|
||||
const endOfDay = new Date(startOfDay.getTime() + 24 * 60 * 60 * 1000);
|
||||
|
||||
// Fetch today's orders. `orders` and `stops` use the legacy schema
|
||||
// (column names like `subtotal`, `brand_id`, `date`); the new-schema
|
||||
// Drizzle `orders` table doesn't have these. Raw SQL via the shared
|
||||
// pg pool.
|
||||
// Fetch today's orders. New schema: `total_cents` (int, divide by 100
|
||||
// for dollars) and `placed_at` (not legacy `subtotal` / `created_at`).
|
||||
const todayOrdersRes = brandId
|
||||
? await pool.query<{ subtotal: number; status: string }>(
|
||||
`SELECT subtotal, status
|
||||
? await pool.query<{ total_cents: number; status: string }>(
|
||||
`SELECT total_cents, status
|
||||
FROM orders
|
||||
WHERE created_at >= $1
|
||||
AND created_at < $2
|
||||
WHERE placed_at >= $1
|
||||
AND placed_at < $2
|
||||
AND brand_id = $3`,
|
||||
[startOfDay.toISOString(), endOfDay.toISOString(), brandId],
|
||||
)
|
||||
: await pool.query<{ subtotal: number; status: string }>(
|
||||
`SELECT subtotal, status
|
||||
: await pool.query<{ total_cents: number; status: string }>(
|
||||
`SELECT total_cents, status
|
||||
FROM orders
|
||||
WHERE created_at >= $1
|
||||
AND created_at < $2`,
|
||||
WHERE placed_at >= $1
|
||||
AND placed_at < $2`,
|
||||
[startOfDay.toISOString(), endOfDay.toISOString()],
|
||||
);
|
||||
const todayOrders = todayOrdersRes.rows;
|
||||
@@ -67,7 +65,7 @@ export async function getDashboardStats(): Promise<DashboardStats> {
|
||||
// Calculate today's revenue and orders
|
||||
const validOrders = todayOrders.filter((o) => o.status !== "cancelled");
|
||||
const todayRevenue = validOrders.reduce(
|
||||
(sum, o) => sum + (o.subtotal || 0),
|
||||
(sum, o) => sum + ((o.total_cents || 0) / 100),
|
||||
0,
|
||||
);
|
||||
const todayOrderCount = validOrders.length;
|
||||
@@ -106,7 +104,7 @@ export async function getDashboardStats(): Promise<DashboardStats> {
|
||||
);
|
||||
const activeProducts = productsRes.rows.length;
|
||||
|
||||
// Fetch weekly orders for chart (last 7 days)
|
||||
// Fetch weekly orders for chart (last 7 days). New schema uses `placed_at`.
|
||||
const weeklyOrders: number[] = [];
|
||||
for (let i = 6; i >= 0; i--) {
|
||||
const dayStart = new Date(startOfDay);
|
||||
@@ -116,48 +114,60 @@ export async function getDashboardStats(): Promise<DashboardStats> {
|
||||
const dayRes = brandId
|
||||
? await pool.query<{ id: string }>(
|
||||
`SELECT id FROM orders
|
||||
WHERE created_at >= $1
|
||||
AND created_at < $2
|
||||
WHERE placed_at >= $1
|
||||
AND placed_at < $2
|
||||
AND brand_id = $3
|
||||
LIMIT 1`,
|
||||
[dayStart.toISOString(), dayEnd.toISOString(), brandId],
|
||||
)
|
||||
: await pool.query<{ id: string }>(
|
||||
`SELECT id FROM orders
|
||||
WHERE created_at >= $1
|
||||
AND created_at < $2
|
||||
WHERE placed_at >= $1
|
||||
AND placed_at < $2
|
||||
LIMIT 1`,
|
||||
[dayStart.toISOString(), dayEnd.toISOString()],
|
||||
);
|
||||
weeklyOrders.push(dayRes.rows.length > 0 ? 1 : 0);
|
||||
}
|
||||
|
||||
// Fetch recent orders (last 10)
|
||||
// Fetch recent orders (last 10). New schema: `total_cents`/`placed_at`
|
||||
// on `orders`; customer name comes from a `customers` join (no
|
||||
// `customer_name` column on `orders`).
|
||||
const recentRes = brandId
|
||||
? await pool.query<{
|
||||
id: string;
|
||||
customer_name: string;
|
||||
subtotal: number;
|
||||
total_cents: number;
|
||||
status: string;
|
||||
created_at: string;
|
||||
placed_at: string;
|
||||
}>(
|
||||
`SELECT id, customer_name, subtotal, status, created_at
|
||||
FROM orders
|
||||
WHERE brand_id = $1
|
||||
ORDER BY created_at DESC
|
||||
`SELECT o.id::text AS id,
|
||||
TRIM(COALESCE(c.first_name, '') || ' ' || COALESCE(c.last_name, '')) AS customer_name,
|
||||
o.total_cents,
|
||||
o.status,
|
||||
o.placed_at::text AS placed_at
|
||||
FROM orders o
|
||||
LEFT JOIN customers c ON c.id = o.customer_id
|
||||
WHERE o.brand_id = $1
|
||||
ORDER BY o.placed_at DESC
|
||||
LIMIT 10`,
|
||||
[brandId],
|
||||
)
|
||||
: await pool.query<{
|
||||
id: string;
|
||||
customer_name: string;
|
||||
subtotal: number;
|
||||
total_cents: number;
|
||||
status: string;
|
||||
created_at: string;
|
||||
placed_at: string;
|
||||
}>(
|
||||
`SELECT id, customer_name, subtotal, status, created_at
|
||||
FROM orders
|
||||
ORDER BY created_at DESC
|
||||
`SELECT o.id::text AS id,
|
||||
TRIM(COALESCE(c.first_name, '') || ' ' || COALESCE(c.last_name, '')) AS customer_name,
|
||||
o.total_cents,
|
||||
o.status,
|
||||
o.placed_at::text AS placed_at
|
||||
FROM orders o
|
||||
LEFT JOIN customers c ON c.id = o.customer_id
|
||||
ORDER BY o.placed_at DESC
|
||||
LIMIT 10`,
|
||||
);
|
||||
|
||||
@@ -166,9 +176,9 @@ export async function getDashboardStats(): Promise<DashboardStats> {
|
||||
.map((o) => ({
|
||||
id: o.id || "",
|
||||
customer_name: o.customer_name || "Guest",
|
||||
total: o.subtotal || 0,
|
||||
total: (o.total_cents || 0) / 100,
|
||||
status: o.status || "unknown",
|
||||
created_at: formatTimeAgo(o.created_at),
|
||||
created_at: formatTimeAgo(o.placed_at),
|
||||
}));
|
||||
|
||||
return {
|
||||
|
||||
@@ -73,9 +73,9 @@ export async function getShippingOrders(): Promise<GetShippingOrdersResult> {
|
||||
}>(
|
||||
`SELECT
|
||||
o.id::text AS id,
|
||||
c.name AS customer_name,
|
||||
c.email AS customer_email,
|
||||
c.phone AS customer_phone,
|
||||
TRIM(COALESCE(c.first_name, '') || ' ' || COALESCE(c.last_name, '')) AS customer_name,
|
||||
c.primary_email AS customer_email,
|
||||
c.primary_phone AS customer_phone,
|
||||
o.status,
|
||||
o.total_cents::float / 100.0 AS subtotal,
|
||||
o.placed_at::text AS created_at,
|
||||
|
||||
Reference in New Issue
Block a user