import React, { useEffect } from 'react'; import { Box, Typography, TablePagination, Button, FormControl, Select, MenuItem } from '@mui/material'; import FilterListIcon from '@mui/icons-material/FilterList'; import HistoryIcon from '@mui/icons-material/History'; import { TopFilterBar } from '../../components/common/TopFilterBar'; import { FeedTabs } from './FeedTabs'; import { AnomalyTable } from './AnomalyTable'; import { PreviewPanel } from './PreviewPanel'; import { useFeedStore } from '../../store/feedStore'; import { useAuthStore } from '../../store/authStore'; import { activityFeedsService } from '../../api/activityFeedsService'; export const GlobalFeed: React.FC = () => { const { setAssets, setAssetTypes, addTab, tabs, selectedTabIndex, updateTab, sites, setSites, selectedSite, setSelectedSite, auditStatus, isInitialized, setIsInitialized, lastFetchedFilterKey, setLastFetchedFilterKey } = useFeedStore(); const { user } = useAuthStore(); // Ref to skip the first run of the site/auditStatus effect const isFirstRender = React.useRef(true); useEffect(() => { // Initial Load const loadInitialData = async () => { try { const assetRes: any = await activityFeedsService.getAssets(); if (assetRes && assetRes.asset_types) { // Map assets mimicking the Angular logic let flattenedAssets: any[] = []; assetRes.asset_types.forEach((type: any) => { const mapped = type.assets.map((a: any) => ({ id: a.asset_id, name: a.asset_name, asset_type_id: type.type_id })); flattenedAssets = [...flattenedAssets, ...mapped]; }); setAssets(flattenedAssets); setAssetTypes(assetRes.asset_types); // store raw grouped data for filters dialog } // Fetch Sites let defaultSite = null; if (user?.org_id) { try { const sitesRes: any = await activityFeedsService.getOrganizationSites(user.org_id); let parsedSites: any[] = []; if (sitesRes?.responseData?.[0]?.records) { parsedSites = sitesRes.responseData[0].records; // Handle nested array response [ [ {site1}, {site2} ] ] if (parsedSites.length === 1 && Array.isArray(parsedSites[0])) { parsedSites = parsedSites[0]; } else if (parsedSites.length > 0 && Array.isArray(parsedSites[0])) { parsedSites = parsedSites.flat(); } } else if (Array.isArray(sitesRes)) { parsedSites = sitesRes; } else if (sitesRes?.data && Array.isArray(sitesRes.data)) { parsedSites = sitesRes.data; } if (parsedSites.length > 0) { // Filter to ensure we only show sites for the currently selected org parsedSites = parsedSites.filter(site => { let siteOrgId = site.org_id; if (siteOrgId === undefined && site.sub_sites && site.sub_sites.length > 0) { siteOrgId = site.sub_sites[0].org_id; } if (siteOrgId !== undefined && siteOrgId !== null) { return String(siteOrgId) === String(user.org_id); } return true; }).map(site => { // Normalize site_id: if missing, aggregate from sub_sites if (!site.site_id && site.sub_sites && site.sub_sites.length > 0) { site.site_id = site.sub_sites.map((sub: any) => sub.site_id).filter(Boolean).join(','); } return site; }); setSites(parsedSites); defaultSite = { site_id: 'All Sites', site_name: 'All Sites' }; setSelectedSite(defaultSite); } } catch (siteErr) { console.error("Failed to fetch sites", siteErr); } } // Add default Unaudited tab if empty and fetch data if (useFeedStore.getState().tabs.length === 0) { addTab({ date: '', // Unaudited isRectificationTab: false, totalAnomalyCount: 0, anomalies: [], anomaliesCopy: [], selectedAnomalyIndex: 0, filters: {}, page: 0, rowsPerPage: 20 }); try { const historyRes: any = await activityFeedsService.getHistory({ date: '', pageNo: 0, pageSize: 20, siteIds: defaultSite?.site_id === 'All Sites' ? '' : (defaultSite?.site_id || ''), isTrueFalse: auditStatus === 'False Audits' ? 'true' : 'both', auto_audit: false, isRectificationEnabled: false }); if (historyRes && historyRes.anomalies) { updateTab(0, { anomalies: historyRes.anomalies, anomaliesCopy: historyRes.anomalies, totalAnomalyCount: historyRes.count || historyRes.anomalies.length }); } } catch (historyErr) { console.error("[GlobalFeed] Failed to load history", historyErr); } } } catch (error) { console.error("Failed to load initial feed data", error); } finally { setIsInitialized(true); // Mark as initialized regardless, to prevent retry loops } }; // GUARD: Only run initial load once across tab switches if (useFeedStore.getState().isInitialized) { return; } loadInitialData(); }, []); // eslint-disable-line react-hooks/exhaustive-deps // Fetch data when selectedSite or auditStatus changes useEffect(() => { // GUARD: Skip if tabs are not initialized (initial load hasn't finished) if (useFeedStore.getState().tabs.length === 0) { return; } // Build a key for the current filter state const filterKey = `${selectedSite?.site_id ?? ''}|${auditStatus}`; // CACHE HIT: This exact filter combo was already fetched — skip re-fetch on remount if (filterKey === lastFetchedFilterKey) { return; } const fetchFilteredHistory = async () => { const state = useFeedStore.getState(); const currentTabIndex = state.selectedTabIndex > -1 ? state.selectedTabIndex : 0; const activeTab = state.tabs[currentTabIndex]; const siteIdParam = (!selectedSite || selectedSite?.site_id === 'All Sites') ? '' : String(selectedSite.site_id || ''); try { const historyRes: any = await activityFeedsService.getHistory({ date: activeTab?.date || '', pageNo: 0, pageSize: 20, siteIds: siteIdParam, isTrueFalse: auditStatus === 'False Audits' ? 'true' : 'both', auto_audit: false, isRectificationEnabled: false, ...(activeTab?.filters || {}) }); if (historyRes && historyRes.anomalies) { useFeedStore.getState().updateTab(currentTabIndex, { anomalies: historyRes.anomalies, anomaliesCopy: historyRes.anomalies, totalAnomalyCount: historyRes.count || historyRes.anomalies.length }); setLastFetchedFilterKey(filterKey); // mark this combo as fetched } } catch (err) { console.error('[GlobalFeed] Failed to load history for site/audit status change', err); } }; fetchFilteredHistory(); // Use primitive/stable values to avoid spurious re-runs. // selectedSite?.site_id and auditStatus are both strings/primitives. }, [selectedSite?.site_id, auditStatus]); // eslint-disable-line react-hooks/exhaustive-deps const handlePageChange = async (event: unknown, newPage: number) => { if (selectedTabIndex < 0 || tabs.length === 0) return; const activeTab = tabs[selectedTabIndex]; const allSiteIds = sites.map(s => s.site_id).filter(Boolean).join(','); const rowsPerPage = activeTab.rowsPerPage || 20; try { const historyRes: any = await activityFeedsService.getHistory({ date: activeTab?.date || '', pageNo: newPage * rowsPerPage, pageSize: rowsPerPage, siteIds: selectedSite?.site_id === 'All Sites' ? '' : (selectedSite?.site_id || ''), isTrueFalse: auditStatus === 'False Audits' ? 'true' : 'both', auto_audit: false, isRectificationEnabled: false, ...(activeTab?.filters || {}) }); if (historyRes && historyRes.anomalies) { updateTab(selectedTabIndex, { anomalies: historyRes.anomalies, anomaliesCopy: historyRes.anomalies, totalAnomalyCount: historyRes.count || historyRes.anomalies.length, page: newPage, }); } } catch (err) { console.error("Failed to load history for page change", err); } }; const handleRowsPerPageChange = async (event: React.ChangeEvent) => { if (selectedTabIndex < 0 || tabs.length === 0) return; const activeTab = tabs[selectedTabIndex]; const allSiteIds = sites.map(s => s.site_id).filter(Boolean).join(','); const newRowsPerPage = parseInt(event.target.value, 10); const newPage = 0; try { const historyRes: any = await activityFeedsService.getHistory({ date: activeTab?.date || '', pageNo: newPage * newRowsPerPage, pageSize: newRowsPerPage, siteIds: selectedSite?.site_id === 'All Sites' ? '' : (selectedSite?.site_id || ''), isTrueFalse: auditStatus === 'False Audits' ? 'true' : 'both', auto_audit: false, isRectificationEnabled: false, ...(activeTab?.filters || {}) }); if (historyRes && historyRes.anomalies) { updateTab(selectedTabIndex, { anomalies: historyRes.anomalies, anomaliesCopy: historyRes.anomalies, totalAnomalyCount: historyRes.count || historyRes.anomalies.length, page: newPage, rowsPerPage: newRowsPerPage, }); } } catch (err) { console.error("Failed to load history for rowsPerPage change", err); } }; return ( {/* Left side Table */} {/* Right side Preview Panel */} ); };