Spaces:
Paused
Paused
| import axios, { AxiosError } from "axios"; | |
| import type { Job, SearchFilters, GeneratedCV } from "@/types"; | |
| const API_BASE = import.meta.env.VITE_API_URL || ""; | |
| const api = axios.create({ | |
| baseURL: API_BASE, | |
| headers: { | |
| "Content-Type": "application/json", | |
| }, | |
| }); | |
| // Request interceptor to add auth headers if needed | |
| api.interceptors.request.use((config) => { | |
| return config; | |
| }); | |
| // Response interceptor for error handling | |
| api.interceptors.response.use( | |
| (response) => response, | |
| (error: AxiosError) => { | |
| console.error("API Error:", error.response?.data || error.message); | |
| return Promise.reject(error); | |
| } | |
| ); | |
| export const jobsAPI = { | |
| // Load runtime app config/settings | |
| async getConfig(): Promise<{ | |
| tailoring_mode: "local" | "api"; | |
| llm_provider: "ollama" | "groq" | "grok" | "openai"; | |
| enable_professional_summary: boolean; | |
| include_cover_letters: boolean; | |
| }> { | |
| const response = await api.get("/api/config"); | |
| return response.data; | |
| }, | |
| // Save user profile for the Auto-Apply Extension | |
| async saveUserProfile(profileData: { | |
| first_name: string; | |
| last_name: string; | |
| email: string; | |
| phone: string; | |
| linkedin: string; | |
| github: string; | |
| }): Promise<{ success: boolean; message?: string }> { | |
| const response = await api.post("/api/user-profile", profileData); | |
| return response.data; | |
| }, | |
| // Run deep semantic analysis for a specific job | |
| evaluateJobFit: async ( | |
| jobId: string | |
| ): Promise<{ | |
| success: boolean; | |
| match_score?: number; | |
| matched_skills?: string[]; | |
| missing_skills?: string[]; | |
| error?: string; | |
| }> => { | |
| const response = await api.post("/api/evaluate-fit", { job_id: jobId }); | |
| return response.data; | |
| }, | |
| // Save runtime app settings | |
| async saveSettings(payload: { | |
| tailoring_mode: "local" | "api"; | |
| llm_provider: "ollama" | "groq" | "grok" | "openai"; | |
| enable_professional_summary: boolean; | |
| include_cover_letters: boolean; | |
| }): Promise<{ success: boolean; settings: unknown }> { | |
| const response = await api.post("/api/settings", payload); | |
| return response.data; | |
| }, | |
| // Search for jobs | |
| async searchJobs( | |
| filters: SearchFilters | |
| ): Promise<{ jobs: Job[]; excel_file: string }> { | |
| const response = await api.post("/api/search", { | |
| keyword: filters.keyword, | |
| location: filters.location, | |
| max_jobs: filters.maxJobs, | |
| max_days_old: filters.maxDaysOld || 30, | |
| }); | |
| return { | |
| jobs: response.data.jobs || [], | |
| excel_file: response.data.excel_file || "", | |
| }; | |
| }, | |
| // Fast Search using Apify | |
| async searchJobsApify( | |
| filters: SearchFilters | |
| ): Promise<{ jobs: Job[]; json_file: string }> { | |
| const response = await api.post("/api/apify-search", { | |
| keyword: filters.keyword, | |
| location: filters.location, | |
| max_jobs: filters.maxJobs, | |
| max_days_old: filters.maxDaysOld || 30, | |
| }); | |
| return { | |
| jobs: response.data.jobs || [], | |
| json_file: response.data.json_file || "", | |
| }; | |
| }, | |
| // Upload CV template | |
| async uploadCV(file: File): Promise<{ success: boolean; message: string }> { | |
| const formData = new FormData(); | |
| formData.append("file", file); | |
| const response = await api.post("/api/upload-cv", formData, { | |
| headers: { | |
| "Content-Type": "multipart/form-data", | |
| }, | |
| }); | |
| return response.data; | |
| }, | |
| // Get jobs list | |
| async getJobsList(): Promise<Job[]> { | |
| const response = await api.get("/api/jobs"); | |
| return response.data.jobs || []; | |
| }, | |
| // Generate CV for single job | |
| async generateCV(jobId: string): Promise<{ | |
| success: boolean; | |
| filename: string; | |
| url: string; | |
| job?: Job; // <--- Temporary | |
| cover_letter_filename?: string; | |
| cover_letter_url?: string; | |
| }> { | |
| const response = await api.post(`/api/generate-cv/${jobId}`); | |
| const coverLetterFilename = | |
| response.data.cover_letter_filename || undefined; | |
| return { | |
| success: response.data.success, | |
| filename: response.data.filename, | |
| url: `/api/download/${encodeURIComponent(response.data.filename)}`, | |
| job: response.data.job, // <--- Temporary | |
| cover_letter_filename: coverLetterFilename, | |
| cover_letter_url: coverLetterFilename | |
| ? `/api/download/${encodeURIComponent(coverLetterFilename)}` | |
| : undefined, | |
| }; | |
| }, | |
| // Generate CVs for all selected jobs | |
| async generateAllCVs(jobIds?: string[]): Promise<{ | |
| success: boolean; | |
| successful: GeneratedCV[]; | |
| failed: Array<{ jobId: string; error: string }>; | |
| zip_filename: string; | |
| }> { | |
| // Updated the IDs of each job to represent actual IDs and avoid duplicated scrapes and Stop converting UUID strings into Numbers! | |
| const jobIndices = jobIds || []; | |
| const response = await api.post("/api/generate-all-cvs", { | |
| job_indices: jobIndices, | |
| }); | |
| const successful: GeneratedCV[] = (response.data.successful || []).map( | |
| (cv: any) => ({ | |
| jobId: String(cv.job_index), | |
| jobTitle: cv.job_title || "", | |
| company: cv.company || "", | |
| filename: cv.filename, | |
| url: `/api/download/${encodeURIComponent(cv.filename)}`, | |
| coverLetterFilename: cv.cover_letter_filename || undefined, | |
| coverLetterUrl: cv.cover_letter_filename | |
| ? `/api/download/${encodeURIComponent(cv.cover_letter_filename)}` | |
| : undefined, | |
| generatedAt: new Date().toISOString(), | |
| status: "success", | |
| }) | |
| ); | |
| const failed = (response.data.failed || []).map((item: any) => ({ | |
| jobId: String(item.job_index), | |
| error: item.error || "Unknown error", | |
| })); | |
| return { | |
| success: response.data.success, | |
| successful, | |
| failed, | |
| zip_filename: response.data.zip_filename, | |
| }; | |
| }, | |
| // Download file | |
| async downloadFile(filename: string): Promise<Blob> { | |
| const encodedFilename = encodeURIComponent(filename); | |
| const response = await api.get(`/api/download/${encodedFilename}`, { | |
| responseType: "blob", | |
| }); | |
| return response.data; | |
| }, | |
| // Get batch progress (for real-time updates) | |
| async getBatchProgress(): Promise<{ | |
| completed: number; | |
| total: number; | |
| current_job?: string; | |
| failed: number; | |
| }> { | |
| return { | |
| completed: 0, | |
| total: 0, | |
| failed: 0, | |
| current_job: undefined, | |
| }; | |
| }, | |
| // Health check | |
| async healthCheck(): Promise<{ status: string }> { | |
| const response = await api.get("/api/health"); | |
| return response.data; | |
| }, | |
| }; | |
| export default api; | |