commit
32a36cceff
444 files changed
+67186
No files matched your search
@@ -0,0 +1,119 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import {
|
||||
LineChart,
|
||||
Line,
|
||||
XAxis,
|
||||
YAxis,
|
||||
CartesianGrid,
|
||||
Tooltip,
|
||||
Legend,
|
||||
ResponsiveContainer,
|
||||
} from 'recharts';
|
||||
import { PopulationDataPoint } from '../../types.ts';
|
||||
import { useAppContext } from '../../context/AppContext.tsx';
|
||||
import { formatNumber, createYAxisFormatter } from '../../utils/formatters.ts';
|
||||
|
||||
interface KandangPopulationData {
|
||||
name: string;
|
||||
data: PopulationDataPoint[];
|
||||
color: string;
|
||||
}
|
||||
|
||||
interface PopulationTrendChartProps {
|
||||
data: PopulationDataPoint[];
|
||||
perKandangData?: KandangPopulationData[];
|
||||
}
|
||||
|
||||
const PopulationTrendChart: React.FC<PopulationTrendChartProps> = ({ data, perKandangData }) => {
|
||||
const { apiMode: _apiMode } = useAppContext();
|
||||
|
||||
// Merge per-kandang data into chart data
|
||||
const mergedData = useMemo(() => {
|
||||
if (!perKandangData || perKandangData.length === 0) return data;
|
||||
|
||||
return data.map((point, index) => {
|
||||
const merged: any = { ...point };
|
||||
perKandangData.forEach((kandang) => {
|
||||
if (kandang.data[index]) {
|
||||
merged[kandang.name] = kandang.data[index].population;
|
||||
}
|
||||
});
|
||||
return merged;
|
||||
});
|
||||
}, [data, perKandangData]);
|
||||
|
||||
// Show empty state if no data
|
||||
if (data.length === 0) {
|
||||
return (
|
||||
<div className="bg-white p-6 rounded-xl shadow-sm">
|
||||
<h2 className="text-xl font-bold mb-4">Tren Populasi</h2>
|
||||
<div className="h-60 flex items-center justify-center">
|
||||
<div className="text-center text-gray-500">
|
||||
<i className="fa-solid fa-chart-line text-5xl text-gray-300 mb-3"></i>
|
||||
<p className="font-medium text-gray-600">Tidak ada data</p>
|
||||
<p className="text-sm text-gray-500 mt-1">Belum ada data populasi yang tersedia</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const hasPerKandang = perKandangData && perKandangData.length > 0;
|
||||
|
||||
return (
|
||||
<div className="bg-white p-6 rounded-xl shadow-sm">
|
||||
<h2 className="text-xl font-bold mb-4">Tren Populasi</h2>
|
||||
<div className={hasPerKandang ? 'h-80' : 'h-60'}>
|
||||
<ResponsiveContainer width="100%" height="100%">
|
||||
<LineChart data={mergedData} margin={{ top: 5, right: 20, left: -15, bottom: 5 }}>
|
||||
<CartesianGrid strokeDasharray="3 3" stroke="#e0e0e0" />
|
||||
<XAxis dataKey="date" fontSize={12} tick={{ fill: '#6b7280' }} />
|
||||
<YAxis
|
||||
fontSize={12}
|
||||
tick={{ fill: '#6b7280' }}
|
||||
domain={['dataMin - 50', 'dataMax + 50']}
|
||||
tickFormatter={createYAxisFormatter()}
|
||||
/>
|
||||
<Tooltip
|
||||
contentStyle={{
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.9)',
|
||||
border: '1px solid #ccc',
|
||||
borderRadius: '8px',
|
||||
}}
|
||||
formatter={(value: number, name: string) => [
|
||||
formatNumber(value),
|
||||
name === 'population' ? 'Total' : name,
|
||||
]}
|
||||
/>
|
||||
{hasPerKandang && <Legend />}
|
||||
<Line
|
||||
type="monotone"
|
||||
dataKey="population"
|
||||
stroke="#ef4444"
|
||||
strokeWidth={2.5}
|
||||
name={hasPerKandang ? 'Total' : 'Populasi'}
|
||||
dot={{ r: hasPerKandang ? 3 : 4 }}
|
||||
activeDot={{ r: 6 }}
|
||||
/>
|
||||
{hasPerKandang &&
|
||||
perKandangData!.map((kandang) => (
|
||||
<Line
|
||||
key={kandang.name}
|
||||
type="monotone"
|
||||
dataKey={kandang.name}
|
||||
stroke={kandang.color}
|
||||
strokeWidth={1.5}
|
||||
strokeDasharray="5 5"
|
||||
name={kandang.name}
|
||||
dot={false}
|
||||
activeDot={{ r: 4 }}
|
||||
/>
|
||||
))}
|
||||
</LineChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PopulationTrendChart;
|
||||
Reference in new issue
Block a user