diff --git a/src/api/activityFeedsService.ts b/src/api/activityFeedsService.ts index 0ce2555..4fdb2d1 100644 --- a/src/api/activityFeedsService.ts +++ b/src/api/activityFeedsService.ts @@ -6,9 +6,9 @@ export const activityFeedsService = { return response; }, - getHistoryDates: async (isRectificationEnabled: boolean) => { + getHistoryDates: async (isRectificationEnabled: boolean, siteIds?: string) => { const response = await axiosClient.get('/api/audit/anomaly/get_history_dates', { - params: { isRectificationEnabled } + params: { isRectificationEnabled, siteIds: siteIds || '' } }); return response; }, diff --git a/src/components/common/TopFilterBar.tsx b/src/components/common/TopFilterBar.tsx index c210d3b..7f4dd0c 100644 --- a/src/components/common/TopFilterBar.tsx +++ b/src/components/common/TopFilterBar.tsx @@ -112,22 +112,46 @@ export const TopFilterBar: React.FC = () => { return `Asset: ${id}`; }; - const [historyDates, setHistoryDates] = useState([]); + const [historyDates, setHistoryDates] = useState([]); // normal history (isRectificationEnabled=false) + const [rectHistoryDates, setRectHistoryDates] = useState([]); // rectification history (isRectificationEnabled=true) const [isBulkDeleteDialogOpen, setIsBulkDeleteDialogOpen] = useState(false); const [loadingHistory, setLoadingHistory] = useState(false); + const [loadingRectHistory, setLoadingRectHistory] = useState(false); const [isHistoryDialogOpen, setIsHistoryDialogOpen] = useState(false); const [selectedHistoryDate, setSelectedHistoryDate] = useState('2026-06-12'); + const [historyDropdownValue, setHistoryDropdownValue] = useState(''); + + // Fetch normal history dates (isRectificationEnabled=false) for the History dropdown + // Rectification history (isRectificationEnabled=true) is fetched separately by the RECTIFICATION HISTORY button + useEffect(() => { + if (!selectedSite || selectedSite.site_id === 'All Sites') return; + const fetchDates = async () => { + setLoadingHistory(true); + try { + const siteIds = String(selectedSite.site_id); + const dates: any = await activityFeedsService.getHistoryDates(false, siteIds); + setHistoryDates(Array.isArray(dates) ? dates : (dates?.responseData || [])); + } catch (err) { + console.error("Failed to load history dates", err); + } finally { + setLoadingHistory(false); + } + }; + fetchDates(); + }, [selectedSite?.site_id]); // eslint-disable-line react-hooks/exhaustive-deps const handleHistoryClick = async () => { setIsHistoryDialogOpen(true); - setLoadingHistory(true); + if (!selectedSite || selectedSite.site_id === 'All Sites') return; + setLoadingRectHistory(true); try { - const dates: any = await activityFeedsService.getHistoryDates(true); - setHistoryDates(Array.isArray(dates) ? dates : (dates?.responseData || [])); + const siteIds = String(selectedSite.site_id); + const dates: any = await activityFeedsService.getHistoryDates(true, siteIds); + setRectHistoryDates(Array.isArray(dates) ? dates : (dates?.responseData || [])); } catch (err) { - console.error("Failed to load history dates", err); + console.error("Failed to load rectification history dates", err); } finally { - setLoadingHistory(false); + setLoadingRectHistory(false); } }; @@ -208,6 +232,48 @@ export const TopFilterBar: React.FC = () => { > BULK DELETE + + {/* History dropdown — only visible when a specific site is selected */} + {localSiteId !== 'All Sites' && + + } + setIsHistoryDialogOpen(false)} @@ -254,14 +320,14 @@ export const TopFilterBar: React.FC = () => { Quick Select (Audited Dates) - {loadingHistory ? ( + {loadingRectHistory ? ( - ) : historyDates.length === 0 ? ( + ) : rectHistoryDates.length === 0 ? ( No dates found ) : ( - historyDates.map((d) => { + rectHistoryDates.map((d) => { const dateStr = d.audited_on ? d.audited_on.split(' ')[0] : ''; return ( { - {/* Search ID Textfield */} - handleSearchChange(e.target.value)} - slotProps={{ - input: { - sx: { - borderRadius: 2, - bgcolor: 'rgba(255,255,255,0.1)', - color: 'white', - fontWeight: 'bold', - width: 150, - '& fieldset': { border: 'none' }, - '& .MuiInputBase-input': { py: 1, px: 2, '&::placeholder': { color: 'rgba(255,255,255,0.5)', opacity: 1 } } - } - } - }} - /> - - {/* Search Plaza Textfield */} - handlePlazaChange(e.target.value)} - slotProps={{ - input: { - sx: { - borderRadius: 2, - bgcolor: 'rgba(255,255,255,0.1)', - color: 'white', - fontWeight: 'bold', - width: 150, - '& fieldset': { border: 'none' }, - '& .MuiInputBase-input': { py: 1, px: 2, '&::placeholder': { color: 'rgba(255,255,255,0.5)', opacity: 1 } } - } - } - }} - /> + {/* Search ID and Search Plaza — only visible when a specific site is selected */} + {localSiteId !== 'All Sites' && ( + <> + handleSearchChange(e.target.value)} + slotProps={{ + input: { + sx: { + borderRadius: 2, + bgcolor: 'rgba(255,255,255,0.1)', + color: 'white', + fontWeight: 'bold', + width: 150, + '& fieldset': { border: 'none' }, + '& .MuiInputBase-input': { py: 1, px: 2, '&::placeholder': { color: 'rgba(255,255,255,0.5)', opacity: 1 } } + } + } + }} + /> + handlePlazaChange(e.target.value)} + slotProps={{ + input: { + sx: { + borderRadius: 2, + bgcolor: 'rgba(255,255,255,0.1)', + color: 'white', + fontWeight: 'bold', + width: 150, + '& fieldset': { border: 'none' }, + '& .MuiInputBase-input': { py: 1, px: 2, '&::placeholder': { color: 'rgba(255,255,255,0.5)', opacity: 1 } } + } + } + }} + /> + + )} + @@ -1105,9 +1106,60 @@ export const AuditSession: React.FC = () => { )} + {/* Keyboard Shortcuts Dialog */} + setIsShortcutsDialogOpen(false)} + sx={{ zIndex: 10000 }} + slotProps={{ paper: { sx: { bgcolor: '#0f172a', color: '#f8fafc', border: '1px solid #1e293b', minWidth: 420 } } }} + > + + Keyboard Shortcuts + + + {[ + { section: 'Classification' }, + { key: 'A', desc: 'Mark as Anomaly' }, + { key: 'S', desc: 'Mark as Safe (Not an Anomaly)' }, + { section: 'Category Selection' }, + { key: '1 – 0', desc: 'Select category (0 = category 10)' }, + { section: 'Plant sub-categories (Anomaly → Category 1)' }, + { key: '1', desc: 'In Grown' }, + { key: '2', desc: 'Out Grown' }, + { section: 'Not-an-Anomaly sub-categories (Safe → Category 1)' }, + { key: '1', desc: 'Comparison Error' }, + { key: '2', desc: 'Low Light' }, + { key: '3', desc: 'Far Asset' }, + { key: '4', desc: 'No Detection' }, + { key: '5', desc: 'Image Mismatch' }, + { section: 'Navigation' }, + { key: '→', desc: 'Next item' }, + { key: '←', desc: 'Previous item' }, + { key: 'Tab', desc: 'Skip current item' }, + { key: 'Enter', desc: 'Save & Next (or Skip if no category)' }, + ].map((row, i) => + 'section' in row ? ( + + {row.section} + + ) : ( + + + {row.desc} + + ) + )} + + + + + + {/* Session Completion In-place Modal */} - setShowCompletionModal(false)} sx={{ zIndex: 10000 }} slotProps={{