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>
78 lines
2.1 KiB
TypeScript
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 };
|
|
}
|