first commit
This commit is contained in:
commit
b54624be96
226 files changed
+108840
No files matched your search
@@ -0,0 +1,181 @@
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import { cobbStandardPerformance } from '../mockData/cobbStandard.ts';
|
||||
import { useFarm } from '../context/FarmContext.tsx';
|
||||
import { formatNumber, formatRatio } from '../utils/format.ts';
|
||||
|
||||
const CobbStandardPage: React.FC = () => {
|
||||
const { selectedCycle } = useFarm();
|
||||
const [initialBw, setInitialBw] = useState<number>(selectedCycle?.chick_in_weight || 42);
|
||||
const [capacity, setCapacity] = useState<number>(selectedCycle?.doc_in_count || 50000);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedCycle?.chick_in_weight) setInitialBw(selectedCycle.chick_in_weight);
|
||||
if (selectedCycle?.doc_in_count) setCapacity(selectedCycle.doc_in_count);
|
||||
}, [selectedCycle?.chick_in_weight, selectedCycle?.doc_in_count]);
|
||||
|
||||
const calculatedData = useMemo(() => {
|
||||
const results = [];
|
||||
let lastBw = Number(initialBw);
|
||||
let lastCumFeed = 0;
|
||||
for (const standard of cobbStandardPerformance) {
|
||||
const currentBw = lastBw * (1 + standard.dailyGainPercent);
|
||||
const dailyGainG = currentBw - lastBw;
|
||||
const cumFeedG = currentBw * standard.fcr;
|
||||
const dailyFeedG = cumFeedG - lastCumFeed;
|
||||
const feedKg = (dailyFeedG * capacity) / 1000;
|
||||
const feedBag = feedKg / 50;
|
||||
results.push({
|
||||
day: standard.day,
|
||||
bw: Math.round(currentBw),
|
||||
dailyGainG: Math.round(dailyGainG),
|
||||
dailyGainPercent: standard.dailyGainPercent,
|
||||
dailyFeedG: Math.round(dailyFeedG),
|
||||
cumFeedG: Math.round(cumFeedG),
|
||||
fcr: standard.fcr,
|
||||
feedKg: Math.round(feedKg),
|
||||
feedBag: Math.round(feedBag),
|
||||
});
|
||||
lastBw = currentBw;
|
||||
lastCumFeed = cumFeedG;
|
||||
}
|
||||
return results;
|
||||
}, [initialBw, capacity]);
|
||||
|
||||
return (
|
||||
<main className="pb-8">
|
||||
<div className="flex items-center mb-6">
|
||||
<i className="fa-solid fa-calculator text-3xl text-red-600 mr-4"></i>
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold text-gray-800">Kalkulator Standar Performa</h1>
|
||||
<p className="mt-1 text-gray-600">
|
||||
Hitung proyeksi pertumbuhan dan kebutuhan pakan berdasarkan bobot awal dan kapasitas
|
||||
kandang.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 mb-8">
|
||||
<div className="bg-white p-4 rounded-xl shadow-sm">
|
||||
<label htmlFor="initialBw" className="block text-sm font-medium text-gray-700">
|
||||
Bobot Awal DOC (g)
|
||||
</label>
|
||||
<input
|
||||
type="number"
|
||||
id="initialBw"
|
||||
value={initialBw}
|
||||
onChange={(e) => setInitialBw(Number(e.target.value))}
|
||||
className="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:ring-red-500 focus:border-red-500 text-lg font-semibold"
|
||||
/>
|
||||
</div>
|
||||
<div className="bg-white p-4 rounded-xl shadow-sm">
|
||||
<label htmlFor="capacity" className="block text-sm font-medium text-gray-700">
|
||||
Kapasitas Kandang (ekor)
|
||||
</label>
|
||||
<input
|
||||
type="number"
|
||||
id="capacity"
|
||||
value={capacity}
|
||||
onChange={(e) => setCapacity(Number(e.target.value))}
|
||||
className="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:ring-red-500 focus:border-red-500 text-lg font-semibold"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-white p-4 rounded-xl shadow-sm overflow-hidden">
|
||||
<div className="overflow-auto max-h-[calc(100vh-380px)]">
|
||||
<table className="min-w-full border-collapse text-sm">
|
||||
<thead className="bg-gray-700 text-white sticky top-0 z-10">
|
||||
<tr>
|
||||
<th
|
||||
rowSpan={2}
|
||||
className="p-2 border border-gray-800 font-semibold sticky left-0 bg-gray-700 z-20"
|
||||
>
|
||||
Umur
|
||||
</th>
|
||||
<th rowSpan={2} className="p-2 border border-gray-800 font-semibold">
|
||||
BW (g)
|
||||
</th>
|
||||
<th colSpan={2} className="p-2 border border-gray-800 font-semibold">
|
||||
Daily Gain
|
||||
</th>
|
||||
<th colSpan={2} className="p-2 border border-gray-800 font-semibold">
|
||||
Feed Consumption
|
||||
</th>
|
||||
<th rowSpan={2} className="p-2 border border-gray-800 font-semibold">
|
||||
FCR
|
||||
</th>
|
||||
<th colSpan={2} className="p-2 border border-gray-800 font-semibold">
|
||||
Kebutuhan Pakan
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th className="p-2 border border-gray-800 font-semibold">(g)</th>
|
||||
<th className="p-2 border border-gray-800 font-semibold">(%)</th>
|
||||
<th className="p-2 border border-gray-800 font-semibold">Daily (g)</th>
|
||||
<th className="p-2 border border-gray-800 font-semibold">Cum (g)</th>
|
||||
<th className="p-2 border border-gray-800 font-semibold">(Kg)</th>
|
||||
<th className="p-2 border border-gray-800 font-semibold">(Bag)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="font-mono">
|
||||
<tr className="bg-yellow-100 font-bold">
|
||||
<td className="p-1 border border-gray-200 text-center bg-gray-700 text-white sticky left-0 z-10">
|
||||
0
|
||||
</td>
|
||||
<td className="p-1 border border-gray-200 text-center text-gray-700">
|
||||
{initialBw}
|
||||
</td>
|
||||
<td className="p-1 border border-gray-200 text-center" colSpan={7}></td>
|
||||
</tr>
|
||||
{calculatedData.map((row) => {
|
||||
const isSeventhDay = row.day > 0 && row.day % 7 === 0;
|
||||
const rowClass = `hover:bg-red-50 ${isSeventhDay ? 'font-bold bg-blue-100' : 'even:bg-gray-100/50'}`;
|
||||
const cellBorderClass = isSeventhDay
|
||||
? 'border-b-[3px] border-double border-blue-400'
|
||||
: 'border-gray-200';
|
||||
return (
|
||||
<tr key={row.day} className={rowClass}>
|
||||
<td
|
||||
className={`p-1 border ${cellBorderClass} text-center bg-gray-700 text-white font-semibold sticky left-0 z-10`}
|
||||
>
|
||||
{row.day}
|
||||
</td>
|
||||
<td
|
||||
className={`p-1 border ${cellBorderClass} text-center font-bold text-red-600`}
|
||||
>
|
||||
{formatNumber(row.bw)}
|
||||
</td>
|
||||
<td className={`p-1 border ${cellBorderClass} text-center text-gray-700`}>
|
||||
{formatNumber(row.dailyGainG)}
|
||||
</td>
|
||||
<td className={`p-1 border ${cellBorderClass} text-center text-gray-700`}>
|
||||
{(row.dailyGainPercent * 100).toFixed(0)}%
|
||||
</td>
|
||||
<td className={`p-1 border ${cellBorderClass} text-center text-gray-700`}>
|
||||
{formatNumber(row.dailyFeedG)}
|
||||
</td>
|
||||
<td className={`p-1 border ${cellBorderClass} text-center text-gray-700`}>
|
||||
{formatNumber(row.cumFeedG)}
|
||||
</td>
|
||||
<td className={`p-1 border ${cellBorderClass} text-center text-gray-700`}>
|
||||
{formatRatio(row.fcr)}
|
||||
</td>
|
||||
<td className={`p-1 border ${cellBorderClass} text-center text-gray-700`}>
|
||||
{formatNumber(row.feedKg)}
|
||||
</td>
|
||||
<td className={`p-1 border ${cellBorderClass} text-center text-gray-700`}>
|
||||
{formatNumber(row.feedBag)}
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-right mt-2 text-xs text-gray-500 font-semibold">
|
||||
Standar Performa COBB (2018)
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
};
|
||||
|
||||
export default CobbStandardPage;
|
||||
Reference in new issue
Block a user