# Capture Time Override UI Implementation Plan > **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. **Goal:** Add a capture-time override form in the MediaPanel viewer to POST override timestamps and display current effective/base timestamps. **Architecture:** Extend the existing MediaPanel viewer admin controls with a small form that reads/writes to `/api/assets/:id/override-capture-ts` using the existing admin token from sessionStorage. Keep UI state local to MediaPanel and refresh the viewer asset timestamps after submit. **Tech Stack:** React (Next.js app router), TypeScript, fetch API ### Task 1: Add override state and helpers **Files:** - Modify: `apps/web/app/components/MediaPanel.tsx` **Step 1: Write the failing test** No tests (user-approved). **Step 2: Add state for override input, status, and effective/base timestamps** ```ts const [captureOverrideInput, setCaptureOverrideInput] = useState(""); const [captureOverrideError, setCaptureOverrideError] = useState(null); const [captureOverrideBusy, setCaptureOverrideBusy] = useState(false); ``` **Step 3: Add helper to derive effective/base timestamp** ```ts const effectiveTs = viewer?.asset.capture_ts_utc ?? null; const baseTs = viewer?.asset.capture_ts_utc ?? null; // updated when override applied ``` **Step 4: Commit** No commit yet; continue tasks. ### Task 2: Add override POST handler **Files:** - Modify: `apps/web/app/components/MediaPanel.tsx` **Step 1: Write the failing test** No tests (user-approved). **Step 2: Implement submit handler** ```ts async function handleOverrideCaptureTs() { if (!viewer) return; setCaptureOverrideError(null); setCaptureOverrideBusy(true); try { const token = sessionStorage.getItem("porthole_admin_token") ?? ""; if (!token) throw new Error("missing_admin_token"); const res = await fetch(`/api/assets/${viewer.asset.id}/override-capture-ts`, { method: "POST", headers: { "X-Porthole-Admin-Token": token, "Content-Type": "application/json", }, body: JSON.stringify({ capture_ts_utc: captureOverrideInput || null }), }); if (!res.ok) throw new Error(`override_failed:${res.status}`); // refresh viewer asset timestamps (re-fetch list or update local) } catch (err) { setCaptureOverrideError(err instanceof Error ? err.message : String(err)); } finally { setCaptureOverrideBusy(false); } } ``` **Step 3: Commit** No commit yet; continue tasks. ### Task 3: Add UI above Tags & Albums **Files:** - Modify: `apps/web/app/components/MediaPanel.tsx` **Step 1: Write the failing test** No tests (user-approved). **Step 2: Add form UI** ```tsx
Capture time override
Effective: {effectiveTs ?? "(none)"}
Base: {baseTs ?? "(unknown)"}
setCaptureOverrideInput(e.target.value)} style={{ flex: 1, padding: 6 }} disabled={captureOverrideBusy} />
{captureOverrideError ? (
{captureOverrideError}
) : null}
``` **Step 3: Commit** No commit yet; continue tasks. ### Task 4: Finalize, verify, and commit **Files:** - Modify: `apps/web/app/components/MediaPanel.tsx` **Step 1: Quick manual check** Run: `npm test` (skip) Expected: (skipped per user) **Step 2: Commit** ```bash git add apps/web/app/components/MediaPanel.tsx git commit -m "feat: add UI for capture time override" ```