GrantForge Bot
Deploy to Hugging Face
afd56bc
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;
};