adjust dashboard berat page to match dashboard-cpsp
This commit is contained in:
1 parent
c71dcc188b
commit
236c83ec3d
2 files changed
+21
-27
No files matched your search
@@ -2,8 +2,12 @@ import React, { useEffect, useMemo, useState } from 'react';
|
|||||||
import { api, ApiError, errorMessage } from '../services/apiClient.ts';
|
import { api, ApiError, errorMessage } from '../services/apiClient.ts';
|
||||||
import type { ChickenWeight, LatestAvg } from '../types/api.ts';
|
import type { ChickenWeight, LatestAvg } from '../types/api.ts';
|
||||||
import { useFarm } from '../context/FarmContext.tsx';
|
import { useFarm } from '../context/FarmContext.tsx';
|
||||||
import { Banner, EmptyFarm, Spinner } from './ui/Feedback.tsx';
|
import { Banner, EmptyFarm, PageHeader, Spinner } from './ui/Feedback.tsx';
|
||||||
import { formatDateId, formatNumber } from '../utils/format.ts';
|
import {
|
||||||
|
formatDateId,
|
||||||
|
formatDateRange,
|
||||||
|
formatNumber,
|
||||||
|
} from '../utils/format.ts';
|
||||||
import { formatWeight } from '../utils/weight.ts';
|
import { formatWeight } from '../utils/weight.ts';
|
||||||
import { buildWeightInsightContext } from '../utils/insightContextBuilders.ts';
|
import { buildWeightInsightContext } from '../utils/insightContextBuilders.ts';
|
||||||
import { PageAiInsight } from './shared/PageAiInsight.tsx';
|
import { PageAiInsight } from './shared/PageAiInsight.tsx';
|
||||||
@@ -13,7 +17,7 @@ import DailyWeightGainChart from './weight/DailyWeightGainChart.tsx';
|
|||||||
import DailyDetailPanel from './weight/DailyDetailPanel.tsx';
|
import DailyDetailPanel from './weight/DailyDetailPanel.tsx';
|
||||||
|
|
||||||
const WeightMonitoringPage: React.FC<{ onSettings: () => void }> = ({ onSettings }) => {
|
const WeightMonitoringPage: React.FC<{ onSettings: () => void }> = ({ onSettings }) => {
|
||||||
const { selectedCycle, selectedKandang } = useFarm();
|
const { selectedCycle, selectedSite, selectedKandang } = useFarm();
|
||||||
const [rows, setRows] = useState<ChickenWeight[]>([]);
|
const [rows, setRows] = useState<ChickenWeight[]>([]);
|
||||||
const [latest, setLatest] = useState<LatestAvg | null>(null);
|
const [latest, setLatest] = useState<LatestAvg | null>(null);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
@@ -119,26 +123,11 @@ const WeightMonitoringPage: React.FC<{ onSettings: () => void }> = ({ onSettings
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<main className="pb-8">
|
<main className="pb-8">
|
||||||
{/* Header */}
|
<PageHeader
|
||||||
<div className="flex flex-col lg:flex-row lg:items-center lg:justify-between gap-4 mb-6">
|
title="Dashboard Berat Ayam"
|
||||||
<div className="flex items-center">
|
icon={<i className="fa-solid fa-chart-line" />}
|
||||||
<div className="bg-red-600 text-white p-3 rounded-lg mr-4">
|
subtitle={`Analisis pertumbuhan bobot ayam secara real-time · ${selectedSite?.site_name ?? ''} · ${selectedKandang?.kandang_name ?? ''} · Siklus ${formatDateRange(selectedCycle.start_date, selectedCycle.end_date)}`}
|
||||||
<i className="fa-solid fa-chart-line text-2xl sm:text-3xl"></i>
|
/>
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<h1 className="text-2xl lg:text-3xl font-bold text-gray-800">Dashboard Berat Ayam</h1>
|
|
||||||
<p className="mt-1 text-gray-600">
|
|
||||||
Analisis pertumbuhan dan kesejahteraan bobot ayam secara real-time.
|
|
||||||
</p>
|
|
||||||
{!loading && latest?.date && (
|
|
||||||
<p className="mt-1 text-sm text-gray-500">
|
|
||||||
<i className="fa-solid fa-clock mr-1"></i>
|
|
||||||
Terakhir diperbarui: {formatDateId(latest.date)}
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{error && (
|
{error && (
|
||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
@@ -295,9 +284,8 @@ const WeightMonitoringPage: React.FC<{ onSettings: () => void }> = ({ onSettings
|
|||||||
<div>
|
<div>
|
||||||
<h3 className="font-semibold text-blue-900 mb-2">Informasi Pembaruan Data</h3>
|
<h3 className="font-semibold text-blue-900 mb-2">Informasi Pembaruan Data</h3>
|
||||||
<ul className="text-sm text-blue-800 space-y-1">
|
<ul className="text-sm text-blue-800 space-y-1">
|
||||||
<li>• Data berat diperbarui lewat Sync di header (dari site dashboard)</li>
|
<li>• Hari 0 memakai berat DOC dan uniformity 0%</li>
|
||||||
<li>• Grafik menampilkan data dari awal siklus hingga hari ini</li>
|
<li>• Target berat menggunakan standar performa berdasarkan DOC</li>
|
||||||
<li>• Target berat menggunakan standar performa Ross 308</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+7
-1
@@ -61,6 +61,7 @@ function pickStandardValues(docWeight: number): number[] {
|
|||||||
|
|
||||||
/** Target body weight (grams) for a given age from the Ross 308 DOC-weight table. */
|
/** Target body weight (grams) for a given age from the Ross 308 DOC-weight table. */
|
||||||
export function getTargetWeightForAge(age: number, docWeight: number): number | null {
|
export function getTargetWeightForAge(age: number, docWeight: number): number | null {
|
||||||
|
if (age === 0) return docWeight > 0 ? docWeight : null;
|
||||||
const values = pickStandardValues(docWeight);
|
const values = pickStandardValues(docWeight);
|
||||||
if (age < 1 || age > values.length) return null;
|
if (age < 1 || age > values.length) return null;
|
||||||
return values[age - 1] ?? null;
|
return values[age - 1] ?? null;
|
||||||
@@ -69,7 +70,12 @@ export function getTargetWeightForAge(age: number, docWeight: number): number |
|
|||||||
/** Target daily weight gain (grams/day) for a given age from the standard. */
|
/** Target daily weight gain (grams/day) for a given age from the standard. */
|
||||||
export function getTargetGainForAge(age: number, docWeight: number): number | null {
|
export function getTargetGainForAge(age: number, docWeight: number): number | null {
|
||||||
const values = pickStandardValues(docWeight);
|
const values = pickStandardValues(docWeight);
|
||||||
if (age < 2 || age > values.length) return null;
|
if (age < 1 || age > values.length) return null;
|
||||||
|
if (age === 1) {
|
||||||
|
const current = values[0];
|
||||||
|
if (current == null || docWeight <= 0) return null;
|
||||||
|
return current - docWeight;
|
||||||
|
}
|
||||||
const current = values[age - 1];
|
const current = values[age - 1];
|
||||||
const previous = values[age - 2];
|
const previous = values[age - 2];
|
||||||
if (current == null || previous == null) return null;
|
if (current == null || previous == null) return null;
|
||||||
|
|||||||
Reference in new issue
Block a user