Ver código fonte

fix fg goods status dasboard bug

MergeProblem1
Tommy\2Fi-Staff 5 dias atrás
pai
commit
53cc1692ad
1 arquivos alterados com 109 adições e 61 exclusões
  1. +109
    -61
      src/components/DashboardPage/truckSchedule/TruckScheduleDashboard.tsx

+ 109
- 61
src/components/DashboardPage/truckSchedule/TruckScheduleDashboard.tsx Ver arquivo

@@ -32,33 +32,52 @@ interface CompletedTracker {
refreshCount: number;
}

// Data stored per date for instant switching
interface DateData {
today: TruckScheduleDashboardItem[];
tomorrow: TruckScheduleDashboardItem[];
dayAfterTomorrow: TruckScheduleDashboardItem[];
}

const TruckScheduleDashboard: React.FC = () => {
const { t } = useTranslation("dashboard");
const [selectedStore, setSelectedStore] = useState<string>("");
const [selectedDate, setSelectedDate] = useState<string>("today");
const [data, setData] = useState<TruckScheduleDashboardItem[]>([]);
// Store data for all three dates for instant switching
const [allData, setAllData] = useState<DateData>({ today: [], tomorrow: [], dayAfterTomorrow: [] });
const [loading, setLoading] = useState<boolean>(true);
// Initialize as null to avoid SSR/client hydration mismatch
const [currentTime, setCurrentTime] = useState<dayjs.Dayjs | null>(null);
const [isClient, setIsClient] = useState<boolean>(false);
const completedTrackerRef = useRef<Map<string, CompletedTracker>>(new Map());
const refreshCountRef = useRef<number>(0);
// Track completed items per date
const completedTrackerRef = useRef<Map<string, Map<string, CompletedTracker>>>(new Map([
['today', new Map()],
['tomorrow', new Map()],
['dayAfterTomorrow', new Map()]
]));
const refreshCountRef = useRef<Map<string, number>>(new Map([
['today', 0],
['tomorrow', 0],
['dayAfterTomorrow', 0]
]));
// Get date label for display (e.g., "2026-01-17")
const getDateLabel = (offset: number): string => {
return dayjs().add(offset, 'day').format('YYYY-MM-DD');
};

// Get day offset based on date option
const getDateOffset = (dateOption: string): number => {
if (dateOption === "today") return 0;
if (dateOption === "tomorrow") return 1;
if (dateOption === "dayAfterTomorrow") return 2;
return 0;
};

// Convert date option to YYYY-MM-DD format for API
const getDateParam = (dateOption: string): string => {
if (dateOption === "today") {
return dayjs().format('YYYY-MM-DD');
} else if (dateOption === "tomorrow") {
return dayjs().add(1, 'day').format('YYYY-MM-DD');
} else if (dateOption === "dayAfterTomorrow") {
return dayjs().add(2, 'day').format('YYYY-MM-DD');
}
return dayjs().add(1, 'day').format('YYYY-MM-DD');
const offset = getDateOffset(dateOption);
return dayjs().add(offset, 'day').format('YYYY-MM-DD');
};
// Set client flag and time on mount
@@ -109,7 +128,7 @@ const TruckScheduleDashboard: React.FC = () => {
};

// Calculate time remaining for truck departure
const calculateTimeRemaining = useCallback((departureTime: string | number[] | null): string => {
const calculateTimeRemaining = useCallback((departureTime: string | number[] | null, dateOption: string): string => {
if (!departureTime || !currentTime) return '-';
const now = currentTime;
@@ -129,8 +148,9 @@ const TruckScheduleDashboard: React.FC = () => {
return '-';
}
// Create departure datetime for today
const departure = now.clone().hour(departureHour).minute(departureMinute).second(0);
// Create departure datetime for the selected date (today, tomorrow, or day after tomorrow)
const dateOffset = getDateOffset(dateOption);
const departure = now.clone().add(dateOffset, 'day').hour(departureHour).minute(departureMinute).second(0);
const diffMinutes = departure.diff(now, 'minute');
if (diffMinutes < 0) {
@@ -151,58 +171,81 @@ const TruckScheduleDashboard: React.FC = () => {
return `${item.storeId}-${item.truckLanceCode}-${item.truckDepartureTime}`;
};

// Load data from API
const loadData = useCallback(async () => {
// Process data for a specific date option with completed tracker logic
const processDataForDate = (result: TruckScheduleDashboardItem[], dateOption: string): TruckScheduleDashboardItem[] => {
const tracker = completedTrackerRef.current.get(dateOption) || new Map();
const currentRefresh = (refreshCountRef.current.get(dateOption) || 0) + 1;
refreshCountRef.current.set(dateOption, currentRefresh);
result.forEach(item => {
const key = getItemKey(item);
// If all tickets are completed, track it
if (item.numberOfPickTickets > 0 && item.numberOfTicketsCompleted >= item.numberOfPickTickets) {
const existing = tracker.get(key);
if (!existing) {
tracker.set(key, { key, refreshCount: currentRefresh });
}
} else {
// Remove from tracker if no longer completed
tracker.delete(key);
}
});
completedTrackerRef.current.set(dateOption, tracker);
// Filter out items that have been completed for 2+ refresh cycles
return result.filter(item => {
const key = getItemKey(item);
const itemTracker = tracker.get(key);
if (itemTracker) {
// Hide if completed for 2 or more refresh cycles
if (currentRefresh - itemTracker.refreshCount >= 2) {
return false;
}
}
return true;
});
};

// Load data for all three dates in parallel for instant switching
const loadData = useCallback(async (isInitialLoad: boolean = false) => {
// Only show loading spinner on initial load, not during refresh
if (isInitialLoad) {
setLoading(true);
}
try {
const dateParam = getDateParam(selectedDate);
const result = await fetchTruckScheduleDashboardClient(dateParam);
const dateOptions = ['today', 'tomorrow', 'dayAfterTomorrow'] as const;
const dateParams = dateOptions.map(opt => getDateParam(opt));
// Update completed tracker
refreshCountRef.current += 1;
const currentRefresh = refreshCountRef.current;
// Fetch all three dates in parallel
const [todayResult, tomorrowResult, dayAfterResult] = await Promise.all([
fetchTruckScheduleDashboardClient(dateParams[0]),
fetchTruckScheduleDashboardClient(dateParams[1]),
fetchTruckScheduleDashboardClient(dateParams[2])
]);
result.forEach(item => {
const key = getItemKey(item);
// If all tickets are completed, track it
if (item.numberOfPickTickets > 0 && item.numberOfTicketsCompleted >= item.numberOfPickTickets) {
const existing = completedTrackerRef.current.get(key);
if (!existing) {
completedTrackerRef.current.set(key, { key, refreshCount: currentRefresh });
}
} else {
// Remove from tracker if no longer completed
completedTrackerRef.current.delete(key);
}
// Process each date's data with completed tracker logic
setAllData({
today: processDataForDate(todayResult, 'today'),
tomorrow: processDataForDate(tomorrowResult, 'tomorrow'),
dayAfterTomorrow: processDataForDate(dayAfterResult, 'dayAfterTomorrow')
});
// Filter out items that have been completed for 2+ refresh cycles
const filteredResult = result.filter(item => {
const key = getItemKey(item);
const tracker = completedTrackerRef.current.get(key);
if (tracker) {
// Hide if completed for 2 or more refresh cycles
if (currentRefresh - tracker.refreshCount >= 2) {
return false;
}
}
return true;
});
setData(filteredResult);
} catch (error) {
console.error('Error fetching truck schedule dashboard:', error);
} finally {
setLoading(false);
if (isInitialLoad) {
setLoading(false);
}
}
}, [selectedDate]);
}, []);

// Initial load and auto-refresh every 5 minutes
useEffect(() => {
loadData();
loadData(true); // Initial load - show spinner
const refreshInterval = setInterval(() => {
loadData();
}, 0.1 * 60 * 1000); // 5 minutes
loadData(false); // Refresh - don't show spinner, keep existing data visible
}, 5 * 60 * 1000); // 5 minutes
return () => clearInterval(refreshInterval);
}, [loadData]);
@@ -218,14 +261,17 @@ const TruckScheduleDashboard: React.FC = () => {
return () => clearInterval(timeInterval);
}, [isClient]);

// Filter data by selected store
// Get data for selected date, then filter by store - both filters are instant
const filteredData = useMemo(() => {
if (!selectedStore) return data;
return data.filter(item => item.storeId === selectedStore);
}, [data, selectedStore]);
// First get the data for the selected date
const dateData = allData[selectedDate as keyof DateData] || [];
// Then filter by store if selected
if (!selectedStore) return dateData;
return dateData.filter(item => item.storeId === selectedStore);
}, [allData, selectedDate, selectedStore]);

// Get chip color based on time remaining
const getTimeChipColor = (departureTime: string | number[] | null): "success" | "warning" | "error" | "default" => {
const getTimeChipColor = (departureTime: string | number[] | null, dateOption: string): "success" | "warning" | "error" | "default" => {
if (!departureTime || !currentTime) return "default";
const now = currentTime;
@@ -245,7 +291,9 @@ const TruckScheduleDashboard: React.FC = () => {
return "default";
}
const departure = now.clone().hour(departureHour).minute(departureMinute).second(0);
// Create departure datetime for the selected date (today, tomorrow, or day after tomorrow)
const dateOffset = getDateOffset(dateOption);
const departure = now.clone().add(dateOffset, 'day').hour(departureHour).minute(departureMinute).second(0);
const diffMinutes = departure.diff(now, 'minute');
if (diffMinutes < 0) return "error"; // Past due
@@ -332,8 +380,8 @@ const TruckScheduleDashboard: React.FC = () => {
</TableRow>
) : (
filteredData.map((row, index) => {
const timeRemaining = calculateTimeRemaining(row.truckDepartureTime);
const chipColor = getTimeChipColor(row.truckDepartureTime);
const timeRemaining = calculateTimeRemaining(row.truckDepartureTime, selectedDate);
const chipColor = getTimeChipColor(row.truckDepartureTime, selectedDate);
return (
<TableRow


Carregando…
Cancelar
Salvar