update all page to refresh immidiately without clicking sync button or refreshing the whole page

This commit is contained in:
Alberto-Audrix committed 2026-10-07 11:17:44 +07:00
1 parent d5b0c12c40
commit 83a41fbbcb
15 files changed
+490 -151

No files matched your search

+72 -34
View File
@@ -1,4 +1,4 @@
import React, { useEffect, useMemo, useState } from 'react';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { api, ApiError, errorMessage } from '../services/apiClient.ts';
import type {
ChickenCounting,
@@ -44,7 +44,8 @@ function chronological<T>(rows: T[]): T[] {
}
const Dashboard: React.FC<Props> = ({ setView }) => {
const { selectedCycle, selectedKandang, selectedSite, selectedKandangId } = useFarm();
const { selectedCycle, selectedKandang, selectedSite, selectedKandangId, reload, updateCycle } =
useFarm();
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [kpis, setKpis] = useState<KPI[]>([]);
@@ -56,27 +57,29 @@ const Dashboard: React.FC<Props> = ({ setView }) => {
const [reportOpen, setReportOpen] = useState(false);
const [reportType, setReportType] = useState<DashboardReportType>('daily');
useEffect(() => {
if (!selectedCycle) {
setKpis([]);
setCountings([]);
setManualInputs([]);
setWeights([]);
setKarung(null);
setIotDayAggregates([]);
return;
}
const cycleId = selectedCycle.id;
let cancelled = false;
(async () => {
setLoading(true);
const fetchDashboardData = useCallback(
async (isBackground = false) => {
if (!selectedCycle) {
setKpis([]);
setCountings([]);
setManualInputs([]);
setWeights([]);
setKarung(null);
setIotDayAggregates([]);
return;
}
const cycleId = selectedCycle.id;
if (!isBackground) {
setLoading(true);
}
setError(null);
try {
const [kpiRows, countingRows, manualRows, weightRows] = await Promise.all([
const [kpiRows, countingRows, manualRows, weightRows, freshCycle] = await Promise.all([
api.kpis.list({ cycle_id: cycleId }),
api.countings.list({ cycle_id: cycleId }),
api.manualInputs.list({ cycle_id: cycleId }),
api.weights.list({ cycle_id: cycleId }),
api.cycles?.retrieve?.(cycleId)?.catch(() => null) ?? Promise.resolve(null),
]);
let latestKarung: Karung | null = null;
try {
@@ -84,7 +87,9 @@ const Dashboard: React.FC<Props> = ({ setView }) => {
} catch (err) {
if (!(err instanceof ApiError && err.status === 404)) throw err;
}
if (cancelled) return;
if (freshCycle) {
updateCycle(cycleId, freshCycle);
}
setKpis(kpiRows);
setCountings(countingRows);
setManualInputs(manualRows);
@@ -115,29 +120,61 @@ const Dashboard: React.FC<Props> = ({ setView }) => {
);
iotRows = batches.flat();
}
if (!cancelled) {
setIotDayAggregates(
buildIotDayAggregates({
rows: iotRows,
cycleStartDate: selectedCycle.start_date,
getTempTarget: getTempStandardByDay,
})
);
}
setIotDayAggregates(
buildIotDayAggregates({
rows: iotRows,
cycleStartDate: selectedCycle.start_date,
getTempTarget: getTempStandardByDay,
})
);
}
} catch {
if (!cancelled) setIotDayAggregates([]);
setIotDayAggregates([]);
}
} catch (err) {
if (!cancelled) setError(errorMessage(err));
if (!isBackground) {
setError(errorMessage(err));
}
} finally {
if (!cancelled) setLoading(false);
if (!isBackground) {
setLoading(false);
}
}
},
[selectedCycle?.id, selectedCycle?.start_date, selectedKandang, updateCycle]
);
useEffect(() => {
// Initial fetch (with full loading indicator on cycle change)
void fetchDashboardData(false);
// Auto-refresh interval: 1 minute (60,000 ms)
const interval = setInterval(() => {
if (document.visibilityState === 'visible') {
void fetchDashboardData(true);
}
}, 60000);
const handleFocusOrVisibility = () => {
if (document.visibilityState === 'visible') {
void fetchDashboardData(true);
}
})();
return () => {
cancelled = true;
};
}, [selectedCycle, selectedKandang]);
document.addEventListener('visibilitychange', handleFocusOrVisibility);
window.addEventListener('focus', handleFocusOrVisibility);
return () => {
clearInterval(interval);
document.removeEventListener('visibilitychange', handleFocusOrVisibility);
window.removeEventListener('focus', handleFocusOrVisibility);
};
}, [fetchDashboardData]);
const handleManualInputSaved = useCallback(async () => {
await fetchDashboardData(false);
void reload();
}, [fetchDashboardData, reload]);
const cards: KpiCardModel[] = useMemo(() => {
// All cards share the latest complete KPI day (not calendar current_day / live IoT).
@@ -475,6 +512,7 @@ const Dashboard: React.FC<Props> = ({ setView }) => {
cycleId={selectedCycle.id}
currentDay={selectedCycle.current_day}
readOnly={cycleStatus !== 'active'}
onSaved={handleManualInputSaved}
/>
</>
)}