export type B03AnalysisStatus = "pending" | "in_progress" | "completed" | "failed"; export interface B03StoredFileState { slot: string; fileName: string; fileSize: number; status: "completed" | "failed"; } export interface B03ProjectState { projectId: string; uploadedAt: number; files: B03StoredFileState[]; analysisStatus: B03AnalysisStatus; analysisProgress: number; lastCheckedAt: number; } function key(projectId: string): string { return `b03_project_state_${projectId}`; } export function loadB03ProjectState(projectId: string): B03ProjectState | null { const raw = localStorage.getItem(key(projectId)); if (!raw) return null; try { return JSON.parse(raw) as B03ProjectState; } catch { localStorage.removeItem(key(projectId)); return null; } } export function saveB03UploadedFile( projectId: string, file: { slot: string; fileName: string; fileSize: number }, ): B03ProjectState { const current = loadB03ProjectState(projectId); const nextFiles = (current?.files ?? []).filter((item) => item.slot !== file.slot); nextFiles.push({ ...file, status: "completed" }); const next: B03ProjectState = { projectId, uploadedAt: current?.uploadedAt ?? Date.now(), files: nextFiles, analysisStatus: "pending", analysisProgress: 0, lastCheckedAt: Date.now(), }; localStorage.setItem(key(projectId), JSON.stringify(next)); return next; } export function updateB03AnalysisState( projectId: string, status: B03AnalysisStatus, progress: number, ): B03ProjectState { const current = loadB03ProjectState(projectId); const next: B03ProjectState = { projectId, uploadedAt: current?.uploadedAt ?? Date.now(), files: current?.files ?? [], analysisStatus: status, analysisProgress: progress, lastCheckedAt: Date.now(), }; localStorage.setItem(key(projectId), JSON.stringify(next)); return next; } export function restoreB03ProjectState(options: { projectId: string; label: string; container: HTMLElement; poll: (projectId: string) => Promise; onComplete: () => void; }): void { const state = loadB03ProjectState(options.projectId); if (!state) return; options.container.replaceChildren(); const message = document.createElement("span"); message.textContent = `${options.label}: ${state.analysisProgress}%`; options.container.append(message); options.container.classList.add("is-visible"); if (state.analysisStatus === "in_progress" || state.analysisStatus === "pending") { void options.poll(options.projectId).then((completed) => { if (completed) options.onComplete(); }); } }