update karung related component to follow cutoff time
This commit is contained in:
1 parent
26c69b340d
commit
03c7dcb646
5 files changed
+78
-47
No files matched your search
@@ -0,0 +1 @@
|
|||||||
|
*.sh text eol=lf
|
||||||
+41
-26
@@ -1,5 +1,5 @@
|
|||||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import { api, ApiError, errorMessage } from '../services/apiClient.ts';
|
import { api, errorMessage } from '../services/apiClient.ts';
|
||||||
import type {
|
import type {
|
||||||
ChickenCounting,
|
ChickenCounting,
|
||||||
ChickenWeight,
|
ChickenWeight,
|
||||||
@@ -52,7 +52,7 @@ const Dashboard: React.FC<Props> = ({ setView }) => {
|
|||||||
const [countings, setCountings] = useState<ChickenCounting[]>([]);
|
const [countings, setCountings] = useState<ChickenCounting[]>([]);
|
||||||
const [manualInputs, setManualInputs] = useState<ManualInput[]>([]);
|
const [manualInputs, setManualInputs] = useState<ManualInput[]>([]);
|
||||||
const [weights, setWeights] = useState<ChickenWeight[]>([]);
|
const [weights, setWeights] = useState<ChickenWeight[]>([]);
|
||||||
const [karung, setKarung] = useState<Karung | null>(null);
|
const [karungs, setKarungs] = useState<Karung[]>([]);
|
||||||
const [iotDayAggregates, setIotDayAggregates] = useState<EndCycleIotDayAggregate[]>([]);
|
const [iotDayAggregates, setIotDayAggregates] = useState<EndCycleIotDayAggregate[]>([]);
|
||||||
const [reportOpen, setReportOpen] = useState(false);
|
const [reportOpen, setReportOpen] = useState(false);
|
||||||
const [reportType, setReportType] = useState<DashboardReportType>('daily');
|
const [reportType, setReportType] = useState<DashboardReportType>('daily');
|
||||||
@@ -64,7 +64,7 @@ const Dashboard: React.FC<Props> = ({ setView }) => {
|
|||||||
setCountings([]);
|
setCountings([]);
|
||||||
setManualInputs([]);
|
setManualInputs([]);
|
||||||
setWeights([]);
|
setWeights([]);
|
||||||
setKarung(null);
|
setKarungs([]);
|
||||||
setIotDayAggregates([]);
|
setIotDayAggregates([]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -78,19 +78,15 @@ const Dashboard: React.FC<Props> = ({ setView }) => {
|
|||||||
api.countings.sync({ cycle_id: cycleId }).catch(() => null),
|
api.countings.sync({ cycle_id: cycleId }).catch(() => null),
|
||||||
api.weights.sync({ cycle_id: cycleId }).catch(() => null),
|
api.weights.sync({ cycle_id: cycleId }).catch(() => null),
|
||||||
]);
|
]);
|
||||||
const [kpiRows, countingRows, manualRows, weightRows, freshCycle] = await Promise.all([
|
const [kpiRows, countingRows, manualRows, weightRows, karungRows, freshCycle] =
|
||||||
|
await Promise.all([
|
||||||
api.kpis.list({ cycle_id: cycleId }),
|
api.kpis.list({ cycle_id: cycleId }),
|
||||||
api.countings.list({ cycle_id: cycleId }),
|
api.countings.list({ cycle_id: cycleId }),
|
||||||
api.manualInputs.list({ cycle_id: cycleId }),
|
api.manualInputs.list({ cycle_id: cycleId }),
|
||||||
api.weights.list({ cycle_id: cycleId }),
|
api.weights.list({ cycle_id: cycleId }),
|
||||||
|
api.karungs.list({ cycle_id: cycleId }).catch(() => []),
|
||||||
api.cycles?.retrieve?.(cycleId)?.catch(() => null) ?? Promise.resolve(null),
|
api.cycles?.retrieve?.(cycleId)?.catch(() => null) ?? Promise.resolve(null),
|
||||||
]);
|
]);
|
||||||
let latestKarung: Karung | null = null;
|
|
||||||
try {
|
|
||||||
latestKarung = await api.karungs.latest({ cycle_id: cycleId });
|
|
||||||
} catch (err) {
|
|
||||||
if (!(err instanceof ApiError && err.status === 404)) throw err;
|
|
||||||
}
|
|
||||||
if (freshCycle) {
|
if (freshCycle) {
|
||||||
updateCycle(cycleId, freshCycle);
|
updateCycle(cycleId, freshCycle);
|
||||||
}
|
}
|
||||||
@@ -98,7 +94,7 @@ const Dashboard: React.FC<Props> = ({ setView }) => {
|
|||||||
setCountings(countingRows);
|
setCountings(countingRows);
|
||||||
setManualInputs(manualRows);
|
setManualInputs(manualRows);
|
||||||
setWeights(weightRows);
|
setWeights(weightRows);
|
||||||
setKarung(latestKarung);
|
setKarungs(karungRows);
|
||||||
|
|
||||||
// Best-effort IoT aggregates for end-cycle root cause (non-blocking on failure).
|
// Best-effort IoT aggregates for end-cycle root cause (non-blocking on failure).
|
||||||
try {
|
try {
|
||||||
@@ -180,10 +176,23 @@ const Dashboard: React.FC<Props> = ({ setView }) => {
|
|||||||
void reload();
|
void reload();
|
||||||
}, [fetchDashboardData, reload]);
|
}, [fetchDashboardData, reload]);
|
||||||
|
|
||||||
|
const visibleThrough = selectedCycle?.visible_through_date;
|
||||||
|
const visibleKpis = useMemo(
|
||||||
|
() => throughVisibleDate(kpis, visibleThrough),
|
||||||
|
[kpis, visibleThrough]
|
||||||
|
);
|
||||||
|
const sortedKarungs = useMemo(
|
||||||
|
() => [...karungs].sort((a, b) => a.date.localeCompare(b.date)),
|
||||||
|
[karungs]
|
||||||
|
);
|
||||||
|
const visibleKarungs = useMemo(
|
||||||
|
() => throughVisibleDate(sortedKarungs, visibleThrough),
|
||||||
|
[sortedKarungs, visibleThrough]
|
||||||
|
);
|
||||||
|
const latestKarung = visibleKarungs[visibleKarungs.length - 1] ?? null;
|
||||||
|
|
||||||
const cards: KpiCardModel[] = useMemo(() => {
|
const cards: KpiCardModel[] = useMemo(() => {
|
||||||
// All cards share the latest complete KPI day (not calendar current_day / live IoT).
|
// All cards share the latest complete KPI day (not calendar current_day / live IoT).
|
||||||
const visibleThrough = selectedCycle?.visible_through_date;
|
|
||||||
const visibleKpis = throughVisibleDate(kpis, visibleThrough);
|
|
||||||
const latest = visibleKpis[0] ?? null;
|
const latest = visibleKpis[0] ?? null;
|
||||||
const anchorDate = latest?.date ?? null;
|
const anchorDate = latest?.date ?? null;
|
||||||
|
|
||||||
@@ -285,7 +294,7 @@ const Dashboard: React.FC<Props> = ({ setView }) => {
|
|||||||
targetView: 'eefDashboard',
|
targetView: 'eefDashboard',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}, [kpis, weights, selectedCycle]);
|
}, [visibleKpis, weights, selectedCycle, visibleThrough]);
|
||||||
|
|
||||||
const countingAccuracyWarning = useMemo(() => {
|
const countingAccuracyWarning = useMemo(() => {
|
||||||
if (!selectedCycle) return null;
|
if (!selectedCycle) return null;
|
||||||
@@ -299,8 +308,6 @@ const Dashboard: React.FC<Props> = ({ setView }) => {
|
|||||||
|
|
||||||
const insightContext = useMemo(() => {
|
const insightContext = useMemo(() => {
|
||||||
if (selectedKandangId == null) return null;
|
if (selectedKandangId == null) return null;
|
||||||
const visibleThrough = selectedCycle?.visible_through_date;
|
|
||||||
const visibleKpis = throughVisibleDate(kpis, visibleThrough);
|
|
||||||
const latest = visibleKpis[0] ?? null;
|
const latest = visibleKpis[0] ?? null;
|
||||||
const anchorDate = latest?.date ?? null;
|
const anchorDate = latest?.date ?? null;
|
||||||
const weightsThroughAnchor = anchorDate
|
const weightsThroughAnchor = anchorDate
|
||||||
@@ -330,8 +337,10 @@ const Dashboard: React.FC<Props> = ({ setView }) => {
|
|||||||
? null
|
? null
|
||||||
: Math.round((100 - latest.chicken_life_percentage) * 100) / 100,
|
: Math.round((100 - latest.chicken_life_percentage) * 100) / 100,
|
||||||
karung_dituang_hari_ini:
|
karung_dituang_hari_ini:
|
||||||
latest != null ? latest.feed : (karung?.feed_use_today ?? karung?.out_today ?? null),
|
latest != null
|
||||||
pakan_kumulatif_karung: karung?.feed_use_total ?? latest?.feed_total ?? null,
|
? latest.feed
|
||||||
|
: (latestKarung?.feed_use_today ?? latestKarung?.out_today ?? null),
|
||||||
|
pakan_kumulatif_karung: latestKarung?.feed_use_total ?? latest?.feed_total ?? null,
|
||||||
ayam_hidup_ekor:
|
ayam_hidup_ekor:
|
||||||
latest != null ? Math.max(0, latest.chicken_life - (latest.harvest_total ?? 0)) : null,
|
latest != null ? Math.max(0, latest.chicken_life - (latest.harvest_total ?? 0)) : null,
|
||||||
persen_hidup: latest?.chicken_life_percentage ?? null,
|
persen_hidup: latest?.chicken_life_percentage ?? null,
|
||||||
@@ -343,12 +352,18 @@ const Dashboard: React.FC<Props> = ({ setView }) => {
|
|||||||
latest?.harvest_weight_total != null ? latest.harvest_weight_total / 1000 : null,
|
latest?.harvest_weight_total != null ? latest.harvest_weight_total / 1000 : null,
|
||||||
uniformity_persen: weightOnAnchor?.uniformity ?? null,
|
uniformity_persen: weightOnAnchor?.uniformity ?? null,
|
||||||
});
|
});
|
||||||
}, [selectedKandangId, selectedKandang, selectedCycle, kpis, weights, karung]);
|
}, [
|
||||||
|
selectedKandangId,
|
||||||
|
selectedKandang,
|
||||||
|
selectedCycle,
|
||||||
|
visibleKpis,
|
||||||
|
weights,
|
||||||
|
visibleThrough,
|
||||||
|
latestKarung,
|
||||||
|
]);
|
||||||
|
|
||||||
const insightMetrics = useMemo(() => {
|
const insightMetrics = useMemo(() => {
|
||||||
if (selectedKandangId == null) return null;
|
if (selectedKandangId == null) return null;
|
||||||
const visibleThrough = selectedCycle?.visible_through_date;
|
|
||||||
const visibleKpis = throughVisibleDate(kpis, visibleThrough);
|
|
||||||
const latest = visibleKpis[0] ?? null;
|
const latest = visibleKpis[0] ?? null;
|
||||||
const anchorDate = latest?.date ?? null;
|
const anchorDate = latest?.date ?? null;
|
||||||
const weightsThroughAnchor = anchorDate
|
const weightsThroughAnchor = anchorDate
|
||||||
@@ -360,17 +375,17 @@ const Dashboard: React.FC<Props> = ({ setView }) => {
|
|||||||
weights: weightsThroughAnchor,
|
weights: weightsThroughAnchor,
|
||||||
currentDay: latest?.age ?? selectedCycle?.current_day ?? null,
|
currentDay: latest?.age ?? selectedCycle?.current_day ?? null,
|
||||||
docInCount: selectedCycle?.doc_in_count ?? null,
|
docInCount: selectedCycle?.doc_in_count ?? null,
|
||||||
pakanKumulatifFallback: karung?.feed_use_total ?? latest?.feed_total ?? null,
|
pakanKumulatifFallback: latestKarung?.feed_use_total ?? latest?.feed_total ?? null,
|
||||||
});
|
});
|
||||||
}, [selectedKandangId, selectedCycle, kpis, weights, karung]);
|
}, [selectedKandangId, selectedCycle, visibleKpis, weights, visibleThrough, latestKarung]);
|
||||||
|
|
||||||
if (!selectedCycle) return <EmptyFarm onSettings={() => setView('kandangSettings')} />;
|
if (!selectedCycle) return <EmptyFarm onSettings={() => setView('kandangSettings')} />;
|
||||||
|
|
||||||
const feedTotal = karung?.feed_use_total ?? kpis[0]?.feed_total ?? 0;
|
const feedTotal = latestKarung?.feed_use_total ?? visibleKpis[0]?.feed_total ?? 0;
|
||||||
const feedWeightKg = feedTotal * 50;
|
const feedWeightKg = feedTotal * 50;
|
||||||
const feedInitialBalance = selectedCycle?.feed_initial_balance ?? 0;
|
const feedInitialBalance = selectedCycle?.feed_initial_balance ?? 0;
|
||||||
const feedInTotal = (karung?.in_total ?? 0) + feedInitialBalance;
|
const feedInTotal = (latestKarung?.in_total ?? 0) + feedInitialBalance;
|
||||||
const feedOutTotal = karung?.out_total ?? 0;
|
const feedOutTotal = latestKarung?.out_total ?? 0;
|
||||||
const feedSaldo = feedInTotal - feedTotal - feedOutTotal;
|
const feedSaldo = feedInTotal - feedTotal - feedOutTotal;
|
||||||
const feedSaldoColor =
|
const feedSaldoColor =
|
||||||
feedSaldo > 0 ? 'text-green-600' : feedSaldo < 0 ? 'text-red-600' : 'text-gray-600';
|
feedSaldo > 0 ? 'text-green-600' : feedSaldo < 0 ? 'text-red-600' : 'text-gray-600';
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
resolveCycleStatus,
|
resolveCycleStatus,
|
||||||
todayIso,
|
todayIso,
|
||||||
} from '../utils/format.ts';
|
} from '../utils/format.ts';
|
||||||
|
import { throughVisibleDate } from '../utils/visibility.ts';
|
||||||
import { buildFeedSackInsightContext } from '../utils/insightContextBuilders.ts';
|
import { buildFeedSackInsightContext } from '../utils/insightContextBuilders.ts';
|
||||||
import FeedStats from './feedSack/FeedStats.tsx';
|
import FeedStats from './feedSack/FeedStats.tsx';
|
||||||
import FeedSaldoCard from './feedSack/FeedSaldoCard.tsx';
|
import FeedSaldoCard from './feedSack/FeedSaldoCard.tsx';
|
||||||
@@ -91,22 +92,30 @@ const FeedSackCountingPage: React.FC<{ onSettings: () => void }> = ({ onSettings
|
|||||||
}, [selectedCycle?.id, reload]);
|
}, [selectedCycle?.id, reload]);
|
||||||
|
|
||||||
const sortedRows = useMemo(() => [...rows].sort((a, b) => a.date.localeCompare(b.date)), [rows]);
|
const sortedRows = useMemo(() => [...rows].sort((a, b) => a.date.localeCompare(b.date)), [rows]);
|
||||||
const latest = sortedRows[sortedRows.length - 1] ?? null;
|
|
||||||
|
|
||||||
const sortedManualRows = useMemo(
|
const sortedManualRows = useMemo(
|
||||||
() => [...manualRows].sort((a, b) => a.date.localeCompare(b.date)),
|
() => [...manualRows].sort((a, b) => a.date.localeCompare(b.date)),
|
||||||
[manualRows]
|
[manualRows]
|
||||||
);
|
);
|
||||||
const visibleThrough = selectedCycle?.visible_through_date;
|
const visibleThrough = selectedCycle?.visible_through_date;
|
||||||
// Display date follows the latest IoT snapshot; without any snapshot, fall back to today.
|
const visibleRows = useMemo(
|
||||||
// Manual cards only use a same-date row — never fall back to an older day.
|
() => throughVisibleDate(sortedRows, visibleThrough),
|
||||||
|
[sortedRows, visibleThrough]
|
||||||
|
);
|
||||||
|
const visibleManualRows = useMemo(
|
||||||
|
() => throughVisibleDate(sortedManualRows, visibleThrough),
|
||||||
|
[sortedManualRows, visibleThrough]
|
||||||
|
);
|
||||||
|
const latest = visibleRows[visibleRows.length - 1] ?? null;
|
||||||
|
|
||||||
|
// Display date follows the latest visible IoT snapshot; without any snapshot, fall back to cutoff date / today.
|
||||||
const displayDate = latest?.date ?? visibleThrough ?? todayIso();
|
const displayDate = latest?.date ?? visibleThrough ?? todayIso();
|
||||||
const latestManual = sortedManualRows.find((row) => row.date === displayDate) ?? null;
|
const latestManual = visibleManualRows.find((row) => row.date === displayDate) ?? null;
|
||||||
|
const lastRecordedManual = visibleManualRows[visibleManualRows.length - 1] ?? null;
|
||||||
const currentDay = useMemo(() => {
|
const currentDay = useMemo(() => {
|
||||||
if (!selectedCycle) return 0;
|
if (!selectedCycle) return 0;
|
||||||
const boundDate = selectedCycle.end_date ?? selectedCycle.visible_through_date ?? todayIso();
|
const boundDate = selectedCycle.end_date ?? visibleThrough ?? todayIso();
|
||||||
return cycleDayForDate(selectedCycle.start_date, boundDate);
|
return cycleDayForDate(selectedCycle.start_date, boundDate);
|
||||||
}, [selectedCycle]);
|
}, [selectedCycle, visibleThrough]);
|
||||||
|
|
||||||
const handleSync = useCallback(async () => {
|
const handleSync = useCallback(async () => {
|
||||||
if (!selectedCycle) return;
|
if (!selectedCycle) return;
|
||||||
@@ -151,8 +160,8 @@ const FeedSackCountingPage: React.FC<{ onSettings: () => void }> = ({ onSettings
|
|||||||
|
|
||||||
const insightContext = useMemo(() => {
|
const insightContext = useMemo(() => {
|
||||||
if (!selectedKandang || !selectedCycle) return null;
|
if (!selectedKandang || !selectedCycle) return null;
|
||||||
const manualByDate = new Map(sortedManualRows.map((row) => [row.date, row]));
|
const manualByDate = new Map(visibleManualRows.map((row) => [row.date, row]));
|
||||||
const dailyRows = sortedRows.map((row) => {
|
const dailyRows = visibleRows.map((row) => {
|
||||||
const hari = cycleDayForDate(selectedCycle.start_date, row.date);
|
const hari = cycleDayForDate(selectedCycle.start_date, row.date);
|
||||||
const manual = manualByDate.get(row.date);
|
const manual = manualByDate.get(row.date);
|
||||||
return {
|
return {
|
||||||
@@ -174,10 +183,10 @@ const FeedSackCountingPage: React.FC<{ onSettings: () => void }> = ({ onSettings
|
|||||||
total_karung_masuk_iot: latest?.in_total ?? null,
|
total_karung_masuk_iot: latest?.in_total ?? null,
|
||||||
total_karung_dituang_iot: latest?.feed_use_total ?? null,
|
total_karung_dituang_iot: latest?.feed_use_total ?? null,
|
||||||
total_karung_keluar_iot: latest?.out_total ?? null,
|
total_karung_keluar_iot: latest?.out_total ?? null,
|
||||||
total_karung_masuk_manual: latestManual?.feed_in_manual_total ?? null,
|
total_karung_masuk_manual: lastRecordedManual?.feed_in_manual_total ?? null,
|
||||||
total_karung_dituang_manual: latestManual?.feed_use_manual_total ?? null,
|
total_karung_dituang_manual: lastRecordedManual?.feed_use_manual_total ?? null,
|
||||||
});
|
});
|
||||||
}, [selectedKandang, selectedCycle, sortedRows, sortedManualRows, latest, latestManual]);
|
}, [selectedKandang, selectedCycle, visibleRows, visibleManualRows, latest, lastRecordedManual]);
|
||||||
|
|
||||||
if (!selectedCycle) return <EmptyFarm onSettings={onSettings} />;
|
if (!selectedCycle) return <EmptyFarm onSettings={onSettings} />;
|
||||||
|
|
||||||
@@ -321,7 +330,7 @@ const FeedSackCountingPage: React.FC<{ onSettings: () => void }> = ({ onSettings
|
|||||||
{selectedKandang && insightContext ? (
|
{selectedKandang && insightContext ? (
|
||||||
<PageAiInsight
|
<PageAiInsight
|
||||||
topic="hitung_karung"
|
topic="hitung_karung"
|
||||||
cacheKey={`pakan-k${selectedKandang.id}-c${selectedCycle.id}-day${currentDay}-rows${sortedRows.length}`}
|
cacheKey={`pakan-k${selectedKandang.id}-c${selectedCycle.id}-day${currentDay}-rows${visibleRows.length}`}
|
||||||
contextData={insightContext}
|
contextData={insightContext}
|
||||||
chartSpec={{
|
chartSpec={{
|
||||||
type: 'trend',
|
type: 'trend',
|
||||||
@@ -399,6 +408,7 @@ const FeedSackCountingPage: React.FC<{ onSettings: () => void }> = ({ onSettings
|
|||||||
<FeedStats
|
<FeedStats
|
||||||
latest={latest}
|
latest={latest}
|
||||||
latestManual={latestManual}
|
latestManual={latestManual}
|
||||||
|
lastRecordedManual={lastRecordedManual}
|
||||||
cycleStartDate={selectedCycle.start_date}
|
cycleStartDate={selectedCycle.start_date}
|
||||||
displayDate={displayDate}
|
displayDate={displayDate}
|
||||||
/>
|
/>
|
||||||
@@ -413,15 +423,15 @@ const FeedSackCountingPage: React.FC<{ onSettings: () => void }> = ({ onSettings
|
|||||||
|
|
||||||
{/* Tren harian */}
|
{/* Tren harian */}
|
||||||
<FeedTrendChart
|
<FeedTrendChart
|
||||||
rows={sortedRows}
|
rows={visibleRows}
|
||||||
cycleStartDate={selectedCycle.start_date}
|
cycleStartDate={selectedCycle.start_date}
|
||||||
currentDay={currentDay}
|
currentDay={currentDay}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Rincian harian */}
|
{/* Rincian harian */}
|
||||||
<FeedDailySummaryTable
|
<FeedDailySummaryTable
|
||||||
rows={sortedRows}
|
rows={visibleRows}
|
||||||
manualRows={manualRows}
|
manualRows={visibleManualRows}
|
||||||
cycleStartDate={selectedCycle.start_date}
|
cycleStartDate={selectedCycle.start_date}
|
||||||
currentDay={currentDay}
|
currentDay={currentDay}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ const StatItem: React.FC<{
|
|||||||
interface FeedStatsProps {
|
interface FeedStatsProps {
|
||||||
latest: Karung | null;
|
latest: Karung | null;
|
||||||
latestManual: ManualInput | null;
|
latestManual: ManualInput | null;
|
||||||
|
lastRecordedManual?: ManualInput | null;
|
||||||
cycleStartDate: string;
|
cycleStartDate: string;
|
||||||
displayDate: string;
|
displayDate: string;
|
||||||
}
|
}
|
||||||
@@ -37,6 +38,7 @@ interface FeedStatsProps {
|
|||||||
const FeedStats: React.FC<FeedStatsProps> = ({
|
const FeedStats: React.FC<FeedStatsProps> = ({
|
||||||
latest,
|
latest,
|
||||||
latestManual,
|
latestManual,
|
||||||
|
lastRecordedManual,
|
||||||
cycleStartDate,
|
cycleStartDate,
|
||||||
displayDate,
|
displayDate,
|
||||||
}) => {
|
}) => {
|
||||||
@@ -61,6 +63,8 @@ const FeedStats: React.FC<FeedStatsProps> = ({
|
|||||||
return `Total (${start} - ${end})`;
|
return `Total (${start} - ${end})`;
|
||||||
}, [cycleStartDate, displayDate]);
|
}, [cycleStartDate, displayDate]);
|
||||||
|
|
||||||
|
const cumulativeManual = lastRecordedManual ?? latestManual;
|
||||||
|
|
||||||
const columns = useMemo(
|
const columns = useMemo(
|
||||||
() => [
|
() => [
|
||||||
{
|
{
|
||||||
@@ -71,7 +75,7 @@ const FeedStats: React.FC<FeedStatsProps> = ({
|
|||||||
iotToday: latest?.in_today ?? 0,
|
iotToday: latest?.in_today ?? 0,
|
||||||
iotTotal: latest?.in_total ?? 0,
|
iotTotal: latest?.in_total ?? 0,
|
||||||
manualToday: latestManual?.feed_in_manual ?? 0,
|
manualToday: latestManual?.feed_in_manual ?? 0,
|
||||||
manualTotal: latestManual?.feed_in_manual_total ?? 0,
|
manualTotal: cumulativeManual?.feed_in_manual_total ?? 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'dituang',
|
key: 'dituang',
|
||||||
@@ -81,7 +85,7 @@ const FeedStats: React.FC<FeedStatsProps> = ({
|
|||||||
iotToday: latest?.feed_use_today ?? 0,
|
iotToday: latest?.feed_use_today ?? 0,
|
||||||
iotTotal: latest?.feed_use_total ?? 0,
|
iotTotal: latest?.feed_use_total ?? 0,
|
||||||
manualToday: latestManual?.feed_use_manual ?? 0,
|
manualToday: latestManual?.feed_use_manual ?? 0,
|
||||||
manualTotal: latestManual?.feed_use_manual_total ?? 0,
|
manualTotal: cumulativeManual?.feed_use_manual_total ?? 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'keluar',
|
key: 'keluar',
|
||||||
@@ -91,10 +95,10 @@ const FeedStats: React.FC<FeedStatsProps> = ({
|
|||||||
iotToday: latest?.out_today ?? 0,
|
iotToday: latest?.out_today ?? 0,
|
||||||
iotTotal: latest?.out_total ?? 0,
|
iotTotal: latest?.out_total ?? 0,
|
||||||
manualToday: latestManual?.feed_out_manual ?? 0,
|
manualToday: latestManual?.feed_out_manual ?? 0,
|
||||||
manualTotal: latestManual?.feed_out_manual_total ?? 0,
|
manualTotal: cumulativeManual?.feed_out_manual_total ?? 0,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
[latest, latestManual]
|
[latest, latestManual, cumulativeManual]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -22,7 +22,8 @@ COPY backend/ .
|
|||||||
COPY docker/entrypoint-api.sh docker/entrypoint-cron.sh docker/karung_sync_loop.sh docker/edge_sync_loop.sh /app/
|
COPY docker/entrypoint-api.sh docker/entrypoint-cron.sh docker/karung_sync_loop.sh docker/edge_sync_loop.sh /app/
|
||||||
COPY docker/crontab /etc/cron.d/newdashboard
|
COPY docker/crontab /etc/cron.d/newdashboard
|
||||||
|
|
||||||
RUN chmod +x /app/entrypoint-api.sh /app/entrypoint-cron.sh /app/karung_sync_loop.sh /app/edge_sync_loop.sh \
|
RUN sed -i 's/\r$//' /app/*.sh \
|
||||||
|
&& chmod +x /app/entrypoint-api.sh /app/entrypoint-cron.sh /app/karung_sync_loop.sh /app/edge_sync_loop.sh \
|
||||||
&& mkdir -p /app/data /app/media /app/staticfiles \
|
&& mkdir -p /app/data /app/media /app/staticfiles \
|
||||||
&& chmod 0644 /etc/cron.d/newdashboard \
|
&& chmod 0644 /etc/cron.d/newdashboard \
|
||||||
&& crontab /etc/cron.d/newdashboard
|
&& crontab /etc/cron.d/newdashboard
|
||||||
|
|||||||
Reference in new issue
Block a user