nxtgauge-admin-solid/src/lib/admin/dashboard.ts

186 lines
4.8 KiB
TypeScript
Raw Normal View History

export type DashboardWidgetSize = 'S' | 'M' | 'L';
export type WidgetDataState = 'loading' | 'success' | 'empty' | 'error' | 'unavailable';
export type WidgetDataResult<T = unknown> = {
state: WidgetDataState;
payload?: T;
message?: string;
};
export type KpiPayload = {
value: string;
delta: string;
note: string;
tone: 'up' | 'down';
icon: 'users' | 'building' | 'trend' | 'card';
};
export type LineChartPayload = {
labels: string[];
series: number[];
maxY: number;
};
export type BarChartPayload = {
labels: string[];
series: number[];
maxY: number;
};
export type AdminWidgetPayload =
| { kind: 'kpi'; data: KpiPayload }
| { kind: 'line_chart'; data: LineChartPayload }
| { kind: 'bar_chart'; data: BarChartPayload };
export type DashboardWidgetDefinition = {
widgetKey: string;
title: string;
moduleKey: string;
defaultSize: DashboardWidgetSize;
defaultVisible: boolean;
order: number;
dataAdapterKey: string;
readiness: 'ready' | 'planned';
description?: string;
};
const kpiData: Record<string, KpiPayload> = {
totalUsers: {
value: '12,458',
delta: '+12.5%',
note: '+1,245 this month',
tone: 'up',
icon: 'users',
},
activeCompanies: {
value: '1,234',
delta: '+8.2%',
note: '+94 this month',
tone: 'up',
icon: 'building',
},
openLeads: {
value: '847',
delta: '-3.1%',
note: '27 fewer than last month',
tone: 'down',
icon: 'trend',
},
creditsPurchased: {
value: '₹45,890',
delta: '+18.7%',
note: '₹7,234 more this month',
tone: 'up',
icon: 'card',
},
};
const lineChartData: LineChartPayload = {
labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'],
series: [62, 70, 81, 75, 88, 102],
maxY: 120,
};
const barChartData: BarChartPayload = {
labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'],
series: [42000, 48000, 55000, 51000, 62000, 69000],
maxY: 80000,
};
const adapterMap: Record<string, () => Promise<WidgetDataResult<AdminWidgetPayload>>> = {
kpi_total_users: async () => ({ state: 'success', payload: { kind: 'kpi', data: kpiData.totalUsers } }),
kpi_active_companies: async () => ({ state: 'success', payload: { kind: 'kpi', data: kpiData.activeCompanies } }),
kpi_open_leads: async () => ({ state: 'success', payload: { kind: 'kpi', data: kpiData.openLeads } }),
kpi_credits_purchased: async () => ({ state: 'success', payload: { kind: 'kpi', data: kpiData.creditsPurchased } }),
chart_leads_trend: async () => ({ state: 'success', payload: { kind: 'line_chart', data: lineChartData } }),
chart_revenue_overview: async () => ({ state: 'success', payload: { kind: 'bar_chart', data: barChartData } }),
};
export const ADMIN_DASHBOARD_WIDGETS: DashboardWidgetDefinition[] = [
{
widgetKey: 'kpi_total_users',
title: 'Total Users',
moduleKey: 'USER_MANAGEMENT',
defaultSize: 'S',
defaultVisible: true,
order: 1,
dataAdapterKey: 'kpi_total_users',
readiness: 'ready',
},
{
widgetKey: 'kpi_active_companies',
title: 'Active Companies',
moduleKey: 'COMPANY_MANAGEMENT',
defaultSize: 'S',
defaultVisible: true,
order: 2,
dataAdapterKey: 'kpi_active_companies',
readiness: 'ready',
},
{
widgetKey: 'kpi_open_leads',
title: 'Open Leads',
moduleKey: 'REQUIREMENTS_MANAGEMENT',
defaultSize: 'S',
defaultVisible: true,
order: 3,
dataAdapterKey: 'kpi_open_leads',
readiness: 'ready',
},
{
widgetKey: 'kpi_credits_purchased',
title: 'Credits Purchased',
moduleKey: 'CREDIT_MANAGEMENT',
defaultSize: 'S',
defaultVisible: true,
order: 4,
dataAdapterKey: 'kpi_credits_purchased',
readiness: 'ready',
},
{
widgetKey: 'chart_leads_trend',
title: 'Leads Trend',
moduleKey: 'REPORTS',
defaultSize: 'M',
defaultVisible: true,
order: 5,
dataAdapterKey: 'chart_leads_trend',
readiness: 'planned',
description: 'Monthly leads performance overview',
},
{
widgetKey: 'chart_revenue_overview',
title: 'Revenue Overview',
moduleKey: 'REVENUE_LEDGER',
defaultSize: 'M',
defaultVisible: true,
order: 6,
dataAdapterKey: 'chart_revenue_overview',
readiness: 'planned',
description: 'Monthly revenue vs expenses comparison',
},
];
export function loadWidgetData(definition: DashboardWidgetDefinition): Promise<WidgetDataResult<AdminWidgetPayload>> {
if (definition.readiness !== 'ready') {
return Promise.resolve({
state: 'unavailable',
message: 'This widget will switch to module-backed data when that module is completed.',
});
}
const loader = adapterMap[definition.dataAdapterKey];
if (!loader) {
return Promise.resolve({
state: 'error',
message: `Missing data adapter: ${definition.dataAdapterKey}`,
});
}
return loader().catch(() => ({
state: 'error',
message: 'Failed to load widget data.',
}));
}