Files
lti-web-client/src/components/pages/dashboard/chart/DashboardStats.tsx
T
2026-01-29 14:50:16 +07:00

178 lines
5.4 KiB
TypeScript

import Alert from '@/components/Alert';
import Card from '@/components/Card';
import { formatNumber } from '@/lib/helper';
import { DashboardStatisticsData } from '@/types/api/dashboard/dashboard';
import { Icon } from '@iconify/react';
interface DashboardStatsProps {
data: DashboardStatisticsData[];
}
// Konfigurasi untuk setiap kartu
const CARD_CONFIG = [
{
key: 'HPP Global',
icon: 'heroicons:banknotes',
alertColor: 'warning' as const,
suffix: ' /Kg',
prefix: 'RP ',
},
{
key: 'Avg. Selling Price',
icon: 'heroicons:document-currency-dollar',
alertColor: 'success' as const,
suffix: ' /Kg Telur',
prefix: '',
},
{
key: 'FCR',
icon: 'heroicons:clipboard-document-list',
alertColor: 'info' as const,
suffix: '',
prefix: '',
},
{
key: 'Mortality',
icon: 'heroicons:exclamation-triangle',
alertColor: 'error' as const,
suffix: ' %',
prefix: '',
},
];
const DashboardStats = ({ data }: DashboardStatsProps) => {
// Helper to get trend icon and color
const getTrendDisplay = (percent: number) => {
const isPositive = percent >= 0;
return {
icon: isPositive
? 'heroicons:arrow-trending-up'
: 'heroicons:arrow-trending-down',
color: isPositive ? 'text-[#008000]' : 'text-[#FF3A3A]',
value: Math.abs(percent),
};
};
// Helper to format value
const formatValue = (value: number, prefix: string, suffix: string) => {
return (
<>
{prefix}
{formatNumber(value)}
{suffix && (
<span className='text-sm font-normal text-base-content/50'>
{suffix}
</span>
)}
</>
);
};
return (
<div className='grid sm:grid-cols-2 xl:grid-cols-4 gap-3'>
{CARD_CONFIG.map((config) => {
// Find matching data from API
const cardData = data.find((item) => item.label === config.key);
if (!cardData) {
// Show placeholder card for missing data (FCR & Mortality)
return (
<Card
key={config.key}
className={{
wrapper: 'w-full rounded-xl border border-base-content/10',
body: 'p-0',
wrapperContent:
'h-full flex flex-col items-between justify-between',
footer: 'mt-0!',
}}
variant='bordered'
footer={
<div className='flex flex-row justify-between px-4 pb-4 max-h-12'>
<div className='text-base-content/50 font-semibold text-xs'>
From last month
</div>
<div className='text-base-content/50 font-semibold text-xs'>
Filter Required
</div>
</div>
}
>
<div className='flex flex-row items-center gap-3 px-4 py-4'>
<Alert
variant='soft'
className={`rounded-lg p-0 w-12.5 h-12.5 bg-[${config.alertColor}]/12 flex items-center justify-center`}
>
<Icon
icon={config.icon}
width={24}
height={24}
className='text-base-content/50'
/>
</Alert>
<div>
<h3 className='text-base-content/50 font-semibold text-sm'>
{config.key}
</h3>
<p className='text-xl font-semibold text-base-content/50'>
********
</p>
</div>
</div>
</Card>
);
}
const trend = getTrendDisplay(cardData.percent_last_month);
return (
<Card
key={config.key}
className={{
wrapper: 'w-full rounded-xl border border-base-content/10',
body: 'p-0',
wrapperContent:
'h-full flex flex-col items-between justify-between',
footer: 'mt-0!',
}}
variant='bordered'
footer={
<div className='flex flex-row justify-between px-4 pb-4'>
<div className='text-base-content/50 font-semibold text-xs'>
From last month
</div>
<div
className={`${trend.color} font-semibold flex flex-row items-center gap-2 text-xs`}
>
<Icon icon={trend.icon} width={16} height={16} />
{trend.value}%
</div>
</div>
}
>
<div className='flex flex-row items-center gap-4 px-4 pt-4'>
<Alert
variant='soft'
color={config.alertColor}
className={`rounded-lg p-3 bg-[${config.alertColor}]/12 flex items-center justify-center`}
>
<Icon icon={config.icon} width={24} height={24} />
</Alert>
<div className='space-y-1'>
<h3 className='text-base-content/50 font-semibold text-sm'>
{cardData.label}
</h3>
<p className='text-xl font-semibold'>
{formatValue(cardData.value, config.prefix, config.suffix)}
</p>
</div>
</div>
</Card>
);
})}
</div>
);
};
export default DashboardStats;