// API client module: thin typed fetch wrappers over the contract endpoints. // Base URL from VITE_API_BASE, defaulting to http://localhost:8000/api. import type { AiAssistResponse, Application, Approval, Artifact, BatchScoringResponse, CoverLetterResponse, CritiqueComment, CvImportConfirmResponse, CvImportResponse, CvSection, DemoSeedResponse, InterviewPrepResponse, JobPosting, PostingsFetchResponse, Profile, RenderCvResponse, ScoreResponse, TaskRun, TodayResponse } from '@/types' const API_BASE: string = import.meta.env.VITE_API_BASE ?? 'http://localhost:8000/api' export class HttpError extends Error { status: number body: unknown constructor(status: number, body: unknown, message?: string) { super(message ?? `HTTP ${status}`) this.status = status this.body = body } } async function request( path: string, options: RequestInit = {} ): Promise { const res = await fetch(`${API_BASE}${path}`, { headers: { 'Content-Type': 'application/json', ...options.headers }, ...options }) if (!res.ok) { let body: unknown = null try { body = await res.json() } catch { // non-JSON error body } throw new HttpError(res.status, body, `HTTP ${res.status}`) } return res.json() as Promise } // --- Profile & CV --- export function getProfile(): Promise { return request('/profile') } export function updateProfile(data: Partial): Promise { return request('/profile', { method: 'PUT', body: JSON.stringify(data) }) } export function getSections(): Promise { return request('/profile/sections') } export function createSection(data: Partial): Promise { return request('/profile/sections', { method: 'POST', body: JSON.stringify(data) }) } export function updateSection(id: string, data: Partial): Promise { return request(`/profile/sections/${id}`, { method: 'PUT', body: JSON.stringify(data) }) } export function deleteSection(id: string): Promise { return request(`/profile/sections/${id}`, { method: 'DELETE' }) } export function aiAssist(sectionId: string, instruction: string): Promise { return request(`/profile/sections/${sectionId}/ai-assist`, { method: 'POST', body: JSON.stringify({ instruction }) }) } export function renderCv(): Promise { return request('/profile/render-cv', { method: 'POST' }) } // --- Job postings --- export function getPostings(): Promise { return request('/postings') } export function createPosting(url: string): Promise { return request('/postings', { method: 'POST', body: JSON.stringify({ url }) }) } export function scorePosting(postingId: string): Promise { return request(`/postings/${postingId}/score`, { method: 'POST' }) } // --- Applications --- export function getApplications(): Promise { return request('/applications') } export function transitionApplication(id: string, to: string): Promise { return request(`/applications/${id}/transition`, { method: 'POST', body: JSON.stringify({ to }) }) } export function getArtifacts(applicationId: string): Promise { return request(`/applications/${applicationId}/artifacts`) } export function createCoverLetter( applicationId: string, letterText: string ): Promise { return request(`/applications/${applicationId}/artifacts/cover-letter`, { method: 'POST', body: JSON.stringify({ letter_text: letterText }) }) } // --- Approval & outbox --- export function createApproval( applicationId: string, action: string, artifactId: string ): Promise { return request(`/applications/${applicationId}/approvals`, { method: 'POST', body: JSON.stringify({ action, artifact_id: artifactId }) }) } export function confirmApproval(approvalId: string): Promise { return request(`/approvals/${approvalId}/confirm`, { method: 'POST' }) } export function rejectApproval(approvalId: string): Promise { return request(`/approvals/${approvalId}/reject`, { method: 'POST' }) } export function outboxSend(approvalId: string, payload: Record): Promise { return request('/outbox/send', { method: 'POST', body: JSON.stringify({ approval_id: approvalId, payload }) }) } // --- Telemetry --- export function getTelemetryTasks(): Promise { return request('/telemetry/tasks') } // --- v1.0 additions (api-contract-v2.md) --- export function importCv(filename: string, contentBase64: string): Promise { return request('/cv/import', { method: 'POST', body: JSON.stringify({ filename, content_base64: contentBase64 }) }) } export function confirmCvImport(drafts: CvImportResponse['drafts']): Promise { return request('/cv/import/confirm', { method: 'POST', body: JSON.stringify({ drafts }) }) } export function fetchPostings(query: string, region?: string): Promise { const body: Record = { query } if (region) body.region = region return request('/postings/fetch', { method: 'POST', body: JSON.stringify(body) }) } export function batchScore(applicationIds: string[]): Promise { return request('/scoring/batch', { method: 'POST', body: JSON.stringify({ application_ids: applicationIds }) }) } export function getToday(): Promise { return request('/today') } export function interviewPrep(applicationId: string): Promise { return request(`/applications/${applicationId}/interview-prep`, { method: 'POST' }) } export function seedDemo(): Promise { return request('/concierge/seed-demo', { method: 'POST' }) } // Re-export types for convenience export type { AiAssistResponse, Application, Approval, Artifact, BatchScoringResponse, CoverLetterResponse, CritiqueComment, CvImportConfirmResponse, CvImportResponse, CvSection, DemoSeedResponse, InterviewPrepResponse, JobPosting, PostingsFetchResponse, Profile, RenderCvResponse, ScoreResponse, TaskRun, TodayResponse }