Skip to content

Commit 884de23

Browse files
tlgimenesclaude
andcommitted
fix(settings): auto-save plugin toggle instead of requiring manual save
Remove the pending changes pattern and Save/Cancel buttons from the plugins form. Toggling a plugin switch now immediately persists the change via mutation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 910a55c commit 884de23

1 file changed

Lines changed: 25 additions & 96 deletions

File tree

apps/mesh/src/web/components/settings/project-plugins-form.tsx

Lines changed: 25 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1-
import { useState, type ReactNode } from "react";
1+
import { type ReactNode } from "react";
22
import { useMutation, useQueryClient } from "@tanstack/react-query";
33
import {
44
useProjectContext,
55
useMCPClient,
66
SELF_MCP_ALIAS_ID,
77
} from "@decocms/mesh-sdk";
88
import { KEYS } from "@/web/lib/query-keys";
9-
import { Button } from "@deco/ui/components/button.tsx";
109
import { Switch } from "@deco/ui/components/switch.tsx";
1110
import { toast } from "sonner";
1211
import { Container } from "@untitledui/icons";
@@ -113,51 +112,8 @@ export function ProjectPluginsForm() {
113112
orgId: org.id,
114113
});
115114

116-
// Track only pending changes (pluginId -> intended state)
117-
const [pendingChanges, setPendingChanges] = useState<Record<string, boolean>>(
118-
{},
119-
);
120-
const [isSaving, setIsSaving] = useState(false);
121-
122115
const serverPlugins = project.enabledPlugins ?? [];
123116

124-
// Derive whether a plugin is enabled: pending changes override server state
125-
const isPluginEnabled = (pluginId: string): boolean => {
126-
const pending = pendingChanges[pluginId];
127-
if (pending !== undefined) {
128-
return pending;
129-
}
130-
return serverPlugins.includes(pluginId);
131-
};
132-
133-
// Compute the full list of enabled plugins for saving
134-
const getEnabledPluginsList = (): string[] => {
135-
const result = new Set(serverPlugins);
136-
for (const [pluginId, enabled] of Object.entries(pendingChanges)) {
137-
if (enabled) {
138-
result.add(pluginId);
139-
} else {
140-
result.delete(pluginId);
141-
}
142-
}
143-
return Array.from(result);
144-
};
145-
146-
const hasChanges = Object.keys(pendingChanges).length > 0;
147-
148-
const handleTogglePlugin = (pluginId: string, enabled: boolean) => {
149-
const serverEnabled = serverPlugins.includes(pluginId);
150-
151-
if (enabled === serverEnabled) {
152-
setPendingChanges((prev) => {
153-
const { [pluginId]: _, ...rest } = prev;
154-
return rest;
155-
});
156-
} else {
157-
setPendingChanges((prev) => ({ ...prev, [pluginId]: enabled }));
158-
}
159-
};
160-
161117
const mutation = useMutation({
162118
mutationFn: async (input: { enabledPlugins: string[] }) => {
163119
const result = await client.callTool({
@@ -187,28 +143,20 @@ export function ProjectPluginsForm() {
187143
queryClient.invalidateQueries({
188144
queryKey: KEYS.organizationSettings(org.id),
189145
});
190-
191-
setPendingChanges({});
192-
toast.success("Plugins updated successfully");
193146
},
194147
onError: (error) => {
195148
toast.error(
196149
"Failed to update plugins: " +
197150
(error instanceof Error ? error.message : "Unknown error"),
198151
);
199152
},
200-
onSettled: () => {
201-
setIsSaving(false);
202-
},
203153
});
204154

205-
const handleSave = () => {
206-
setIsSaving(true);
207-
mutation.mutate({ enabledPlugins: getEnabledPluginsList() });
208-
};
209-
210-
const handleCancel = () => {
211-
setPendingChanges({});
155+
const handleTogglePlugin = (pluginId: string, enabled: boolean) => {
156+
const updated = enabled
157+
? [...serverPlugins, pluginId]
158+
: serverPlugins.filter((id) => id !== pluginId);
159+
mutation.mutate({ enabledPlugins: updated });
212160
};
213161

214162
// Get plugin metadata from sidebar groups
@@ -231,44 +179,25 @@ export function ProjectPluginsForm() {
231179
}
232180

233181
return (
234-
<div className="space-y-4">
235-
<div className="flex flex-col">
236-
{sourcePlugins.map((plugin) => {
237-
const meta = getPluginMeta(plugin.id);
238-
const description = getPluginDescription(plugin.id);
239-
const isEnabled = isPluginEnabled(plugin.id);
240-
241-
return (
242-
<PluginRow
243-
key={plugin.id}
244-
plugin={plugin}
245-
isEnabled={isEnabled}
246-
isSaving={isSaving}
247-
description={description}
248-
label={meta?.label ?? plugin.id}
249-
icon={meta?.icon ?? <Container size={14} />}
250-
onToggle={handleTogglePlugin}
251-
/>
252-
);
253-
})}
254-
</div>
255-
256-
<div className="flex items-center gap-3 pt-4">
257-
<Button
258-
variant="outline"
259-
onClick={handleCancel}
260-
disabled={!hasChanges || isSaving}
261-
>
262-
Cancel
263-
</Button>
264-
<Button
265-
onClick={handleSave}
266-
disabled={!hasChanges || isSaving}
267-
className="min-w-24"
268-
>
269-
{isSaving ? "Saving..." : "Save Changes"}
270-
</Button>
271-
</div>
182+
<div className="flex flex-col">
183+
{sourcePlugins.map((plugin) => {
184+
const meta = getPluginMeta(plugin.id);
185+
const description = getPluginDescription(plugin.id);
186+
const isEnabled = serverPlugins.includes(plugin.id);
187+
188+
return (
189+
<PluginRow
190+
key={plugin.id}
191+
plugin={plugin}
192+
isEnabled={isEnabled}
193+
isSaving={mutation.isPending}
194+
description={description}
195+
label={meta?.label ?? plugin.id}
196+
icon={meta?.icon ?? <Container size={14} />}
197+
onToggle={handleTogglePlugin}
198+
/>
199+
);
200+
})}
272201
</div>
273202
);
274203
}

0 commit comments

Comments
 (0)