diff --git a/frontend/src/components/WidgetConfigDialog.tsx b/frontend/src/components/WidgetConfigDialog.tsx index 5638792..04d0d1c 100644 --- a/frontend/src/components/WidgetConfigDialog.tsx +++ b/frontend/src/components/WidgetConfigDialog.tsx @@ -228,6 +228,9 @@ export function WidgetConfigDialog({ const [draft, setDraft] = useState(null); const [draftBaseline, setDraftBaseline] = useState(null); + // When opened via editWidgetId, closing the edit should close the dialog + // entirely (not fall back to the list view). + const directEdit = Boolean(editWidgetId); // When editWidgetId is set and the dialog opens, auto-enter edit mode for // that widget (instead of showing the list view). @@ -281,6 +284,12 @@ export function WidgetConfigDialog({ } function reset() { + // If we were opened via direct edit, closing should close the dialog + // entirely, not fall back to the list view. + if (directEdit) { + onClose(); + return; + } setDraft(null); setDraftBaseline(null); } diff --git a/frontend/src/components/WidgetInstance.tsx b/frontend/src/components/WidgetInstance.tsx index 3d6e017..96e0552 100644 --- a/frontend/src/components/WidgetInstance.tsx +++ b/frontend/src/components/WidgetInstance.tsx @@ -52,7 +52,7 @@ export function WidgetInstanceCard({ widget, onEdit, onCopy }: Props) { : `Unknown widget: ${widget.widget_kind} (built-in)`; return (
- {(onEdit || onCopy) ? actionButtons : null} + {onEdit || onCopy ? actionButtons : null} {label} @@ -65,7 +65,7 @@ export function WidgetInstanceCard({ widget, onEdit, onCopy }: Props) { const Component = resolved.component; return (
- {(onEdit || onCopy) ? actionButtons : null} + {onEdit || onCopy ? actionButtons : null} ) { - return + return ; } function DialogTrigger({ - ...props + ...props }: React.ComponentProps) { - return + return ; } function DialogPortal({ - ...props + ...props }: React.ComponentProps) { - return + return ; } function DialogClose({ - ...props + ...props }: React.ComponentProps) { - return + return ; } function DialogOverlay({ - className, - ...props + className, + ...props }: React.ComponentProps) { - return ( - - ) + return ( + + ); } function DialogContent({ - className, - children, - showCloseButton = true, - ...props + className, + children, + showCloseButton = true, + ...props }: React.ComponentProps & { - showCloseButton?: boolean + showCloseButton?: boolean; }) { - return ( - - - - {children} - {showCloseButton && ( - - - - )} - - - ) + return ( + + + + {children} + {showCloseButton && ( + + + + )} + + + ); } function DialogHeader({ className, ...props }: React.ComponentProps<"div">) { - return ( -
- ) + return ( +
+ ); } function DialogFooter({ - className, - showCloseButton = false, - children, - ...props + className, + showCloseButton = false, + children, + ...props }: React.ComponentProps<"div"> & { - showCloseButton?: boolean + showCloseButton?: boolean; }) { - return ( -
- {children} - {showCloseButton && ( - - - - )} -
- ) + return ( +
+ {children} + {showCloseButton && ( + + + + )} +
+ ); } function DialogTitle({ - className, - ...props + className, + ...props }: React.ComponentProps) { - return ( - - ) + return ( + + ); } function DialogDescription({ - className, - ...props + className, + ...props }: React.ComponentProps) { - return ( - - ) + return ( + + ); } export { - Dialog, - DialogClose, - DialogContent, - DialogDescription, - DialogFooter, - DialogHeader, - DialogOverlay, - DialogPortal, - DialogTitle, - DialogTrigger, -} + Dialog, + DialogClose, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogOverlay, + DialogPortal, + DialogTitle, + DialogTrigger, +}; diff --git a/frontend/src/components/ui/sheet-form.tsx b/frontend/src/components/ui/sheet-form.tsx index 452158a..12e2130 100644 --- a/frontend/src/components/ui/sheet-form.tsx +++ b/frontend/src/components/ui/sheet-form.tsx @@ -82,7 +82,7 @@ export function SheetForm({ { // Prevent Radix's default Escape close so our guard runs instead. if (isDirty) { diff --git a/frontend/src/pages/Dashboard.tsx b/frontend/src/pages/Dashboard.tsx index c618d06..ff8bb70 100644 --- a/frontend/src/pages/Dashboard.tsx +++ b/frontend/src/pages/Dashboard.tsx @@ -31,7 +31,11 @@ import { useDeleteDashboardShortcut, useSaveDashboardShortcut, } from "../hooks/useDashboard"; -import { useDetachWidgetReference, useWidgetInstances, useWidgetReferences } from "../hooks/useWidgets"; +import { + useDetachWidgetReference, + useWidgetInstances, + useWidgetReferences, +} from "../hooks/useWidgets"; import { useServiceInstances } from "../hooks/useServices"; import { useIsMobile } from "../hooks/useIsMobile"; import type { @@ -98,9 +102,11 @@ function groupWidgetsBySection( function MobileWidgetSections({ sections, onEditWidget, + onCopyWidget, }: { sections: { id: SectionId; widgets: WidgetInstance[] }[]; onEditWidget?: (widgetId: string) => void; + onCopyWidget?: (widgetId: string) => void; }) { return ( <> @@ -143,6 +149,7 @@ function MobileWidgetSections({ key={widget.id} widget={widget} onEdit={onEditWidget ? (id) => onEditWidget(id) : undefined} + onCopy={onCopyWidget ? (id) => onCopyWidget(id) : undefined} /> ))} @@ -584,6 +591,10 @@ export function Dashboard() { setEditWidgetId(id); setWidgetDialogOpen(true); }} + onCopyWidget={(id) => { + const ref = widgetReferences.find((r) => r.widget.id === id); + if (ref) detachRef.mutate(ref.id); + }} /> ) : ( visibleWidgets.map((widget) => ( @@ -598,10 +609,12 @@ export function Dashboard() { referencedWidgetIds.has(widget.id) ? () => { // Detach: find the reference and clone it. - const ref = widgetReferences.find((r) => r.widget.id === widget.id); + const ref = widgetReferences.find( + (r) => r.widget.id === widget.id, + ); if (ref) detachRef.mutate(ref.id); } - : undefined + : undefined } /> )) diff --git a/frontend/src/pages/Settings.tsx b/frontend/src/pages/Settings.tsx index 98f355e..4eb6d78 100644 --- a/frontend/src/pages/Settings.tsx +++ b/frontend/src/pages/Settings.tsx @@ -1486,8 +1486,8 @@ function ServiceConfigEditor({
{instance.name} - - {instance.enabled ? "enabled" : "disabled"} + + {enabled ? "enabled" : "disabled"}