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
|
`SELECT
|
||||||
o.id::text AS id,
|
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.total_cents::float / 100.0 AS subtotal,
|
||||||
o.status,
|
o.status,
|
||||||
o.placed_at::text AS created_at,
|
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 startOfDay = new Date(today.getFullYear(), today.getMonth(), today.getDate());
|
||||||
const endOfDay = new Date(startOfDay.getTime() + 24 * 60 * 60 * 1000);
|
const endOfDay = new Date(startOfDay.getTime() + 24 * 60 * 60 * 1000);
|
||||||
|
|
||||||
// Fetch today's orders. `orders` and `stops` use the legacy schema
|
// Fetch today's orders. New schema: `total_cents` (int, divide by 100
|
||||||
// (column names like `subtotal`, `brand_id`, `date`); the new-schema
|
// for dollars) and `placed_at` (not legacy `subtotal` / `created_at`).
|
||||||
// Drizzle `orders` table doesn't have these. Raw SQL via the shared
|
|
||||||
// pg pool.
|
|
||||||
const todayOrdersRes = brandId
|
const todayOrdersRes = brandId
|
||||||
? await pool.query<{ subtotal: number; status: string }>(
|
? await pool.query<{ total_cents: number; status: string }>(
|
||||||
`SELECT subtotal, status
|
`SELECT total_cents, status
|
||||||
FROM orders
|
FROM orders
|
||||||
WHERE created_at >= $1
|
WHERE placed_at >= $1
|
||||||
AND created_at < $2
|
AND placed_at < $2
|
||||||
AND brand_id = $3`,
|
AND brand_id = $3`,
|
||||||
[startOfDay.toISOString(), endOfDay.toISOString(), brandId],
|
[startOfDay.toISOString(), endOfDay.toISOString(), brandId],
|
||||||
)
|
)
|
||||||
: await pool.query<{ subtotal: number; status: string }>(
|
: await pool.query<{ total_cents: number; status: string }>(
|
||||||
`SELECT subtotal, status
|
`SELECT total_cents, status
|
||||||
FROM orders
|
FROM orders
|
||||||
WHERE created_at >= $1
|
WHERE placed_at >= $1
|
||||||
AND created_at < $2`,
|
AND placed_at < $2`,
|
||||||
[startOfDay.toISOString(), endOfDay.toISOString()],
|
[startOfDay.toISOString(), endOfDay.toISOString()],
|
||||||
);
|
);
|
||||||
const todayOrders = todayOrdersRes.rows;
|
const todayOrders = todayOrdersRes.rows;
|
||||||
@@ -67,7 +65,7 @@ export async function getDashboardStats(): Promise<DashboardStats> {
|
|||||||
// Calculate today's revenue and orders
|
// Calculate today's revenue and orders
|
||||||
const validOrders = todayOrders.filter((o) => o.status !== "cancelled");
|
const validOrders = todayOrders.filter((o) => o.status !== "cancelled");
|
||||||
const todayRevenue = validOrders.reduce(
|
const todayRevenue = validOrders.reduce(
|
||||||
(sum, o) => sum + (o.subtotal || 0),
|
(sum, o) => sum + ((o.total_cents || 0) / 100),
|
||||||
0,
|
0,
|
||||||
);
|
);
|
||||||
const todayOrderCount = validOrders.length;
|
const todayOrderCount = validOrders.length;
|
||||||
@@ -106,7 +104,7 @@ export async function getDashboardStats(): Promise<DashboardStats> {
|
|||||||
);
|
);
|
||||||
const activeProducts = productsRes.rows.length;
|
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[] = [];
|
const weeklyOrders: number[] = [];
|
||||||
for (let i = 6; i >= 0; i--) {
|
for (let i = 6; i >= 0; i--) {
|
||||||
const dayStart = new Date(startOfDay);
|
const dayStart = new Date(startOfDay);
|
||||||
@@ -116,48 +114,60 @@ export async function getDashboardStats(): Promise<DashboardStats> {
|
|||||||
const dayRes = brandId
|
const dayRes = brandId
|
||||||
? await pool.query<{ id: string }>(
|
? await pool.query<{ id: string }>(
|
||||||
`SELECT id FROM orders
|
`SELECT id FROM orders
|
||||||
WHERE created_at >= $1
|
WHERE placed_at >= $1
|
||||||
AND created_at < $2
|
AND placed_at < $2
|
||||||
AND brand_id = $3
|
AND brand_id = $3
|
||||||
LIMIT 1`,
|
LIMIT 1`,
|
||||||
[dayStart.toISOString(), dayEnd.toISOString(), brandId],
|
[dayStart.toISOString(), dayEnd.toISOString(), brandId],
|
||||||
)
|
)
|
||||||
: await pool.query<{ id: string }>(
|
: await pool.query<{ id: string }>(
|
||||||
`SELECT id FROM orders
|
`SELECT id FROM orders
|
||||||
WHERE created_at >= $1
|
WHERE placed_at >= $1
|
||||||
AND created_at < $2
|
AND placed_at < $2
|
||||||
LIMIT 1`,
|
LIMIT 1`,
|
||||||
[dayStart.toISOString(), dayEnd.toISOString()],
|
[dayStart.toISOString(), dayEnd.toISOString()],
|
||||||
);
|
);
|
||||||
weeklyOrders.push(dayRes.rows.length > 0 ? 1 : 0);
|
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
|
const recentRes = brandId
|
||||||
? await pool.query<{
|
? await pool.query<{
|
||||||
id: string;
|
id: string;
|
||||||
customer_name: string;
|
customer_name: string;
|
||||||
subtotal: number;
|
total_cents: number;
|
||||||
status: string;
|
status: string;
|
||||||
created_at: string;
|
placed_at: string;
|
||||||
}>(
|
}>(
|
||||||
`SELECT id, customer_name, subtotal, status, created_at
|
`SELECT o.id::text AS id,
|
||||||
FROM orders
|
TRIM(COALESCE(c.first_name, '') || ' ' || COALESCE(c.last_name, '')) AS customer_name,
|
||||||
WHERE brand_id = $1
|
o.total_cents,
|
||||||
ORDER BY created_at DESC
|
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`,
|
LIMIT 10`,
|
||||||
[brandId],
|
[brandId],
|
||||||
)
|
)
|
||||||
: await pool.query<{
|
: await pool.query<{
|
||||||
id: string;
|
id: string;
|
||||||
customer_name: string;
|
customer_name: string;
|
||||||
subtotal: number;
|
total_cents: number;
|
||||||
status: string;
|
status: string;
|
||||||
created_at: string;
|
placed_at: string;
|
||||||
}>(
|
}>(
|
||||||
`SELECT id, customer_name, subtotal, status, created_at
|
`SELECT o.id::text AS id,
|
||||||
FROM orders
|
TRIM(COALESCE(c.first_name, '') || ' ' || COALESCE(c.last_name, '')) AS customer_name,
|
||||||
ORDER BY created_at DESC
|
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`,
|
LIMIT 10`,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -166,9 +176,9 @@ export async function getDashboardStats(): Promise<DashboardStats> {
|
|||||||
.map((o) => ({
|
.map((o) => ({
|
||||||
id: o.id || "",
|
id: o.id || "",
|
||||||
customer_name: o.customer_name || "Guest",
|
customer_name: o.customer_name || "Guest",
|
||||||
total: o.subtotal || 0,
|
total: (o.total_cents || 0) / 100,
|
||||||
status: o.status || "unknown",
|
status: o.status || "unknown",
|
||||||
created_at: formatTimeAgo(o.created_at),
|
created_at: formatTimeAgo(o.placed_at),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -73,9 +73,9 @@ export async function getShippingOrders(): Promise<GetShippingOrdersResult> {
|
|||||||
}>(
|
}>(
|
||||||
`SELECT
|
`SELECT
|
||||||
o.id::text AS id,
|
o.id::text AS id,
|
||||||
c.name AS customer_name,
|
TRIM(COALESCE(c.first_name, '') || ' ' || COALESCE(c.last_name, '')) AS customer_name,
|
||||||
c.email AS customer_email,
|
c.primary_email AS customer_email,
|
||||||
c.phone AS customer_phone,
|
c.primary_phone AS customer_phone,
|
||||||
o.status,
|
o.status,
|
||||||
o.total_cents::float / 100.0 AS subtotal,
|
o.total_cents::float / 100.0 AS subtotal,
|
||||||
o.placed_at::text AS created_at,
|
o.placed_at::text AS created_at,
|
||||||
|
|||||||
Reference in New Issue
Block a user