import { useEffect, useState, type ReactNode } from 'react'; import { DashboardLayout } from '../../components/dashboard/DashboardLayout'; import { getDevDiagnostics, runDevStorageCheck } from '../../lib/api'; import type { DevDiagnostics, DevStorageCheck } from '../../types/dev'; interface UploadErrorLog { at: string; filename: string; message: string; } export default function DevPage() { const [diagnostics, setDiagnostics] = useState(null); const [storageCheck, setStorageCheck] = useState(null); const [uploadError, setUploadError] = useState(null); const [loading, setLoading] = useState(true); const [checking, setChecking] = useState(false); const [error, setError] = useState(''); async function refresh() { setLoading(true); setError(''); try { setDiagnostics(await getDevDiagnostics()); } catch (reason) { setError(reason instanceof Error ? reason.message : 'Could not load diagnostics.'); } finally { setLoading(false); } } useEffect(() => { void refresh(); try { const raw = window.localStorage.getItem('studio:last-upload-error'); if (raw) setUploadError(JSON.parse(raw) as UploadErrorLog); } catch { setUploadError(null); } }, []); async function checkStorage() { setChecking(true); try { setStorageCheck(await runDevStorageCheck()); } catch (reason) { setStorageCheck({ ok: false, step: 'api', error: reason instanceof Error ? reason.message : 'Storage check failed.', }); } finally { setChecking(false); void refresh(); } } const browser = { origin: window.location.origin, online: navigator.onLine, xhr: typeof XMLHttpRequest !== 'undefined', fileApi: typeof File !== 'undefined' && typeof FileReader !== 'undefined', secureContext: window.isSecureContext, }; return (

Workspace / diagnostics

Dev Console

Local runtime diagnostics and storage checks.

{error &&

{error}

}
{loading || !diagnostics ? ( ) : ( <> )} {loading || !diagnostics ? ( ) : ( <> )} {loading || !diagnostics ? ( ) : ( <> {diagnostics.storage.error && (

{diagnostics.storage.error}

)} )}

05 / Upload path

What happens when you choose a file.

01 API URL Creates media metadata 02 Presigned PUT Browser to MinIO 03 Complete Stat object and queue processing 04 Ready Preview becomes visible

If an upload stops at 0%, the failure is usually the browser reaching the presigned MinIO origin. Check that the browser origin below is listed in the MinIO bucket CORS rule.

{diagnostics && (
Effective browser origins {diagnostics.http.corsOrigins.map((origin) => ( {origin} ))}
)}

06 / Last client failure

Recent upload signal.

{uploadError ? (
!
{uploadError.filename}

{uploadError.message}

{new Date(uploadError.at).toLocaleString()}
) : (

No client-side upload failures have been recorded in this browser.

)} {storageCheck && (
{storageCheck.ok ? 'Storage check passed' : 'Storage check failed'} {storageCheck.ok ? `${storageCheck.bytes} bytes / ${storageCheck.contentType}` : `${storageCheck.step}: ${storageCheck.error}`}
)}
Show raw diagnostics JSON
{diagnostics ? JSON.stringify(diagnostics, null, 2) : 'No response yet.'}
); } function DevPanel({ title, eyebrow, children, }: { title: string; eyebrow: string; children: ReactNode; }) { return (

{eyebrow}

{title}

{children}
); } function DevRow({ label, value }: { label: string; value: string }) { return (
{label} {value}
); } function DevStatus({ label, ok }: { label: string; ok: boolean }) { return (
{label} {ok ? 'reachable' : 'unavailable'}
); } function DevLoading() { return (
); }