feat: wire admin dashboard to real metrics and fix JSX errors

- Wire dashboard home to live /api/admin/dashboard/metrics with 8 KPI widgets
- Add pending_approvals and total_revenue widget definitions
- Fix JSX syntax errors in [...module].tsx and modules.tsx
- Fix '>' character in DashboardDesignPreview.tsx
This commit is contained in:
Ashwin Kumar 2026-04-06 06:19:14 +02:00
parent ac146c4d36
commit 0a0c51adc1
5 changed files with 71 additions and 25 deletions

View file

@ -4094,7 +4094,7 @@ export default function DashboardDesignPreview(props: {
<div style="border:1px solid #E5E7EB;background:white;border-radius:16px;padding:14px;box-shadow:0 1px 4px rgba(0,0,0,0.06)"> <div style="border:1px solid #E5E7EB;background:white;border-radius:16px;padding:14px;box-shadow:0 1px 4px rgba(0,0,0,0.06)">
<div style="display:flex;align-items:center;justify-content:space-between;gap:8px"> <div style="display:flex;align-items:center;justify-content:space-between;gap:8px">
<div> <div>
<p style="margin:0;font-size:11px;letter-spacing:0.05em;text-transform:uppercase;color:#9CA3AF">Requirements > Details</p> <p style="margin:0;font-size:11px;letter-spacing:0.05em;text-transform:uppercase;color:#9CA3AF">Requirements {'>'} Details</p>
<p style="margin:6px 0 0;font-size:38px;line-height:1.05;font-weight:800;color:#111827">{selectedRow.title}</p> <p style="margin:6px 0 0;font-size:38px;line-height:1.05;font-weight:800;color:#111827">{selectedRow.title}</p>
<div style="display:flex;align-items:center;gap:8px;margin-top:8px"> <div style="display:flex;align-items:center;gap:8px;margin-top:8px">
<span style={`height:22px;padding:0 10px;border-radius:999px;display:inline-flex;align-items:center;font-size:11px;font-weight:700;background:${status.bg};color:${status.c}`}>{status.label}</span> <span style={`height:22px;padding:0 10px;border-radius:999px;display:inline-flex;align-items:center;font-size:11px;font-weight:700;background:${status.bg};color:${status.c}`}>{status.label}</span>

View file

@ -128,13 +128,33 @@ export const ADMIN_DASHBOARD_WIDGETS: DashboardWidgetDefinition[] = [
dataAdapterKey: 'kpi_open_leads', dataAdapterKey: 'kpi_open_leads',
readiness: 'ready', readiness: 'ready',
}, },
{
widgetKey: 'kpi_pending_approvals',
title: 'Pending Approvals',
moduleKey: 'APPROVAL_MANAGEMENT',
defaultSize: 'S',
defaultVisible: true,
order: 4,
dataAdapterKey: 'kpi_pending_approvals',
readiness: 'ready',
},
{
widgetKey: 'kpi_total_revenue',
title: 'Total Revenue',
moduleKey: 'REVENUE_LEDGER',
defaultSize: 'S',
defaultVisible: true,
order: 5,
dataAdapterKey: 'kpi_total_revenue',
readiness: 'ready',
},
{ {
widgetKey: 'kpi_credits_purchased', widgetKey: 'kpi_credits_purchased',
title: 'Credits Purchased', title: 'Credits Purchased',
moduleKey: 'CREDIT_MANAGEMENT', moduleKey: 'CREDIT_MANAGEMENT',
defaultSize: 'S', defaultSize: 'S',
defaultVisible: true, defaultVisible: true,
order: 4, order: 6,
dataAdapterKey: 'kpi_credits_purchased', dataAdapterKey: 'kpi_credits_purchased',
readiness: 'ready', readiness: 'ready',
}, },
@ -144,10 +164,10 @@ export const ADMIN_DASHBOARD_WIDGETS: DashboardWidgetDefinition[] = [
moduleKey: 'REPORTS', moduleKey: 'REPORTS',
defaultSize: 'M', defaultSize: 'M',
defaultVisible: true, defaultVisible: true,
order: 5, order: 7,
dataAdapterKey: 'chart_leads_trend', dataAdapterKey: 'chart_leads_trend',
readiness: 'planned', readiness: 'ready',
description: 'Monthly leads performance overview', description: 'Weekly leads performance overview',
}, },
{ {
widgetKey: 'chart_revenue_overview', widgetKey: 'chart_revenue_overview',
@ -155,10 +175,10 @@ export const ADMIN_DASHBOARD_WIDGETS: DashboardWidgetDefinition[] = [
moduleKey: 'REVENUE_LEDGER', moduleKey: 'REVENUE_LEDGER',
defaultSize: 'M', defaultSize: 'M',
defaultVisible: true, defaultVisible: true,
order: 6, order: 8,
dataAdapterKey: 'chart_revenue_overview', dataAdapterKey: 'chart_revenue_overview',
readiness: 'planned', readiness: 'ready',
description: 'Monthly revenue vs expenses comparison', description: 'Weekly revenue overview',
}, },
]; ];

