Spaces:
Running
Running
| 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<string, any> }) => { | |
| 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<SectionVersion[]> => { | |
| 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; | |
| }; | |