| 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", |
| }, |
| }); |
|
|
| |
| api.interceptors.request.use((config) => { |
| return config; |
| }); |
|
|
| |
| api.interceptors.response.use( |
| (response) => response, |
| (error: AxiosError) => { |
| console.error("API Error:", error.response?.data || error.message); |
| return Promise.reject(error); |
| } |
| ); |
|
|
| export const jobsAPI = { |
| |
| 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; |
| }, |
|
|
| |
| 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; |
| }, |
|
|
| |
| 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; |
| }, |
|
|
| |
| 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; |
| }, |
|
|
| |
| 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 || "", |
| }; |
| }, |
|
|
| |
| 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 || "", |
| }; |
| }, |
|
|
| |
| 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; |
| }, |
|
|
| |
| async getJobsList(): Promise<Job[]> { |
| const response = await api.get("/api/jobs"); |
| return response.data.jobs || []; |
| }, |
|
|
| |
| async generateCV(jobId: string): Promise<{ |
| success: boolean; |
| filename: string; |
| url: string; |
| job?: Job; |
| 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, |
| cover_letter_filename: coverLetterFilename, |
| cover_letter_url: coverLetterFilename |
| ? `/api/download/${encodeURIComponent(coverLetterFilename)}` |
| : undefined, |
| }; |
| }, |
|
|
| |
| async generateAllCVs(jobIds?: string[]): Promise<{ |
| success: boolean; |
| successful: GeneratedCV[]; |
| failed: Array<{ jobId: string; error: string }>; |
| zip_filename: string; |
| }> { |
| |
| 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, |
| }; |
| }, |
|
|
| |
| async downloadFile(filename: string): Promise<Blob> { |
| const encodedFilename = encodeURIComponent(filename); |
| const response = await api.get(`/api/download/${encodedFilename}`, { |
| responseType: "blob", |
| }); |
| return response.data; |
| }, |
|
|
| |
| async getBatchProgress(): Promise<{ |
| completed: number; |
| total: number; |
| current_job?: string; |
| failed: number; |
| }> { |
| return { |
| completed: 0, |
| total: 0, |
| failed: 0, |
| current_job: undefined, |
| }; |
| }, |
|
|
| |
| async healthCheck(): Promise<{ status: string }> { |
| const response = await api.get("/api/health"); |
| return response.data; |
| }, |
| }; |
|
|
| export default api; |
|
|