refactor(onboarding): extract useAgentStatus and useGenerateEdgeKey hooks

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Théo LAGACHE
2026-06-19 10:56:46 +02:00
co-authored by Claude Sonnet 4.6
parent e044833d20
commit 8d64376a56
4 changed files with 96 additions and 79 deletions
@@ -0,0 +1,23 @@
"use client";
import { useQuery } from "@tanstack/react-query";
import { useOnboarding } from "@onboardjs/react";
import { getAgentStatusAction } from "@/features/onboarding/actions/get-agent-status.action";
import type { OnboardingAgent } from "@/features/onboarding/types";
export const useAgentStatus = () => {
const { state } = useOnboarding();
const agents = (state?.context.flowData.agents ?? []) as OnboardingAgent[];
const firstAgentId = agents[0]?.id;
return useQuery({
queryKey: ["onboarding-agent-status", firstAgentId],
queryFn: async () => {
if (!firstAgentId) return { connected: false };
const result = await getAgentStatusAction({ agentId: firstAgentId });
return result?.data ?? { connected: false };
},
refetchInterval: 3_000,
enabled: !!firstAgentId,
});
};
@@ -0,0 +1,17 @@
"use client";
import { useQuery } from "@tanstack/react-query";
import { generateEdgeKeyAction } from "@/features/onboarding/actions/generate-edge-key.action";
export const useGenerateEdgeKey = (agentId: string) => {
return useQuery({
queryKey: ["onboarding-edge-key", agentId],
queryFn: async () => {
const result = await generateEdgeKeyAction({ agentId });
if (!result?.data?.key) throw new Error("Failed to generate key");
return result.data.key;
},
staleTime: Infinity,
enabled: !!agentId,
});
};
@@ -1,62 +1,53 @@
"use client"; "use client";
import { useOnboarding } from "@onboardjs/react"; import { useOnboarding } from "@onboardjs/react";
import { useQuery } from "@tanstack/react-query";
import { Button } from "@/components/ui/button";
import { Loader2 } from "lucide-react"; import { Loader2 } from "lucide-react";
import { Button } from "@/components/ui/button";
import { CodeSnippet } from "@/components/common/code-snippet"; import { CodeSnippet } from "@/components/common/code-snippet";
import { generateEdgeKeyAction } from "@/features/onboarding/actions/generate-edge-key.action"; import { useGenerateEdgeKey } from "@/features/onboarding/hooks/use-generate-edge-key";
import type { OnboardingAgent } from "@/features/onboarding/types"; import type { OnboardingAgent } from "@/features/onboarding/types";
export const StepAgentKey = () => { export const StepAgentKey = () => {
const { next, state } = useOnboarding(); const { next, state } = useOnboarding();
const agents = (state?.context.flowData.agents ?? []) as OnboardingAgent[]; const agents = (state?.context.flowData.agents ?? []) as OnboardingAgent[];
return ( return (
<div className="flex flex-col gap-6"> <div className="flex flex-col gap-6">
<div> <div>
<h1 className="text-2xl font-semibold">Connect your agent</h1> <h1 className="text-2xl font-semibold">Connect your agent</h1>
<p className="text-sm text-muted-foreground mt-1"> <p className="text-sm text-muted-foreground mt-1">
Run the command below on your server. The next step waits for the agent to connect. Run the command below on your server. The next step waits for the agent to connect.
</p> </p>
</div> </div>
{agents.map((agent) => ( {agents.map((agent) => (
<AgentKeyBlock key={agent.id} agent={agent} /> <AgentKeyBlock key={agent.id} agent={agent} />
))} ))}
<Button type="button" onClick={() => next()}> <Button type="button" onClick={() => next()}>
I&apos;ve run the command I&apos;ve run the command
</Button> </Button>
</div> </div>
); );
}; };
const AgentKeyBlock = ({ agent }: { agent: OnboardingAgent }) => { const AgentKeyBlock = ({ agent }: { agent: OnboardingAgent }) => {
const { data, isLoading } = useQuery({ const { data, isLoading } = useGenerateEdgeKey(agent.id);
queryKey: ["edge-key", agent.id],
queryFn: async () => {
const result = await generateEdgeKeyAction({ agentId: agent.id });
if (!result?.data?.key) throw new Error("Failed to generate key");
return result.data.key;
},
staleTime: Infinity,
});
if (isLoading) {
return (
<div className="flex items-center gap-2 text-sm text-muted-foreground">
<Loader2 className="size-4 animate-spin" />
Generating key for {agent.name}
</div>
);
}
const command = `portabase agent "${agent.name}" --key ${data}`;
if (isLoading) {
return ( return (
<div className="flex flex-col gap-3 p-4 rounded-lg border border-border"> <div className="flex items-center gap-2 text-sm text-muted-foreground">
<p className="text-sm font-medium">{agent.name}</p> <Loader2 className="size-4 animate-spin" />
<CodeSnippet title="Installation Command" code={command} /> Generating key for {agent.name}
<CodeSnippet title="Agent Key (manual)" code={data ?? ""} /> </div>
</div>
); );
}
const command = `portabase agent "${agent.name}" --key ${data}`;
return (
<div className="flex flex-col gap-3 p-4 rounded-lg border border-border">
<p className="text-sm font-medium">{agent.name}</p>
<CodeSnippet title="Installation Command" code={command} />
<CodeSnippet title="Agent Key (manual)" code={data ?? ""} />
</div>
);
}; };
@@ -2,43 +2,29 @@
import { useEffect } from "react"; import { useEffect } from "react";
import { useOnboarding } from "@onboardjs/react"; import { useOnboarding } from "@onboardjs/react";
import { useQuery } from "@tanstack/react-query";
import { Loader2 } from "lucide-react"; import { Loader2 } from "lucide-react";
import { getAgentStatusAction } from "@/features/onboarding/actions/get-agent-status.action"; import { useAgentStatus } from "@/features/onboarding/hooks/use-agent-status";
import type { OnboardingAgent } from "@/features/onboarding/types"; import type { OnboardingAgent } from "@/features/onboarding/types";
export const StepAgentWaiting = () => { export const StepAgentWaiting = () => {
const { next, state } = useOnboarding(); const { next, state } = useOnboarding();
const agents = (state?.context.flowData.agents ?? []) as OnboardingAgent[]; const agents = (state?.context.flowData.agents ?? []) as OnboardingAgent[];
const firstAgentId = agents[0]?.id; const { data } = useAgentStatus();
const { data } = useQuery({ useEffect(() => {
queryKey: ["agent-status", firstAgentId], if (data?.connected) {
queryFn: async () => { next();
if (!firstAgentId) return { connected: false }; }
const result = await getAgentStatusAction({ agentId: firstAgentId }); }, [data?.connected, next]);
return result?.data ?? { connected: false };
},
refetchInterval: 3000,
enabled: !!firstAgentId,
});
useEffect(() => { return (
if (data?.connected) { <div className="flex flex-col items-center justify-center gap-4 h-full text-center">
next(); <Loader2 className="size-10 animate-spin text-primary" />
} <h1 className="text-xl font-semibold">Waiting for your agent</h1>
}, [data?.connected, next]); <p className="text-sm text-muted-foreground">Checking connectivity every 3 seconds</p>
{agents.length > 0 && (
return ( <p className="text-xs text-muted-foreground">Agent: {agents[0].name}</p>
<div className="flex flex-col items-center justify-center gap-4 h-full text-center"> )}
<Loader2 className="size-10 animate-spin text-primary" /> </div>
<h1 className="text-xl font-semibold">Waiting for your agent</h1> );
<p className="text-sm text-muted-foreground">
Checking connectivity every 3 seconds
</p>
{agents.length > 0 && (
<p className="text-xs text-muted-foreground">Agent: {agents[0].name}</p>
)}
</div>
);
}; };