'use client' import { useTranslations } from 'next-intl' import { Card, CardContent } from '@/components/ui/card' import { InfoTooltip } from '@/components/ui/info-tooltip' import { formatCurrency } from '@/lib/utils' import { KPI_DEFINITIONS, getDefaultPreferences } from '@/lib/reports/kpi-definitions' import type { KPIReport, KPIPreferences } from '@/types' interface KPIHeroCardsProps { report: KPIReport preferences?: KPIPreferences } function getKPISubtitleKey( report: KPIReport, id: string, ): { key: string; args?: Record } { switch (id) { case 'netResult': return { key: 'sub_netto' } case 'cashPosition': return { key: 'sub_likvida_medel' } case 'outstandingReceivables': if (report.overdueReceivables > 0) { return { key: 'sub_overdue', args: { amount: formatCurrency(report.overdueReceivables) } } } return { key: 'sub_utestaende' } case 'vatLiability': if (report.vatLiability > 0) return { key: 'sub_att_betala' } if (report.vatLiability < 0) return { key: 'sub_att_aterfa' } return { key: 'sub_jamnt' } case 'grossMargin': return { key: 'sub_av_intakter' } case 'expenseRatio': return { key: 'sub_av_intakter' } case 'avgPaymentDays': return { key: 'sub_snitt' } default: return { key: '' } } } function getKPIValue(report: KPIReport, id: string): number | null { switch (id) { case 'netResult': return report.netResult case 'cashPosition': return report.cashPosition case 'outstandingReceivables': return report.outstandingReceivables case 'vatLiability': return report.vatLiability case 'grossMargin': return report.grossMargin case 'expenseRatio': return report.expenseRatio case 'avgPaymentDays': return report.avgPaymentDays default: return null } } function formatKPIValue(value: number | null, format: string, id: string, daysSuffix: string): string { if (value === null) return '—' if (format === 'currency') { if (id === 'vatLiability') return formatCurrency(Math.abs(value)) return formatCurrency(value) } if (format === 'percentage') return `${value}%` if (format === 'days') return `${value} ${daysSuffix}` return String(value) } function getValueColor(value: number | null, colorLogic: string): string { if (value === null) return 'text-muted-foreground' if (colorLogic === 'neutral') return '' if (colorLogic === 'positive-good') { return value >= 0 ? 'text-[hsl(var(--chart-1))]' : 'text-[hsl(var(--chart-2))]' } if (colorLogic === 'negative-good') { return value <= 0 ? 'text-[hsl(var(--chart-1))]' : 'text-[hsl(var(--chart-2))]' } return '' } export function KPIHeroCards({ report, preferences }: KPIHeroCardsProps) { const t = useTranslations('kpi') const prefs = preferences ?? getDefaultPreferences() const visibleDefs = prefs.kpiOrder .map((id) => KPI_DEFINITIONS.find((d) => d.id === id)) .filter((d) => d && prefs.visibleKpis.includes(d.id)) as typeof KPI_DEFINITIONS if (visibleDefs.length === 0) { return ( {t('empty_no_kpis_chosen')} ) } const gridCols = visibleDefs.length <= 2 ? 'grid-cols-2' : visibleDefs.length === 3 ? 'grid-cols-2 md:grid-cols-3' : 'grid-cols-2 md:grid-cols-4' return (
{visibleDefs.map((def) => { const value = getKPIValue(report, def.id) const sub = getKPISubtitleKey(report, def.id) const formatted = formatKPIValue(value, def.format, def.id, t('value_days_suffix')) const color = getValueColor(value, def.colorLogic) const hasOverride = prefs.accountOverrides[def.id] && prefs.accountOverrides[def.id].length > 0 const tooltipContent = (

{t(`def_${def.id}_description`)}

{t('tooltip_formula')} {t(`def_${def.id}_formula`)}
{t('tooltip_accounts')} {t(`def_${def.id}_accounts`)}
{hasOverride && (
{t('tooltip_overrides')} {prefs.accountOverrides[def.id].join(', ')}
)}
) return (

{t(`def_${def.id}_label`)}

{formatted}

{sub.key ? t(sub.key, sub.args) : ''}

) })}
) }