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,
} 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);
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]);
useEffect(() => {
refresh();
}, [refresh]);
// Auto-poll while directive is active or has an orchestrator task
useEffect(() => {
if (!directive) return;
const needsPolling =
directive.status === "active" || directive.orchestratorTaskId != null;
if (!needsPolling) return;
const interval = setInterval(refresh, 5000);
return () => clearInterval(interval);
}, [directive?.status, directive?.orchestratorTaskId, refresh]);
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]);
return {
directive, loading, error, refresh,
update, addStep, removeStep,
start, pause, advance,
completeStep, failStep, skipStep,
updateGoal,
};
}