mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-09-27 01:21:25 +00:00
Expand the OpenAPI contract for frontend auth and workflow routes, regenerate the TypeScript Axios client, and route web API calls through generated client classes while preserving SSE and install exceptions.
185 lines
4.8 KiB
TypeScript
185 lines
4.8 KiB
TypeScript
import useSWRMutation from "swr/mutation";
|
|
import { useSWRConfig } from "swr";
|
|
import type {
|
|
PreviewUrlResponse,
|
|
RunStatusResponse,
|
|
SteerRunRequest,
|
|
SubmitAnswerRequest,
|
|
} from "@qltysh/fabro-api-client";
|
|
|
|
import {
|
|
apiData,
|
|
authApi,
|
|
humanInTheLoopApi,
|
|
} from "./api-client";
|
|
import { queryKeys } from "./query-keys";
|
|
import type { LifecycleAction, LifecycleActionError } from "./run-actions";
|
|
import {
|
|
archiveRun,
|
|
cancelRun,
|
|
isLifecycleActionError,
|
|
unarchiveRun,
|
|
} from "./run-actions";
|
|
|
|
export type PreviewRunArg = {
|
|
port: number;
|
|
expires_in_secs: number;
|
|
};
|
|
|
|
export type PreviewMutationResult = {
|
|
intent: "preview";
|
|
url: string;
|
|
};
|
|
|
|
export type LifecycleMutationResult =
|
|
| {
|
|
intent: LifecycleAction;
|
|
ok: true;
|
|
run: RunStatusResponse;
|
|
}
|
|
| {
|
|
intent: LifecycleAction;
|
|
ok: false;
|
|
error: LifecycleActionError | null;
|
|
};
|
|
|
|
export function usePreviewRun(id: string | undefined) {
|
|
return useSWRMutation(
|
|
id ? queryKeys.runs.preview(id) : null,
|
|
async (_key, { arg }: { arg: PreviewRunArg }): Promise<PreviewMutationResult> => {
|
|
const result = await apiData<PreviewUrlResponse>(() =>
|
|
humanInTheLoopApi.generatePreviewUrl(id!, arg),
|
|
);
|
|
return { intent: "preview", url: result.url };
|
|
},
|
|
);
|
|
}
|
|
|
|
export function useCancelRun(id: string | undefined) {
|
|
return useLifecycleMutation(id, "cancel", cancelRun);
|
|
}
|
|
|
|
export function useArchiveRun(id: string | undefined) {
|
|
return useLifecycleMutation(id, "archive", archiveRun);
|
|
}
|
|
|
|
export function useUnarchiveRun(id: string | undefined) {
|
|
return useLifecycleMutation(id, "unarchive", unarchiveRun);
|
|
}
|
|
|
|
function useLifecycleMutation(
|
|
id: string | undefined,
|
|
intent: LifecycleAction,
|
|
action: (id: string) => Promise<RunStatusResponse>,
|
|
) {
|
|
const { mutate } = useSWRConfig();
|
|
const key = id ? queryKeys.runs[intent](id) : null;
|
|
return useSWRMutation(
|
|
key,
|
|
async (): Promise<LifecycleMutationResult> => {
|
|
if (!id) {
|
|
return { intent, ok: false, error: null };
|
|
}
|
|
try {
|
|
return { intent, ok: true, run: await action(id) };
|
|
} catch (error) {
|
|
return {
|
|
intent,
|
|
ok: false,
|
|
error: isLifecycleActionError(error) ? error : null,
|
|
};
|
|
}
|
|
},
|
|
{
|
|
onSuccess: (result) => {
|
|
if (!id || !result.ok) return;
|
|
void mutate(queryKeys.runs.detail(id));
|
|
void mutate(queryKeys.boards.runs());
|
|
void mutate(queryKeys.runs.billing(id));
|
|
},
|
|
},
|
|
);
|
|
}
|
|
|
|
export type SubmitInterviewAnswerArg = SubmitAnswerRequest & { questionId: string };
|
|
|
|
export function useSubmitInterviewAnswer(runId: string | undefined) {
|
|
const { mutate } = useSWRConfig();
|
|
return useSWRMutation(
|
|
runId ? `interview-answer:${runId}` : null,
|
|
async (_key: string, { arg }: { arg: SubmitInterviewAnswerArg }) => {
|
|
if (!runId) throw new Error("runId is required");
|
|
const { questionId, ...body } = arg;
|
|
await apiData(() => humanInTheLoopApi.submitRunAnswer(runId, questionId, body));
|
|
},
|
|
{
|
|
onSuccess: () => {
|
|
if (!runId) return;
|
|
void mutate(queryKeys.runs.questions(runId, 25, 0));
|
|
void mutate(queryKeys.runs.detail(runId));
|
|
},
|
|
},
|
|
);
|
|
}
|
|
|
|
export type SteerRunArg = SteerRunRequest;
|
|
|
|
export function useInterruptRun(runId: string | undefined) {
|
|
const { mutate } = useSWRConfig();
|
|
return useSWRMutation(
|
|
runId ? `interrupt-run:${runId}` : null,
|
|
async (_key: string) => {
|
|
if (!runId) throw new Error("runId is required");
|
|
await apiData(() => humanInTheLoopApi.interruptRun(runId));
|
|
},
|
|
{
|
|
onSuccess: () => {
|
|
if (!runId) return;
|
|
void mutate(queryKeys.runs.detail(runId));
|
|
},
|
|
},
|
|
);
|
|
}
|
|
|
|
export function useSteerRun(runId: string | undefined) {
|
|
const { mutate } = useSWRConfig();
|
|
return useSWRMutation(
|
|
runId ? `steer-run:${runId}` : null,
|
|
async (_key: string, { arg }: { arg: SteerRunArg }) => {
|
|
if (!runId) throw new Error("runId is required");
|
|
await apiData(() => humanInTheLoopApi.steerRun(runId, arg));
|
|
},
|
|
{
|
|
onSuccess: () => {
|
|
if (!runId) return;
|
|
void mutate(queryKeys.runs.detail(runId));
|
|
},
|
|
},
|
|
);
|
|
}
|
|
|
|
export function useToggleDemoMode() {
|
|
const { mutate } = useSWRConfig();
|
|
return useSWRMutation(
|
|
queryKeys.demo.toggle(),
|
|
async (_key, { arg }: { arg: { enabled: boolean } }) => {
|
|
await apiData(() => authApi.toggleDemo(arg));
|
|
},
|
|
{
|
|
onSuccess: () => {
|
|
void mutate(queryKeys.auth.me());
|
|
},
|
|
},
|
|
);
|
|
}
|
|
|
|
export function useLoginDevToken() {
|
|
return useSWRMutation(
|
|
queryKeys.auth.loginDevToken(),
|
|
async (_key, { arg }: { arg: { token: string } }) => {
|
|
return apiData(() => authApi.loginDevToken(arg), {
|
|
redirectOnUnauthorized: false,
|
|
});
|
|
},
|
|
);
|
|
}
|