Files
bakery-ia/frontend/src/components/dashboard/InsightsGrid.tsx
2025-11-20 19:14:49 +01:00

130 lines
3.8 KiB
TypeScript

// ================================================================
// frontend/src/components/dashboard/InsightsGrid.tsx
// ================================================================
/**
* Insights Grid - Key metrics at a glance
*
* 2x2 grid of important metrics: savings, inventory, waste, deliveries.
* Mobile-first design with large touch targets.
*/
import React from 'react';
import { useTranslation } from 'react-i18next';
import { Insights } from '../../api/hooks/newDashboard';
interface InsightsGridProps {
insights: Insights;
loading?: boolean;
}
const colorConfig = {
green: {
bgColor: 'var(--color-success-50)',
borderColor: 'var(--color-success-200)',
textColor: 'var(--color-success-800)',
detailColor: 'var(--color-success-600)',
},
amber: {
bgColor: 'var(--color-warning-50)',
borderColor: 'var(--color-warning-200)',
textColor: 'var(--color-warning-900)',
detailColor: 'var(--color-warning-600)',
},
red: {
bgColor: 'var(--color-error-50)',
borderColor: 'var(--color-error-200)',
textColor: 'var(--color-error-900)',
detailColor: 'var(--color-error-600)',
},
};
function InsightCard({
color,
i18n,
}: {
color: 'green' | 'amber' | 'red';
i18n: {
label: {
key: string;
params?: Record<string, any>;
};
value: {
key: string;
params?: Record<string, any>;
};
detail: {
key: string;
params?: Record<string, any>;
} | null;
};
}) {
const { t } = useTranslation('dashboard');
const config = colorConfig[color];
// Translate using i18n keys
const displayLabel = t(i18n.label.key, i18n.label.params);
const displayValue = t(i18n.value.key, i18n.value.params);
const displayDetail = i18n.detail ? t(i18n.detail.key, i18n.detail.params) : '';
return (
<div
className="border-2 rounded-xl p-4 md:p-6 transition-all duration-200 hover:shadow-lg cursor-pointer"
style={{
backgroundColor: config.bgColor,
borderColor: config.borderColor,
}}
>
{/* Label */}
<div className="text-sm md:text-base font-bold mb-2" style={{ color: 'var(--text-primary)' }}>{displayLabel}</div>
{/* Value */}
<div className="text-xl md:text-2xl font-bold mb-1" style={{ color: config.textColor }}>{displayValue}</div>
{/* Detail */}
<div className="text-sm font-medium" style={{ color: config.detailColor }}>{displayDetail}</div>
</div>
);
}
export function InsightsGrid({ insights, loading }: InsightsGridProps) {
if (loading) {
return (
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
{[1, 2, 3, 4].map((i) => (
<div key={i} className="animate-pulse rounded-xl p-6" style={{ backgroundColor: 'var(--bg-tertiary)' }}>
<div className="h-4 rounded w-1/2 mb-3" style={{ backgroundColor: 'var(--bg-quaternary)' }}></div>
<div className="h-8 rounded w-3/4 mb-2" style={{ backgroundColor: 'var(--bg-quaternary)' }}></div>
<div className="h-4 rounded w-2/3" style={{ backgroundColor: 'var(--bg-quaternary)' }}></div>
</div>
))}
</div>
);
}
// Guard against undefined values
if (!insights || !insights.savings || !insights.inventory || !insights.waste || !insights.deliveries) {
return null;
}
return (
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<InsightCard
color={insights.savings.color}
i18n={insights.savings.i18n}
/>
<InsightCard
color={insights.inventory.color}
i18n={insights.inventory.i18n}
/>
<InsightCard
color={insights.waste.color}
i18n={insights.waste.i18n}
/>
<InsightCard
color={insights.deliveries.color}
i18n={insights.deliveries.i18n}
/>
</div>
);
}