import { getToken } from "./auth"; const API_URL = process.env.NEXT_PUBLIC_API_URL || "http://localhost:8521"; export interface ApiUser { id: string; name: string; email: string; identifier: string; phone?: string; role: string; status: string; created_at: string; } export interface ApiCompany { id: string; name: string; slug: string; email?: string; phone?: string; website?: string; address?: string; active: boolean; verified: boolean; created_at: string; } async function apiRequest( endpoint: string, options: RequestInit = {} ): Promise { const token = getToken(); // Sanitize API_URL: remove trailing slash let baseUrl = API_URL.replace(/\/+$/, ""); // Sanitize endpoint: ensure leading slash let cleanEndpoint = endpoint.startsWith("/") ? endpoint : `/${endpoint}`; // Detect and fix double prefixing of /api/v1 // Case 1: BaseURL ends with /api/v1 AND endpoint starts with /api/v1 if (baseUrl.endsWith("/api/v1") && cleanEndpoint.startsWith("/api/v1")) { cleanEndpoint = cleanEndpoint.replace("/api/v1", ""); } // Case 2: Double /api/v1 inside endpoint itself (if passed incorrectly) if (cleanEndpoint.includes("/api/v1/api/v1")) { cleanEndpoint = cleanEndpoint.replace("/api/v1/api/v1", "/api/v1"); } const url = `${baseUrl}${cleanEndpoint}`; console.log(`[API Request] ${url}`); // Debug log const res = await fetch(url, { ...options, headers: { "Content-Type": "application/json", ...(token && { Authorization: `Bearer ${token}` }), ...options.headers, }, }); if (!res.ok) { const error = await res.text(); throw new Error(error || `API Error: ${res.status}`); } return res.json(); } // Users API export const usersApi = { list: () => apiRequest("/api/v1/users"), create: (data: { name: string; email: string; password: string; role: string }) => apiRequest("/api/v1/users", { method: "POST", body: JSON.stringify(data), }), delete: (id: string) => apiRequest(`/api/v1/users/${id}`, { method: "DELETE", }), }; // Companies API export const companiesApi = { list: () => apiRequest("/api/v1/companies"), create: (data: { name: string; slug: string; email?: string }) => apiRequest("/api/v1/companies", { method: "POST", body: JSON.stringify(data), }), }; // Jobs API (public) export interface ApiJob { id: number; companyId: number; createdBy: number; title: string; description: string; salaryMin?: number; salaryMax?: number; salaryType?: string; employmentType?: string; workMode?: string; workingHours?: string; location?: string; regionId?: number; cityId?: number; requirements?: Record | string[]; benefits?: Record | string[]; visaSupport: boolean; languageLevel?: string; status: string; createdAt: string; updatedAt: string; companyName?: string; companyLogoUrl?: string; regionName?: string; cityName?: string; } export interface PaginatedResponse { data: T[]; pagination: { page: number; limit: number; total: number; }; } export const jobsApi = { list: (params?: { page?: number; limit?: number; companyId?: number }) => { const query = new URLSearchParams(); if (params?.page) query.set('page', String(params.page)); if (params?.limit) query.set('limit', String(params.limit)); if (params?.companyId) query.set('companyId', String(params.companyId)); const queryStr = query.toString(); return apiRequest>(`/jobs${queryStr ? `?${queryStr}` : ''}`); }, getById: (id: number) => apiRequest(`/jobs/${id}`), }; // Transform API job to frontend Job format export function transformApiJobToFrontend(apiJob: ApiJob): import('./types').Job { // Format salary let salary: string | undefined; if (apiJob.salaryMin && apiJob.salaryMax) { salary = `R$ ${apiJob.salaryMin.toLocaleString('pt-BR')} - R$ ${apiJob.salaryMax.toLocaleString('pt-BR')}`; } else if (apiJob.salaryMin) { salary = `A partir de R$ ${apiJob.salaryMin.toLocaleString('pt-BR')}`; } else if (apiJob.salaryMax) { salary = `Até R$ ${apiJob.salaryMax.toLocaleString('pt-BR')}`; } // Determine type type JobType = 'full-time' | 'part-time' | 'contract' | 'Remoto' | 'Tempo Integral'; let type: JobType = 'Tempo Integral'; if (apiJob.employmentType === 'full-time') type = 'Tempo Integral'; else if (apiJob.employmentType === 'part-time') type = 'part-time'; else if (apiJob.employmentType === 'contract') type = 'contract'; else if (apiJob.location?.toLowerCase().includes('remoto')) type = 'Remoto'; // Extract requirements const requirements: string[] = []; if (apiJob.requirements) { if (Array.isArray(apiJob.requirements)) { requirements.push(...apiJob.requirements.map(String)); } else if (typeof apiJob.requirements === 'object') { Object.values(apiJob.requirements).forEach(v => requirements.push(String(v))); } } return { id: String(apiJob.id), title: apiJob.title, company: apiJob.companyName || 'Empresa', location: apiJob.location || apiJob.cityName || 'Localização não informada', type, workMode: apiJob.workMode as any, salary, description: apiJob.description, requirements: requirements.length > 0 ? requirements : ['Ver detalhes'], postedAt: apiJob.createdAt?.split('T')[0] || new Date().toISOString().split('T')[0], }; }