@@ -357,6 +357,8 @@ export function DashboardClient() {
357357 const userOperationsRefreshInFlightRef = useRef ( false ) ;
358358 const userOperationsEventCursorRef = useRef ( 0 ) ;
359359 const userOperationsInteractionUntilRef = useRef ( 0 ) ;
360+ const adminOperationsRefreshInFlightRef = useRef ( false ) ;
361+ const adminOperationsEventCursorRef = useRef ( 0 ) ;
360362 const shownBinFullAlertRanksRef = useRef < Map < string , number > > ( new Map ( ) ) ;
361363
362364 const cameraStream = useMemo ( ( ) => {
@@ -909,6 +911,29 @@ export function DashboardClient() {
909911 setOperationsHealth ( healthData ) ;
910912 }
911913
914+ async function refreshAdminRealtimeOperations ( ) {
915+ if ( adminOperationsRefreshInFlightRef . current ) {
916+ return ;
917+ }
918+ adminOperationsRefreshInFlightRef . current = true ;
919+ try {
920+ const [ binMapData , alertsData , schedulesData ] = await Promise . all ( [
921+ cloudFetch < BinMapResponse > ( "/api/admin/bin-map" , { timeoutMs : 45_000 } , agentToken ) ,
922+ cloudFetch < AlertsResponse > ( "/api/admin/alerts?include_resolved=false" , { timeoutMs : 45_000 } , agentToken ) ,
923+ cloudFetch < CollectionSchedulesResponse > ( "/api/admin/collection-schedules" , { timeoutMs : 45_000 } , agentToken )
924+ ] ) ;
925+ setAdminBinMap ( binMapData ) ;
926+ setAdminAlerts ( alertsData ) ;
927+ showNewBinFullnessPopup ( alertsData , "admin" ) ;
928+ setAdminSchedules ( schedulesData ) ;
929+ } catch {
930+ // The full Admin refresh remains available from the map button if a
931+ // transient realtime request fails.
932+ } finally {
933+ adminOperationsRefreshInFlightRef . current = false ;
934+ }
935+ }
936+
912937 async function refreshUserOperations ( options ?: { background ?: boolean } ) {
913938 if ( userOperationsRefreshInFlightRef . current ) {
914939 return ;
@@ -1777,6 +1802,58 @@ export function DashboardClient() {
17771802 } ;
17781803 } , [ agentToken , auth ?. role , auth ?. password_default , userView ] ) ;
17791804
1805+ useEffect ( ( ) => {
1806+ const refreshableAdminTabs : TabId [ ] = [ "bin-map" , "alerts" , "devices" , "reports" ] ;
1807+ if ( auth ?. role !== "admin" || auth . password_default || ! agentToken || ! refreshableAdminTabs . includes ( active ) ) {
1808+ return ;
1809+ }
1810+ let cancelled = false ;
1811+ let eventRequestInFlight = false ;
1812+ adminOperationsEventCursorRef . current = 0 ;
1813+ const pollOperationEvents = async ( ) => {
1814+ if ( cancelled || eventRequestInFlight || document . visibilityState === "hidden" ) {
1815+ return ;
1816+ }
1817+ eventRequestInFlight = true ;
1818+ try {
1819+ const payload = await cloudFetch < OperationEventsResponse > (
1820+ `/api/admin/operation-events?after=${ adminOperationsEventCursorRef . current } ` ,
1821+ { timeoutMs : 8000 } ,
1822+ agentToken
1823+ ) ;
1824+ if ( cancelled ) return ;
1825+ adminOperationsEventCursorRef . current = payload . cursor ;
1826+ if ( payload . changed ) {
1827+ void refreshAdminRealtimeOperations ( ) ;
1828+ }
1829+ } catch {
1830+ // The six-second fallback below keeps the Admin map current when the
1831+ // event cursor is briefly unavailable.
1832+ } finally {
1833+ eventRequestInFlight = false ;
1834+ }
1835+ } ;
1836+ void pollOperationEvents ( ) ;
1837+ const eventTimer = window . setInterval ( ( ) => void pollOperationEvents ( ) , 1200 ) ;
1838+ return ( ) => {
1839+ cancelled = true ;
1840+ window . clearInterval ( eventTimer ) ;
1841+ } ;
1842+ } , [ active , agentToken , auth ?. role , auth ?. password_default ] ) ;
1843+
1844+ useEffect ( ( ) => {
1845+ const refreshableAdminTabs : TabId [ ] = [ "bin-map" , "alerts" , "devices" , "reports" ] ;
1846+ if ( auth ?. role !== "admin" || auth . password_default || ! agentToken || ! refreshableAdminTabs . includes ( active ) ) {
1847+ return ;
1848+ }
1849+ const timer = window . setInterval ( ( ) => {
1850+ if ( document . visibilityState !== "hidden" ) {
1851+ void refreshAdminRealtimeOperations ( ) ;
1852+ }
1853+ } , 6000 ) ;
1854+ return ( ) => window . clearInterval ( timer ) ;
1855+ } , [ active , agentToken , auth ?. role , auth ?. password_default ] ) ;
1856+
17801857 useEffect ( ( ) => {
17811858 const refreshableUserViews : UserView [ ] = [ "dashboard" , "map" , "alerts" , "schedule" , "collect" , "report-issue" ] ;
17821859 if ( auth ?. role !== "user" || auth . password_default || ! agentToken || ! refreshableUserViews . includes ( userView ) ) {
0 commit comments