diff --git a/src/components/modals/DialogFrame.tsx b/src/components/modals/DialogFrame.tsx index c7ee38b..dcdfd9f 100644 --- a/src/components/modals/DialogFrame.tsx +++ b/src/components/modals/DialogFrame.tsx @@ -95,7 +95,7 @@ export function DialogFrame({ >
@@ -166,7 +166,7 @@ export function DialogFrame({ {footer && (
void; + schemaUrl: string; + title: ReactNode; + description?: ReactNode; + icon?: ReactNode; + size?: DialogFrameSize; + sourceLabel?: string; + previewSummary?: ReactNode; + backgroundNotice?: ReactNode; +} + +export function ModelViewDialog({ + isOpen, + onClose, + schemaUrl, + title, + description, + icon, + size = 'full', + sourceLabel = 'Open source', + previewSummary = 'SVG preview of the current backend schema.', + backgroundNotice = 'Some hosts block embedded frame previews. This dialog uses image preview mode for compatibility.', +}: ModelViewDialogProps) { + // Schema diagrams range from ~520px to ~1830px wide, so the preview fits the + // stage by default and only scrolls at natural size when the user opts in. + const [isActualSize, setIsActualSize] = useState(false); + + const handleClose = () => { + setIsActualSize(false); + onClose(); + }; + + const altText = typeof title === 'string' ? title : 'Backend model schema'; + + return ( + } + size={size} + // Bound the panel to the viewport (the frame adds p-4 around it) and lay it + // out as a column so only the preview stage absorbs the leftover height. + panelClassName='flex max-h-[calc(100dvh-2rem)] flex-col' + bodyClassName='flex min-h-0 flex-col space-y-0 p-0' + > +
+

+ {previewSummary} +

+ +
+ + + + {sourceLabel} + + +
+
+ + {/* basis-[70vh] is the preferred stage height; min-h-0 lets it shrink + instead of pushing the panel past its max-height on short viewports. */} +
+ {isActualSize ? ( + // min-w-max keeps the centred diagram from overflowing past the + // scroll container's start edge, which would make it unreachable. + + + + ) : ( + // Absolute inset-0 gives the image a definite box in both axes, so + // object-contain can scale the diagram down to fit rather than clip. + + + + )} +
+ +
+

{backgroundNotice}

+
+
+ ); +} diff --git a/src/features/cases/components/CasesInfoDrawer.tsx b/src/features/cases/components/CasesInfoDrawer.tsx index ea3120c..f49b281 100644 --- a/src/features/cases/components/CasesInfoDrawer.tsx +++ b/src/features/cases/components/CasesInfoDrawer.tsx @@ -1,7 +1,8 @@ import { useState } from 'react'; -import { Briefcase, FolderSync } from 'lucide-react'; +import { Briefcase, Database, FolderSync } from 'lucide-react'; import { DrawerFrame } from '@/components/modals/DrawerFrame'; import { InfoDrawerActionCard } from '@/components/modals/InfoDrawerActionCard'; +import { ModelViewDialog } from '@/components/modals/ModelViewDialog'; import { formatTableDate } from '@/utils/timeFormat'; import { useCaseSyncFromRedcapAutoHistoryModel } from '../api/cases.api'; // Manual case creation is temporarily unavailable because the Case Manager API @@ -16,12 +17,16 @@ interface CasesInfoDrawerProps { onClose: () => void; } +const CASE_MANAGER_SCHEMA_SVG_URL = + 'https://raw.githubusercontent.com/OrcaBus/service-case-manager/refs/heads/main/case-manager/docs/schema.drawio.svg'; + export function CasesInfoDrawer({ isOpen, onClose }: CasesInfoDrawerProps) { // Manual case creation is temporarily unavailable. Preserve this state for // restoration alongside AddCaseModal when POST /case/ returns. // const [showAddCaseModal, setShowAddCaseModal] = useState(false); const [showAutoImportModal, setShowAutoImportModal] = useState(false); const [showSyncHistoryModal, setShowSyncHistoryModal] = useState(false); + const [showSchemaPreviewModal, setShowSchemaPreviewModal] = useState(false); const { data: syncHistoryData, @@ -78,6 +83,15 @@ export function CasesInfoDrawer({ isOpen, onClose }: CasesInfoDrawerProps) { buttonIcon={} variant='secondary' /> + setShowSchemaPreviewModal(true)} + icon={} + buttonIcon={} + variant='secondary' + /> {/* Manual case creation is temporarily unavailable because the Case Manager API no longer exposes POST /case/. Restore this card with AddCaseModal when collection create support returns. @@ -105,6 +119,17 @@ export function CasesInfoDrawer({ isOpen, onClose }: CasesInfoDrawerProps) { isOpen={showAutoImportModal} onClose={() => setShowAutoImportModal(false)} /> + + setShowSchemaPreviewModal(false)} + schemaUrl={CASE_MANAGER_SCHEMA_SVG_URL} + title='Case Manager Entity Schema' + description='Case Manager model entity diagram from service-case-manager.' + icon={} + previewSummary='SVG preview of the current Case Manager model schema.' + backgroundNotice='This source is theme-aware, so dark mode can render a dark canvas background.' + /> ); } diff --git a/src/features/cases/components/__tests__/CaseDetailsOverviewCard.test.tsx b/src/features/cases/components/__tests__/CaseDetailsOverviewCard.test.tsx index 9a1e078..1ae79d4 100644 --- a/src/features/cases/components/__tests__/CaseDetailsOverviewCard.test.tsx +++ b/src/features/cases/components/__tests__/CaseDetailsOverviewCard.test.tsx @@ -23,6 +23,7 @@ const caseDetail: CaseDetailModel = { isNataAccredited: true, alias: ['CASE-ALIAS'], links: {}, + redcapPayload: {}, dueDate: '2026-08-31', externalEntitySet: [], userSet: [], diff --git a/src/features/cases/components/__tests__/EditCaseModal.test.tsx b/src/features/cases/components/__tests__/EditCaseModal.test.tsx index ed5f04e..9c36488 100644 --- a/src/features/cases/components/__tests__/EditCaseModal.test.tsx +++ b/src/features/cases/components/__tests__/EditCaseModal.test.tsx @@ -41,6 +41,7 @@ const caseDetail: CaseDetailModel = { isNataAccredited: false, alias: ['CASE-ALIAS'], links: { trello: 'https://example.com/card' }, + redcapPayload: {}, dueDate: '2026-08-31', externalEntitySet: [], userSet: [], diff --git a/src/features/lab/root/components/LabInfoDrawer.tsx b/src/features/lab/root/components/LabInfoDrawer.tsx index 40e2430..166499f 100644 --- a/src/features/lab/root/components/LabInfoDrawer.tsx +++ b/src/features/lab/root/components/LabInfoDrawer.tsx @@ -1,7 +1,8 @@ import { useState } from 'react'; -import { LibraryBig, RefreshCw } from 'lucide-react'; +import { Database, LibraryBig, RefreshCw } from 'lucide-react'; import { DrawerFrame } from '@/components/modals/DrawerFrame'; import { InfoDrawerActionCard } from '@/components/modals/InfoDrawerActionCard'; +import { ModelViewDialog } from '@/components/modals/ModelViewDialog'; import { SyncMetadataModal } from './SyncMetadataModal'; interface LabInfoDrawerProps { @@ -9,8 +10,12 @@ interface LabInfoDrawerProps { onClose: () => void; } +const METADATA_MANAGER_SCHEMA_SVG_URL = + 'https://github.com/OrcaBus/service-metadata-manager/raw/refs/heads/main/metadata-manager/docs/schema.drawio.svg'; + export function LabInfoDrawer({ isOpen, onClose }: LabInfoDrawerProps) { const [showSyncModal, setShowSyncModal] = useState(false); + const [showModelViewModal, setShowModelViewModal] = useState(false); return ( <> @@ -32,7 +37,7 @@ export function LabInfoDrawer({ isOpen, onClose }: LabInfoDrawerProps) {

Actions

-
+
} buttonIcon={} /> + + setShowModelViewModal(true)} + icon={} + buttonIcon={} + variant='secondary' + />
setShowSyncModal(false)} /> + + setShowModelViewModal(false)} + schemaUrl={METADATA_MANAGER_SCHEMA_SVG_URL} + title='Metadata Manager Entity Schema' + description='Metadata Manager model entity diagram from service-metadata-manager.' + icon={} + previewSummary='SVG preview of the current Metadata Manager model schema.' + backgroundNotice='This source is theme-aware, so dark mode can render a dark canvas background.' + /> ); } diff --git a/src/features/runs/sequence-runs/pages/SequenceRunsPage.tsx b/src/features/runs/sequence-runs/pages/SequenceRunsPage.tsx index 1a9967e..9aa6223 100644 --- a/src/features/runs/sequence-runs/pages/SequenceRunsPage.tsx +++ b/src/features/runs/sequence-runs/pages/SequenceRunsPage.tsx @@ -9,6 +9,9 @@ import { buildSequenceRunsFilterBadges } from '../utils/buildSequenceRunsFilterB import { SequenceRunsStatusCards } from '../components/SequenceRunsStatusCards'; import SequenceRunsTable from '../components/SequenceRunsTable'; +const SEQUENCE_RUN_MANAGER_SCHEMA_SVG_URL = + 'https://raw.githubusercontent.com/OrcaBus/service-sequence-run-manager/refs/heads/main/docs/diagram/sequence-run-manager-entity-diagram.drawio.svg'; + export function SequenceRunsPage() { const title = 'Sequence Runs'; const description = 'Monitor instrument runs and sequencing run status.'; @@ -134,6 +137,13 @@ export function SequenceRunsPage() { onClose={closeInfoDrawer} title={title} description={description} + modelSchema={{ + url: SEQUENCE_RUN_MANAGER_SCHEMA_SVG_URL, + title: 'Sequence Run Manager Entity Schema', + description: + 'Sequence Run Manager model entity diagram from service-sequence-run-manager.', + previewSummary: 'SVG preview of the current Sequence Run Manager model schema.', + }} /> ); diff --git a/src/features/runs/shared/components/RunsInfoDrawer.tsx b/src/features/runs/shared/components/RunsInfoDrawer.tsx index bac0821..387ff2a 100644 --- a/src/features/runs/shared/components/RunsInfoDrawer.tsx +++ b/src/features/runs/shared/components/RunsInfoDrawer.tsx @@ -1,28 +1,79 @@ -import { Activity } from 'lucide-react'; +import { useState } from 'react'; +import { Activity, Database } from 'lucide-react'; import { DrawerFrame } from '@/components/modals/DrawerFrame'; +import { InfoDrawerActionCard } from '@/components/modals/InfoDrawerActionCard'; +import { ModelViewDialog } from '@/components/modals/ModelViewDialog'; interface RunsInfoDrawerProps { isOpen: boolean; onClose: () => void; title: string; description: string; + modelSchema?: { + url: string; + title: string; + description: string; + previewSummary: string; + }; } -export function RunsInfoDrawer({ isOpen, onClose, title, description }: RunsInfoDrawerProps) { +export function RunsInfoDrawer({ + isOpen, + onClose, + title, + description, + modelSchema, +}: RunsInfoDrawerProps) { + const [showModelViewModal, setShowModelViewModal] = useState(false); + return ( - } - size='md' - > -
-

