import { useState } from 'react'; import { ChevronLeft } from 'lucide-react'; import { cn } from '@/lib/utils'; import ErrorBoundary from '../ErrorBoundary'; import StackAnatomyPanel from '../StackAnatomyPanel'; import { MobileComposeEditor } from './MobileComposeEditor'; import { StackIdentityHeader, ContainersHealth, StackLogsSection } from './editor-view-blocks'; import { RecoveryPanel } from './RecoveryPanel'; import { StackOperationBanner } from './StackOperationBanner'; import { retryHandlerFor } from './recovery-retry'; import type { EditorViewProps } from './EditorView'; const SEGMENTS = [ { id: 'health', label: 'Health' }, { id: 'logs', label: 'Logs' }, { id: 'compose', label: 'Compose' }, ] as const; type Segment = (typeof SEGMENTS)[number]['id']; // Full-screen stack detail for the mobile shell (below md). The desktop // two-pane grid does not fit a phone, so the same identity header, container // health, logs, and anatomy are reorganized into a tracked-mono segmented // control. Logs is the default segment (first-read, without copying Dockge's // layout). From the Compose segment, an editor with stack:edit can open the // full-screen MobileComposeEditor for small, safe compose/.env edits. export function MobileStackDetail(props: EditorViewProps) { const { stackName, activeNode, containers, containerStats, containerStatsError, content, envContent, envExists, envFiles, selectedEnvFile, isFileLoading, gitSourcePendingMap, notifications, loadingAction, stackMisconfigScanning, can, isAdmin, trivy, backupInfo, logsMode, activeTab, editingCompose, deployStack, restartStack, stopStack, updateStack, rollbackStack, scanStackConfig, requestSave, requestSaveAndDeploy, setContent, setEnvContent, changeEnvFile, openLogViewer, openBashModal, onInspectImage, onOpenMonitor, onOpenServiceMonitor, serviceAction, effectiveServices = [], serviceUpdateStatuses, serviceUpdateInProgress, onRequestServiceUpdate, setLogsMode, setActiveTab, setGitSourceOpen, requestDeleteStack, requestTakeDownStack, showTakeDown, isSelfStack = false, canSaveAndReapply = false, onMobileBack, onCloseEditor, hasUnsavedChanges, headerActions, recoveryResult, onRefreshState, onDismissRecovery, panelStartedAt, stackMuteActions, openComposeEditor, } = props; const [segment, setSegment] = useState('logs'); const safeContainers = containers || []; const isMultiContainerLayout = safeContainers.length > 1 || effectiveServices.length > 1; const isRunning = safeContainers.some(c => c.State === 'running'); const canEditStack = can('stack:edit', 'stack', stackName, activeNode?.id); // The writable editor layer renders only for an editor; a stale editingCompose // while the user lacks stack:edit falls back to the read-only Compose segment. if (editingCompose && canEditStack) { return ( ); } return (
{/* Detail header: back to list + identity + action bar */}
{headerActions ?
{headerActions}
: null}
{recoveryResult && loadingAction == null && (
)} {/* Segmented control: Health · Logs · Compose */}
{SEGMENTS.map(seg => { const on = seg.id === segment; return ( ); })}
{/* Active segment */}
{segment === 'health' && (
)} {segment === 'logs' && ( )} {segment === 'compose' && (
setGitSourceOpen(true)} onApplyUpdate={() => { void updateStack(); }} applying={loadingAction === 'update'} canEdit={canEditStack} notifications={notifications} />
)}
); }