import { useState, useEffect, useCallback } from "react";
import {
type DirectiveSummary,
type DirectiveWithSteps,
type CreateDirectiveRequest,
type UpdateDirectiveRequest,
type CreateDirectiveStepRequest,
listDirectives,
createDirective,
getDirective,
updateDirective,
deleteDirective,
createDirectiveStep,
deleteDirectiveStep,
startDirective,
pauseDirective,
advanceDirective,
completeDirectiveStep,
failDirectiveStep,
skipDirectiveStep,
updateDirectiveGoal,
cleanupDirective,
pickUpOrders as pickUpOrdersApi,
createDirectivePR,
} from "../lib/api";
export function useDirectives() {
const [directives, setDirectives] = useState<DirectiveSummary[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const refresh = useCallback(async () => {
try {
setLoading(true);
setError(null);
const res = await listDirectives();
setDirectives(res.directives);
} catch (e) {
setError(e instanceof Error ? e.message : "Failed to load directives");
} finally {
setLoading(false);
}
}, []);
useEffect(() => {
refresh();
}, [refresh]);
const create = useCallback(async (req: CreateDirectiveRequest) => {
const d = await createDirective(req);
await refresh();
return d;
}, [refresh]);
const remove = useCallback(async (id: string) => {
await deleteDirective(id);
await refresh();
}, [refresh]);
return { directives, loading, error, refresh, create, remove };
}
export function useDirective(id: string | undefined) {
const [directive, setDirective] = useState<DirectiveWithSteps | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
// Silently refresh without setting loading state (for polls)
const silentRefresh = useCallback(async () => {
if (!id) return;
try {
const d = await getDirective(id);
setDirective(d);
setError(null);
} catch (e) {
// Don't overwrite existing data on poll failure
}
}, [id]);
// Full refresh with loading state (for initial load / explicit refresh)
const refresh = useCallback(async () => {
if (!id) return;
try {
setLoading(true);
setError(null);
const d = await getDirective(id);
setDirective(d);
} catch (e) {
setError(e instanceof Error ? e.message : "Failed to load directive");
} finally {
setLoading(false);
}
}, [id]);
// Reset state and fetch when ID changes
useEffect(() => {
setDirective(null);
setError(null);
setLoading(true);
refresh();
}, [id]); // eslint-disable-line react-hooks/exhaustive-deps
// Auto-poll while directive is active, has an orchestrator task, or has a completion task
useEffect(() => {
if (!directive) return;
const needsPolling =
directive.status === "active" ||
directive.orchestratorTaskId != null ||
directive.completionTaskId != null;
if (!needsPolling) return;
const interval = setInterval(silentRefresh, 5000);
return () => clearInterval(interval);
}, [directive?.status, directive?.orchestratorTaskId, directive?.completionTaskId, silentRefresh]);
const update = useCallback(async (req: UpdateDirectiveRequest) => {
if (!id) return;
await updateDirective(id, req);
await refresh();
}, [id, refresh]);
const addStep = useCallback(async (req: CreateDirectiveStepRequest) => {
if (!id) return;
await createDirectiveStep(id, req);
await refresh();
}, [id, refresh]);
const removeStep = useCallback(async (stepId: string) => {
if (!id) return;
await deleteDirectiveStep(id, stepId);
await refresh();
}, [id, refresh]);
const start = useCallback(async () => {
if (!id) return;
await startDirective(id);
await refresh();
}, [id, refresh]);
const pause = useCallback(async () => {
if (!id) return;
await pauseDirective(id);
await refresh();
}, [id, refresh]);
const advance = useCallback(async () => {
if (!id) return;
await advanceDirective(id);
await refresh();
}, [id, refresh]);
const completeStep = useCallback(async (stepId: string) => {
if (!id) return;
await completeDirectiveStep(id, stepId);
await refresh();
}, [id, refresh]);
const failStep = useCallback(async (stepId: string) => {
if (!id) return;
await failDirectiveStep(id, stepId);
await refresh();
}, [id, refresh]);
const skipStep = useCallback(async (stepId: string) => {
if (!id) return;
await skipDirectiveStep(id, stepId);
await refresh();
}, [id, refresh]);
const updateGoal = useCallback(async (goal: string) => {
if (!id) return;
await updateDirectiveGoal(id, goal);
await refresh();
}, [id, refresh]);
const cleanup = useCallback(async () => {
if (!id) return;
await cleanupDirective(id);
await refresh();
}, [id, refresh]);
const pickUpOrdersFn = useCallback(async () => {
if (!id) return null;
const result = await pickUpOrdersApi(id);
await refresh();
return result;
}, [id, refresh]);
const createPR = useCallback(async () => {
if (!id) return;
await createDirectivePR(id);
await refresh();
}, [id, refresh]);
return {
directive, loading, error, refresh,
update, addStep, removeStep,
start, pause, advance,
completeStep, failStep, skipStep,
updateGoal, cleanup,
pickUpOrders: pickUpOrdersFn,
createPR,
};
}