Description

-

- {description} -

-
-
+ <> + } + size='md' + > +
+
+

Description

+

+ {description} +

+
+ + {modelSchema && ( +
+

Actions

+
+ setShowModelViewModal(true)} + icon={} + buttonIcon={} + variant='secondary' + /> +
+
+ )} +
+
+ + {modelSchema && ( + setShowModelViewModal(false)} + schemaUrl={modelSchema.url} + title={modelSchema.title} + description={modelSchema.description} + icon={} + previewSummary={modelSchema.previewSummary} + backgroundNotice='This source is theme-aware, so dark mode can render a dark canvas background.' + /> + )} + ); } diff --git a/src/features/runs/workflow-runs/pages/WorkflowRunsPage.tsx b/src/features/runs/workflow-runs/pages/WorkflowRunsPage.tsx index 7251c63..0313964 100644 --- a/src/features/runs/workflow-runs/pages/WorkflowRunsPage.tsx +++ b/src/features/runs/workflow-runs/pages/WorkflowRunsPage.tsx @@ -9,6 +9,9 @@ import WorkflowRunsTable from '../components/WorkflowRunsTable'; import { buildWorkflowRunsFilterBadges } from '../utils/buildWorkflowRunsFilterBadges'; import { WorkflowRunsStatsCards } from '../components/WorkflowRunsStatsCards'; +const WORKFLOW_MANAGER_SCHEMA_SVG_URL = + 'https://raw.githubusercontent.com/OrcaBus/service-workflow-manager/refs/heads/main/docs/diagrams/workflow-manager-entity-diagram.drawio.svg'; + export function WorkflowRunsPage() { const title = 'Workflow Runs'; const description = 'Monitor workflow executions, run states, and related operational metadata.'; @@ -112,6 +115,12 @@ export function WorkflowRunsPage() { onClose={closeInfoDrawer} title={title} description={description} + modelSchema={{ + url: WORKFLOW_MANAGER_SCHEMA_SVG_URL, + title: 'Workflow Manager Entity Schema', + description: 'Workflow model entity diagram from service-workflow-manager.', + previewSummary: 'SVG preview of the current Workflow Manager model schema.', + }} /> );