api.ts 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638
  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 submitOrder = async (orderData: FormData) => {
  51. const token = localStorage.getItem("token");
  52. const headers: Record<string, string> = {};
  53. if (token) {
  54. headers['Authorization'] = `Bearer ${token}`;
  55. }
  56. const response = await fetch(`${API_BASE_URL}/orders?lang=${i18n.global.locale.value}`, {
  57. method: 'POST',
  58. body: orderData,
  59. headers: headers,
  60. // Note: Do not set Content-Type header manually when using FormData
  61. // The browser will automatically set it with the correct boundary
  62. });
  63. if (!response.ok) {
  64. throw new Error(await getErrorMessage(response, 'Failed to submit order'));
  65. }
  66. return response.json();
  67. };
  68. export const getMyOrders = async (page = 1, size = 10) => {
  69. const token = localStorage.getItem("token");
  70. if (!token) return { orders: [], total: 0 };
  71. const query = new URLSearchParams({ page: page.toString(), size: size.toString() });
  72. const response = await fetch(`${API_BASE_URL}/orders/my?${query.toString()}&lang=${i18n.global.locale.value}`, {
  73. headers: {
  74. 'Authorization': `Bearer ${token}`
  75. }
  76. });
  77. if (!response.ok) {
  78. throw new Error('Failed to fetch orders');
  79. }
  80. return response.json();
  81. };
  82. export const registerUser = async (userData: any) => {
  83. const response = await fetch(`${API_BASE_URL}/auth/register?lang=${i18n.global.locale.value}`, {
  84. method: 'POST',
  85. headers: {
  86. 'Content-Type': 'application/json'
  87. },
  88. body: JSON.stringify(userData)
  89. });
  90. if (!response.ok) {
  91. throw new Error(await getErrorMessage(response, 'Failed to register'));
  92. }
  93. return response.json();
  94. };
  95. export const submitContactForm = async (formData: FormData) => {
  96. const response = await fetch(`${API_BASE_URL}/contact?lang=${i18n.global.locale.value}`, {
  97. method: 'POST',
  98. body: formData,
  99. });
  100. if (!response.ok) {
  101. throw new Error(await getErrorMessage(response, 'Failed to send message'));
  102. }
  103. return response.json();
  104. };
  105. headers: {
  106. 'Content-Type': 'application/json'
  107. },
  108. body: JSON.stringify(userData),
  109. });
  110. if (!response.ok) {
  111. throw new Error(await getErrorMessage(response, 'Failed to register'));
  112. }
  113. return response.json();
  114. };
  115. export const loginUser = async (userData: any) => {
  116. const response = await fetch(`${API_BASE_URL}/auth/login?lang=${i18n.global.locale.value}`, {
  117. method: 'POST',
  118. headers: {
  119. 'Content-Type': 'application/json'
  120. },
  121. body: JSON.stringify(userData),
  122. });
  123. if (!response.ok) {
  124. throw new Error(await getErrorMessage(response, 'Incorrect credentials'));
  125. }
  126. return response.json();
  127. };
  128. export const logoutUser = async () => {
  129. const token = localStorage.getItem("token");
  130. if (!token) return;
  131. await fetch(`${API_BASE_URL}/auth/logout`, {
  132. method: 'POST',
  133. headers: { 'Authorization': `Bearer ${token}` }
  134. });
  135. };
  136. export const socialLogin = async (socialData: any) => {
  137. const response = await fetch(`${API_BASE_URL}/auth/social-login?lang=${i18n.global.locale.value}`, {
  138. method: 'POST',
  139. headers: {
  140. 'Content-Type': 'application/json'
  141. },
  142. body: JSON.stringify(socialData),
  143. });
  144. if (!response.ok) {
  145. throw new Error(await getErrorMessage(response, 'Social login failed'));
  146. }
  147. return response.json();
  148. };
  149. export const verifyEmail = async (token: string) => {
  150. const response = await fetch(`${API_BASE_URL}/auth/verify-email?token=${token}&lang=${i18n.global.locale.value}`);
  151. if (!response.ok) {
  152. throw new Error(await getErrorMessage(response, 'Verification failed'));
  153. }
  154. return response.json();
  155. };
  156. export const getCurrentUser = async () => {
  157. const token = localStorage.getItem("token");
  158. if (!token) return null;
  159. const response = await fetch(`${API_BASE_URL}/auth/me?lang=${i18n.global.locale.value}`, {
  160. headers: {
  161. 'Authorization': `Bearer ${token}`
  162. }
  163. });
  164. if (!response.ok) {
  165. if (response.status === 401) {
  166. localStorage.removeItem("token");
  167. }
  168. return null;
  169. }
  170. return response.json();
  171. };
  172. export const updateProfile = async (userData: any) => {
  173. const token = localStorage.getItem("token");
  174. if (!token) throw new Error("No token found");
  175. const response = await fetch(`${API_BASE_URL}/auth/me?lang=${i18n.global.locale.value}`, {
  176. method: 'PUT',
  177. headers: {
  178. 'Content-Type': 'application/json',
  179. 'Authorization': `Bearer ${token}`
  180. },
  181. body: JSON.stringify(userData),
  182. });
  183. if (!response.ok) {
  184. throw new Error(await getErrorMessage(response, 'Failed to update profile'));
  185. }
  186. return response.json();
  187. };
  188. export const forgotPassword = async (email: string) => {
  189. const response = await fetch(`${API_BASE_URL}/auth/forgot-password?lang=${i18n.global.locale.value}`, {
  190. method: "POST",
  191. headers: { "Content-Type": "application/json" },
  192. body: JSON.stringify({ email }),
  193. });
  194. if (!response.ok) {
  195. throw new Error(await getErrorMessage(response, 'Failed to request reset'));
  196. }
  197. return response.json();
  198. };
  199. export const resetPassword = async (data: any) => {
  200. const response = await fetch(`${API_BASE_URL}/auth/reset-password?lang=${i18n.global.locale.value}`, {
  201. method: 'POST',
  202. headers: {
  203. 'Content-Type': 'application/json'
  204. },
  205. body: JSON.stringify(data),
  206. });
  207. if (!response.ok) {
  208. throw new Error(await getErrorMessage(response, 'Failed to reset password'));
  209. }
  210. return response.json();
  211. };
  212. export const adminGetOrders = async (filters: { search?: string, status?: string, date_from?: string, date_to?: string } = {}) => {
  213. const token = localStorage.getItem("token");
  214. const query = new URLSearchParams();
  215. if (filters.search) query.append("search", filters.search);
  216. if (filters.status) query.append("status", filters.status);
  217. if (filters.date_from) query.append("date_from", filters.date_from);
  218. if (filters.date_to) query.append("date_to", filters.date_to);
  219. const response = await fetch(`${API_BASE_URL}/orders/admin/list?${query.toString()}&lang=${i18n.global.locale.value}`, {
  220. headers: { 'Authorization': `Bearer ${token}` }
  221. });
  222. if (!response.ok) throw new Error("Failed to fetch admin orders");
  223. return response.json();
  224. };
  225. export const adminUpdateOrder = async (orderId: number, data: any) => {
  226. const token = localStorage.getItem("token");
  227. const response = await fetch(`${API_BASE_URL}/orders/${orderId}?lang=${i18n.global.locale.value}`, {
  228. method: 'PATCH',
  229. headers: {
  230. 'Content-Type': 'application/json',
  231. 'Authorization': `Bearer ${token}`
  232. },
  233. body: JSON.stringify(data)
  234. });
  235. if (!response.ok) throw new Error("Failed to update order");
  236. return response.json();
  237. };
  238. export const adminDeleteOrder = async (orderId: number) => {
  239. const token = localStorage.getItem("token");
  240. const response = await fetch(`${API_BASE_URL}/orders/${orderId}/admin?lang=${i18n.global.locale.value}`, {
  241. method: 'DELETE',
  242. headers: { 'Authorization': `Bearer ${token}` }
  243. });
  244. if (!response.ok) throw new Error("Failed to delete order");
  245. return response.json();
  246. };
  247. export const getPriceEstimate = async (data: any) => {
  248. const response = await fetch(`${API_BASE_URL}/orders/estimate?lang=${i18n.global.locale.value}`, {
  249. method: 'POST',
  250. headers: { 'Content-Type': 'application/json' },
  251. body: JSON.stringify(data)
  252. });
  253. if (!response.ok) throw new Error("Failed to get estimate");
  254. return response.json();
  255. };
  256. export const adminGetMaterials = async () => {
  257. const token = localStorage.getItem("token");
  258. const response = await fetch(`${API_BASE_URL}/admin/materials?lang=${i18n.global.locale.value}`, {
  259. headers: { 'Authorization': `Bearer ${token}` }
  260. });
  261. if (!response.ok) throw new Error("Failed to fetch admin materials");
  262. return response.json();
  263. };
  264. export const adminCreateMaterial = async (data: any) => {
  265. const token = localStorage.getItem("token");
  266. const response = await fetch(`${API_BASE_URL}/admin/materials?lang=${i18n.global.locale.value}`, {
  267. method: 'POST',
  268. headers: {
  269. 'Content-Type': 'application/json',
  270. 'Authorization': `Bearer ${token}`
  271. },
  272. body: JSON.stringify(data)
  273. });
  274. if (!response.ok) throw new Error("Failed to create material");
  275. return response.json();
  276. };
  277. export const adminUpdateMaterial = async (id: number, data: any) => {
  278. const token = localStorage.getItem("token");
  279. const response = await fetch(`${API_BASE_URL}/admin/materials/${id}?lang=${i18n.global.locale.value}`, {
  280. method: 'PATCH',
  281. headers: {
  282. 'Content-Type': 'application/json',
  283. 'Authorization': `Bearer ${token}`
  284. },
  285. body: JSON.stringify(data)
  286. });
  287. if (!response.ok) throw new Error("Failed to update material");
  288. return response.json();
  289. };
  290. export const adminGetServices = async () => {
  291. const token = localStorage.getItem("token");
  292. const response = await fetch(`${API_BASE_URL}/admin/services?lang=${i18n.global.locale.value}`, {
  293. headers: { 'Authorization': `Bearer ${token}` }
  294. });
  295. if (!response.ok) throw new Error("Failed to fetch admin services");
  296. return response.json();
  297. };
  298. export const adminCreateService = async (data: any) => {
  299. const token = localStorage.getItem("token");
  300. const response = await fetch(`${API_BASE_URL}/admin/services?lang=${i18n.global.locale.value}`, {
  301. method: 'POST',
  302. headers: {
  303. 'Content-Type': 'application/json',
  304. 'Authorization': `Bearer ${token}`
  305. },
  306. body: JSON.stringify(data)
  307. });
  308. if (!response.ok) throw new Error("Failed to create service");
  309. return response.json();
  310. };
  311. export const adminUpdateService = async (id: number, data: any) => {
  312. const token = localStorage.getItem("token");
  313. const response = await fetch(`${API_BASE_URL}/admin/services/${id}?lang=${i18n.global.locale.value}`, {
  314. method: 'PATCH',
  315. headers: {
  316. 'Content-Type': 'application/json',
  317. 'Authorization': `Bearer ${token}`
  318. },
  319. body: JSON.stringify(data)
  320. });
  321. if (!response.ok) throw new Error("Failed to update service");
  322. return response.json();
  323. };
  324. export const adminDeleteMaterial = async (id: number) => {
  325. const token = localStorage.getItem("token");
  326. const response = await fetch(`${API_BASE_URL}/admin/materials/${id}?lang=${i18n.global.locale.value}`, {
  327. method: 'DELETE',
  328. headers: { 'Authorization': `Bearer ${token}` }
  329. });
  330. if (!response.ok) throw new Error("Failed to delete material");
  331. return response.json();
  332. };
  333. export const adminDeleteService = async (id: number) => {
  334. const token = localStorage.getItem("token");
  335. const response = await fetch(`${API_BASE_URL}/admin/services/${id}?lang=${i18n.global.locale.value}`, {
  336. method: 'DELETE',
  337. headers: { 'Authorization': `Bearer ${token}` }
  338. });
  339. if (!response.ok) throw new Error("Failed to delete service");
  340. return response.json();
  341. };
  342. export const adminUploadOrderPhoto = async (orderId: number, formData: FormData) => {
  343. const token = localStorage.getItem("token");
  344. const response = await fetch(`${API_BASE_URL}/admin/orders/${orderId}/photos?lang=${i18n.global.locale.value}`, {
  345. method: 'POST',
  346. headers: {
  347. 'Authorization': `Bearer ${token}`
  348. },
  349. body: formData
  350. });
  351. if (!response.ok) throw new Error("Failed to upload photo");
  352. return response.json();
  353. };
  354. export const adminDeletePhoto = async (photoId: number) => {
  355. const token = localStorage.getItem("token");
  356. const response = await fetch(`${API_BASE_URL}/admin/photos/${photoId}?lang=${i18n.global.locale.value}`, {
  357. method: 'DELETE',
  358. headers: { 'Authorization': `Bearer ${token}` }
  359. });
  360. if (!response.ok) throw new Error("Failed to delete photo");
  361. return response.json();
  362. };
  363. export const adminGetAllPhotos = async () => {
  364. const token = localStorage.getItem("token");
  365. const response = await fetch(`${API_BASE_URL}/admin/all-photos?lang=${i18n.global.locale.value}`, {
  366. headers: { 'Authorization': `Bearer ${token}` }
  367. });
  368. if (!response.ok) throw new Error("Failed to fetch all photos");
  369. return response.json();
  370. };
  371. export const adminAttachFile = async (orderId: number, formData: FormData) => {
  372. const token = localStorage.getItem("token");
  373. const response = await fetch(`${API_BASE_URL}/orders/${orderId}/attach-file?lang=${i18n.global.locale.value}`, {
  374. method: 'POST',
  375. headers: {
  376. 'Authorization': `Bearer ${token}`
  377. },
  378. body: formData
  379. });
  380. if (!response.ok) throw new Error("Failed to attach file");
  381. return response.json();
  382. };
  383. export const adminDeleteFile = async (orderId: number, fileId: number) => {
  384. const token = localStorage.getItem("token");
  385. const response = await fetch(`${API_BASE_URL}/orders/${orderId}/files/${fileId}?lang=${i18n.global.locale.value}`, {
  386. method: 'DELETE',
  387. headers: {
  388. 'Authorization': `Bearer ${token}`
  389. }
  390. });
  391. if (!response.ok) throw new Error("Failed to delete file");
  392. return response.json();
  393. };
  394. export const adminUpdatePhotoStatus = async (photoId: number, data: { is_public: boolean }) => {
  395. const token = localStorage.getItem("token");
  396. const response = await fetch(`${API_BASE_URL}/admin/photos/${photoId}?lang=${i18n.global.locale.value}`, {
  397. method: 'PATCH',
  398. headers: {
  399. 'Content-Type': 'application/json',
  400. 'Authorization': `Bearer ${token}`
  401. },
  402. body: JSON.stringify(data)
  403. });
  404. if (!response.ok) throw new Error("Failed to update photo status");
  405. return response.json();
  406. };
  407. export const getPortfolio = async () => {
  408. const response = await fetch(`${API_BASE_URL}/portfolio?lang=${i18n.global.locale.value}`);
  409. if (!response.ok) throw new Error("Failed to fetch portfolio");
  410. return response.json();
  411. };
  412. export const getOrderDetails = async (orderId: number) => {
  413. const response = await fetch(`${API_BASE_URL}/orders/${orderId}?lang=${i18n.global.locale.value}`);
  414. if (!response.ok) throw new Error("Failed to fetch order details");
  415. return response.json();
  416. };
  417. export const getOrderMessages = async (orderId: number, lang: string = i18n.global.locale.value) => {
  418. const token = localStorage.getItem("token");
  419. const response = await fetch(`${API_BASE_URL}/orders/${orderId}/messages?lang=${lang}`, {
  420. headers: { 'Authorization': `Bearer ${token}` }
  421. });
  422. if (!response.ok) throw new Error("Failed to fetch messages");
  423. return response.json();
  424. };
  425. export const sendOrderMessage = async (orderId: number, message: string, lang: string = i18n.global.locale.value) => {
  426. const token = localStorage.getItem("token");
  427. const response = await fetch(`${API_BASE_URL}/orders/${orderId}/messages?lang=${lang}`, {
  428. method: 'POST',
  429. headers: {
  430. 'Content-Type': 'application/json',
  431. 'Authorization': `Bearer ${token}`
  432. },
  433. body: JSON.stringify({ message })
  434. });
  435. if (response.status === 429) {
  436. const err = await response.json();
  437. throw new Error(err.detail || "Rate limit exceeded");
  438. }
  439. if (!response.ok) throw new Error("Failed to send message");
  440. return response.json();
  441. };
  442. export const authPing = async () => {
  443. const token = localStorage.getItem("token");
  444. if (!token) return null;
  445. try {
  446. const response = await fetch(`${API_BASE_URL}/auth/ping?lang=${i18n.global.locale.value}`, {
  447. method: "POST",
  448. headers: { "Authorization": `Bearer ${token}` }
  449. });
  450. if (response.ok) {
  451. return response.json();
  452. }
  453. } catch (e) {
  454. // silently fail
  455. }
  456. return null;
  457. };
  458. // Blog API
  459. export const getBlogPosts = async (publishedOnly: boolean = true) => {
  460. const response = await fetch(`${API_BASE_URL}/blog?published_only=${publishedOnly}&lang=${i18n.global.locale.value}`);
  461. if (!response.ok) throw new Error("Failed to fetch blog posts");
  462. return response.json();
  463. };
  464. export const getBlogPost = async (idOrSlug: string) => {
  465. const response = await fetch(`${API_BASE_URL}/blog/${idOrSlug}?lang=${i18n.global.locale.value}`);
  466. if (!response.ok) throw new Error("Failed to fetch blog post");
  467. return response.json();
  468. };
  469. export const adminCreatePost = async (data: any) => {
  470. const token = localStorage.getItem("token");
  471. const response = await fetch(`${API_BASE_URL}/blog?lang=${i18n.global.locale.value}`, {
  472. method: 'POST',
  473. headers: {
  474. 'Content-Type': 'application/json',
  475. 'Authorization': `Bearer ${token}`
  476. },
  477. body: JSON.stringify(data)
  478. });
  479. if (!response.ok) throw new Error("Failed to create blog post");
  480. return response.json();
  481. };
  482. export const adminUpdatePost = async (id: number, data: any) => {
  483. const token = localStorage.getItem("token");
  484. const response = await fetch(`${API_BASE_URL}/blog/${id}?lang=${i18n.global.locale.value}`, {
  485. method: 'PUT',
  486. headers: {
  487. 'Content-Type': 'application/json',
  488. 'Authorization': `Bearer ${token}`
  489. },
  490. body: JSON.stringify(data)
  491. });
  492. if (!response.ok) throw new Error("Failed to update blog post");
  493. return response.json();
  494. };
  495. export const adminDeletePost = async (id: number) => {
  496. const token = localStorage.getItem("token");
  497. const response = await fetch(`${API_BASE_URL}/blog/${id}?lang=${i18n.global.locale.value}`, {
  498. method: 'DELETE',
  499. headers: { 'Authorization': `Bearer ${token}` }
  500. });
  501. if (!response.ok) throw new Error("Failed to delete blog post");
  502. return response.json();
  503. };
  504. export const adminGetUsers = async (page = 1, size = 50, search = "") => {
  505. const token = localStorage.getItem("token");
  506. const query = new URLSearchParams({ page: page.toString(), size: size.toString(), search });
  507. const response = await fetch(`${API_BASE_URL}/auth/admin/users?${query.toString()}`, {
  508. headers: { 'Authorization': `Bearer ${token}` }
  509. });
  510. if (!response.ok) throw new Error("Failed to fetch users");
  511. return response.json();
  512. };
  513. export const adminCreateUser = async (data: any) => {
  514. const token = localStorage.getItem("token");
  515. const response = await fetch(`${API_BASE_URL}/auth/admin/users`, {
  516. method: 'POST',
  517. headers: {
  518. 'Content-Type': 'application/json',
  519. 'Authorization': `Bearer ${token}`
  520. },
  521. body: JSON.stringify(data)
  522. });
  523. if (!response.ok) {
  524. const err = await response.json();
  525. throw new Error(err.detail || "Failed to create user");
  526. }
  527. return response.json();
  528. };
  529. export const adminUpdateUser = async (userId: number, data: any) => {
  530. const token = localStorage.getItem("token");
  531. const response = await fetch(`${API_BASE_URL}/auth/users/${userId}/admin?lang=${i18n.global.locale.value}`, {
  532. method: 'PATCH',
  533. headers: {
  534. 'Content-Type': 'application/json',
  535. 'Authorization': `Bearer ${token}`
  536. },
  537. body: JSON.stringify(data)
  538. });
  539. if (!response.ok) throw new Error("Failed to update user");
  540. return response.json();
  541. };
  542. export const adminGetAuditLogs = async (page = 1, size = 50, action = "") => {
  543. const token = localStorage.getItem("token");
  544. const query = new URLSearchParams({ page: page.toString(), size: size.toString() });
  545. if (action) query.append("action", action);
  546. const response = await fetch(`${API_BASE_URL}/admin/audit-logs?${query.toString()}`, {
  547. headers: { 'Authorization': `Bearer ${token}` }
  548. });
  549. if (!response.ok) throw new Error("Failed to fetch audit logs");
  550. return response.json();
  551. };
  552. export const adminGetOrderItems = async (orderId: number) => {
  553. const token = localStorage.getItem("token");
  554. const response = await fetch(`${API_BASE_URL}/orders/${orderId}/items?lang=${i18n.global.locale.value}`, {
  555. headers: { 'Authorization': `Bearer ${token}` }
  556. });
  557. if (!response.ok) throw new Error("Failed to fetch order items");
  558. return response.json();
  559. };
  560. export const adminUpdateOrderItems = async (orderId: number, items: any[]) => {
  561. const token = localStorage.getItem("token");
  562. const response = await fetch(`${API_BASE_URL}/orders/${orderId}/items?lang=${i18n.global.locale.value}`, {
  563. method: 'PUT',
  564. headers: {
  565. 'Content-Type': 'application/json',
  566. 'Authorization': `Bearer ${token}`
  567. },
  568. body: JSON.stringify(items)
  569. });
  570. if (!response.ok) throw new Error("Failed to update order items");
  571. return response.json();
  572. };