feat(db): add get_dashboard_summary RPC for v2 dashboard
This commit is contained in:
@@ -0,0 +1,89 @@
|
|||||||
|
-- 0091_dashboard_summary_rpc.sql
|
||||||
|
--
|
||||||
|
-- Dashboard summary RPC for the mobile-first admin v2 dashboard
|
||||||
|
-- (`/admin/v2`).
|
||||||
|
--
|
||||||
|
-- Returns a single JSONB document that powers the four stat cards +
|
||||||
|
-- the "Today's stops" timeline + the 7-day mini-chart:
|
||||||
|
--
|
||||||
|
-- {
|
||||||
|
-- "orders_today": <int>, -- count of orders placed today
|
||||||
|
-- "revenue_today": <int cents>, -- sum of total_cents (excl. canceled)
|
||||||
|
-- "pending_fulfillment": <int>, -- count of orders awaiting pickup/ship
|
||||||
|
-- "stops_today": <int>, -- count of stops scheduled for today
|
||||||
|
-- "orders_last_7_days": [{date, count}] -- 7-element array, oldest → today
|
||||||
|
-- }
|
||||||
|
--
|
||||||
|
-- Schema notes (vs. the plan's draft):
|
||||||
|
-- * `orders` uses `placed_at` (TIMESTAMPTZ) — not `created_at`
|
||||||
|
-- * `orders.status` is the legacy enum: 'pending' | 'confirmed' |
|
||||||
|
-- 'fulfilled' | 'canceled' (not the v2 vocabulary 'placed'/'ready')
|
||||||
|
-- * `orders.total_cents` is INTEGER (NOT NULL DEFAULT 0 in 0001) — no
|
||||||
|
-- column-add step is required
|
||||||
|
-- * `stops` has a `date DATE` column (not `scheduled_at`); the legacy
|
||||||
|
-- stops page (and v2 stops page) both key off `stops.date`
|
||||||
|
--
|
||||||
|
-- SECURITY DEFINER so the v2 server action can call it without depending
|
||||||
|
-- on RLS; brand scoping is enforced inside the function via the
|
||||||
|
-- `p_brand_id` parameter (NULL → platform_admin "all brands" scope).
|
||||||
|
-- Re-runs are safe — this is `CREATE OR REPLACE FUNCTION`.
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION get_dashboard_summary(p_brand_id UUID)
|
||||||
|
RETURNS JSONB
|
||||||
|
LANGUAGE plpgsql
|
||||||
|
SECURITY DEFINER
|
||||||
|
AS $$
|
||||||
|
DECLARE
|
||||||
|
result JSONB;
|
||||||
|
brand_filter TEXT;
|
||||||
|
BEGIN
|
||||||
|
-- The brand filter is the same text fragment for every SELECT in this
|
||||||
|
-- function. NULL = "all brands" (platform_admin scope); non-NULL =
|
||||||
|
-- scope to a single brand.
|
||||||
|
IF p_brand_id IS NULL THEN
|
||||||
|
brand_filter := '';
|
||||||
|
ELSE
|
||||||
|
brand_filter := format(' AND brand_id = %L', p_brand_id);
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
EXECUTE format(
|
||||||
|
$q$
|
||||||
|
SELECT jsonb_build_object(
|
||||||
|
'orders_today', (
|
||||||
|
SELECT COUNT(*) FROM orders
|
||||||
|
WHERE placed_at::date = CURRENT_DATE %s
|
||||||
|
),
|
||||||
|
'revenue_today', (
|
||||||
|
SELECT COALESCE(SUM(total_cents), 0)::bigint FROM orders
|
||||||
|
WHERE placed_at::date = CURRENT_DATE
|
||||||
|
AND status <> 'canceled' %s
|
||||||
|
),
|
||||||
|
'pending_fulfillment', (
|
||||||
|
SELECT COUNT(*) FROM orders
|
||||||
|
WHERE status IN ('pending', 'confirmed') %s
|
||||||
|
),
|
||||||
|
'stops_today', (
|
||||||
|
SELECT COUNT(*) FROM stops
|
||||||
|
WHERE "date" = CURRENT_DATE %s
|
||||||
|
),
|
||||||
|
'orders_last_7_days', (
|
||||||
|
SELECT COALESCE(jsonb_agg(jsonb_build_object('date', d::date, 'count', COALESCE(o.cnt, 0)) ORDER BY d), '[]'::jsonb)
|
||||||
|
FROM generate_series(CURRENT_DATE - INTERVAL '6 days', CURRENT_DATE, '1 day') d
|
||||||
|
LEFT JOIN (
|
||||||
|
SELECT placed_at::date AS day, COUNT(*) AS cnt
|
||||||
|
FROM orders
|
||||||
|
WHERE placed_at >= CURRENT_DATE - INTERVAL '7 days' %s
|
||||||
|
GROUP BY 1
|
||||||
|
) o ON o.day = d::date
|
||||||
|
)
|
||||||
|
)
|
||||||
|
$q$,
|
||||||
|
brand_filter, brand_filter, brand_filter, brand_filter, brand_filter
|
||||||
|
) INTO result;
|
||||||
|
|
||||||
|
RETURN result;
|
||||||
|
END;
|
||||||
|
$$;
|
||||||
|
|
||||||
|
COMMENT ON FUNCTION get_dashboard_summary(UUID) IS
|
||||||
|
'Returns a single JSONB document with the four dashboard stat counts + 7-day order history. p_brand_id = NULL means platform_admin scope (all brands).';
|
||||||
Reference in New Issue
Block a user