commit
32a36cceff
444 files changed
+67186
No files matched your search
@@ -0,0 +1,121 @@
|
||||
import React, { useMemo, useEffect, useState } from 'react';
|
||||
import { LiveCount } from '../../types.ts';
|
||||
import { CalculatorIcon } from '../icons/Icons.tsx';
|
||||
import { useAppContext } from '../../context/AppContext.tsx';
|
||||
import { apiClient } from '../../services/apiService.ts';
|
||||
import { formatNumber } from '../../utils/formatters.ts';
|
||||
|
||||
const StatItem: React.FC<{
|
||||
icon: React.ReactNode;
|
||||
label: string;
|
||||
value: string;
|
||||
valueColor?: string;
|
||||
}> = ({ icon, label, value, valueColor = 'text-gray-900' }) => (
|
||||
<div className="flex items-center">
|
||||
<div className="text-xl text-gray-500 w-8">{icon}</div>
|
||||
<div>
|
||||
<p className="text-sm text-gray-600">{label}</p>
|
||||
<p className={`text-lg font-bold ${valueColor}`}>{value}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
interface CountingStatsProps {
|
||||
liveCounts: LiveCount[];
|
||||
kandangId?: number | null;
|
||||
initialPop?: number;
|
||||
}
|
||||
|
||||
const CountingStats: React.FC<CountingStatsProps> = ({ liveCounts, kandangId, initialPop }) => {
|
||||
const { cycleData, apiMode } = useAppContext();
|
||||
const [currentPopulation, setCurrentPopulation] = useState<number | null>(null);
|
||||
|
||||
// Fetch current population from mortality data
|
||||
useEffect(() => {
|
||||
const fetchPopulation = async () => {
|
||||
try {
|
||||
const dbRecords = await apiClient.getMortalityRecords(cycleData.id, kandangId ?? undefined);
|
||||
const initPop = initialPop ?? cycleData.docInCount ?? 20000;
|
||||
|
||||
// Calculate total mortality
|
||||
const totalMortality = dbRecords.reduce(
|
||||
(sum: number, record: { mortalityCount?: number; afkir?: number }) =>
|
||||
sum + (record.mortalityCount || 0) + (record.afkir || 0),
|
||||
0
|
||||
);
|
||||
const population = initPop - totalMortality;
|
||||
|
||||
setCurrentPopulation(population);
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch population:', error);
|
||||
setCurrentPopulation(null);
|
||||
}
|
||||
};
|
||||
|
||||
if (apiMode === 'LIVE') {
|
||||
fetchPopulation();
|
||||
}
|
||||
}, [cycleData, apiMode, kandangId, initialPop]);
|
||||
|
||||
const { totalCount, activeCameras } = useMemo(() => {
|
||||
const onlineCams = liveCounts.filter((cam) => cam.status === 'online');
|
||||
const total = onlineCams.reduce((sum, cam) => sum + cam.count, 0);
|
||||
return {
|
||||
totalCount: total,
|
||||
activeCameras: onlineCams.length,
|
||||
};
|
||||
}, [liveCounts]);
|
||||
|
||||
const totalCameras = liveCounts.length;
|
||||
|
||||
// In LIVE mode, use real population data and show N/A for unimplemented features
|
||||
const displayTotalCount =
|
||||
apiMode === 'LIVE' && currentPopulation !== null
|
||||
? formatNumber(currentPopulation)
|
||||
: apiMode === 'DEMO'
|
||||
? formatNumber(totalCount)
|
||||
: 'N/A';
|
||||
|
||||
const displayCameras = apiMode === 'DEMO' ? `${activeCameras} / ${totalCameras}` : 'N/A';
|
||||
|
||||
const displayAccuracy = apiMode === 'DEMO' ? '98.2%' : 'N/A';
|
||||
const displayLastUpdate = apiMode === 'DEMO' ? 'Baru saja' : 'N/A';
|
||||
|
||||
return (
|
||||
<div className="bg-white p-6 rounded-xl shadow-sm">
|
||||
<h2 className="text-xl font-bold mb-4">Statistik Agregat</h2>
|
||||
<div className="space-y-4">
|
||||
<StatItem
|
||||
icon={<CalculatorIcon />}
|
||||
label="Total Populasi Terhitung"
|
||||
value={`${displayTotalCount} ekor`}
|
||||
valueColor={apiMode === 'LIVE' ? 'text-red-600' : 'text-red-600'}
|
||||
/>
|
||||
<StatItem
|
||||
icon={<i className="fa-solid fa-video"></i>}
|
||||
label="Kamera Aktif (Menghitung)"
|
||||
value={displayCameras}
|
||||
valueColor={
|
||||
apiMode === 'DEMO' && activeCameras === totalCameras
|
||||
? 'text-green-600'
|
||||
: 'text-gray-500'
|
||||
}
|
||||
/>
|
||||
<StatItem
|
||||
icon={<i className="fa-solid fa-crosshairs"></i>}
|
||||
label="Akurasi Model AI (Avg)"
|
||||
value={displayAccuracy}
|
||||
valueColor={apiMode === 'DEMO' ? 'text-green-600' : 'text-gray-500'}
|
||||
/>
|
||||
<StatItem
|
||||
icon={<i className="fa-solid fa-clock"></i>}
|
||||
label="Pembaruan Terakhir"
|
||||
value={displayLastUpdate}
|
||||
valueColor="text-gray-500"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CountingStats;
|
||||
Reference in new issue
Block a user