This commit is contained in:
@@ -1,84 +1,43 @@
|
||||
import { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useDashboardWidgets } from '../../hooks/useDashboardWidgets';
|
||||
import { WidgetTabBar, WORKER_TAB_SLUG } from './WidgetTabBar';
|
||||
import { WidgetTabBar, WORKER_TAB_SLUG, NODE_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. */
|
||||
/** Controlled-active tab slug. Defaults to the worker tab. */
|
||||
activeSlug?: string;
|
||||
onActiveSlugChange?: (slug: string) => void;
|
||||
collapsed?: boolean;
|
||||
onToggleCollapse?: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Side Info Panel: two fixed live-status views, worker and node (GPU).
|
||||
* The configurable Markdown-widget dashboard was removed in 2026-06.
|
||||
*/
|
||||
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>
|
||||
)}
|
||||
{activeSlug === NODE_TAB_SLUG ? <NodeStatusWidget /> : <WorkerStatusWidget />}
|
||||
</div>
|
||||
)}
|
||||
<AddWidgetDialog
|
||||
open={dialogOpen}
|
||||
existingSlugs={widgets.map(w => w.slug)}
|
||||
onClose={() => setDialogOpen(false)}
|
||||
onCreate={async (input) => {
|
||||
await create.mutateAsync(input);
|
||||
setActive(input.slug);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user