import axios from 'axios'; import toast from 'react-hot-toast'; // Bazowy URL to backend export const apiClient = axios.create({ baseURL: import.meta.env.VITE_API_URL ? import.meta.env.VITE_API_URL.replace('/api', '') : import.meta.env.VITE_LANGSERVE_URL ? import.meta.env.VITE_LANGSERVE_URL.replace('/api', '') : 'http://localhost:8000', headers: { 'Content-Type': 'application/json' }, }); // --- Interceptory --- // Request: automatyczne wstrzykiwanie tokena Clerk apiClient.interceptors.request.use((config) => { const token = localStorage.getItem('token'); if (token && !config.headers['Authorization']) { config.headers['Authorization'] = `Bearer ${token}`; } return config; }); // Response: obsługa błędów globalnych apiClient.interceptors.response.use( (response) => response, (error) => { const status = error?.response?.status; if (status === 429) { const retryAfter = error.response?.data?.retry_after; const msg = retryAfter ? `Przekroczono limit zapytań. Spróbuj za ${retryAfter}s.` : 'Przekroczono limit zapytań. Poczekaj chwilę.'; toast.error(`⏳ ${msg}`, { duration: 8000, id: 'rate-limit' }); } return Promise.reject(error); } ); export const getSubscriptionStatus = async () => { // /api/me zwraca {tier, wizard_iterations_today, tokens_used_month, limits} const { data } = await apiClient.get('/api/me'); return data; }; export const getMe = getSubscriptionStatus; export const deleteUserAccount = async () => { const { data } = await apiClient.delete('/api/account'); return data; }; export const getAccountExport = async () => { const { data } = await apiClient.get('/api/account/export'); return data; }; export const updateAccountSettings = async (settings: { gdpr_consent_accepted?: boolean; ai_disclaimer_enabled?: boolean }) => { const { data } = await apiClient.post('/api/account/settings', settings); return data; }; export const submitFeedback = async (text: string, type: string = "feedback") => { const { data } = await apiClient.post('/api/feedback', { text, type }); return data; }; export const lookupCompany = async (nip: string) => { const { data } = await apiClient.get(`/api/projects/lookup-company?nip=${nip}`); return data; }; export const getAdminStats = async () => { const { data } = await apiClient.get('/api/admin/stats'); return data; }; export const getCurrentSession = async () => { // Stub w fazie bez połączenia pełnego PostgresSaver w backendzie const { data } = await apiClient.get('/api/session/current').catch(() => ({ data: { thread_id: "test-thread-123", status: "wizard", agent: "wizard", critic_evaluation: { is_approved: false, feedback: "Brak sekcji z oceną ryzyka ekologicznego." }, tokens_used: 1250, active_step: 3 } })); return data; }; // Projects API export const getProjects = async () => { const { data } = await apiClient.get('/api/projects'); return data; }; export const getProject = async (projectId: string) => { const { data } = await apiClient.get(`/api/projects/${projectId}`); return data; }; export const updateProject = async (projectId: string, updateData: any) => { const { data } = await apiClient.put(`/api/projects/${projectId}`, updateData); return data; }; export const deleteProject = async (projectId: string) => { await apiClient.delete(`/api/projects/${projectId}`); }; export const createProject = async (projectData: { title: string; program_type: string; description?: string; program_name?: string; estimated_value?: number; external_context?: Record }) => { const { data } = await apiClient.post('/api/projects', projectData); return data; }; export const matchProgram = async (description: string, nip?: string) => { const { data } = await apiClient.post('/api/projects/match-program', { description: description, nip: nip }); return data; }; export const createWelcomeSeed = async () => { const { data } = await apiClient.post('/api/projects/welcome-seed'); return data; }; // --- SECTION ENDPOINTS --- export const getProjectSections = async (projectId: string) => { const { data } = await apiClient.get(`/api/projects/${projectId}/sections`); return data; }; export const generateProjectSection = async (projectId: string, sectionType: string, promptContext?: string) => { const { data } = await apiClient.post(`/api/projects/${projectId}/generate-section`, { section_type: sectionType, prompt_context: promptContext }); return data; }; // --- Q&A / VERIFIER ENDPOINTS --- export const getProjectQuestionsHistory = async (projectId: string) => { const { data } = await apiClient.get(`/api/projects/${projectId}/ask/history`); return data; }; export const askProjectQuestion = async (projectId: string, question: string) => { const { data } = await apiClient.post(`/api/projects/${projectId}/ask`, { question }); return data; }; export const clearProjectQuestionsHistory = async (projectId: string) => { const { data } = await apiClient.delete(`/api/projects/${projectId}/ask/history`); return data; }; export const updateProjectSection = async (projectId: string, sectionId: string, content: string) => { const { data } = await apiClient.put(`/api/projects/${projectId}/sections/${sectionId}`, { content: content }); return data; }; export const reviewProjectSection = async (projectId: string, sectionType: string, content: string) => { const { data } = await apiClient.post(`/api/projects/${projectId}/review-section`, { section_type: sectionType, content: content }); return data; }; export interface SectionVersion { id: string; old_content: string; author: string; summary: string | null; timestamp: string; } export const getSectionVersions = async (projectId: string, sectionId: string): Promise => { const { data } = await apiClient.get(`/api/projects/${projectId}/sections/${sectionId}/versions`); return data; }; export const restoreSectionVersion = async (projectId: string, sectionId: string, versionId: string) => { const { data } = await apiClient.post(`/api/projects/${projectId}/sections/${sectionId}/versions/${versionId}/restore`); return data; }; export const previewProject = async (projectId: string) => { const { data } = await apiClient.get(`/api/projects/${projectId}/preview`); return data; }; export const compileFinalDocument = async (projectId: string, approvedOnly: boolean = false) => { const { data } = await apiClient.post(`/api/projects/${projectId}/compile-final`, { approved_only: approvedOnly }); return data; }; export const auditFinalDocument = async (projectId: string) => { const { data } = await apiClient.post(`/api/projects/${projectId}/global-audit`); return data; }; export const clearGlobalAudit = async (projectId: string) => { // Czyści wynik audytu przez zapis pustego stanu (backend nie ma DELETE /audit - // resetujemy przez POST z flagą) – fallback do PATCH try { const { data } = await apiClient.delete(`/api/projects/${projectId}/global-audit`); return data; } catch { // Backend może nie mieć DELETE — wrócimy z sukcesem, audit zostanie nadpisany przy kolejnym uruchomieniu return { status: 'cleared' }; } }; export const autofixProjectSection = async (projectId: string, sectionId: string) => { const { data } = await apiClient.post(`/api/projects/${projectId}/sections/${sectionId}/autofix`); return data; }; export const syncRAGKnowledge = async () => { const { data } = await apiClient.post('/api/rag/sync', { category: "ALL" }); return data; }; // --- CHATBOT PROJECT ENDPOINTS --- export const getProjectChatHistory = async (projectId: string) => { const { data } = await apiClient.get(`/api/projects/${projectId}/chat`); return data; }; export const clearProjectChatHistory = async (projectId: string) => { const { data } = await apiClient.delete(`/api/projects/${projectId}/chat`); return data; }; export const sendProjectChatMessage = async (projectId: string, content: string, activeSectionId?: string, activeSectionTitle?: string) => { const payload: any = { content }; if (activeSectionId) { payload.active_section = activeSectionId; if (activeSectionTitle) payload.active_section_title = activeSectionTitle; } const { data } = await apiClient.post(`/api/projects/${projectId}/chat`, payload); return data; }; // --- EXPORT ENDPOINT --- export const exportProjectDocument = async (projectId: string, format: string, template: string) => { // Return the response so we can extract blob return await apiClient.post(`/api/projects/${projectId}/export`, { format, template }, { responseType: 'blob' // Ważne, żeby Axios traktował to jako strumień binarny }); }; export const getProjectVersions = async (projectId: string) => { const { data } = await apiClient.get(`/api/projects/${projectId}/versions`); return data; }; export const createProjectVersion = async (projectId: string, title?: string) => { const { data } = await apiClient.post(`/api/projects/${projectId}/versions`, { title }); return data; }; export const exportProjectPDF = async (projectId: string, versionId?: string) => { const payload: any = { format: 'pdf', template: 'standard' }; if (versionId) payload.version_id = versionId; const response = await apiClient.post(`/api/projects/${projectId}/export`, payload, { responseType: 'blob' }); const url = window.URL.createObjectURL(new Blob([response.data])); const link = document.createElement('a'); link.href = url; link.setAttribute('download', versionId ? `dotacja_v${versionId.substring(0, 6)}.pdf` : `dotacja_${projectId}.pdf`); document.body.appendChild(link); link.click(); link.remove(); }; export const exportProjectDOCX = async (projectId: string, approvedOnly: boolean = false, versionId?: string) => { const payload: any = { format: 'docx', template: 'standard' }; if (versionId) payload.version_id = versionId; const response = await apiClient.post(`/api/projects/${projectId}/export`, payload, { responseType: 'blob' }); const url = window.URL.createObjectURL(new Blob([response.data])); const link = document.createElement('a'); link.href = url; link.setAttribute('download', versionId ? `dotacja_v${versionId.substring(0, 6)}.docx` : `dotacja_${projectId}.docx`); document.body.appendChild(link); link.click(); link.remove(); }; // --- EXTERNAL KNOWLEDGE / DOCUMENTS ENDPOINTS --- export const uploadProjectDocument = async (projectId: string, file: File) => { const formData = new FormData(); formData.append('file', file); const { data } = await apiClient.post(`/api/projects/${projectId}/documents`, formData, { headers: { 'Content-Type': 'multipart/form-data' } }); return data; }; export const getProjectDocuments = async (projectId: string) => { const { data } = await apiClient.get(`/api/projects/${projectId}/documents`); return data; }; export const deleteProjectDocument = async (projectId: string, filename: string) => { const { data } = await apiClient.delete(`/api/projects/${projectId}/documents/${filename}`); return data; }; // --- AUDIT ENDPOINTS --- export const getProjectAudit = async (projectId: string) => { const { data } = await apiClient.get(`/api/projects/${projectId}`); return data.final_document_audit_result; }; export const runProjectAudit = async (projectId: string) => { const { data } = await apiClient.post(`/api/projects/${projectId}/global-audit`); return data; }; export const getHolisticReview = async (projectId: string) => { const { data } = await apiClient.get(`/api/projects/${projectId}/holistic-review`); return data; }; export const runHolisticReview = async (projectId: string) => { const { data } = await apiClient.post(`/api/projects/${projectId}/holistic-review`); return data; }; // --- GRANTS / NABORY ENDPOINTS --- export const getGrantNabory = async (forceRefresh = false) => { const { data } = await apiClient.get('/api/grants/nabory', { params: { force_refresh: forceRefresh }, }); return data as { status: string; count: number; nabory: any[] }; }; export interface UserAnswer { question: string; answer: string; } export const matchGrantsForProject = async (projectId: string, userAnswers: UserAnswer[] = []) => { const { data } = await apiClient.post('/api/grants/match', { project_id: projectId, user_answers: userAnswers }); return data as { status: string; project_id: string; needs_more_info: boolean; clarifying_questions: string[]; matches: any[] }; }; // --- HEALTH --- export const getApiHealth = async () => { const { data } = await apiClient.get('/api/health'); return data; };