Files
resolutionflow/frontend/src/components/tree-editor/TreeEditorLayout.tsx
chihlasm 48f2b3faaf fix: stale chunk auto-reload + image paste upload UX
- Add lazyWithRetry wrapper for all lazy-loaded routes to auto-reload
  on stale chunk errors after deploys (prevents ErrorBoundary flash)
- Show toast notification when image paste/upload fails due to storage
  not configured (503), instead of silent tiny error thumbnails
- Remove failed uploads from thumbnail strip on 503 (was showing
  confusing retry icon)
- Pass completed upload IDs in navigation state from dashboard input
- Suppress Sentry dialog for chunk load errors (deploy artifacts)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 04:21:41 +00:00

106 lines
3.3 KiB
TypeScript

import { Suspense } from 'react'
import { lazyWithRetry } from '@/lib/lazyWithRetry'
import { TreePreviewPanel } from '@/components/tree-preview/TreePreviewPanel'
import { FlowCanvas } from './FlowCanvas'
import { NodeEditorPanel } from './NodeEditorPanel'
import { MetadataSidePanel } from './MetadataSidePanel'
import { useTreeEditorStore } from '@/store/treeEditorStore'
import { cn } from '@/lib/utils'
import { Spinner } from '@/components/common/Spinner'
// Lazy load CodeModeEditor (Monaco is ~2MB)
const CodeModeEditor = lazyWithRetry(() =>
import('./code-mode/CodeModeEditor').then(m => ({ default: m.CodeModeEditor }))
)
interface TreeEditorLayoutProps {
isMobile?: boolean
isMetadataOpen?: boolean
onCloseMetadata?: () => void
editingNodeId: string | null
onNodeSelect: (nodeId: string | null) => void
onSelectAnswerType: (nodeId: string, type: 'decision' | 'action' | 'solution') => void
onNodeDelete?: (nodeId: string) => void
onNodeContextMenu?: (e: React.MouseEvent, nodeId: string) => void
}
export function TreeEditorLayout({
isMobile = false,
isMetadataOpen = false,
onCloseMetadata = () => {},
editingNodeId,
onNodeSelect,
onSelectAnswerType,
onNodeDelete,
onNodeContextMenu,
}: TreeEditorLayoutProps) {
const editorMode = useTreeEditorStore(s => s.editorMode)
return (
<div
className={cn(
'flex min-h-0 flex-1 overflow-hidden',
isMobile ? 'flex-col' : 'flex-row'
)}
>
{editorMode === 'code' ? (
<>
{/* Code Mode: Monaco editor (60%) + Preview (40%) — unchanged */}
<div className={cn(
'flex flex-col overflow-hidden border-border',
isMobile ? 'h-full w-full border-b' : 'w-3/5 border-r'
)}>
<Suspense fallback={
<div className="flex h-full items-center justify-center bg-card">
<Spinner size="sm" className="h-6 w-6 border-t-foreground" />
</div>
}>
<CodeModeEditor />
</Suspense>
</div>
{/* Right Panel - Preview */}
<div className={cn(
'flex-1 overflow-hidden bg-accent/50',
isMobile ? 'hidden' : 'block'
)}>
<TreePreviewPanel />
</div>
</>
) : (
<>
{/* Flow Mode: React Flow canvas + side panels */}
<div className="flex-1 overflow-hidden">
<FlowCanvas
selectedNodeId={editingNodeId}
onNodeSelect={onNodeSelect}
onSelectAnswerType={onSelectAnswerType}
onNodeDelete={onNodeDelete}
onNodeContextMenu={onNodeContextMenu}
/>
</div>
{/* Node editor panel — takes real layout space */}
{editingNodeId && (
<NodeEditorPanel
nodeId={editingNodeId}
onClose={() => onNodeSelect(null)}
onSelectType={onSelectAnswerType}
/>
)}
{/* Metadata side panel — only show when node editor is closed */}
{!editingNodeId && (
<MetadataSidePanel
isOpen={isMetadataOpen}
onClose={onCloseMetadata}
/>
)}
</>
)}
</div>
)
}
export default TreeEditorLayout