import React, { useMemo } from 'react'; import { FeedSackLogEntry, ManualFeedSackEntry, FeedSackColumnConfig, } from '../../types/counting.ts'; import { useAppContext } from '../../context/AppContext.tsx'; import { formatNumber } from '../../utils/formatters.ts'; import { getEffectiveDate } from '../../utils/dateFilter.ts'; const StatItem: React.FC<{ icon: React.ReactNode; label: string; manualValue: string; iotValue: string; colorOverride?: string; }> = ({ icon, label, manualValue, iotValue, colorOverride }) => (
{icon}

{label}

{/* Manual */}

Manual

{manualValue}

karung

{/* IOT */}

IOT

{iotValue}

karung

); interface FeedStatsProps { data: FeedSackLogEntry[]; manualEntries: ManualFeedSackEntry[]; columnConfig: FeedSackColumnConfig[]; effectiveDate?: Date; } const FeedStats: React.FC = ({ data, manualEntries, columnConfig, effectiveDate, }) => { const { cycleData } = useAppContext(); const displayDate = effectiveDate ?? getEffectiveDate(); const todayLabel = useMemo(() => { return new Intl.DateTimeFormat('id-ID', { day: 'numeric', month: 'long', year: 'numeric', timeZone: 'Asia/Jakarta', }).format(displayDate); }, [displayDate]); const totalCycleLabel = useMemo(() => { const startDate = cycleData.startDate.toLocaleDateString('id-ID', { day: 'numeric', month: 'short', }); const endDate = displayDate.toLocaleDateString('id-ID', { day: 'numeric', month: 'short', }); return `Total (${startDate} - ${endDate})`; }, [cycleData.startDate, displayDate]); const columnStats = useMemo(() => { return columnConfig.map((col) => { const cameraIotData = data.filter((log) => log.cameraName === col.cameraName); const cameraManualData = manualEntries.filter((entry) => entry.cameraName === col.cameraName); const calculateIotCount = (dataSet: FeedSackLogEntry[], days: number | 'cycle') => { let startDate: Date; if (days === 'cycle') { startDate = new Date(cycleData.startDate); } else { startDate = new Date(displayDate); startDate.setDate(displayDate.getDate() - (days - 1)); } startDate.setHours(0, 0, 0, 0); return dataSet .filter((log) => log.timestamp >= startDate) .reduce((sum, log) => sum + log.count, 0); }; const calculateManualCount = (dataSet: ManualFeedSackEntry[], days: number | 'cycle') => { let startDate: Date; if (days === 'cycle') { startDate = new Date(cycleData.startDate); } else { startDate = new Date(displayDate); startDate.setDate(displayDate.getDate() - (days - 1)); } startDate.setHours(0, 0, 0, 0); return dataSet .filter((entry) => { const entryDate = new Date(entry.date); return !isNaN(entryDate.getTime()) && entryDate >= startDate; }) .reduce((sum, entry) => sum + entry.count, 0); }; return { ...col, todayManual: calculateManualCount(cameraManualData, 1), todayIot: calculateIotCount(cameraIotData, 1), weekManual: calculateManualCount(cameraManualData, 'cycle'), weekIot: calculateIotCount(cameraIotData, 'cycle'), }; }); }, [data, manualEntries, columnConfig, cycleData, displayDate]); const gridCols = columnStats.length <= 2 ? 'md:grid-cols-2' : columnStats.length <= 3 ? 'md:grid-cols-3' : 'md:grid-cols-2 lg:grid-cols-4'; return (
{columnStats.map((stat) => (

{stat.label}

{stat.cameraName}

} label={todayLabel} manualValue={formatNumber(stat.todayManual)} iotValue={formatNumber(stat.todayIot)} colorOverride={stat.color} /> } label={totalCycleLabel} manualValue={formatNumber(stat.weekManual)} iotValue={formatNumber(stat.weekIot)} colorOverride={stat.color} />
))}
); }; export default FeedStats;