Replace hardcoded Today cards with a backend summary endpoint and composable frontend widgets filtered by org type and tab permissions. Co-authored-by: Cursor <cursoragent@cursor.com>
52 lines
1.5 KiB
TypeScript
52 lines
1.5 KiB
TypeScript
'use client';
|
|
|
|
import { useTranslations } from 'next-intl';
|
|
import { useAuth } from '@/lib/hooks/useAuth';
|
|
import { KpiCard } from '@/components/today/KpiCard';
|
|
import { getEligibleTodayKpis, getVisibleTodayKpis } from '@/components/today/widget-registry';
|
|
import type { TodaySummaryWidgets } from '@/types/today';
|
|
|
|
interface TodayKpiGridProps {
|
|
widgets: TodaySummaryWidgets;
|
|
loading?: boolean;
|
|
}
|
|
|
|
export function TodayKpiGrid({ widgets, loading = false }: TodayKpiGridProps) {
|
|
const t = useTranslations('today');
|
|
const { currentOrganization } = useAuth();
|
|
const definitions = loading
|
|
? getEligibleTodayKpis(currentOrganization)
|
|
: getVisibleTodayKpis(currentOrganization, widgets);
|
|
|
|
if (!loading && definitions.length === 0) {
|
|
return (
|
|
<p className="text-sm text-text-muted">{t('noWidgets')}</p>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-4">
|
|
{definitions.map((definition) => {
|
|
const value = definition.formatValue(widgets) ?? '—';
|
|
const subtitleKey = definition.formatSubtitle?.(widgets);
|
|
const subtitle =
|
|
subtitleKey === 'unlimited'
|
|
? t('seatsUnlimited')
|
|
: definition.formatSubtitle?.(widgets);
|
|
|
|
return (
|
|
<KpiCard
|
|
key={definition.key}
|
|
title={t(definition.titleKey)}
|
|
value={value}
|
|
subtitle={subtitle}
|
|
icon={definition.icon}
|
|
color={definition.color}
|
|
loading={loading}
|
|
/>
|
|
);
|
|
})}
|
|
</div>
|
|
);
|
|
}
|