api.ts 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804
  1. import i18n from "../i18n";
  2. export const API_BASE_URL = import.meta.env.VITE_API_URL || 'http://localhost:8000';
  3. export const RESOURCES_BASE_URL = API_BASE_URL.replace(/\/api\/?$/, '');
  4. console.log("DEBUG: Resources will be loaded from:", RESOURCES_BASE_URL);
  5. const getErrorMessage = async (response: Response, defaultMsg: string) => {
  6. try {
  7. const errorData = await response.json();
  8. if (errorData && errorData.detail) {
  9. if (Array.isArray(errorData.detail)) {
  10. // FastAPI/Pydantic validation error
  11. const firstError = errorData.detail[0];
  12. const errorType = firstError.type;
  13. const fieldName = firstError.loc[firstError.loc.length - 1];
  14. // Try to translate the error type using i18n
  15. // Pydantic types look like 'string_too_short', 'value_error.email'
  16. const translationKey = `errors.${errorType}`;
  17. const translatedMsg = i18n.global.t(translationKey, {
  18. ...firstError.ctx,
  19. defaultValue: firstError.msg
  20. });
  21. return `${fieldName}: ${translatedMsg}`;
  22. }
  23. return errorData.detail;
  24. }
  25. } catch (e) {}
  26. return defaultMsg;
  27. };
  28. export const getMaterials = async () => {
  29. const response = await fetch(`${API_BASE_URL}/materials?lang=${i18n.global.locale.value}`);
  30. if (!response.ok) {
  31. throw new Error('Failed to fetch materials');
  32. }
  33. return response.json();
  34. };
  35. export const getServices = async () => {
  36. const response = await fetch(`${API_BASE_URL}/services?lang=${i18n.global.locale.value}`);
  37. if (!response.ok) {
  38. throw new Error('Failed to fetch services');
  39. }
  40. return response.json();
  41. };
  42. export const uploadFilesToServer = async (data: FormData) => {
  43. const response = await fetch(`${API_BASE_URL}/files/upload`, {
  44. method: "POST",
  45. body: data,
  46. });
  47. if (!response.ok) throw new Error("Failed to upload files");
  48. return response.json();
  49. };
  50. export const importFromUrl = async (url: string) => {
  51. const token = localStorage.getItem("token");
  52. const response = await fetch(`${API_BASE_URL}/importer/url`, {
  53. method: "POST",
  54. headers: {
  55. "Content-Type": "application/json",
  56. "Authorization": `Bearer ${token}`
  57. },
  58. body: JSON.stringify({ url }),
  59. });
  60. if (!response.ok) throw new Error(await getErrorMessage(response, "Failed to import from URL"));
  61. return response.json();
  62. };
  63. export const submitOrder = async (orderData: FormData) => {
  64. const token = localStorage.getItem("token");
  65. const headers: Record<string, string> = {};
  66. if (token) {
  67. headers['Authorization'] = `Bearer ${token}`;
  68. }
  69. const response = await fetch(`${API_BASE_URL}/orders?lang=${i18n.global.locale.value}`, {
  70. method: 'POST',
  71. body: orderData,
  72. headers: headers,
  73. // Note: Do not set Content-Type header manually when using FormData
  74. // The browser will automatically set it with the correct boundary
  75. });
  76. if (!response.ok) {
  77. throw new Error(await getErrorMessage(response, 'Failed to submit order'));
  78. }
  79. return response.json();
  80. };
  81. export const getMyOrders = async (page = 1, size = 10) => {
  82. const token = localStorage.getItem("token");
  83. if (!token) return { orders: [], total: 0 };
  84. const query = new URLSearchParams({ page: page.toString(), size: size.toString() });
  85. const response = await fetch(`${API_BASE_URL}/orders/my?${query.toString()}&lang=${i18n.global.locale.value}`, {
  86. headers: {
  87. 'Authorization': `Bearer ${token}`
  88. }
  89. });
  90. if (!response.ok) {
  91. throw new Error('Failed to fetch orders');
  92. }
  93. return response.json();
  94. };
  95. export const registerUser = async (userData: any) => {
  96. const response = await fetch(`${API_BASE_URL}/auth/register?lang=${i18n.global.locale.value}`, {
  97. method: 'POST',
  98. headers: {
  99. 'Content-Type': 'application/json'
  100. },
  101. body: JSON.stringify(userData)
  102. });
  103. if (!response.ok) {
  104. throw new Error(await getErrorMessage(response, 'Failed to register'));
  105. }
  106. return response.json();
  107. };
  108. export const submitContactForm = async (formData: FormData) => {
  109. const response = await fetch(`${API_BASE_URL}/contact?lang=${i18n.global.locale.value}`, {
  110. method: 'POST',
  111. body: formData,
  112. });
  113. if (!response.ok) {
  114. throw new Error(await getErrorMessage(response, 'Failed to send message'));
  115. }
  116. return response.json();
  117. };
  118. export const submitOrderReview = async (orderId: number, data: { rating: number, review_text: string }) => {
  119. const token = localStorage.getItem("token");
  120. const response = await fetch(`${API_BASE_URL}/orders/${orderId}/review?lang=${i18n.global.locale.value}`, {
  121. method: 'POST',
  122. headers: {
  123. 'Content-Type': 'application/json',
  124. 'Authorization': `Bearer ${token}`
  125. },
  126. body: JSON.stringify(data),
  127. });
  128. if (!response.ok) {
  129. throw new Error(await getErrorMessage(response, 'Failed to submit review'));
  130. }
  131. return response.json();
  132. };
  133. export const approveOrderReview = async (orderId: number) => {
  134. const token = localStorage.getItem('token');
  135. const response = await fetch(`${API_BASE_URL}/orders/${orderId}/review/approve?lang=${i18n.global.locale.value}`, {
  136. method: 'PATCH',
  137. headers: { 'Authorization': `Bearer ${token}` }
  138. });
  139. if (!response.ok) {
  140. throw new Error(await getErrorMessage(response, 'Failed to approve review'));
  141. }
  142. return response.json();
  143. };
  144. export const getPublicReviews = async () => {
  145. const response = await fetch(`${API_BASE_URL}/orders/reviews/public?lang=${i18n.global.locale.value}`);
  146. if (!response.ok) return [];
  147. return response.json();
  148. };
  149. export const loginUser = async (userData: any) => {
  150. const response = await fetch(`${API_BASE_URL}/auth/login?lang=${i18n.global.locale.value}`, {
  151. method: 'POST',
  152. headers: {
  153. 'Content-Type': 'application/json'
  154. },
  155. body: JSON.stringify(userData),
  156. });
  157. if (!response.ok) {
  158. throw new Error(await getErrorMessage(response, 'Incorrect credentials'));
  159. }
  160. return response.json();
  161. };
  162. export const logoutUser = async () => {
  163. const token = localStorage.getItem("token");
  164. if (!token) return;
  165. await fetch(`${API_BASE_URL}/auth/logout`, {
  166. method: 'POST',
  167. headers: { 'Authorization': `Bearer ${token}` }
  168. });
  169. };
  170. export const socialLogin = async (socialData: any) => {
  171. const response = await fetch(`${API_BASE_URL}/auth/social-login?lang=${i18n.global.locale.value}`, {
  172. method: 'POST',
  173. headers: {
  174. 'Content-Type': 'application/json'
  175. },
  176. body: JSON.stringify(socialData),
  177. });
  178. if (!response.ok) {
  179. throw new Error(await getErrorMessage(response, 'Social login failed'));
  180. }
  181. return response.json();
  182. };
  183. export const verifyEmail = async (token: string) => {
  184. const response = await fetch(`${API_BASE_URL}/auth/verify-email?token=${token}&lang=${i18n.global.locale.value}`);
  185. if (!response.ok) {
  186. throw new Error(await getErrorMessage(response, 'Verification failed'));
  187. }
  188. return response.json();
  189. };
  190. export const getCurrentUser = async () => {
  191. const token = localStorage.getItem("token");
  192. if (!token) return null;
  193. const response = await fetch(`${API_BASE_URL}/auth/me?lang=${i18n.global.locale.value}`, {
  194. headers: {
  195. 'Authorization': `Bearer ${token}`
  196. }
  197. });
  198. if (!response.ok) {
  199. if (response.status === 401) {
  200. localStorage.removeItem("token");
  201. }
  202. return null;
  203. }
  204. return response.json();
  205. };
  206. export const getUnreadCount = async () => {
  207. const token = localStorage.getItem("token");
  208. if (!token) return { count: 0 };
  209. const response = await fetch(`${API_BASE_URL}/auth/unread-count?lang=${i18n.global.locale.value}`, {
  210. headers: {
  211. 'Authorization': `Bearer ${token}`
  212. }
  213. });
  214. if (!response.ok) return { count: 0 };
  215. return response.json();
  216. };
  217. export const updateProfile = async (userData: any) => {
  218. const token = localStorage.getItem("token");
  219. if (!token) throw new Error("No token found");
  220. const response = await fetch(`${API_BASE_URL}/auth/me?lang=${i18n.global.locale.value}`, {
  221. method: 'PUT',
  222. headers: {
  223. 'Content-Type': 'application/json',
  224. 'Authorization': `Bearer ${token}`
  225. },
  226. body: JSON.stringify(userData),
  227. });
  228. if (!response.ok) {
  229. throw new Error(await getErrorMessage(response, 'Failed to update profile'));
  230. }
  231. return response.json();
  232. };
  233. export const forgotPassword = async (email: string) => {
  234. const response = await fetch(`${API_BASE_URL}/auth/forgot-password?lang=${i18n.global.locale.value}`, {
  235. method: "POST",
  236. headers: { "Content-Type": "application/json" },
  237. body: JSON.stringify({ email }),
  238. });
  239. if (!response.ok) {
  240. throw new Error(await getErrorMessage(response, 'Failed to request reset'));
  241. }
  242. return response.json();
  243. };
  244. export const resetPassword = async (data: any) => {
  245. const response = await fetch(`${API_BASE_URL}/auth/reset-password?lang=${i18n.global.locale.value}`, {
  246. method: 'POST',
  247. headers: {
  248. 'Content-Type': 'application/json'
  249. },
  250. body: JSON.stringify(data),
  251. });
  252. if (!response.ok) {
  253. throw new Error(await getErrorMessage(response, 'Failed to reset password'));
  254. }
  255. return response.json();
  256. };
  257. export const verifyResetToken = async (token: string) => {
  258. const response = await fetch(`${API_BASE_URL}/auth/verify-reset-token?lang=${i18n.global.locale.value}`, {
  259. method: 'POST',
  260. headers: { 'Content-Type': 'application/json' },
  261. body: JSON.stringify({ token })
  262. });
  263. if (!response.ok) {
  264. throw new Error(await getErrorMessage(response, 'Invalid or expired token'));
  265. }
  266. return response.json();
  267. };
  268. export const adminGetOrders = async (filters: { search?: string, status?: string, date_from?: string, date_to?: string } = {}) => {
  269. const token = localStorage.getItem("token");
  270. const query = new URLSearchParams();
  271. if (filters.search) query.append("search", filters.search);
  272. if (filters.status) query.append("status", filters.status);
  273. if (filters.date_from) query.append("date_from", filters.date_from);
  274. if (filters.date_to) query.append("date_to", filters.date_to);
  275. const response = await fetch(`${API_BASE_URL}/orders/admin/list?${query.toString()}&lang=${i18n.global.locale.value}`, {
  276. headers: { 'Authorization': `Bearer ${token}` }
  277. });
  278. if (!response.ok) throw new Error("Failed to fetch admin orders");
  279. return response.json();
  280. };
  281. export const adminUpdateOrder = async (orderId: number, data: any) => {
  282. const token = localStorage.getItem("token");
  283. const response = await fetch(`${API_BASE_URL}/orders/${orderId}?lang=${i18n.global.locale.value}`, {
  284. method: 'PATCH',
  285. headers: {
  286. 'Content-Type': 'application/json',
  287. 'Authorization': `Bearer ${token}`
  288. },
  289. body: JSON.stringify(data)
  290. });
  291. if (!response.ok) throw new Error("Failed to update order");
  292. return response.json();
  293. };
  294. export const adminDeleteOrder = async (orderId: number) => {
  295. const token = localStorage.getItem("token");
  296. const response = await fetch(`${API_BASE_URL}/orders/${orderId}/admin?lang=${i18n.global.locale.value}`, {
  297. method: 'DELETE',
  298. headers: { 'Authorization': `Bearer ${token}` }
  299. });
  300. if (!response.ok) throw new Error("Failed to delete order");
  301. return response.json();
  302. };
  303. export const getPriceEstimate = async (data: any) => {
  304. const response = await fetch(`${API_BASE_URL}/orders/estimate?lang=${i18n.global.locale.value}`, {
  305. method: 'POST',
  306. headers: { 'Content-Type': 'application/json' },
  307. body: JSON.stringify(data)
  308. });
  309. if (!response.ok) throw new Error("Failed to get estimate");
  310. return response.json();
  311. };
  312. export const adminGetMaterials = async () => {
  313. const token = localStorage.getItem("token");
  314. const response = await fetch(`${API_BASE_URL}/admin/materials?lang=${i18n.global.locale.value}`, {
  315. headers: { 'Authorization': `Bearer ${token}` }
  316. });
  317. if (!response.ok) throw new Error("Failed to fetch admin materials");
  318. return response.json();
  319. };
  320. export const adminCreateMaterial = async (data: any) => {
  321. const token = localStorage.getItem("token");
  322. const response = await fetch(`${API_BASE_URL}/admin/materials?lang=${i18n.global.locale.value}`, {
  323. method: 'POST',
  324. headers: {
  325. 'Content-Type': 'application/json',
  326. 'Authorization': `Bearer ${token}`
  327. },
  328. body: JSON.stringify(data)
  329. });
  330. if (!response.ok) throw new Error("Failed to create material");
  331. return response.json();
  332. };
  333. export const adminUpdateMaterial = async (id: number, data: any) => {
  334. const token = localStorage.getItem("token");
  335. const response = await fetch(`${API_BASE_URL}/admin/materials/${id}?lang=${i18n.global.locale.value}`, {
  336. method: 'PATCH',
  337. headers: {
  338. 'Content-Type': 'application/json',
  339. 'Authorization': `Bearer ${token}`
  340. },
  341. body: JSON.stringify(data)
  342. });
  343. if (!response.ok) throw new Error("Failed to update material");
  344. return response.json();
  345. };
  346. export const adminGetServices = async () => {
  347. const token = localStorage.getItem("token");
  348. const response = await fetch(`${API_BASE_URL}/admin/services?lang=${i18n.global.locale.value}`, {
  349. headers: { 'Authorization': `Bearer ${token}` }
  350. });
  351. if (!response.ok) throw new Error("Failed to fetch admin services");
  352. return response.json();
  353. };
  354. export const adminCreateService = async (data: any) => {
  355. const token = localStorage.getItem("token");
  356. const response = await fetch(`${API_BASE_URL}/admin/services?lang=${i18n.global.locale.value}`, {
  357. method: 'POST',
  358. headers: {
  359. 'Content-Type': 'application/json',
  360. 'Authorization': `Bearer ${token}`
  361. },
  362. body: JSON.stringify(data)
  363. });
  364. if (!response.ok) throw new Error("Failed to create service");
  365. return response.json();
  366. };
  367. export const adminUpdateService = async (id: number, data: any) => {
  368. const token = localStorage.getItem("token");
  369. const response = await fetch(`${API_BASE_URL}/admin/services/${id}?lang=${i18n.global.locale.value}`, {
  370. method: 'PATCH',
  371. headers: {
  372. 'Content-Type': 'application/json',
  373. 'Authorization': `Bearer ${token}`
  374. },
  375. body: JSON.stringify(data)
  376. });
  377. if (!response.ok) throw new Error("Failed to update service");
  378. return response.json();
  379. };
  380. export const adminDeleteMaterial = async (id: number) => {
  381. const token = localStorage.getItem("token");
  382. const response = await fetch(`${API_BASE_URL}/admin/materials/${id}?lang=${i18n.global.locale.value}`, {
  383. method: 'DELETE',
  384. headers: { 'Authorization': `Bearer ${token}` }
  385. });
  386. if (!response.ok) throw new Error("Failed to delete material");
  387. return response.json();
  388. };
  389. export const adminDeleteService = async (id: number) => {
  390. const token = localStorage.getItem("token");
  391. const response = await fetch(`${API_BASE_URL}/admin/services/${id}?lang=${i18n.global.locale.value}`, {
  392. method: 'DELETE',
  393. headers: { 'Authorization': `Bearer ${token}` }
  394. });
  395. if (!response.ok) throw new Error("Failed to delete service");
  396. return response.json();
  397. };
  398. export const adminUploadOrderPhoto = async (orderId: number, formData: FormData) => {
  399. const token = localStorage.getItem("token");
  400. const response = await fetch(`${API_BASE_URL}/admin/orders/${orderId}/photos?lang=${i18n.global.locale.value}`, {
  401. method: 'POST',
  402. headers: {
  403. 'Authorization': `Bearer ${token}`
  404. },
  405. body: formData
  406. });
  407. if (!response.ok) throw new Error("Failed to upload photo");
  408. return response.json();
  409. };
  410. export const adminDeletePhoto = async (photoId: number) => {
  411. const token = localStorage.getItem("token");
  412. const response = await fetch(`${API_BASE_URL}/admin/photos/${photoId}?lang=${i18n.global.locale.value}`, {
  413. method: 'DELETE',
  414. headers: { 'Authorization': `Bearer ${token}` }
  415. });
  416. if (!response.ok) throw new Error("Failed to delete photo");
  417. return response.json();
  418. };
  419. export const adminGetAllPhotos = async () => {
  420. const token = localStorage.getItem("token");
  421. const response = await fetch(`${API_BASE_URL}/admin/all-photos?lang=${i18n.global.locale.value}`, {
  422. headers: { 'Authorization': `Bearer ${token}` }
  423. });
  424. if (!response.ok) throw new Error("Failed to fetch all photos");
  425. return response.json();
  426. };
  427. export const adminAttachFile = async (orderId: number, formData: FormData) => {
  428. const token = localStorage.getItem("token");
  429. const response = await fetch(`${API_BASE_URL}/orders/${orderId}/attach-file?lang=${i18n.global.locale.value}`, {
  430. method: 'POST',
  431. headers: {
  432. 'Authorization': `Bearer ${token}`
  433. },
  434. body: formData
  435. });
  436. if (!response.ok) throw new Error("Failed to attach file");
  437. return response.json();
  438. };
  439. export const adminDeleteFile = async (orderId: number, fileId: number) => {
  440. const token = localStorage.getItem("token");
  441. const response = await fetch(`${API_BASE_URL}/orders/${orderId}/files/${fileId}?lang=${i18n.global.locale.value}`, {
  442. method: 'DELETE',
  443. headers: {
  444. 'Authorization': `Bearer ${token}`
  445. }
  446. });
  447. if (!response.ok) throw new Error("Failed to delete file");
  448. return response.json();
  449. };
  450. export const adminUpdateFile = async (orderId: number, fileId: number, data: any) => {
  451. const token = localStorage.getItem("token");
  452. const response = await fetch(`${API_BASE_URL}/orders/${orderId}/files/${fileId}?lang=${i18n.global.locale.value}`, {
  453. method: 'PATCH',
  454. headers: {
  455. 'Authorization': `Bearer ${token}`,
  456. 'Content-Type': 'application/json'
  457. },
  458. body: JSON.stringify(data)
  459. });
  460. if (!response.ok) throw new Error("Failed to update file");
  461. return response.json();
  462. };
  463. export const adminUpdatePhotoStatus = async (photoId: number, data: { is_public: boolean, caption?: string }) => {
  464. const token = localStorage.getItem("token");
  465. const response = await fetch(`${API_BASE_URL}/admin/photos/${photoId}?lang=${i18n.global.locale.value}`, {
  466. method: 'PATCH',
  467. headers: {
  468. 'Content-Type': 'application/json',
  469. 'Authorization': `Bearer ${token}`
  470. },
  471. body: JSON.stringify(data)
  472. });
  473. if (!response.ok) throw new Error("Failed to update photo status");
  474. return response.json();
  475. };
  476. export const adminUploadPortfolioPhoto = async (formData: FormData) => {
  477. const token = localStorage.getItem("token");
  478. const response = await fetch(`${API_BASE_URL}/admin/portfolio/upload?lang=${i18n.global.locale.value}`, {
  479. method: 'POST',
  480. headers: {
  481. 'Authorization': `Bearer ${token}`
  482. },
  483. body: formData
  484. });
  485. if (!response.ok) throw new Error("Failed to upload portfolio photo");
  486. return response.json();
  487. };
  488. export const getPortfolio = async () => {
  489. const response = await fetch(`${API_BASE_URL}/portfolio?lang=${i18n.global.locale.value}`);
  490. if (!response.ok) throw new Error("Failed to fetch portfolio");
  491. return response.json();
  492. };
  493. export const getOrderDetails = async (orderId: number) => {
  494. const response = await fetch(`${API_BASE_URL}/orders/${orderId}?lang=${i18n.global.locale.value}`);
  495. if (!response.ok) throw new Error("Failed to fetch order details");
  496. return response.json();
  497. };
  498. export const getOrderMessages = async (orderId: number, lang: string = i18n.global.locale.value) => {
  499. const token = localStorage.getItem("token");
  500. const response = await fetch(`${API_BASE_URL}/orders/${orderId}/messages?lang=${lang}`, {
  501. headers: { 'Authorization': `Bearer ${token}` }
  502. });
  503. if (!response.ok) throw new Error("Failed to fetch messages");
  504. return response.json();
  505. };
  506. export const sendOrderMessage = async (orderId: number, message: string, lang: string = i18n.global.locale.value) => {
  507. const token = localStorage.getItem("token");
  508. const response = await fetch(`${API_BASE_URL}/orders/${orderId}/messages?lang=${lang}`, {
  509. method: 'POST',
  510. headers: {
  511. 'Content-Type': 'application/json',
  512. 'Authorization': `Bearer ${token}`
  513. },
  514. body: JSON.stringify({ message })
  515. });
  516. if (response.status === 429) {
  517. const err = await response.json();
  518. throw new Error(err.detail || "Rate limit exceeded");
  519. }
  520. if (!response.ok) throw new Error("Failed to send message");
  521. return response.json();
  522. };
  523. export const authPing = async () => {
  524. const token = localStorage.getItem("token");
  525. if (!token) return null;
  526. try {
  527. const response = await fetch(`${API_BASE_URL}/auth/ping?lang=${i18n.global.locale.value}`, {
  528. method: "POST",
  529. headers: { "Authorization": `Bearer ${token}` }
  530. });
  531. if (response.ok) {
  532. return response.json();
  533. }
  534. } catch (e) {
  535. // silently fail
  536. }
  537. return null;
  538. };
  539. // Blog API
  540. export const getBlogPosts = async (publishedOnly: boolean = true) => {
  541. const response = await fetch(`${API_BASE_URL}/blog?published_only=${publishedOnly}&lang=${i18n.global.locale.value}`);
  542. if (!response.ok) throw new Error("Failed to fetch blog posts");
  543. return response.json();
  544. };
  545. export const getBlogPost = async (idOrSlug: string) => {
  546. const response = await fetch(`${API_BASE_URL}/blog/${idOrSlug}?lang=${i18n.global.locale.value}`);
  547. if (!response.ok) throw new Error("Failed to fetch blog post");
  548. return response.json();
  549. };
  550. export const adminCreatePost = async (data: any) => {
  551. const token = localStorage.getItem("token");
  552. const response = await fetch(`${API_BASE_URL}/blog?lang=${i18n.global.locale.value}`, {
  553. method: 'POST',
  554. headers: {
  555. 'Content-Type': 'application/json',
  556. 'Authorization': `Bearer ${token}`
  557. },
  558. body: JSON.stringify(data)
  559. });
  560. if (!response.ok) throw new Error("Failed to create blog post");
  561. return response.json();
  562. };
  563. export const adminUpdatePost = async (id: number, data: any) => {
  564. const token = localStorage.getItem("token");
  565. const response = await fetch(`${API_BASE_URL}/blog/${id}?lang=${i18n.global.locale.value}`, {
  566. method: 'PUT',
  567. headers: {
  568. 'Content-Type': 'application/json',
  569. 'Authorization': `Bearer ${token}`
  570. },
  571. body: JSON.stringify(data)
  572. });
  573. if (!response.ok) throw new Error("Failed to update blog post");
  574. return response.json();
  575. };
  576. export const adminDeletePost = async (id: number) => {
  577. const token = localStorage.getItem("token");
  578. const response = await fetch(`${API_BASE_URL}/blog/${id}?lang=${i18n.global.locale.value}`, {
  579. method: 'DELETE',
  580. headers: { 'Authorization': `Bearer ${token}` }
  581. });
  582. if (!response.ok) throw new Error("Failed to delete blog post");
  583. return response.json();
  584. };
  585. export const adminGetUsers = async (page = 1, size = 50, search = "") => {
  586. const token = localStorage.getItem("token");
  587. const query = new URLSearchParams({ page: page.toString(), size: size.toString(), search });
  588. const response = await fetch(`${API_BASE_URL}/auth/admin/users?${query.toString()}`, {
  589. headers: { 'Authorization': `Bearer ${token}` }
  590. });
  591. if (!response.ok) throw new Error("Failed to fetch users");
  592. return response.json();
  593. };
  594. export const adminCreateUser = async (data: any) => {
  595. const token = localStorage.getItem("token");
  596. const response = await fetch(`${API_BASE_URL}/auth/admin/users`, {
  597. method: 'POST',
  598. headers: {
  599. 'Content-Type': 'application/json',
  600. 'Authorization': `Bearer ${token}`
  601. },
  602. body: JSON.stringify(data)
  603. });
  604. if (!response.ok) {
  605. const err = await response.json();
  606. throw new Error(err.detail || "Failed to create user");
  607. }
  608. return response.json();
  609. };
  610. export const adminUpdateUser = async (userId: number, data: any) => {
  611. const token = localStorage.getItem("token");
  612. const response = await fetch(`${API_BASE_URL}/auth/users/${userId}/admin?lang=${i18n.global.locale.value}`, {
  613. method: 'PATCH',
  614. headers: {
  615. 'Content-Type': 'application/json',
  616. 'Authorization': `Bearer ${token}`
  617. },
  618. body: JSON.stringify(data)
  619. });
  620. if (!response.ok) throw new Error("Failed to update user");
  621. return response.json();
  622. };
  623. export const adminGetReviews = async (page = 1, size = 50) => {
  624. const token = localStorage.getItem("token");
  625. const query = new URLSearchParams({ page: page.toString(), size: size.toString() });
  626. const response = await fetch(`${API_BASE_URL}/admin/reviews?${query.toString()}&lang=${i18n.global.locale.value}`, {
  627. headers: { 'Authorization': `Bearer ${token}` }
  628. });
  629. if (!response.ok) throw new Error("Failed to fetch admin reviews");
  630. return response.json();
  631. };
  632. export const adminGetAuditLogs = async (page = 1, size = 50, action = "") => {
  633. const token = localStorage.getItem("token");
  634. const query = new URLSearchParams({ page: page.toString(), size: size.toString() });
  635. if (action) query.append("action", action);
  636. const response = await fetch(`${API_BASE_URL}/admin/audit-logs?${query.toString()}`, {
  637. headers: { 'Authorization': `Bearer ${token}` }
  638. });
  639. if (!response.ok) throw new Error("Failed to fetch audit logs");
  640. return response.json();
  641. };
  642. export const adminGetOrderItems = async (orderId: number) => {
  643. const token = localStorage.getItem("token");
  644. const response = await fetch(`${API_BASE_URL}/orders/${orderId}/items?lang=${i18n.global.locale.value}`, {
  645. headers: { 'Authorization': `Bearer ${token}` }
  646. });
  647. if (!response.ok) throw new Error("Failed to fetch order items");
  648. return response.json();
  649. };
  650. export const adminUpdateOrderItems = async (orderId: number, items: any[]) => {
  651. const token = localStorage.getItem("token");
  652. const response = await fetch(`${API_BASE_URL}/orders/${orderId}/items?lang=${i18n.global.locale.value}`, {
  653. method: 'PUT',
  654. headers: {
  655. 'Content-Type': 'application/json',
  656. 'Authorization': `Bearer ${token}`
  657. },
  658. body: JSON.stringify(items)
  659. });
  660. if (!response.ok) throw new Error("Failed to update order items");
  661. return response.json();
  662. };
  663. export const adminGetWarehouseStock = async (page = 1, size = 50, materialId?: number) => {
  664. const token = localStorage.getItem("token");
  665. const query = new URLSearchParams({ page: page.toString(), size: size.toString() });
  666. if (materialId) query.append("material_id", materialId.toString());
  667. const response = await fetch(`${API_BASE_URL}/admin/warehouse/stock?${query.toString()}`, {
  668. headers: { 'Authorization': `Bearer ${token}` }
  669. });
  670. if (!response.ok) throw new Error("Failed to fetch warehouse stock");
  671. return response.json();
  672. };
  673. export const adminAddWarehouseStock = async (data: any) => {
  674. const token = localStorage.getItem("token");
  675. const response = await fetch(`${API_BASE_URL}/admin/warehouse/stock`, {
  676. method: 'POST',
  677. headers: {
  678. 'Content-Type': 'application/json',
  679. 'Authorization': `Bearer ${token}`
  680. },
  681. body: JSON.stringify(data)
  682. });
  683. if (!response.ok) throw new Error(await getErrorMessage(response, "Failed to add stock item"));
  684. return response.json();
  685. };
  686. export const adminUpdateWarehouseStock = async (itemId: number, data: any) => {
  687. const token = localStorage.getItem("token");
  688. const response = await fetch(`${API_BASE_URL}/admin/warehouse/stock/${itemId}`, {
  689. method: 'PATCH',
  690. headers: {
  691. 'Content-Type': 'application/json',
  692. 'Authorization': `Bearer ${token}`
  693. },
  694. body: JSON.stringify(data)
  695. });
  696. if (!response.ok) throw new Error("Failed to update stock item");
  697. return response.json();
  698. };
  699. export const adminDeleteWarehouseStock = async (itemId: number) => {
  700. const token = localStorage.getItem("token");
  701. const response = await fetch(`${API_BASE_URL}/admin/warehouse/stock/${itemId}`, {
  702. method: 'DELETE',
  703. headers: { 'Authorization': `Bearer ${token}` }
  704. });
  705. if (!response.ok) throw new Error("Failed to delete stock item");
  706. return response.json();
  707. };
  708. export const adminGetWarehouseReport = async () => {
  709. const token = localStorage.getItem("token");
  710. const response = await fetch(`${API_BASE_URL}/admin/warehouse/report`, {
  711. headers: { 'Authorization': `Bearer ${token}` }
  712. });
  713. if (!response.ok) throw new Error("Failed to generate report");
  714. return response.json();
  715. };