fix(#338): Route all state-changing fetch() calls through API client

- Replace raw fetch() with apiPost/apiPatch/apiDelete in:
  - ImportExportActions.tsx: POST for file imports
  - KanbanBoard.tsx: PATCH for task status updates
  - ActiveProjectsWidget.tsx: POST for widget data fetches
  - useLayouts.ts: POST/PATCH/DELETE for layout management
- Add apiPostFormData() method to API client for FormData uploads
- Ensures CSRF token is included in all state-changing requests
- Update tests to mock CSRF token fetch for API client usage

Refs #338

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Jason Woltje
2026-02-05 17:06:23 -06:00
parent 5ae07f7a84
commit 344e5df3bb
7 changed files with 198 additions and 119 deletions

View File

@@ -6,6 +6,7 @@
import { useState, useEffect } from "react";
import { FolderOpen, Bot, Activity, Clock, AlertCircle, CheckCircle2 } from "lucide-react";
import type { WidgetProps } from "@mosaic/shared";
import { apiPost } from "@/lib/api/client";
interface ActiveProject {
id: string;
@@ -43,14 +44,9 @@ export function ActiveProjectsWidget({ id: _id, config: _config }: WidgetProps):
useEffect(() => {
const fetchProjects = async (): Promise<void> => {
try {
const response = await fetch("/api/widgets/data/active-projects", {
method: "POST",
headers: { "Content-Type": "application/json" },
});
if (response.ok) {
const data = (await response.json()) as ActiveProject[];
setProjects(data);
}
// Use API client to ensure CSRF token is included
const data = await apiPost<ActiveProject[]>("/api/widgets/data/active-projects");
setProjects(data);
} catch (error) {
console.error("Failed to fetch active projects:", error);
} finally {
@@ -71,14 +67,9 @@ export function ActiveProjectsWidget({ id: _id, config: _config }: WidgetProps):
useEffect(() => {
const fetchAgentSessions = async (): Promise<void> => {
try {
const response = await fetch("/api/widgets/data/agent-chains", {
method: "POST",
headers: { "Content-Type": "application/json" },
});
if (response.ok) {
const data = (await response.json()) as AgentSession[];
setAgentSessions(data);
}
// Use API client to ensure CSRF token is included
const data = await apiPost<AgentSession[]>("/api/widgets/data/agent-chains");
setAgentSessions(data);
} catch (error) {
console.error("Failed to fetch agent sessions:", error);
} finally {