export type FormData = {title: string; email: string; space_url: string; pitch: string; uses_buckets: boolean; bucket_details: string; setup_notes: string; expectations_confirmed: boolean; team_confirmed: boolean}; export type Space = {id: string; url: string; title: string; runtime: string; warning?: string | null}; export type Application = FormData & {id: string; username: string; revision: number; state: 'draft'|'submitted'|'withdrawn'; created_at: string; submitted_at?: string; updated_at: string; confirmed: boolean; decision?: string | null; decision_message?: string; space?: Space}; export type ReviewApplication = Application & {review_decision: string; review_notes: string; review_message: string; published_decision?: string; published_message?: string; audit: {at:string; actor:string; action:string}[]}; export type User = {username: string; name: string; email: string; is_reviewer: boolean}; export type Config = {deadline: string; applications_open: boolean; max_demos: number; event_url: string; base_url: string}; export class ApiError extends Error { constructor(message:string, public code:string, public fields:Record = {}, public status:number = 0) {super(message);} } export async function api(path:string, options:RequestInit = {}, csrf?:string):Promise { let response:Response; try { response = await fetch(path, {credentials: 'same-origin', ...options, headers: {'Content-Type':'application/json', ...(csrf ? {'X-CSRF-Token':csrf} : {}), ...options.headers}}); } catch {throw new ApiError('Could not connect. Your current form is still here; please try again.', 'network_error');} const data = await response.json().catch(() => ({})); if (!response.ok) throw new ApiError(data.error?.message || 'The service is temporarily unavailable. Please try again.', data.error?.code || 'request_failed', data.error?.fields || {}, response.status); return data; } export const blankForm:FormData = {title:'',email:'',space_url:'',pitch:'',uses_buckets:false,bucket_details:'',setup_notes:'',expectations_confirmed:false,team_confirmed:false}; export function formFrom(app:Application):FormData {return Object.fromEntries(Object.keys(blankForm).map(k => [k, app[k as keyof FormData] ?? blankForm[k as keyof FormData]])) as FormData;}