adjust ai insight and seed data
This commit is contained in:
1 parent
68e0d1a0bb
commit
a2b5b10977
18 files changed
+1835
-140
No files matched your search
@@ -6,6 +6,7 @@ import type {
|
||||
Karung,
|
||||
KPI,
|
||||
ManualInput,
|
||||
PanelIoT,
|
||||
} from '../types/api.ts';
|
||||
import type { View } from '../types/common.ts';
|
||||
import { useFarm } from '../context/FarmContext.tsx';
|
||||
@@ -23,7 +24,10 @@ import { getCountingAccuracyWarning } from '../utils/countingAccuracyWarning.ts'
|
||||
import {
|
||||
buildDashboardInsightContext,
|
||||
buildDashboardInsightMetrics,
|
||||
buildIotDayAggregates,
|
||||
type EndCycleIotDayAggregate,
|
||||
} from '../utils/insightContextBuilders.ts';
|
||||
import { getTempStandardByDay } from '../utils/cp707EnvStandards.ts';
|
||||
import DashboardReportCtas from './dashboard/DashboardReportCtas.tsx';
|
||||
import DashboardCloseCycleControls from './dashboard/DashboardCloseCycleControls.tsx';
|
||||
import DashboardManualInputForm from './dashboard/DashboardManualInputForm.tsx';
|
||||
@@ -47,6 +51,7 @@ const Dashboard: React.FC<Props> = ({ setView }) => {
|
||||
const [manualInputs, setManualInputs] = useState<ManualInput[]>([]);
|
||||
const [weights, setWeights] = useState<ChickenWeight[]>([]);
|
||||
const [karung, setKarung] = useState<Karung | null>(null);
|
||||
const [iotDayAggregates, setIotDayAggregates] = useState<EndCycleIotDayAggregate[]>([]);
|
||||
const [reportOpen, setReportOpen] = useState(false);
|
||||
const [reportType, setReportType] = useState<DashboardReportType>('daily');
|
||||
|
||||
@@ -57,6 +62,7 @@ const Dashboard: React.FC<Props> = ({ setView }) => {
|
||||
setManualInputs([]);
|
||||
setWeights([]);
|
||||
setKarung(null);
|
||||
setIotDayAggregates([]);
|
||||
return;
|
||||
}
|
||||
const cycleId = selectedCycle.id;
|
||||
@@ -83,6 +89,44 @@ const Dashboard: React.FC<Props> = ({ setView }) => {
|
||||
setManualInputs(manualRows);
|
||||
setWeights(weightRows);
|
||||
setKarung(latestKarung);
|
||||
|
||||
// Best-effort IoT aggregates for end-cycle root cause (non-blocking on failure).
|
||||
try {
|
||||
const flockId =
|
||||
kpiRows.find((row) => row.flock_id != null)?.flock_id ??
|
||||
(selectedKandang
|
||||
? (await api.flocks.list({ kandang_id: selectedKandang.id }))[0]?.id
|
||||
: null);
|
||||
if (flockId == null) {
|
||||
setIotDayAggregates([]);
|
||||
} else {
|
||||
let iotRows: PanelIoT[] = [];
|
||||
try {
|
||||
iotRows = await api.panelIot.list({ cycle_id: cycleId, flock_id: flockId });
|
||||
} catch {
|
||||
// Some deployments require a date — fall back to sampling recent dates.
|
||||
const dates = await api.panelIot.dates({ cycle_id: cycleId, flock_id: flockId });
|
||||
const sample = [...dates].sort().slice(-14);
|
||||
const batches = await Promise.all(
|
||||
sample.map((date) =>
|
||||
api.panelIot.list({ cycle_id: cycleId, flock_id: flockId, date }).catch(() => [])
|
||||
)
|
||||
);
|
||||
iotRows = batches.flat();
|
||||
}
|
||||
if (!cancelled) {
|
||||
setIotDayAggregates(
|
||||
buildIotDayAggregates({
|
||||
rows: iotRows,
|
||||
cycleStartDate: selectedCycle.start_date,
|
||||
getTempTarget: getTempStandardByDay,
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
if (!cancelled) setIotDayAggregates([]);
|
||||
}
|
||||
} catch (err) {
|
||||
if (!cancelled) setError(errorMessage(err));
|
||||
} finally {
|
||||
@@ -92,7 +136,7 @@ const Dashboard: React.FC<Props> = ({ setView }) => {
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [selectedCycle]);
|
||||
}, [selectedCycle, selectedKandang]);
|
||||
|
||||
const cards: KpiCardModel[] = useMemo(() => {
|
||||
// All cards share the latest complete KPI day (not calendar current_day / live IoT).
|
||||
@@ -246,12 +290,19 @@ const Dashboard: React.FC<Props> = ({ setView }) => {
|
||||
karung_dituang_hari_ini:
|
||||
latest != null ? latest.feed : (karung?.feed_use_today ?? karung?.out_today ?? null),
|
||||
pakan_kumulatif_karung: karung?.feed_use_total ?? latest?.feed_total ?? null,
|
||||
ayam_hidup_ekor: latest?.chicken_life ?? null,
|
||||
ayam_hidup_ekor:
|
||||
latest != null
|
||||
? Math.max(0, latest.chicken_life - (latest.harvest_total ?? 0))
|
||||
: null,
|
||||
persen_hidup: latest?.chicken_life_percentage ?? null,
|
||||
doc_in_ekor: selectedCycle?.doc_in_count ?? null,
|
||||
kematian_kumulatif_ekor: latest?.mortality_total ?? null,
|
||||
panen_kumulatif_ekor: latest?.harvest_total ?? null,
|
||||
tonase_panen_kg: latest?.harvest_weight_total ?? null,
|
||||
// KPI harvest_weight_total is grams; insight context expects kg.
|
||||
tonase_panen_kg:
|
||||
latest?.harvest_weight_total != null
|
||||
? latest.harvest_weight_total / 1000
|
||||
: null,
|
||||
uniformity_persen: weightOnAnchor?.uniformity ?? null,
|
||||
});
|
||||
}, [
|
||||
@@ -420,6 +471,7 @@ const Dashboard: React.FC<Props> = ({ setView }) => {
|
||||
kandangId={selectedKandangId}
|
||||
context={insightContext ?? undefined}
|
||||
dashboardMetrics={insightMetrics}
|
||||
iotDayAggregates={iotDayAggregates}
|
||||
page="dashboard"
|
||||
/>
|
||||
) : (
|
||||
|
||||
Reference in new issue
Block a user