85 lines
3.0 KiB
TypeScript
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>
|
|
);
|
|
}
|