Remove command-center page and dependencies
Deploy to route.crispygoat.com / deploy (push) Successful in 4m20s
Deploy to route.crispygoat.com / deploy (push) Successful in 4m20s
The /admin/command-center route was platform_admin-only, used a heavy custom theme (Major Mono / JetBrains / Inter Tight + ~700 lines of schematic CSS), called three RPCs, and overlapped with the per-brand dashboards reachable at /admin, /admin/orders, /admin/stops, and /admin/reports. The page is being removed; it didn't justify the build complexity, font payload, or schema surface area. Changes: - Drop page, dashboard component, CSS, and server actions - Remove the platform_admin nav entry from header + sidebar - Remove the cmd+K palette entry - Add migration 0042 to drop the 3 RPCs, founder_pain_log table, and founder_pain_log_platform view it owned - Decrement route count in docs/pricing-assessment.md (88 -> 87) Verification: tsc clean, eslint clean, npm run build clean, command- center absent from the route list.
This commit is contained in:
@@ -1,165 +0,0 @@
|
||||
"use server";
|
||||
|
||||
import { getAdminUser } from "@/lib/admin-permissions";
|
||||
import { pool } from "@/lib/db";
|
||||
|
||||
// ── Types ────────────────────────────────────────────────────────────────────
|
||||
|
||||
export type PlatformMetrics = {
|
||||
active_brands: number;
|
||||
orders_today: number;
|
||||
revenue_today: number;
|
||||
active_routes: number;
|
||||
failed_orders_today: number;
|
||||
pending_orders_today: number;
|
||||
};
|
||||
|
||||
export type ActivityEvent = {
|
||||
id: string;
|
||||
event_type: string;
|
||||
entity_type: string;
|
||||
entity_id: string;
|
||||
payload: Record<string, unknown>;
|
||||
actor_type: string;
|
||||
source: string;
|
||||
created_at: string;
|
||||
brand_id: string | null;
|
||||
brand_name: string | null;
|
||||
};
|
||||
|
||||
export type BrandHealth = {
|
||||
brand_id: string;
|
||||
brand_name: string;
|
||||
brand_slug: string;
|
||||
plan_tier: string;
|
||||
orders_today: number;
|
||||
revenue_today: number;
|
||||
active_stops: number;
|
||||
failed_orders: number;
|
||||
pending_orders: number;
|
||||
last_activity: string | null;
|
||||
open_pain_items: number;
|
||||
health_status: "healthy" | "warning" | "critical";
|
||||
};
|
||||
|
||||
export type PainLogItem = {
|
||||
id: string;
|
||||
brand_id: string | null;
|
||||
brand_name: string | null;
|
||||
severity: "low" | "medium" | "high" | "critical";
|
||||
category: string;
|
||||
title: string;
|
||||
description: string | null;
|
||||
status: string;
|
||||
created_at: string;
|
||||
};
|
||||
|
||||
export type CreatePainLogData = {
|
||||
brand_id?: string | null;
|
||||
severity: "low" | "medium" | "high" | "critical";
|
||||
category: string;
|
||||
title: string;
|
||||
description?: string | null;
|
||||
};
|
||||
|
||||
// ── Internal RPC helper ──────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Call a platform RPC. The action layer needs to know whether the function
|
||||
* returns a scalar (JSONB) or a setof (TABLE(...)) — `SELECT fn() AS "fn"`
|
||||
* only works for scalar returns; setof functions need `SELECT * FROM fn()`.
|
||||
*
|
||||
* - `kind: "scalar"` → wraps the single result column in a JSON value
|
||||
* - `kind: "setof"` → returns the rows directly as an array
|
||||
*/
|
||||
async function platformRPC<T>(
|
||||
rpcName: string,
|
||||
kind: "scalar" | "setof" = "scalar",
|
||||
): Promise<T> {
|
||||
const adminUser = await getAdminUser();
|
||||
if (!adminUser) throw new Error("Not authenticated");
|
||||
if (adminUser.role !== "platform_admin") throw new Error("Access denied: platform admin only");
|
||||
|
||||
if (kind === "setof") {
|
||||
const { rows } = await pool.query(
|
||||
`SELECT * FROM ${rpcName}()`,
|
||||
);
|
||||
return rows as unknown as T;
|
||||
}
|
||||
|
||||
// scalar: e.g. JSONB return — single cell, possibly null
|
||||
const { rows } = await pool.query<Record<string, T>>(
|
||||
`SELECT ${rpcName}() AS "${rpcName}"`,
|
||||
);
|
||||
const data = rows[0]?.[rpcName];
|
||||
if (data == null) return null as T;
|
||||
return data as T;
|
||||
}
|
||||
|
||||
// ── Platform actions ─────────────────────────────────────────────────────────
|
||||
|
||||
export async function getPlatformMetrics(): Promise<PlatformMetrics> {
|
||||
return platformRPC<PlatformMetrics>("get_platform_command_center_metrics", "scalar");
|
||||
}
|
||||
|
||||
export async function getPlatformActivityFeed(): Promise<ActivityEvent[]> {
|
||||
return platformRPC<ActivityEvent[]>("get_platform_activity_feed", "setof");
|
||||
}
|
||||
|
||||
export async function getBrandHealthSnapshot(): Promise<BrandHealth[]> {
|
||||
return platformRPC<BrandHealth[]>("get_brand_health_snapshot", "setof");
|
||||
}
|
||||
|
||||
export async function getPainLog(): Promise<PainLogItem[]> {
|
||||
const adminUser = await getAdminUser();
|
||||
if (!adminUser) throw new Error("Not authenticated");
|
||||
|
||||
const { rows } = await pool.query<PainLogItem>(
|
||||
`SELECT * FROM founder_pain_log_platform ORDER BY created_at DESC`,
|
||||
);
|
||||
|
||||
return rows;
|
||||
}
|
||||
|
||||
export async function createPainLogItem(data: CreatePainLogData): Promise<{ success: boolean; error?: string }> {
|
||||
try {
|
||||
const adminUser = await getAdminUser();
|
||||
if (!adminUser) return { success: false, error: "Not authenticated" };
|
||||
if (adminUser.role !== "platform_admin") return { success: false, error: "Access denied" };
|
||||
|
||||
await pool.query(
|
||||
`INSERT INTO founder_pain_log (brand_id, severity, category, title, description)
|
||||
VALUES ($1, $2, $3, $4, $5)`,
|
||||
[
|
||||
data.brand_id || null,
|
||||
data.severity,
|
||||
data.category,
|
||||
data.title,
|
||||
data.description || null,
|
||||
],
|
||||
);
|
||||
|
||||
return { success: true };
|
||||
} catch (e) {
|
||||
return { success: false, error: String(e) };
|
||||
}
|
||||
}
|
||||
|
||||
export async function resolvePainLogItem(id: string): Promise<{ success: boolean; error?: string }> {
|
||||
try {
|
||||
const adminUser = await getAdminUser();
|
||||
if (!adminUser) return { success: false, error: "Not authenticated" };
|
||||
if (adminUser.role !== "platform_admin") return { success: false, error: "Access denied" };
|
||||
|
||||
await pool.query(
|
||||
`UPDATE founder_pain_log
|
||||
SET status = 'resolved', resolved_at = now(), resolved_by = $2
|
||||
WHERE id = $1`,
|
||||
[id, adminUser.id],
|
||||
);
|
||||
|
||||
return { success: true };
|
||||
} catch (e) {
|
||||
return { success: false, error: String(e) };
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user