feat: initial public release (MAESTRO)
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
import { useState } from 'react';
|
||||
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 { 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-white">
|
||||
<WidgetTabBar
|
||||
widgets={widgets}
|
||||
activeSlug={activeSlug}
|
||||
onSelect={setActive}
|
||||
onAdd={() => setDialogOpen(true)}
|
||||
onDeleteWidget={async (w) => {
|
||||
if (!window.confirm(`"${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">ウィジェットが見つかりません</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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user