Fix: WidgetConfigDialog scope + reorder race condition
Two bugs in the widget config dialog: 1. Service Overview edit showed main-dashboard widgets. The dialog called useWidgetInstances() with no args, fetching ALL widgets. Now accepts a serviceId prop; OverviewTab passes instance.id so the dialog lists + creates only service-scoped widgets. New built-in widgets added from a service Overview inherit the serviceId. 2. Reorder up/down buttons did nothing. moveInstance fired two saveWidget mutations via Promise.all — the first mutation's onSuccess cache invalidation triggered a refetch before the second completed, reverting the swap. Changed to sequential awaits so both sort_order writes land before the cache refreshes. 127 tests pass; lint/build green.
This commit is contained in:
@@ -38,6 +38,8 @@ import {
|
||||
interface Props {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
/** When set, scope the dialog to a specific service instance's widgets. */
|
||||
serviceId?: string;
|
||||
}
|
||||
|
||||
interface Draft {
|
||||
@@ -164,8 +166,8 @@ function WidgetConfigEditor({
|
||||
);
|
||||
}
|
||||
|
||||
export function WidgetConfigDialog({ open, onClose }: Props) {
|
||||
const { data: instances = [] } = useWidgetInstances();
|
||||
export function WidgetConfigDialog({ open, onClose, serviceId }: Props) {
|
||||
const { data: instances = [] } = useWidgetInstances(serviceId);
|
||||
const { data: services = [] } = useServiceInstances();
|
||||
const { data: tasks = [] } = useTasks();
|
||||
const saveWidget = useSaveWidgetInstance();
|
||||
@@ -184,7 +186,7 @@ export function WidgetConfigDialog({ open, onClose }: Props) {
|
||||
function startAddBuiltIn(kind: string) {
|
||||
const binding = BUILTIN_WIDGETS[kind];
|
||||
setDraft({
|
||||
serviceId: null,
|
||||
serviceId: serviceId ?? null,
|
||||
widgetKind: kind,
|
||||
title: binding?.name ?? kind,
|
||||
config: { ...(binding?.defaultConfig ?? {}) },
|
||||
@@ -255,10 +257,10 @@ export function WidgetConfigDialog({ open, onClose }: Props) {
|
||||
if (targetIndex < 0 || targetIndex >= sortedInstances.length) return;
|
||||
const a = sortedInstances[index];
|
||||
const b = sortedInstances[targetIndex];
|
||||
await Promise.all([
|
||||
saveWidget.mutateAsync({ ...a, sort_order: b.sort_order }),
|
||||
saveWidget.mutateAsync({ ...b, sort_order: a.sort_order }),
|
||||
]);
|
||||
// Sequential (not Promise.all) to avoid a race where the first mutation's
|
||||
// cache invalidation refetches before the second completes, reverting the swap.
|
||||
await saveWidget.mutateAsync({ ...a, sort_order: b.sort_order });
|
||||
await saveWidget.mutateAsync({ ...b, sort_order: a.sort_order });
|
||||
}
|
||||
|
||||
async function removeInstance(instance: WidgetInstance) {
|
||||
|
||||
@@ -72,6 +72,7 @@ export function OverviewTab({ instance }: { instance: ServiceInstance }) {
|
||||
<WidgetConfigDialog
|
||||
open={configOpen}
|
||||
onClose={() => setConfigOpen(false)}
|
||||
serviceId={instance.id}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user