fix: hoist DetailPanel into AppShell detail slot for proper slide-in
DetailPanel is a flex sibling that slides in from the right — it must be rendered at the AppShell level via the detail prop, not inside the page content. Add DetailPanelContext so pages can push their panel content up to LayoutShell, which passes it to AppShell.detail. Applied to Dashboard (exchange detail) and AgentHealth (instance detail). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { useState, useMemo } from 'react';
|
||||
import { useState, useMemo, useEffect } from 'react';
|
||||
import { useParams, Link } from 'react-router';
|
||||
import {
|
||||
StatCard, StatusDot, Badge, MonoText, ProgressBar,
|
||||
@@ -9,6 +9,7 @@ import styles from './AgentHealth.module.css';
|
||||
import { useAgents, useAgentEvents } from '../../api/queries/agents';
|
||||
import { useAgentMetrics } from '../../api/queries/agent-metrics';
|
||||
import type { AgentInstance } from '../../api/types';
|
||||
import { useDetailPanelSlot } from '../../components/DetailPanelContext';
|
||||
|
||||
// ── Helpers ──────────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -218,6 +219,7 @@ export default function AgentHealth() {
|
||||
const { appId } = useParams();
|
||||
const { data: agents } = useAgents(undefined, appId);
|
||||
const { data: events } = useAgentEvents(appId);
|
||||
const { setDetail } = useDetailPanelSlot();
|
||||
|
||||
const [selectedInstance, setSelectedInstance] = useState<AgentInstance | null>(null);
|
||||
const [panelOpen, setPanelOpen] = useState(false);
|
||||
@@ -503,18 +505,41 @@ export default function AgentHealth() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Detail panel */}
|
||||
{selectedInstance && (
|
||||
<DetailPanel
|
||||
open={panelOpen}
|
||||
onClose={() => {
|
||||
setPanelOpen(false);
|
||||
setSelectedInstance(null);
|
||||
}}
|
||||
title={selectedInstance.name ?? selectedInstance.id}
|
||||
tabs={detailTabs}
|
||||
/>
|
||||
)}
|
||||
{/* Detail panel — hoisted to AppShell via context */}
|
||||
<AgentDetailPanelSlot
|
||||
open={panelOpen}
|
||||
onClose={() => { setPanelOpen(false); setSelectedInstance(null); }}
|
||||
selectedInstance={selectedInstance}
|
||||
detailTabs={detailTabs}
|
||||
setDetail={setDetail}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function AgentDetailPanelSlot({
|
||||
open, onClose, selectedInstance, detailTabs, setDetail,
|
||||
}: {
|
||||
open: boolean
|
||||
onClose: () => void
|
||||
selectedInstance: AgentInstance | null
|
||||
detailTabs: any
|
||||
setDetail: (node: React.ReactNode) => void
|
||||
}) {
|
||||
useEffect(() => {
|
||||
if (!selectedInstance) {
|
||||
setDetail(null);
|
||||
return;
|
||||
}
|
||||
setDetail(
|
||||
<DetailPanel
|
||||
open={open}
|
||||
onClose={onClose}
|
||||
title={selectedInstance.name ?? selectedInstance.id}
|
||||
tabs={detailTabs}
|
||||
/>
|
||||
);
|
||||
return () => setDetail(null);
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user