import BashExecModal from '../BashExecModal'; import { PolicyBlockDialog } from '../stack/PolicyBlockDialog'; import { PreDeployScanDialog } from '../stack/PreDeployScanDialog'; import { MissingExternalNetworksDialog } from '../stack/MissingExternalNetworksDialog'; import { UpdateReadinessDialog } from '../stack/UpdateReadinessDialog'; import { SelfStackProtectedDialog } from '../stack/SelfStackProtectedDialog'; import { LocalUpdateConfirmDialog } from '../FleetView/LocalUpdateConfirmDialog'; import { ReconnectingOverlay } from '../FleetView/ReconnectingOverlay'; import { DeleteStackDialog } from './DeleteStackDialog'; import { TakeDownStackDialog } from './TakeDownStackDialog'; import { UnsavedChangesDialog } from './UnsavedChangesDialog'; import { StackAlertSheet } from '../StackAlertSheet'; import { GitSourcePanel } from '../stack/GitSourcePanel'; import { LogViewer } from '../LogViewer'; import { VulnerabilityScanSheet } from '../VulnerabilityScanSheet'; import { ComposeDiffPreviewDialog } from '@/components/ComposeDiffPreviewDialog'; import { resolveComposeDiffActionLabel } from '@/components/resolveComposeDiffActionLabel'; import type { OverlayState } from './hooks/useOverlayState'; import type { StackActionsHook } from './hooks/useStackActions'; import type { PermissionAction } from '@/context/AuthContext'; import type { useComposeReapplyAction } from '../FleetView/hooks/useComposeReapplyAction'; interface ShellOverlaysProps { overlayState: OverlayState; stackActions: StackActionsHook; isDarkMode: boolean; isAdmin: boolean; can: (action: PermissionAction, resourceType?: string, resourceId?: string, nodeId?: number | null) => boolean; selectedFile: string | null; stackName: string; activeNodeId: number | null; gitSourceOpen: boolean; setGitSourceOpen: (open: boolean) => void; canSelfUpdate: boolean; composeReapply: ReturnType; canSaveAndReapply: boolean; canOfferVolumeRemoval: boolean; onOpenFleetNodeUpdates: () => void; } export function ShellOverlays({ overlayState, stackActions, isDarkMode, isAdmin, can, selectedFile, stackName, activeNodeId, gitSourceOpen, setGitSourceOpen, canSelfUpdate, composeReapply, canSaveAndReapply, canOfferVolumeRemoval, onOpenFleetNodeUpdates, }: ShellOverlaysProps) { const { deleteDialogOpen, closeDeleteDialog, stackToDelete, takeDownDialogOpen, closeTakeDownDialog, stackToTakeDown, pendingUnsavedLoad, pendingLeaveAction, bashModalOpen, selectedContainer, logViewerOpen, logContainer, stackMonitor, closeStackMonitor, policyBlock, setPolicyBlock, policyBypassing, updateReadiness, setUpdateReadiness, preDeployAdvisory, missingExternalNetworks, setMissingExternalNetworks, selfStackProtectedOpen, setSelfStackProtectedOpen, composeReapplyCapture, setComposeReapplyCapture, stackMisconfigScanId, setStackMisconfigScanId, diffPreview, setDiffPreview, diffPreviewConfirming, setDiffPreviewConfirming, } = overlayState; return ( <> { if (!open) closeDeleteDialog(); }} stackName={stackToDelete} onConfirm={stackActions.deleteStack} /> { if (!open) closeTakeDownDialog(); }} stackName={stackToTakeDown} showVolumeOption={canOfferVolumeRemoval} onConfirm={stackActions.takeDownStack} /> { if (!open) setComposeReapplyCapture(null); }} onConfirm={() => { const capture = composeReapplyCapture; setComposeReapplyCapture(null); if (!capture || composeReapply.dispatching) return; void composeReapply.runReapply({ nodeId: capture.nodeId, type: capture.nodeType, name: capture.nodeName, }); }} /> {composeReapply.reconnecting && ( )} {/* Bash Exec Modal */} {selectedContainer && ( )} {/* LogViewer Modal */} {logContainer && ( )} {/* Stack monitor (alerts + auto-heal as tabs) */} { if (!open) closeStackMonitor(); }} stackName={stackMonitor?.stackName ?? ''} initialTab={stackMonitor?.tab ?? 'alerts'} initialService={stackMonitor?.serviceName} /> {/* Pre-update readiness check */} setUpdateReadiness(null)} onProceed={() => updateReadiness?.proceed()} /> {/* Pre-deploy scan advisory (visibility only; never blocks) */} preDeployAdvisory?.cancel()} onDeploy={() => preDeployAdvisory?.proceed()} /> { missingExternalNetworks?.cancel(); setMissingExternalNetworks(null); }} onOpenNetworking={() => { missingExternalNetworks?.openNetworking(); setMissingExternalNetworks(null); }} onCreateAndContinue={() => { void missingExternalNetworks?.createAndContinue(); }} /> {/* Pre-deploy policy block */} setPolicyBlock(null)} onBypass={stackActions.bypassPolicyAndRetry} /> {/* Git Source Panel */} {stackName && ( )} {/* Stack config misconfig scan results */} setStackMisconfigScanId(null)} canManageSuppressions={can('stack:edit')} /> {/* Compose diff preview */} { if (!open && !diffPreviewConfirming) setDiffPreview(null); }} stackName={selectedFile ? selectedFile.replace(/\.(yml|yaml)$/, '') : ''} fileName={diffPreview?.fileName ?? ''} language={diffPreview?.language ?? 'yaml'} original={diffPreview?.original ?? ''} modified={diffPreview?.modified ?? ''} actionLabel={resolveComposeDiffActionLabel(diffPreview?.mode, canSaveAndReapply)} confirming={diffPreviewConfirming} isDarkMode={isDarkMode} onConfirm={async () => { const snapshot = diffPreview; setDiffPreviewConfirming(true); try { if (snapshot?.mode === 'save-and-deploy') { const saved = await stackActions.saveFile(); if (saved) await stackActions.deployStack(); } else { await stackActions.saveFile(); } } finally { setDiffPreviewConfirming(false); setDiffPreview(null); } }} /> ); }