refactor(onboarding): extract useCreateAgent and useDeleteAgent hooks

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Théo LAGACHE
2026-06-19 10:31:21 +02:00
co-authored by Claude Sonnet 4.6
parent 808e60557b
commit 8c1025ece9
3 changed files with 129 additions and 104 deletions
@@ -0,0 +1,41 @@
"use client";
import { useMutation } from "@tanstack/react-query";
import { useOnboarding } from "@onboardjs/react";
import { toast } from "sonner";
import { createAgentAction } from "@/features/agents/agents.action";
import type { OnboardingAgent, OnboardingDefaultsData } from "@/features/onboarding/types";
export const useCreateAgent = () => {
const { state, updateContext } = useOnboarding();
return useMutation({
mutationFn: async (name: string) => {
const orgId = (state?.context.flowData.org as any)?.id as string | undefined;
if (!orgId) throw new Error("Missing org ID — cannot create agent");
const defaults = (state?.context.flowData.defaults ?? {}) as OnboardingDefaultsData;
const result = await createAgentAction({
organizationId: orgId,
data: { name, description: "" },
});
if (!result?.data?.data) {
throw new Error(result?.serverError ?? `Failed to create agent "${name}"`);
}
const newAgent: OnboardingAgent = {
id: result.data.data.id,
name: result.data.data.name,
notifierId: defaults.notifierId,
storageId: defaults.storageId,
};
const agents = [
...((state?.context.flowData.agents ?? []) as OnboardingAgent[]),
newAgent,
];
await updateContext({ flowData: { ...state?.context.flowData, agents } });
return newAgent;
},
onError: (err: Error) => toast.error(err.message),
});
};
@@ -0,0 +1,25 @@
"use client";
import { useMutation } from "@tanstack/react-query";
import { useOnboarding } from "@onboardjs/react";
import { toast } from "sonner";
import { deleteAgentAction } from "@/features/agents/agent-delete.action";
import type { OnboardingAgent } from "@/features/onboarding/types";
export const useDeleteAgent = () => {
const { state, updateContext } = useOnboarding();
return useMutation({
mutationFn: async (agentId: string) => {
const orgId = (state?.context.flowData.org as any)?.id as string | undefined;
const result = await deleteAgentAction({ agentId, organizationId: orgId });
if (result?.data?.success === false) throw new Error("Failed to delete agent");
const agents = (
(state?.context.flowData.agents ?? []) as OnboardingAgent[]
).filter((a) => a.id !== agentId);
await updateContext({ flowData: { ...state?.context.flowData, agents } });
},
onError: (err: Error) => toast.error(err.message),
});
};
@@ -2,66 +2,24 @@
import { useState } from "react"; import { useState } from "react";
import { useOnboarding } from "@onboardjs/react"; import { useOnboarding } from "@onboardjs/react";
import { toast } from "sonner"; import { X } from "lucide-react";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { X } from "lucide-react"; import { useCreateAgent } from "@/features/onboarding/hooks/use-create-agent";
import { createAgentAction } from "@/features/agents/agents.action"; import { useDeleteAgent } from "@/features/onboarding/hooks/use-delete-agent";
import { deleteAgentAction } from "@/features/agents/agent-delete.action"; import type { OnboardingAgent } from "@/features/onboarding/types";
import type { OnboardingAgent, OnboardingDefaultsData } from "@/features/onboarding/types";
export const StepAgentCreate = () => { export const StepAgentCreate = () => {
const { next, updateContext, state } = useOnboarding(); const { next, state } = useOnboarding();
const defaults = (state?.context.flowData.defaults ?? {}) as OnboardingDefaultsData;
const agents = (state?.context.flowData.agents ?? []) as OnboardingAgent[]; const agents = (state?.context.flowData.agents ?? []) as OnboardingAgent[];
const orgId = (state?.context.flowData.org as any)?.id as string | undefined;
const [name, setName] = useState(""); const [name, setName] = useState("");
const [adding, setAdding] = useState(false); const createAgent = useCreateAgent();
const deleteAgent = useDeleteAgent();
const addAgent = async () => { const onAdd = () => {
if (!name.trim()) return; if (!name.trim()) return;
if (!orgId) { createAgent.mutate(name.trim(), { onSuccess: () => setName("") });
toast.error("Missing org ID — cannot create agent");
return;
}
setAdding(true);
try {
const result = await createAgentAction({
organizationId: orgId,
data: { name: name.trim(), description: "" },
});
if (!result?.data?.data) {
toast.error(result?.serverError ?? `Failed to create agent "${name.trim()}"`);
return;
}
const newAgent: OnboardingAgent = {
id: result.data.data.id,
name: result.data.data.name,
notifierId: defaults.notifierId,
storageId: defaults.storageId,
};
const updated = [...agents, newAgent];
setName("");
await updateContext({ flowData: { ...state?.context.flowData, agents: updated } });
} finally {
setAdding(false);
}
};
const removeAgent = async (id: string) => {
const updated = agents.filter((a) => a.id !== id);
const result = await deleteAgentAction({ agentId: id, organizationId: orgId });
if (result?.data?.success === false) {
toast.error("Failed to delete agent");
} else {
await updateContext({ flowData: { ...state?.context.flowData, agents: updated } });
}
};
const onContinue = async () => {
await next();
}; };
return ( return (
@@ -83,7 +41,8 @@ export const StepAgentCreate = () => {
<span className="flex-1 truncate">{agent.name}</span> <span className="flex-1 truncate">{agent.name}</span>
<button <button
type="button" type="button"
onClick={() => removeAgent(agent.id)} onClick={() => deleteAgent.mutate(agent.id)}
disabled={deleteAgent.isPending}
className="opacity-50 hover:opacity-100 transition-opacity" className="opacity-50 hover:opacity-100 transition-opacity"
> >
<X className="size-4" /> <X className="size-4" />
@@ -98,20 +57,20 @@ export const StepAgentCreate = () => {
value={name} value={name}
onChange={(e) => setName(e.target.value)} onChange={(e) => setName(e.target.value)}
placeholder="agent-prod" placeholder="agent-prod"
onKeyDown={(e) => e.key === "Enter" && addAgent()} onKeyDown={(e) => e.key === "Enter" && onAdd()}
disabled={adding} disabled={createAgent.isPending}
/> />
<Button <Button
type="button" type="button"
variant="outline" variant="outline"
onClick={addAgent} onClick={onAdd}
disabled={adding || !name.trim()} disabled={createAgent.isPending || !name.trim()}
> >
{adding ? "Adding…" : "Add"} {createAgent.isPending ? "Adding…" : "Add"}
</Button> </Button>
</div> </div>
<Button type="button" onClick={onContinue}> <Button type="button" onClick={() => next()}>
Continue Continue
</Button> </Button>
</div> </div>