View file

@ -55,6 +55,7 @@ export default function LegacyModuleShellPage() {
const legacyUrl = createMemo(() => `${LEGACY_ADMIN_ORIGIN}${legacyPath()}`); const legacyUrl = createMemo(() => `${LEGACY_ADMIN_ORIGIN}${legacyPath()}`);
return ( return (
<div>
<h1 class="text-2xl font-bold text-gray-900">{moduleName()}</h1> <h1 class="text-2xl font-bold text-gray-900">{moduleName()}</h1>
<p class="mt-1 text-sm text-gray-500"> <p class="mt-1 text-sm text-gray-500">
Live legacy module embedded for exact design and functionality parity during migration. Live legacy module embedded for exact design and functionality parity during migration.
@ -69,5 +70,6 @@ export default function LegacyModuleShellPage() {
style={{ width: '100%', height: '72vh', border: '1px solid #e2e8f0', 'border-radius': '10px', 'margin-top': '10px' }} style={{ width: '100%', height: '72vh', border: '1px solid #e2e8f0', 'border-radius': '10px', 'margin-top': '10px' }}
/> />
</section> </section>
</div>
); );
} }

View file

@ -70,25 +70,34 @@ const DEFAULT_LAYOUT: RuntimeDashboardLayout = {
const WIDGET_META: Record<string, WidgetMeta> = { const WIDGET_META: Record<string, WidgetMeta> = {
kpi_total_users: { kpi_total_users: {
state: 'empty', state: 'live',
type: 'summary', type: 'summary',
statusLabel: 'No Data', statusLabel: 'Live Data',
subtitle: 'Powered by USER_MANAGEMENT', subtitle: 'Powered by USER_MANAGEMENT',
emptyMessage: 'No user data available yet',
}, },
kpi_active_companies: { kpi_active_companies: {
state: 'empty', state: 'live',
type: 'summary', type: 'summary',
statusLabel: 'No Data', statusLabel: 'Live Data',
subtitle: 'Powered by COMPANY_MANAGEMENT', subtitle: 'Powered by COMPANY_MANAGEMENT',
emptyMessage: 'No company data available yet',
}, },
kpi_open_leads: { kpi_open_leads: {
state: 'empty', state: 'live',
type: 'summary', type: 'summary',
statusLabel: 'No Data', statusLabel: 'Live Data',
subtitle: 'Powered by REQUIREMENTS_MANAGEMENT', subtitle: 'Powered by REQUIREMENTS_MANAGEMENT',
emptyMessage: 'No lead data available yet', },
kpi_pending_approvals: {
state: 'live',
type: 'summary',
statusLabel: 'Live Data',
subtitle: 'Powered by APPROVAL_MANAGEMENT',
},
kpi_total_revenue: {
state: 'live',
type: 'summary',
statusLabel: 'Live Data',
subtitle: 'Powered by REVENUE_LEDGER',
}, },
kpi_credits_purchased: { kpi_credits_purchased: {
state: 'empty', state: 'empty',
@ -98,18 +107,16 @@ const WIDGET_META: Record<string, WidgetMeta> = {
emptyMessage: 'No credit activity available yet', emptyMessage: 'No credit activity available yet',
}, },
chart_leads_trend: { chart_leads_trend: {
state: 'pending', state: 'live',
type: 'analytics', type: 'analytics',
statusLabel: 'Module Pending', statusLabel: 'Live Data',
subtitle: 'Monthly leads performance overview • Powered by REPORTS', subtitle: 'Weekly leads performance overview • Powered by REPORTS',
pendingMessage: 'This widget will connect to live reporting data once the Reports module is completed',
}, },
chart_revenue_overview: { chart_revenue_overview: {
state: 'pending', state: 'live',
type: 'analytics', type: 'analytics',
statusLabel: 'Module Pending', statusLabel: 'Live Data',
subtitle: 'Monthly revenue vs expenses comparison • Powered by REVENUE_LEDGER', subtitle: 'Weekly revenue overview • Powered by REVENUE_LEDGER',
pendingMessage: 'This widget will connect to ledger-based analytics once the Revenue Ledger module is completed',
}, },
}; };
@ -172,6 +179,7 @@ function iconForWidget(widgetKey: string) {
if (widgetKey.includes('leads')) return <TrendingUp size={22} class={cls} />; if (widgetKey.includes('leads')) return <TrendingUp size={22} class={cls} />;
if (widgetKey.includes('credits')) return <Coins size={22} class={cls} />; if (widgetKey.includes('credits')) return <Coins size={22} class={cls} />;
if (widgetKey.includes('revenue')) return <BarChart3 size={22} class={cls} />; if (widgetKey.includes('revenue')) return <BarChart3 size={22} class={cls} />;
if (widgetKey.includes('approvals')) return <CircleDashed size={22} class={cls} />;
return <LineChart size={22} class={cls} />; return <LineChart size={22} class={cls} />;
} }
@ -242,6 +250,8 @@ export default function AdminHomePage() {
kpi_total_users: 'users', kpi_total_users: 'users',
kpi_active_companies: 'companies', kpi_active_companies: 'companies',
kpi_open_leads: 'leads', kpi_open_leads: 'leads',
kpi_pending_approvals: 'approvals',
kpi_total_revenue: 'revenue',
kpi_credits_purchased: 'credits', kpi_credits_purchased: 'credits',
}; };
if (metrics.loading) return { state: 'pending', statusLabel: 'Loading...' }; if (metrics.loading) return { state: 'pending', statusLabel: 'Loading...' };
@ -249,6 +259,18 @@ export default function AdminHomePage() {
if (m) return { state: 'live', statusLabel: 'Live Data', data: m }; if (m) return { state: 'live', statusLabel: 'Live Data', data: m };
return { state: 'empty', statusLabel: 'No Data' }; return { state: 'empty', statusLabel: 'No Data' };
} }
if (metrics.loading) return { state: 'pending', statusLabel: 'Loading...' };
const m = metrics();
if (key === 'chart_leads_trend') {
const data = m?.trend_series;
if (data && data.length > 0) return { state: 'live', statusLabel: 'Live Data', data: { trend_series: data } };
return { state: 'empty', statusLabel: 'No Data' };
}
if (key === 'chart_revenue_overview') {
const data = m?.rev_series;
if (data && data.length > 0) return { state: 'live', statusLabel: 'Live Data', data: { rev_series: data } };
return { state: 'empty', statusLabel: 'No Data' };
}
const meta = WIDGET_META[key]; const meta = WIDGET_META[key];
return { state: meta?.state || 'empty', statusLabel: meta?.statusLabel || 'No Data' }; return { state: meta?.state || 'empty', statusLabel: meta?.statusLabel || 'No Data' };
}; };

View file

@ -105,6 +105,7 @@ export default function ModulesPage() {
} }
return ( return (
<>
<div class="flex flex-col -mx-6 -mt-6 min-h-full"> <div class="flex flex-col -mx-6 -mt-6 min-h-full">
{/* ── Page header ── */} {/* ── Page header ── */}
@ -255,5 +256,6 @@ export default function ModulesPage() {
</div> </div>
</div> </div>
</Show> </Show>
</>
); );
} }