feat: Mongo-backed Pods with full CRUD + member management
Pods are persisted in MongoDB and CRUD'd via /api/pods (list/create/get/ update/delete + add/remove member). Shared Pod reshaped to a persisted entity (members: string[], description, timestamps) + PodInput. Backend seeds Demo/ Frontend/Backend Pods on first run (Squad -> Pod rename). Frontend replaces hardcoded teams with live data: PodCard (member chips add/remove, inline edit, delete, join) + CreatePodForm; App fetches+mutates via API. Removes teams.ts/ TeamCard; gitignores test artifacts. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+62
-3
@@ -1,7 +1,15 @@
|
||||
import type { InterventionOutcome } from '@podman/shared';
|
||||
import type { InterventionOutcome, Pod, PodInput } from '@podman/shared';
|
||||
|
||||
const BACKEND_URL = import.meta.env.VITE_BACKEND_URL || 'http://localhost:8787';
|
||||
|
||||
async function json<T>(res: Response): Promise<T> {
|
||||
if (!res.ok) {
|
||||
const body = (await res.json().catch(() => ({}))) as { error?: string };
|
||||
throw new Error(body.error || `request failed: ${res.status}`);
|
||||
}
|
||||
return res.json() as Promise<T>;
|
||||
}
|
||||
|
||||
/** Mint a LiveKit token from the backend. */
|
||||
export async function fetchToken(params: {
|
||||
room: string;
|
||||
@@ -14,8 +22,7 @@ export async function fetchToken(params: {
|
||||
headers: { 'content-type': 'application/json' },
|
||||
body: JSON.stringify(params),
|
||||
});
|
||||
if (!res.ok) throw new Error(`token request failed: ${res.status}`);
|
||||
return res.json() as Promise<{ token: string; url: string }>;
|
||||
return json(res);
|
||||
}
|
||||
|
||||
/** Record an intervention outcome for the policy learning loop. */
|
||||
@@ -27,3 +34,55 @@ export async function postOutcome(outcome: InterventionOutcome): Promise<void> {
|
||||
});
|
||||
if (!res.ok) throw new Error(`outcome post failed: ${res.status}`);
|
||||
}
|
||||
|
||||
// --- Pods CRUD ---
|
||||
|
||||
export async function listPods(): Promise<Pod[]> {
|
||||
return json(await fetch(`${BACKEND_URL}/api/pods`));
|
||||
}
|
||||
|
||||
export async function createPod(input: PodInput): Promise<Pod> {
|
||||
return json(
|
||||
await fetch(`${BACKEND_URL}/api/pods`, {
|
||||
method: 'POST',
|
||||
headers: { 'content-type': 'application/json' },
|
||||
body: JSON.stringify(input),
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
export async function updatePod(id: string, patch: PodInput): Promise<Pod> {
|
||||
return json(
|
||||
await fetch(`${BACKEND_URL}/api/pods/${encodeURIComponent(id)}`, {
|
||||
method: 'PATCH',
|
||||
headers: { 'content-type': 'application/json' },
|
||||
body: JSON.stringify(patch),
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
export async function deletePod(id: string): Promise<void> {
|
||||
const res = await fetch(`${BACKEND_URL}/api/pods/${encodeURIComponent(id)}`, {
|
||||
method: 'DELETE',
|
||||
});
|
||||
if (!res.ok) throw new Error(`delete pod failed: ${res.status}`);
|
||||
}
|
||||
|
||||
export async function addMember(id: string, name: string): Promise<Pod> {
|
||||
return json(
|
||||
await fetch(`${BACKEND_URL}/api/pods/${encodeURIComponent(id)}/members`, {
|
||||
method: 'POST',
|
||||
headers: { 'content-type': 'application/json' },
|
||||
body: JSON.stringify({ name }),
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
export async function removeMember(id: string, name: string): Promise<Pod> {
|
||||
return json(
|
||||
await fetch(
|
||||
`${BACKEND_URL}/api/pods/${encodeURIComponent(id)}/members/${encodeURIComponent(name)}`,
|
||||
{ method: 'DELETE' },
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user