Files
IdeA/frontend/src/features/workstate/useProjectWorkState.ts
Blomios 5d88c952ec feat(frontend): surface UI des tâches de fond (F1-F4)
Expose les tâches de fond côté front : modèle domaine et
normalisation du work state, panneau ProjectWorkStatePanel,
intégration dans LayoutGrid et ProjectsView, et abonnement aux
événements backgroundTaskChanged / agentInboxChanged /
agentWakeChanged pour rafraîchir l'état. npm run build + npm test
(449) verts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 21:33:51 +02:00

78 lines
2.1 KiB
TypeScript

/**
* View-model hook for the project work-state read-model.
*
* It consumes the WorkStateGateway only, and optionally subscribes to existing
* domain events through SystemGateway to refresh the read-only snapshot.
*/
import { useCallback, useEffect, useState } from "react";
import type { GatewayError, ProjectWorkState } from "@/domain";
import { useGateways } from "@/app/di";
export interface ProjectWorkStateViewModel {
state: ProjectWorkState | null;
error: string | null;
busy: boolean;
refresh: () => Promise<void>;
}
function describe(e: unknown): string {
if (e && typeof e === "object" && "message" in e) {
return String((e as GatewayError).message);
}
return String(e);
}
export function useProjectWorkState(projectId: string): ProjectWorkStateViewModel {
const { workState, system } = useGateways();
const [state, setState] = useState<ProjectWorkState | null>(null);
const [error, setError] = useState<string | null>(null);
const [busy, setBusy] = useState(false);
const refresh = useCallback(async () => {
setBusy(true);
setError(null);
try {
setState(await workState.getProjectWorkState(projectId));
} catch (e) {
setError(describe(e));
} finally {
setBusy(false);
}
}, [projectId, workState]);
useEffect(() => {
void refresh();
}, [refresh]);
useEffect(() => {
if (!system) return;
let unsubscribe: (() => void) | undefined;
let cancelled = false;
void system.onDomainEvent((event) => {
if (
event.type === "agentLaunched" ||
event.type === "agentExited" ||
event.type === "agentBusyChanged" ||
event.type === "delegationReady" ||
event.type === "orchestratorRequestProcessed" ||
event.type === "backgroundTaskChanged" ||
event.type === "agentInboxChanged" ||
event.type === "agentWakeChanged"
) {
void refresh();
}
}).then((u) => {
if (cancelled) u();
else unsubscribe = u;
});
return () => {
cancelled = true;
unsubscribe?.();
};
}, [refresh, system]);
return { state, error, busy, refresh };
}