maestro/ui/src/components/dashboard/SideInfoPanel.tsx
oss-sync d061ad08d8
Some checks failed
CI / build-and-test (push) Has been cancelled
sync: update from private repo (e62f5c7)
2026-06-11 01:52:48 +00:00

85 lines
3.0 KiB
TypeScript

import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useDashboardWidgets } from '../../hooks/useDashboardWidgets';
import { WidgetTabBar, WORKER_TAB_SLUG } from './WidgetTabBar';
import { WorkerStatusWidget } from './WorkerStatusWidget';
import { MarkdownWidget } from './MarkdownWidget';
import { NodeStatusWidget } from './NodeStatusWidget';
import { AddWidgetDialog } from './AddWidgetDialog';
interface Props {
/** Controlled-active widget slug. Defaults to worker tab. */
activeSlug?: string;
onActiveSlugChange?: (slug: string) => void;
collapsed?: boolean;
onToggleCollapse?: () => void;
}
export function SideInfoPanel({
activeSlug: activeSlugProp,
onActiveSlugChange,
collapsed,
onToggleCollapse,
}: Props) {
const { t } = useTranslation('dashboard');
const { widgets, create, update, remove } = useDashboardWidgets();
const [localActive, setLocalActive] = useState<string>(WORKER_TAB_SLUG);
const activeSlug = activeSlugProp ?? localActive;
const setActive = onActiveSlugChange ?? setLocalActive;
const [dialogOpen, setDialogOpen] = useState(false);
const activeWidget = widgets.find(w => w.slug === activeSlug);
return (
<div className="flex flex-col h-full overflow-hidden bg-canvas">
<WidgetTabBar
widgets={widgets}
activeSlug={activeSlug}
onSelect={setActive}
onAdd={() => setDialogOpen(true)}
onDeleteWidget={async (w) => {
if (!window.confirm(t('panel.deleteConfirm', { title: w.title }))) return;
await remove.mutateAsync(w.id);
if (activeSlug === w.slug) setActive(WORKER_TAB_SLUG);
}}
collapsed={collapsed}
onToggleCollapse={onToggleCollapse}
/>
{!collapsed && (
<div className="flex-1 min-h-0 overflow-hidden">
{activeSlug === WORKER_TAB_SLUG && <WorkerStatusWidget />}
{activeSlug !== WORKER_TAB_SLUG && activeWidget && activeWidget.kind === 'node-status' && (
<NodeStatusWidget key={activeWidget.id} />
)}
{activeSlug !== WORKER_TAB_SLUG && activeWidget && activeWidget.kind !== 'node-status' && (
<MarkdownWidget
key={activeWidget.id}
widget={activeWidget}
onSave={async (patch) => {
await update.mutateAsync({ id: activeWidget.id, patch });
}}
onDelete={async () => {
await remove.mutateAsync(activeWidget.id);
setActive(WORKER_TAB_SLUG);
}}
/>
)}
{activeSlug !== WORKER_TAB_SLUG && !activeWidget && (
<div className="p-3 text-xs text-slate-500">{t('panel.widgetNotFound')}</div>
)}
</div>
)}
<AddWidgetDialog
open={dialogOpen}
existingSlugs={widgets.map(w => w.slug)}
onClose={() => setDialogOpen(false)}
onCreate={async (input) => {
await create.mutateAsync(input);
setActive(input.slug);
}}
/>
</div>
);
}