import i18n from "../i18n"; export const API_BASE_URL = import.meta.env.VITE_API_URL || 'http://localhost:8000'; export const RESOURCES_BASE_URL = API_BASE_URL.replace(/\/api\/?$/, ''); console.log("DEBUG: Resources will be loaded from:", RESOURCES_BASE_URL); const getErrorMessage = async (response: Response, defaultMsg: string) => { try { const errorData = await response.json(); if (errorData && errorData.detail) { if (Array.isArray(errorData.detail)) { // FastAPI/Pydantic validation error const firstError = errorData.detail[0]; const errorType = firstError.type; const fieldName = firstError.loc[firstError.loc.length - 1]; // Try to translate the error type using i18n // Pydantic types look like 'string_too_short', 'value_error.email' const translationKey = `errors.${errorType}`; const translatedMsg = i18n.global.t(translationKey, { ...firstError.ctx, defaultValue: firstError.msg }); return `${fieldName}: ${translatedMsg}`; } return errorData.detail; } } catch (e) {} return defaultMsg; }; export const getMaterials = async () => { const response = await fetch(`${API_BASE_URL}/materials?lang=${i18n.global.locale.value}`); if (!response.ok) { throw new Error('Failed to fetch materials'); } return response.json(); }; export const getServices = async () => { const response = await fetch(`${API_BASE_URL}/services?lang=${i18n.global.locale.value}`); if (!response.ok) { throw new Error('Failed to fetch services'); } return response.json(); }; export const uploadFilesToServer = async (data: FormData) => { const response = await fetch(`${API_BASE_URL}/files/upload`, { method: "POST", body: data, }); if (!response.ok) throw new Error("Failed to upload files"); return response.json(); }; export const submitOrder = async (orderData: FormData) => { const token = localStorage.getItem("token"); const headers: Record = {}; if (token) { headers['Authorization'] = `Bearer ${token}`; } const response = await fetch(`${API_BASE_URL}/orders?lang=${i18n.global.locale.value}`, { method: 'POST', body: orderData, headers: headers, // Note: Do not set Content-Type header manually when using FormData // The browser will automatically set it with the correct boundary }); if (!response.ok) { throw new Error(await getErrorMessage(response, 'Failed to submit order')); } return response.json(); }; export const getMyOrders = async (page = 1, size = 10) => { const token = localStorage.getItem("token"); if (!token) return { orders: [], total: 0 }; const query = new URLSearchParams({ page: page.toString(), size: size.toString() }); const response = await fetch(`${API_BASE_URL}/orders/my?${query.toString()}&lang=${i18n.global.locale.value}`, { headers: { 'Authorization': `Bearer ${token}` } }); if (!response.ok) { throw new Error('Failed to fetch orders'); } return response.json(); }; export const registerUser = async (userData: any) => { const response = await fetch(`${API_BASE_URL}/auth/register?lang=${i18n.global.locale.value}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(userData) }); if (!response.ok) { throw new Error(await getErrorMessage(response, 'Failed to register')); } return response.json(); }; export const submitContactForm = async (formData: FormData) => { const response = await fetch(`${API_BASE_URL}/contact?lang=${i18n.global.locale.value}`, { method: 'POST', body: formData, }); if (!response.ok) { throw new Error(await getErrorMessage(response, 'Failed to send message')); } return response.json(); }; headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(userData), }); if (!response.ok) { throw new Error(await getErrorMessage(response, 'Failed to register')); } return response.json(); }; export const loginUser = async (userData: any) => { const response = await fetch(`${API_BASE_URL}/auth/login?lang=${i18n.global.locale.value}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(userData), }); if (!response.ok) { throw new Error(await getErrorMessage(response, 'Incorrect credentials')); } return response.json(); }; export const logoutUser = async () => { const token = localStorage.getItem("token"); if (!token) return; await fetch(`${API_BASE_URL}/auth/logout`, { method: 'POST', headers: { 'Authorization': `Bearer ${token}` } }); }; export const socialLogin = async (socialData: any) => { const response = await fetch(`${API_BASE_URL}/auth/social-login?lang=${i18n.global.locale.value}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(socialData), }); if (!response.ok) { throw new Error(await getErrorMessage(response, 'Social login failed')); } return response.json(); }; export const verifyEmail = async (token: string) => { const response = await fetch(`${API_BASE_URL}/auth/verify-email?token=${token}&lang=${i18n.global.locale.value}`); if (!response.ok) { throw new Error(await getErrorMessage(response, 'Verification failed')); } return response.json(); }; export const getCurrentUser = async () => { const token = localStorage.getItem("token"); if (!token) return null; const response = await fetch(`${API_BASE_URL}/auth/me?lang=${i18n.global.locale.value}`, { headers: { 'Authorization': `Bearer ${token}` } }); if (!response.ok) { if (response.status === 401) { localStorage.removeItem("token"); } return null; } return response.json(); }; export const updateProfile = async (userData: any) => { const token = localStorage.getItem("token"); if (!token) throw new Error("No token found"); const response = await fetch(`${API_BASE_URL}/auth/me?lang=${i18n.global.locale.value}`, { method: 'PUT', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, body: JSON.stringify(userData), }); if (!response.ok) { throw new Error(await getErrorMessage(response, 'Failed to update profile')); } return response.json(); }; export const forgotPassword = async (email: string) => { const response = await fetch(`${API_BASE_URL}/auth/forgot-password?lang=${i18n.global.locale.value}`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ email }), }); if (!response.ok) { throw new Error(await getErrorMessage(response, 'Failed to request reset')); } return response.json(); }; export const resetPassword = async (data: any) => { const response = await fetch(`${API_BASE_URL}/auth/reset-password?lang=${i18n.global.locale.value}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data), }); if (!response.ok) { throw new Error(await getErrorMessage(response, 'Failed to reset password')); } return response.json(); }; export const adminGetOrders = async (filters: { search?: string, status?: string, date_from?: string, date_to?: string } = {}) => { const token = localStorage.getItem("token"); const query = new URLSearchParams(); if (filters.search) query.append("search", filters.search); if (filters.status) query.append("status", filters.status); if (filters.date_from) query.append("date_from", filters.date_from); if (filters.date_to) query.append("date_to", filters.date_to); const response = await fetch(`${API_BASE_URL}/orders/admin/list?${query.toString()}&lang=${i18n.global.locale.value}`, { headers: { 'Authorization': `Bearer ${token}` } }); if (!response.ok) throw new Error("Failed to fetch admin orders"); return response.json(); }; export const adminUpdateOrder = async (orderId: number, data: any) => { const token = localStorage.getItem("token"); const response = await fetch(`${API_BASE_URL}/orders/${orderId}?lang=${i18n.global.locale.value}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, body: JSON.stringify(data) }); if (!response.ok) throw new Error("Failed to update order"); return response.json(); }; export const adminDeleteOrder = async (orderId: number) => { const token = localStorage.getItem("token"); const response = await fetch(`${API_BASE_URL}/orders/${orderId}/admin?lang=${i18n.global.locale.value}`, { method: 'DELETE', headers: { 'Authorization': `Bearer ${token}` } }); if (!response.ok) throw new Error("Failed to delete order"); return response.json(); }; export const getPriceEstimate = async (data: any) => { const response = await fetch(`${API_BASE_URL}/orders/estimate?lang=${i18n.global.locale.value}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); if (!response.ok) throw new Error("Failed to get estimate"); return response.json(); }; export const adminGetMaterials = async () => { const token = localStorage.getItem("token"); const response = await fetch(`${API_BASE_URL}/admin/materials?lang=${i18n.global.locale.value}`, { headers: { 'Authorization': `Bearer ${token}` } }); if (!response.ok) throw new Error("Failed to fetch admin materials"); return response.json(); }; export const adminCreateMaterial = async (data: any) => { const token = localStorage.getItem("token"); const response = await fetch(`${API_BASE_URL}/admin/materials?lang=${i18n.global.locale.value}`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, body: JSON.stringify(data) }); if (!response.ok) throw new Error("Failed to create material"); return response.json(); }; export const adminUpdateMaterial = async (id: number, data: any) => { const token = localStorage.getItem("token"); const response = await fetch(`${API_BASE_URL}/admin/materials/${id}?lang=${i18n.global.locale.value}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, body: JSON.stringify(data) }); if (!response.ok) throw new Error("Failed to update material"); return response.json(); }; export const adminGetServices = async () => { const token = localStorage.getItem("token"); const response = await fetch(`${API_BASE_URL}/admin/services?lang=${i18n.global.locale.value}`, { headers: { 'Authorization': `Bearer ${token}` } }); if (!response.ok) throw new Error("Failed to fetch admin services"); return response.json(); }; export const adminCreateService = async (data: any) => { const token = localStorage.getItem("token"); const response = await fetch(`${API_BASE_URL}/admin/services?lang=${i18n.global.locale.value}`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, body: JSON.stringify(data) }); if (!response.ok) throw new Error("Failed to create service"); return response.json(); }; export const adminUpdateService = async (id: number, data: any) => { const token = localStorage.getItem("token"); const response = await fetch(`${API_BASE_URL}/admin/services/${id}?lang=${i18n.global.locale.value}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, body: JSON.stringify(data) }); if (!response.ok) throw new Error("Failed to update service"); return response.json(); }; export const adminDeleteMaterial = async (id: number) => { const token = localStorage.getItem("token"); const response = await fetch(`${API_BASE_URL}/admin/materials/${id}?lang=${i18n.global.locale.value}`, { method: 'DELETE', headers: { 'Authorization': `Bearer ${token}` } }); if (!response.ok) throw new Error("Failed to delete material"); return response.json(); }; export const adminDeleteService = async (id: number) => { const token = localStorage.getItem("token"); const response = await fetch(`${API_BASE_URL}/admin/services/${id}?lang=${i18n.global.locale.value}`, { method: 'DELETE', headers: { 'Authorization': `Bearer ${token}` } }); if (!response.ok) throw new Error("Failed to delete service"); return response.json(); }; export const adminUploadOrderPhoto = async (orderId: number, formData: FormData) => { const token = localStorage.getItem("token"); const response = await fetch(`${API_BASE_URL}/admin/orders/${orderId}/photos?lang=${i18n.global.locale.value}`, { method: 'POST', headers: { 'Authorization': `Bearer ${token}` }, body: formData }); if (!response.ok) throw new Error("Failed to upload photo"); return response.json(); }; export const adminDeletePhoto = async (photoId: number) => { const token = localStorage.getItem("token"); const response = await fetch(`${API_BASE_URL}/admin/photos/${photoId}?lang=${i18n.global.locale.value}`, { method: 'DELETE', headers: { 'Authorization': `Bearer ${token}` } }); if (!response.ok) throw new Error("Failed to delete photo"); return response.json(); }; export const adminGetAllPhotos = async () => { const token = localStorage.getItem("token"); const response = await fetch(`${API_BASE_URL}/admin/all-photos?lang=${i18n.global.locale.value}`, { headers: { 'Authorization': `Bearer ${token}` } }); if (!response.ok) throw new Error("Failed to fetch all photos"); return response.json(); }; export const adminAttachFile = async (orderId: number, formData: FormData) => { const token = localStorage.getItem("token"); const response = await fetch(`${API_BASE_URL}/orders/${orderId}/attach-file?lang=${i18n.global.locale.value}`, { method: 'POST', headers: { 'Authorization': `Bearer ${token}` }, body: formData }); if (!response.ok) throw new Error("Failed to attach file"); return response.json(); }; export const adminDeleteFile = async (orderId: number, fileId: number) => { const token = localStorage.getItem("token"); const response = await fetch(`${API_BASE_URL}/orders/${orderId}/files/${fileId}?lang=${i18n.global.locale.value}`, { method: 'DELETE', headers: { 'Authorization': `Bearer ${token}` } }); if (!response.ok) throw new Error("Failed to delete file"); return response.json(); }; export const adminUpdatePhotoStatus = async (photoId: number, data: { is_public: boolean }) => { const token = localStorage.getItem("token"); const response = await fetch(`${API_BASE_URL}/admin/photos/${photoId}?lang=${i18n.global.locale.value}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, body: JSON.stringify(data) }); if (!response.ok) throw new Error("Failed to update photo status"); return response.json(); }; export const getPortfolio = async () => { const response = await fetch(`${API_BASE_URL}/portfolio?lang=${i18n.global.locale.value}`); if (!response.ok) throw new Error("Failed to fetch portfolio"); return response.json(); }; export const getOrderDetails = async (orderId: number) => { const response = await fetch(`${API_BASE_URL}/orders/${orderId}?lang=${i18n.global.locale.value}`); if (!response.ok) throw new Error("Failed to fetch order details"); return response.json(); }; export const getOrderMessages = async (orderId: number, lang: string = i18n.global.locale.value) => { const token = localStorage.getItem("token"); const response = await fetch(`${API_BASE_URL}/orders/${orderId}/messages?lang=${lang}`, { headers: { 'Authorization': `Bearer ${token}` } }); if (!response.ok) throw new Error("Failed to fetch messages"); return response.json(); }; export const sendOrderMessage = async (orderId: number, message: string, lang: string = i18n.global.locale.value) => { const token = localStorage.getItem("token"); const response = await fetch(`${API_BASE_URL}/orders/${orderId}/messages?lang=${lang}`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, body: JSON.stringify({ message }) }); if (response.status === 429) { const err = await response.json(); throw new Error(err.detail || "Rate limit exceeded"); } if (!response.ok) throw new Error("Failed to send message"); return response.json(); }; export const authPing = async () => { const token = localStorage.getItem("token"); if (!token) return null; try { const response = await fetch(`${API_BASE_URL}/auth/ping?lang=${i18n.global.locale.value}`, { method: "POST", headers: { "Authorization": `Bearer ${token}` } }); if (response.ok) { return response.json(); } } catch (e) { // silently fail } return null; }; // Blog API export const getBlogPosts = async (publishedOnly: boolean = true) => { const response = await fetch(`${API_BASE_URL}/blog?published_only=${publishedOnly}&lang=${i18n.global.locale.value}`); if (!response.ok) throw new Error("Failed to fetch blog posts"); return response.json(); }; export const getBlogPost = async (idOrSlug: string) => { const response = await fetch(`${API_BASE_URL}/blog/${idOrSlug}?lang=${i18n.global.locale.value}`); if (!response.ok) throw new Error("Failed to fetch blog post"); return response.json(); }; export const adminCreatePost = async (data: any) => { const token = localStorage.getItem("token"); const response = await fetch(`${API_BASE_URL}/blog?lang=${i18n.global.locale.value}`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, body: JSON.stringify(data) }); if (!response.ok) throw new Error("Failed to create blog post"); return response.json(); }; export const adminUpdatePost = async (id: number, data: any) => { const token = localStorage.getItem("token"); const response = await fetch(`${API_BASE_URL}/blog/${id}?lang=${i18n.global.locale.value}`, { method: 'PUT', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, body: JSON.stringify(data) }); if (!response.ok) throw new Error("Failed to update blog post"); return response.json(); }; export const adminDeletePost = async (id: number) => { const token = localStorage.getItem("token"); const response = await fetch(`${API_BASE_URL}/blog/${id}?lang=${i18n.global.locale.value}`, { method: 'DELETE', headers: { 'Authorization': `Bearer ${token}` } }); if (!response.ok) throw new Error("Failed to delete blog post"); return response.json(); }; export const adminGetUsers = async (page = 1, size = 50, search = "") => { const token = localStorage.getItem("token"); const query = new URLSearchParams({ page: page.toString(), size: size.toString(), search }); const response = await fetch(`${API_BASE_URL}/auth/admin/users?${query.toString()}`, { headers: { 'Authorization': `Bearer ${token}` } }); if (!response.ok) throw new Error("Failed to fetch users"); return response.json(); }; export const adminCreateUser = async (data: any) => { const token = localStorage.getItem("token"); const response = await fetch(`${API_BASE_URL}/auth/admin/users`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, body: JSON.stringify(data) }); if (!response.ok) { const err = await response.json(); throw new Error(err.detail || "Failed to create user"); } return response.json(); }; export const adminUpdateUser = async (userId: number, data: any) => { const token = localStorage.getItem("token"); const response = await fetch(`${API_BASE_URL}/auth/users/${userId}/admin?lang=${i18n.global.locale.value}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, body: JSON.stringify(data) }); if (!response.ok) throw new Error("Failed to update user"); return response.json(); }; export const adminGetAuditLogs = async (page = 1, size = 50, action = "") => { const token = localStorage.getItem("token"); const query = new URLSearchParams({ page: page.toString(), size: size.toString() }); if (action) query.append("action", action); const response = await fetch(`${API_BASE_URL}/admin/audit-logs?${query.toString()}`, { headers: { 'Authorization': `Bearer ${token}` } }); if (!response.ok) throw new Error("Failed to fetch audit logs"); return response.json(); }; export const adminGetOrderItems = async (orderId: number) => { const token = localStorage.getItem("token"); const response = await fetch(`${API_BASE_URL}/orders/${orderId}/items?lang=${i18n.global.locale.value}`, { headers: { 'Authorization': `Bearer ${token}` } }); if (!response.ok) throw new Error("Failed to fetch order items"); return response.json(); }; export const adminUpdateOrderItems = async (orderId: number, items: any[]) => { const token = localStorage.getItem("token"); const response = await fetch(`${API_BASE_URL}/orders/${orderId}/items?lang=${i18n.global.locale.value}`, { method: 'PUT', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, body: JSON.stringify(items) }); if (!response.ok) throw new Error("Failed to update order items"); return response.json(); };