refactor(sidebar): rebuild footer as priority-driven Ops Pulse strip (#1178)

* refactor(sidebar): rebuild footer as priority-driven Ops Pulse strip

Replace the simple notification ticker with a derived activity summary that
picks one of six states (active-op, failure, automation, recent-event,
quiet-live, disconnected) and routes per-state clicks to logs, schedules,
or activity. The hook owns the cascade; the component is pure presentation;
EditorLayout owns wiring.

Failure detection covers unread errors in the last 24h; recent-event is
limited to non-error stack notifications in the last hour; automation reads
the next /scheduled-tasks?action=update run and a debounced state-invalidate
listener; the deploy-panel composite key is used for elapsed-time tracking
so close-then-immediately-reopen counts as a new session.

* refactor(sidebar): apply Ops Pulse audit fixes

- countEnabledAutoUpdates now defaults missing autoUpdateSettings entries to
  enabled, matching the backend's getStackAutoUpdateSettingsForNode contract.
  Previously the automation state could not render even with the documented
  per-row default-true.
- findFailure now requires a stack_name so the sidebar does not select a
  system-level error whose click would no-op through navigateToNotification.
  System errors continue to surface via the top-bar NotificationPanel.
- DeployPanelState gains a monotonic sessionId sourced from the existing
  internal counter, and the new usePanelSessionStartedAt hook keys the
  elapsed-time tracker off it so a same-stack rerun always resets even when
  isOpen stays true across succeeded then preparing.
- buildConfig splits quiet-live out of the default and adds an exhaustiveness
  guard so future SidebarActivitySummary variants fail to compile.
- New unit tests cover the default-true aggregation, the same-stack session
  reset, the non-stack failure guard, and the useNextAutoUpdateRun debounce
  and cleanup paths. Frontend suite: 276 / 276 pass.
This commit is contained in:
Anso
2026-05-23 15:43:06 -04:00
committed by GitHub
parent 21ec5e7e0a
commit bb44db0cb1
11 changed files with 901 additions and 83 deletions
+44 -5
View File
@@ -1,4 +1,4 @@
import { useEffect, useRef } from 'react';
import { useCallback, useEffect, useMemo, useRef } from 'react';
import { Button } from './ui/button';
import { Plus } from 'lucide-react';
import { UserProfileDropdown } from './UserProfileDropdown';
@@ -32,6 +32,10 @@ import { useDeployFeedback } from '@/context/DeployFeedbackContext';
import { useTrivyStatus } from '@/hooks/useTrivyStatus';
import { StackSidebar } from '@/components/sidebar/StackSidebar';
import type { StackRowStatus } from '@/components/sidebar/stack-status-utils';
import { useSidebarActivitySummary, countEnabledAutoUpdates } from '@/components/sidebar/useSidebarActivitySummary';
import { useNextAutoUpdateRun } from '@/components/sidebar/useNextAutoUpdateRun';
import { usePanelSessionStartedAt } from '@/components/sidebar/usePanelSessionStartedAt';
import type { SidebarActivityAction } from '@/components/sidebar/SidebarActivityTicker';
import { useComposeDiffPreviewEnabled } from '@/hooks/use-compose-diff-preview-enabled';
import { toast } from '@/components/ui/toast-store';
@@ -39,7 +43,7 @@ export default function EditorLayout() {
const { isAdmin, can } = useAuth();
const { isPaid, license } = useLicense();
const { status: trivy } = useTrivyStatus();
const { runWithLog } = useDeployFeedback();
const { runWithLog, panelState } = useDeployFeedback();
const editorState = useEditorViewState();
const {
@@ -64,6 +68,7 @@ export default function EditorLayout() {
const stackListState = useStackListState();
const {
files,
selectedFile,
isLoading,
stackActions: stackActionMap,
@@ -71,6 +76,7 @@ export default function EditorLayout() {
searchQuery, setSearchQuery,
stackStatuses,
stackLabelMap,
autoUpdateSettings,
filterChip, setFilterChip,
bulkMode,
selectedFiles,
@@ -178,6 +184,40 @@ export default function EditorLayout() {
pendingLogsRef,
} = stackActions;
const panelStartedAt = usePanelSessionStartedAt(panelState);
const autoUpdateEnabledCount = useMemo(
() => countEnabledAutoUpdates(files, autoUpdateSettings),
[files, autoUpdateSettings],
);
const nextAutoUpdateRunAt = useNextAutoUpdateRun();
const activitySummary = useSidebarActivitySummary({
notifications,
tickerConnected,
panelState,
panelStartedAt,
autoUpdateEnabledCount,
totalStackCount: files.length,
nextAutoUpdateRunAt,
});
const handleActivityAction = useCallback((action: SidebarActivityAction) => {
switch (action.kind) {
case 'open-stack-notification':
stackActions.navigateToNotification(action.summary.notif);
return;
case 'open-auto-updates':
setActiveView('auto-updates');
return;
case 'open-activity':
setActiveView('global-observability');
return;
case 'noop':
return;
}
}, [stackActions, setActiveView]);
const loadingAction = selectedFile ? (stackActionMap[selectedFile] ?? null) : null;
const stackName = selectedFile || '';
@@ -303,9 +343,8 @@ export default function EditorLayout() {
if (node) void stackActions.loadFileOnNode(node, file);
},
}}
notifications={notifications}
tickerConnected={tickerConnected}
onOpenActivity={() => setActiveView('global-observability')}
activitySummary={activitySummary}
onActivityAction={handleActivityAction}
bulkMode={bulkMode}
selectedFiles={selectedFiles}
isPaid={isPaid}