@classytic/commerce-sdk 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. package/LICENSE +14 -0
  2. package/README.md +164 -0
  3. package/dist/adjustment-MNH3AT6S.js +5 -0
  4. package/dist/adjustment-MNH3AT6S.js.map +1 -0
  5. package/dist/analytics/index.d.ts +27 -0
  6. package/dist/analytics/index.js +6 -0
  7. package/dist/analytics/index.js.map +1 -0
  8. package/dist/analytics-DMcD-o8w.d.ts +76 -0
  9. package/dist/api-factory-B_h4RKBm.d.ts +280 -0
  10. package/dist/auth/index.d.ts +39 -0
  11. package/dist/auth/index.js +5 -0
  12. package/dist/auth/index.js.map +1 -0
  13. package/dist/catalog/index.d.ts +571 -0
  14. package/dist/catalog/index.js +9 -0
  15. package/dist/catalog/index.js.map +1 -0
  16. package/dist/chunk-24FDD6UR.js +75 -0
  17. package/dist/chunk-24FDD6UR.js.map +1 -0
  18. package/dist/chunk-2TF7QNYV.js +159 -0
  19. package/dist/chunk-2TF7QNYV.js.map +1 -0
  20. package/dist/chunk-2YAZ5WG6.js +479 -0
  21. package/dist/chunk-2YAZ5WG6.js.map +1 -0
  22. package/dist/chunk-36NLLAVH.js +177 -0
  23. package/dist/chunk-36NLLAVH.js.map +1 -0
  24. package/dist/chunk-3OYSJB3P.js +126 -0
  25. package/dist/chunk-3OYSJB3P.js.map +1 -0
  26. package/dist/chunk-5E57JODA.js +135 -0
  27. package/dist/chunk-5E57JODA.js.map +1 -0
  28. package/dist/chunk-7LZCW4VF.js +13 -0
  29. package/dist/chunk-7LZCW4VF.js.map +1 -0
  30. package/dist/chunk-ANYGZ6O5.js +830 -0
  31. package/dist/chunk-ANYGZ6O5.js.map +1 -0
  32. package/dist/chunk-AQAISI4F.js +183 -0
  33. package/dist/chunk-AQAISI4F.js.map +1 -0
  34. package/dist/chunk-B6MPVOV7.js +328 -0
  35. package/dist/chunk-B6MPVOV7.js.map +1 -0
  36. package/dist/chunk-CILP56G2.js +94 -0
  37. package/dist/chunk-CILP56G2.js.map +1 -0
  38. package/dist/chunk-ERQ52WHY.js +534 -0
  39. package/dist/chunk-ERQ52WHY.js.map +1 -0
  40. package/dist/chunk-FOTUJPM4.js +640 -0
  41. package/dist/chunk-FOTUJPM4.js.map +1 -0
  42. package/dist/chunk-IHCBBLLW.js +198 -0
  43. package/dist/chunk-IHCBBLLW.js.map +1 -0
  44. package/dist/chunk-J4JBQET2.js +76 -0
  45. package/dist/chunk-J4JBQET2.js.map +1 -0
  46. package/dist/chunk-L4OEI4VZ.js +123 -0
  47. package/dist/chunk-L4OEI4VZ.js.map +1 -0
  48. package/dist/chunk-LRV7MWWX.js +616 -0
  49. package/dist/chunk-LRV7MWWX.js.map +1 -0
  50. package/dist/chunk-N43VE355.js +126 -0
  51. package/dist/chunk-N43VE355.js.map +1 -0
  52. package/dist/chunk-PYYLHUV6.js +3 -0
  53. package/dist/chunk-PYYLHUV6.js.map +1 -0
  54. package/dist/chunk-QCTXAMLA.js +261 -0
  55. package/dist/chunk-QCTXAMLA.js.map +1 -0
  56. package/dist/chunk-RIKAPJNG.js +40 -0
  57. package/dist/chunk-RIKAPJNG.js.map +1 -0
  58. package/dist/chunk-U3XT35GZ.js +202 -0
  59. package/dist/chunk-U3XT35GZ.js.map +1 -0
  60. package/dist/chunk-W22WB3WZ.js +148 -0
  61. package/dist/chunk-W22WB3WZ.js.map +1 -0
  62. package/dist/chunk-WTIJMKML.js +27 -0
  63. package/dist/chunk-WTIJMKML.js.map +1 -0
  64. package/dist/chunk-X2CQFJPR.js +75 -0
  65. package/dist/chunk-X2CQFJPR.js.map +1 -0
  66. package/dist/chunk-YYFKLOKO.js +769 -0
  67. package/dist/chunk-YYFKLOKO.js.map +1 -0
  68. package/dist/client-Cs7E_usr.d.ts +113 -0
  69. package/dist/content/index.d.ts +309 -0
  70. package/dist/content/index.js +6 -0
  71. package/dist/content/index.js.map +1 -0
  72. package/dist/core/index.d.ts +166 -0
  73. package/dist/core/index.js +5 -0
  74. package/dist/core/index.js.map +1 -0
  75. package/dist/core/react.d.ts +107 -0
  76. package/dist/core/react.js +5 -0
  77. package/dist/core/react.js.map +1 -0
  78. package/dist/coupon-BZSZ0y3n.d.ts +129 -0
  79. package/dist/coupon-CDzL4bJG.d.ts +655 -0
  80. package/dist/crud.factory-DyKaPHcU.d.ts +181 -0
  81. package/dist/finance/index.d.ts +81 -0
  82. package/dist/finance/index.js +5 -0
  83. package/dist/finance/index.js.map +1 -0
  84. package/dist/finance-BJdfKRw0.d.ts +135 -0
  85. package/dist/index.d.ts +32 -0
  86. package/dist/index.js +29 -0
  87. package/dist/index.js.map +1 -0
  88. package/dist/inventory/index.d.ts +512 -0
  89. package/dist/inventory/index.js +16 -0
  90. package/dist/inventory/index.js.map +1 -0
  91. package/dist/inventory-B5pssqRx.d.ts +748 -0
  92. package/dist/logistics/index.d.ts +248 -0
  93. package/dist/logistics/index.js +7 -0
  94. package/dist/logistics/index.js.map +1 -0
  95. package/dist/logistics-CrpKadKE.d.ts +410 -0
  96. package/dist/media-CNLJK93J.d.ts +721 -0
  97. package/dist/movement-R3CERFAM.js +5 -0
  98. package/dist/movement-R3CERFAM.js.map +1 -0
  99. package/dist/order-B3dCvHgK.d.ts +360 -0
  100. package/dist/payment-BRboLqvU.d.ts +127 -0
  101. package/dist/payments/index.d.ts +55 -0
  102. package/dist/payments/index.js +6 -0
  103. package/dist/payments/index.js.map +1 -0
  104. package/dist/platform/index.d.ts +645 -0
  105. package/dist/platform/index.js +8 -0
  106. package/dist/platform/index.js.map +1 -0
  107. package/dist/pos-BCqkx2-K.d.ts +527 -0
  108. package/dist/product-p09zXkXB.d.ts +260 -0
  109. package/dist/purchase-54PER2PY.js +5 -0
  110. package/dist/purchase-54PER2PY.js.map +1 -0
  111. package/dist/request-MP6NV5ZE.js +5 -0
  112. package/dist/request-MP6NV5ZE.js.map +1 -0
  113. package/dist/sales/index.d.ts +587 -0
  114. package/dist/sales/index.js +9 -0
  115. package/dist/sales/index.js.map +1 -0
  116. package/dist/server.d.ts +23 -0
  117. package/dist/server.js +37 -0
  118. package/dist/server.js.map +1 -0
  119. package/dist/size-guide-DgjzjM5P.d.ts +554 -0
  120. package/dist/stock-2LP4HJSB.js +5 -0
  121. package/dist/stock-2LP4HJSB.js.map +1 -0
  122. package/dist/stock-CfrU5_Wr.d.ts +632 -0
  123. package/dist/supplier-BWJTRZ5Z.js +5 -0
  124. package/dist/supplier-BWJTRZ5Z.js.map +1 -0
  125. package/dist/transaction/index.d.ts +104 -0
  126. package/dist/transaction/index.js +8 -0
  127. package/dist/transaction/index.js.map +1 -0
  128. package/dist/transaction-Bf6WjYCh.d.ts +84 -0
  129. package/dist/transaction-dL3WW-er.d.ts +442 -0
  130. package/dist/transfer-4XSS6HWT.js +5 -0
  131. package/dist/transfer-4XSS6HWT.js.map +1 -0
  132. package/dist/user-data-DdLjAGwO.d.ts +132 -0
  133. package/package.json +147 -0
@@ -0,0 +1,534 @@
1
+ import { getToastHandler } from './chunk-U3XT35GZ.js';
2
+ import { handleApiRequest } from './chunk-IHCBBLLW.js';
3
+ import { useQuery, useQueryClient, useMutation } from '@tanstack/react-query';
4
+
5
+ // src/content/api/cms.ts
6
+ var CMS_BASE = "/api/v1/cms";
7
+ async function getCmsPage({
8
+ slug,
9
+ options = {}
10
+ }) {
11
+ if (!slug) throw new Error("Slug is required");
12
+ try {
13
+ return await handleApiRequest(
14
+ "GET",
15
+ `${CMS_BASE}/${slug}`,
16
+ options
17
+ );
18
+ } catch (error) {
19
+ if (error instanceof Error && error.message.includes("Document not found")) {
20
+ return {
21
+ success: false,
22
+ data: null,
23
+ message: "CMS page not found, using static fallback"
24
+ };
25
+ }
26
+ throw error;
27
+ }
28
+ }
29
+ async function updateCmsPage({
30
+ slug,
31
+ token,
32
+ data,
33
+ options = {}
34
+ }) {
35
+ if (!slug) throw new Error("Slug is required");
36
+ return handleApiRequest(
37
+ "PATCH",
38
+ `${CMS_BASE}/${slug}`,
39
+ { token, body: data, ...options }
40
+ );
41
+ }
42
+
43
+ // src/content/api/media.ts
44
+ var MediaApi = class {
45
+ constructor(config = {}) {
46
+ this.baseUrl = `${config.basePath || "/api/v1"}/media`;
47
+ this.defaultCache = config.cache || "no-store";
48
+ }
49
+ /**
50
+ * Upload single file
51
+ * POST /api/media/upload
52
+ *
53
+ * @example
54
+ * const file = event.target.files[0];
55
+ * const result = await mediaApi.upload({
56
+ * token: 'xxx',
57
+ * file,
58
+ * folder: 'products',
59
+ * alt: 'Product image'
60
+ * });
61
+ */
62
+ async upload({
63
+ token,
64
+ file,
65
+ folder,
66
+ alt,
67
+ title,
68
+ options = {}
69
+ }) {
70
+ const formData = new FormData();
71
+ if (folder) formData.append("folder", folder);
72
+ if (alt) formData.append("alt", alt);
73
+ if (title) formData.append("title", title);
74
+ formData.append("file", file);
75
+ return handleApiRequest("POST", `${this.baseUrl}/upload`, {
76
+ token,
77
+ body: formData,
78
+ cache: this.defaultCache,
79
+ ...options
80
+ });
81
+ }
82
+ /**
83
+ * Upload multiple files (max 20)
84
+ * POST /api/media/upload-multiple
85
+ *
86
+ * @example
87
+ * const files = Array.from(event.target.files);
88
+ * const result = await mediaApi.uploadMultiple({
89
+ * token: 'xxx',
90
+ * files,
91
+ * folder: 'products'
92
+ * });
93
+ */
94
+ async uploadMultiple({
95
+ token,
96
+ files,
97
+ folder,
98
+ options = {}
99
+ }) {
100
+ const formData = new FormData();
101
+ if (folder) formData.append("folder", folder);
102
+ files.forEach((file) => formData.append("files[]", file));
103
+ return handleApiRequest("POST", `${this.baseUrl}/upload-multiple`, {
104
+ token,
105
+ body: formData,
106
+ cache: this.defaultCache,
107
+ ...options
108
+ });
109
+ }
110
+ /**
111
+ * Get all media with filtering
112
+ * GET /api/media
113
+ *
114
+ * @example
115
+ * const result = await mediaApi.getAll({
116
+ * token: 'xxx',
117
+ * params: {
118
+ * folder: 'products',
119
+ * search: 'shirt',
120
+ * limit: 20,
121
+ * sort: '-createdAt'
122
+ * }
123
+ * });
124
+ */
125
+ async getAll({
126
+ token,
127
+ params = {},
128
+ options = {}
129
+ } = {}) {
130
+ const cleanParams = Object.fromEntries(
131
+ Object.entries(params).filter(([, v]) => v !== void 0 && v !== null)
132
+ );
133
+ const queryString = new URLSearchParams(
134
+ cleanParams
135
+ ).toString();
136
+ const url = queryString ? `${this.baseUrl}?${queryString}` : this.baseUrl;
137
+ return handleApiRequest("GET", url, {
138
+ token,
139
+ cache: this.defaultCache,
140
+ ...options
141
+ });
142
+ }
143
+ /**
144
+ * Get single media by ID
145
+ * GET /api/media/:id
146
+ *
147
+ * @example
148
+ * const result = await mediaApi.getById({ token: 'xxx', id: '123abc' });
149
+ */
150
+ async getById({
151
+ token,
152
+ id,
153
+ options = {}
154
+ }) {
155
+ if (!id) throw new Error("ID is required");
156
+ return handleApiRequest("GET", `${this.baseUrl}/${id}`, {
157
+ token,
158
+ cache: this.defaultCache,
159
+ ...options
160
+ });
161
+ }
162
+ /**
163
+ * Update media metadata (alt, title)
164
+ * PATCH /api/media/:id
165
+ *
166
+ * @example
167
+ * await mediaApi.update({
168
+ * token: 'xxx',
169
+ * id: '123',
170
+ * data: { alt: 'New alt text', title: 'New title' }
171
+ * });
172
+ */
173
+ async update({
174
+ token,
175
+ id,
176
+ data,
177
+ options = {}
178
+ }) {
179
+ if (!id) throw new Error("ID is required");
180
+ return handleApiRequest("PATCH", `${this.baseUrl}/${id}`, {
181
+ token,
182
+ body: data,
183
+ cache: this.defaultCache,
184
+ ...options
185
+ });
186
+ }
187
+ /**
188
+ * Delete single media
189
+ * DELETE /api/media/:id
190
+ *
191
+ * @example
192
+ * await mediaApi.delete({ token: 'xxx', id: '123' });
193
+ */
194
+ async delete({
195
+ token,
196
+ id,
197
+ options = {}
198
+ }) {
199
+ if (!id) throw new Error("ID is required");
200
+ return handleApiRequest("DELETE", `${this.baseUrl}/${id}`, {
201
+ token,
202
+ cache: this.defaultCache,
203
+ ...options
204
+ });
205
+ }
206
+ /**
207
+ * Bulk delete multiple files
208
+ * POST /api/media/bulk-delete
209
+ *
210
+ * @example
211
+ * await mediaApi.bulkDelete({
212
+ * token: 'xxx',
213
+ * ids: ['123', '456', '789']
214
+ * });
215
+ */
216
+ async bulkDelete({
217
+ token,
218
+ ids,
219
+ options = {}
220
+ }) {
221
+ if (!ids.length) throw new Error("IDs array is required");
222
+ return handleApiRequest("POST", `${this.baseUrl}/bulk-delete`, {
223
+ token,
224
+ body: { ids },
225
+ cache: this.defaultCache,
226
+ ...options
227
+ });
228
+ }
229
+ /**
230
+ * Move files to different folder
231
+ * POST /api/media/move
232
+ *
233
+ * @example
234
+ * await mediaApi.moveToFolder({
235
+ * token: 'xxx',
236
+ * ids: ['123', '456'],
237
+ * targetFolder: 'banners'
238
+ * });
239
+ */
240
+ async moveToFolder({
241
+ token,
242
+ data,
243
+ options = {}
244
+ }) {
245
+ if (!data.ids.length) throw new Error("IDs array is required");
246
+ if (!data.targetFolder) throw new Error("Target folder is required");
247
+ return handleApiRequest("POST", `${this.baseUrl}/move`, {
248
+ token,
249
+ body: data,
250
+ cache: this.defaultCache,
251
+ ...options
252
+ });
253
+ }
254
+ /**
255
+ * Get allowed folders
256
+ * GET /api/media/folders
257
+ *
258
+ * @example
259
+ * const result = await mediaApi.getFolders({ token: 'xxx' });
260
+ * // result.data = ['general', 'products', 'categories', ...]
261
+ */
262
+ async getFolders({
263
+ token,
264
+ options = {}
265
+ }) {
266
+ return handleApiRequest("GET", `${this.baseUrl}/folders`, {
267
+ token,
268
+ cache: this.defaultCache,
269
+ ...options
270
+ });
271
+ }
272
+ /**
273
+ * Helper: Get variant URL by name
274
+ * Falls back to original URL if variant not found
275
+ *
276
+ * @example
277
+ * const thumbnailUrl = mediaApi.getVariantUrl(media, 'thumbnail');
278
+ * <img src={thumbnailUrl} />
279
+ */
280
+ getVariantUrl(media, variantName) {
281
+ return media.variants?.find((v) => v.name === variantName)?.url || media.url;
282
+ }
283
+ /**
284
+ * Helper: Get thumbnail URL
285
+ */
286
+ getThumbnailUrl(media) {
287
+ return this.getVariantUrl(media, "thumbnail");
288
+ }
289
+ /**
290
+ * Helper: Get medium URL
291
+ */
292
+ getMediumUrl(media) {
293
+ return this.getVariantUrl(media, "medium");
294
+ }
295
+ };
296
+ var mediaApi = new MediaApi();
297
+ var MEDIA_KEYS = {
298
+ all: ["media"],
299
+ lists: () => [...MEDIA_KEYS.all, "list"],
300
+ list: (params) => [...MEDIA_KEYS.lists(), params],
301
+ details: () => [...MEDIA_KEYS.all, "detail"],
302
+ detail: (id) => [...MEDIA_KEYS.details(), id],
303
+ folders: () => [...MEDIA_KEYS.all, "folders"]
304
+ };
305
+ function useMediaList(token, params = {}, options = {}) {
306
+ const {
307
+ enabled = true,
308
+ staleTime = 2 * 60 * 1e3,
309
+ gcTime = 5 * 60 * 1e3,
310
+ refetchOnWindowFocus = false
311
+ } = options;
312
+ return useQuery({
313
+ queryKey: MEDIA_KEYS.list(params),
314
+ queryFn: () => mediaApi.getAll({ token, params }),
315
+ enabled: enabled && !!token,
316
+ staleTime,
317
+ gcTime,
318
+ refetchOnWindowFocus
319
+ });
320
+ }
321
+ function useMediaDetail(token, id, options = {}) {
322
+ const { enabled = true, staleTime = 2 * 60 * 1e3, gcTime = 5 * 60 * 1e3 } = options;
323
+ return useQuery({
324
+ queryKey: MEDIA_KEYS.detail(id),
325
+ queryFn: () => mediaApi.getById({ token, id }),
326
+ enabled: enabled && !!token && !!id,
327
+ staleTime,
328
+ gcTime
329
+ });
330
+ }
331
+ function useMediaFolders(token, options = {}) {
332
+ const { enabled = true, staleTime = 10 * 60 * 1e3, gcTime = 15 * 60 * 1e3 } = options;
333
+ return useQuery({
334
+ queryKey: MEDIA_KEYS.folders(),
335
+ queryFn: () => mediaApi.getFolders({ token }),
336
+ enabled: enabled && !!token,
337
+ staleTime,
338
+ gcTime
339
+ });
340
+ }
341
+ function useMediaUpload(token) {
342
+ const queryClient = useQueryClient();
343
+ const toast = getToastHandler();
344
+ const mutation = useMutation({
345
+ mutationFn: async ({ files, folder }) => {
346
+ const response = await mediaApi.uploadMultiple({ token, files, folder });
347
+ return response.data;
348
+ },
349
+ onSuccess: () => {
350
+ queryClient.invalidateQueries({ queryKey: MEDIA_KEYS.lists() });
351
+ toast.success("Media uploaded successfully");
352
+ },
353
+ onError: (error) => {
354
+ toast.error(error.message || "Failed to upload media");
355
+ }
356
+ });
357
+ return {
358
+ upload: (params) => mutation.mutate(params),
359
+ uploadAsync: (params) => mutation.mutateAsync(params),
360
+ isUploading: mutation.isPending,
361
+ error: mutation.error
362
+ };
363
+ }
364
+ function useMediaBulkDelete(token) {
365
+ const queryClient = useQueryClient();
366
+ const toast = getToastHandler();
367
+ const mutation = useMutation({
368
+ mutationFn: async (ids) => {
369
+ const response = await mediaApi.bulkDelete({ token, ids });
370
+ return response.data;
371
+ },
372
+ onMutate: async (ids) => {
373
+ await queryClient.cancelQueries({ queryKey: MEDIA_KEYS.lists() });
374
+ const previousLists = queryClient.getQueriesData({ queryKey: MEDIA_KEYS.lists() });
375
+ queryClient.setQueriesData({ queryKey: MEDIA_KEYS.lists() }, (old) => {
376
+ const data = old;
377
+ if (!data?.docs) return old;
378
+ return {
379
+ ...data,
380
+ docs: data.docs.filter((item) => !ids.includes(item._id)),
381
+ total: (data.total || 0) - ids.length
382
+ };
383
+ });
384
+ return { previousLists };
385
+ },
386
+ onError: (_error, _ids, context) => {
387
+ if (context?.previousLists) {
388
+ context.previousLists.forEach(([queryKey, data]) => {
389
+ queryClient.setQueryData(queryKey, data);
390
+ });
391
+ }
392
+ toast.error("Failed to delete media");
393
+ },
394
+ onSuccess: (data) => {
395
+ const deletedCount = data.success.length;
396
+ toast.success(`${deletedCount} item(s) deleted successfully`);
397
+ },
398
+ onSettled: () => {
399
+ queryClient.invalidateQueries({ queryKey: MEDIA_KEYS.lists() });
400
+ }
401
+ });
402
+ return {
403
+ bulkDelete: (ids) => mutation.mutate(ids),
404
+ bulkDeleteAsync: (ids) => mutation.mutateAsync(ids),
405
+ isDeleting: mutation.isPending,
406
+ error: mutation.error
407
+ };
408
+ }
409
+ function useMediaMove(token) {
410
+ const queryClient = useQueryClient();
411
+ const toast = getToastHandler();
412
+ const mutation = useMutation({
413
+ mutationFn: async (data) => {
414
+ const response = await mediaApi.moveToFolder({ token, data });
415
+ return response.data;
416
+ },
417
+ onSuccess: (data, variables) => {
418
+ queryClient.invalidateQueries({ queryKey: MEDIA_KEYS.lists() });
419
+ toast.success(`${data.modifiedCount} item(s) moved to ${variables.targetFolder}`);
420
+ },
421
+ onError: (error) => {
422
+ toast.error(error.message || "Failed to move media");
423
+ }
424
+ });
425
+ return {
426
+ move: (data) => mutation.mutate(data),
427
+ moveAsync: (data) => mutation.mutateAsync(data),
428
+ isMoving: mutation.isPending,
429
+ error: mutation.error
430
+ };
431
+ }
432
+ function useMediaUpdate(token) {
433
+ const queryClient = useQueryClient();
434
+ const toast = getToastHandler();
435
+ const mutation = useMutation({
436
+ mutationFn: async ({ id, data }) => {
437
+ const response = await mediaApi.update({ token, id, data });
438
+ return response.data;
439
+ },
440
+ onMutate: async ({ id, data }) => {
441
+ await queryClient.cancelQueries({ queryKey: MEDIA_KEYS.detail(id) });
442
+ const previous = queryClient.getQueryData(MEDIA_KEYS.detail(id));
443
+ queryClient.setQueryData(MEDIA_KEYS.detail(id), (old) => ({
444
+ ...old,
445
+ ...data
446
+ }));
447
+ return { previous, id };
448
+ },
449
+ onError: (_error, _variables, context) => {
450
+ if (context?.previous) {
451
+ queryClient.setQueryData(MEDIA_KEYS.detail(context.id), context.previous);
452
+ }
453
+ toast.error("Failed to update media");
454
+ },
455
+ onSuccess: () => {
456
+ toast.success("Media updated successfully");
457
+ },
458
+ onSettled: (_data, _error, variables) => {
459
+ queryClient.invalidateQueries({ queryKey: MEDIA_KEYS.detail(variables.id) });
460
+ queryClient.invalidateQueries({ queryKey: MEDIA_KEYS.lists() });
461
+ }
462
+ });
463
+ return {
464
+ update: (params) => mutation.mutate(params),
465
+ updateAsync: (params) => mutation.mutateAsync(params),
466
+ isUpdating: mutation.isPending,
467
+ error: mutation.error
468
+ };
469
+ }
470
+ function getMediaVariantUrl(media, variantName) {
471
+ return media.variants?.find((v) => v.name === variantName)?.url || media.url;
472
+ }
473
+ function getMediaThumbnailUrl(media) {
474
+ return getMediaVariantUrl(media, "thumbnail");
475
+ }
476
+ function getMediaMediumUrl(media) {
477
+ return getMediaVariantUrl(media, "medium");
478
+ }
479
+ var CMS_KEYS = {
480
+ all: ["cms"],
481
+ page: (slug) => [...CMS_KEYS.all, slug]
482
+ };
483
+ function useCMSPage(slug, options = {}) {
484
+ const { data, isLoading, isFetching, error, refetch } = useQuery({
485
+ queryKey: CMS_KEYS.page(slug),
486
+ queryFn: async () => {
487
+ const response = await getCmsPage({ slug });
488
+ return response.data;
489
+ },
490
+ enabled: !!slug && options.enabled !== false,
491
+ staleTime: options.staleTime ?? 5 * 60 * 1e3
492
+ // 5 minutes
493
+ });
494
+ return {
495
+ page: data || null,
496
+ isLoading,
497
+ isFetching,
498
+ error,
499
+ refetch
500
+ };
501
+ }
502
+ function useCMSUpdate(token) {
503
+ const queryClient = useQueryClient();
504
+ const toast = getToastHandler();
505
+ const mutation = useMutation({
506
+ mutationFn: async ({ slug, data }) => {
507
+ const response = await updateCmsPage({ slug, token, data });
508
+ return response.data;
509
+ },
510
+ onSuccess: (_, { slug }) => {
511
+ queryClient.invalidateQueries({ queryKey: CMS_KEYS.page(slug) });
512
+ toast.success("Page saved successfully");
513
+ if (typeof window !== "undefined") {
514
+ fetch("/revalidate", {
515
+ method: "POST",
516
+ headers: { "Content-Type": "application/json" },
517
+ body: JSON.stringify({ slug, type: "both" })
518
+ }).catch(() => {
519
+ });
520
+ }
521
+ },
522
+ onError: (error) => {
523
+ toast.error(error.message || "Failed to save page");
524
+ }
525
+ });
526
+ return {
527
+ updatePage: mutation.mutateAsync,
528
+ isUpdating: mutation.isPending
529
+ };
530
+ }
531
+
532
+ export { CMS_KEYS, MEDIA_KEYS, MediaApi, getCmsPage, getMediaMediumUrl, getMediaThumbnailUrl, getMediaVariantUrl, mediaApi, updateCmsPage, useCMSPage, useCMSUpdate, useMediaBulkDelete, useMediaDetail, useMediaFolders, useMediaList, useMediaMove, useMediaUpdate, useMediaUpload };
533
+ //# sourceMappingURL=chunk-ERQ52WHY.js.map
534
+ //# sourceMappingURL=chunk-ERQ52WHY.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/content/api/cms.ts","../src/content/api/media.ts","../src/content/hooks/media.ts","../src/content/hooks/cms.ts"],"names":["useQuery","useQueryClient","useMutation"],"mappings":"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","file":"chunk-ERQ52WHY.js","sourcesContent":["/**\r\n * CMS API - Slug-based content management\r\n *\r\n * Two endpoints only:\r\n * - GET /api/v1/cms/:slug - Fetch page (public)\r\n * - PATCH /api/v1/cms/:slug - Update page (admin, auto-creates if missing)\r\n */\r\n\r\nimport { handleApiRequest } from \"../../core/api-handler\";\r\nimport type { ApiResponse, RequestOptions } from \"../../core/api-factory\";\r\nimport type { CMSPage, CMSPagePayload } from \"../types/cms\";\r\n\r\nconst CMS_BASE = \"/api/v1/cms\";\r\n\r\n/**\r\n * Get CMS page by slug (public)\r\n * Returns page data or null if not found\r\n * Gracefully handles 404 errors by returning null instead of throwing\r\n */\r\nexport async function getCmsPage({\r\n slug,\r\n options = {},\r\n}: {\r\n slug: string;\r\n options?: RequestOptions;\r\n}): Promise<ApiResponse<CMSPage | null>> {\r\n if (!slug) throw new Error(\"Slug is required\");\r\n\r\n try {\r\n return await handleApiRequest<ApiResponse<CMSPage | null>>(\r\n \"GET\",\r\n `${CMS_BASE}/${slug}`,\r\n options\r\n );\r\n } catch (error) {\r\n // If document not found (404), return null data gracefully\r\n if (error instanceof Error && error.message.includes(\"Document not found\")) {\r\n return {\r\n success: false,\r\n data: null,\r\n message: \"CMS page not found, using static fallback\",\r\n };\r\n }\r\n // Re-throw other errors (network issues, server errors, etc.)\r\n throw error;\r\n }\r\n}\r\n\r\n/**\r\n * Update CMS page by slug (admin)\r\n * Backend auto-creates if page doesn't exist\r\n */\r\nexport async function updateCmsPage({\r\n slug,\r\n token,\r\n data,\r\n options = {},\r\n}: {\r\n slug: string;\r\n token: string;\r\n data: CMSPagePayload;\r\n options?: RequestOptions;\r\n}): Promise<ApiResponse<CMSPage>> {\r\n if (!slug) throw new Error(\"Slug is required\");\r\n\r\n return handleApiRequest<ApiResponse<CMSPage>>(\r\n \"PATCH\",\r\n `${CMS_BASE}/${slug}`,\r\n { token, body: data, ...options }\r\n );\r\n}\r\n","// Media API - Image upload and management\r\nimport {\r\n type ApiResponse,\r\n type PaginatedResponse,\r\n type RequestOptions,\r\n type DeleteResponse,\r\n} from \"../../core/api-factory\";\r\nimport { handleApiRequest } from \"../../core/api-handler\";\r\nimport type {\r\n Media,\r\n MediaFolder,\r\n MediaQueryParams,\r\n UpdateMediaPayload,\r\n BulkDeleteResult,\r\n MoveFilesPayload,\r\n MoveFilesResult,\r\n} from \"../types/media\";\r\n\r\ntype FetchOptions = Omit<RequestOptions, \"token\" | \"organizationId\">;\r\n\r\n/**\r\n * Media API - Image upload and management\r\n *\r\n * Endpoints:\r\n * - POST /api/media/upload (single file upload)\r\n * - POST /api/media/upload-multiple (multiple files)\r\n * - GET /api/media (list with filters)\r\n * - GET /api/media/:id (get single)\r\n * - PATCH /api/media/:id (update alt/title)\r\n * - DELETE /api/media/:id (delete single)\r\n * - GET /api/media/folders (get allowed folders)\r\n *\r\n * Features:\r\n * - Auto WebP conversion\r\n * - Auto variants: thumbnail (150x200), medium (600x800)\r\n * - Max size: 50MB\r\n * - S3 storage with CDN\r\n *\r\n * Usage Examples:\r\n * - mediaApi.upload({ token: 'xxx', file, folder: 'products' })\r\n * - mediaApi.getAll({ params: { folder: 'products', limit: 20 }})\r\n * - mediaApi.update({ token: 'xxx', id: '123', data: { alt: 'New alt' }})\r\n * - mediaApi.delete({ token: 'xxx', id: '123' })\r\n */\r\nclass MediaApi {\r\n private readonly baseUrl: string;\r\n private readonly defaultCache: RequestCache;\r\n\r\n constructor(config: { basePath?: string; cache?: RequestCache } = {}) {\r\n this.baseUrl = `${config.basePath || \"/api/v1\"}/media`;\r\n this.defaultCache = config.cache || \"no-store\";\r\n }\r\n\r\n /**\r\n * Upload single file\r\n * POST /api/media/upload\r\n *\r\n * @example\r\n * const file = event.target.files[0];\r\n * const result = await mediaApi.upload({\r\n * token: 'xxx',\r\n * file,\r\n * folder: 'products',\r\n * alt: 'Product image'\r\n * });\r\n */\r\n async upload({\r\n token,\r\n file,\r\n folder,\r\n alt,\r\n title,\r\n options = {},\r\n }: {\r\n token: string;\r\n file: File | Blob;\r\n folder?: MediaFolder;\r\n alt?: string;\r\n title?: string;\r\n options?: FetchOptions;\r\n }): Promise<ApiResponse<Media>> {\r\n const formData = new FormData();\r\n // Fields MUST come before files for Fastify multipart streaming parser\r\n if (folder) formData.append(\"folder\", folder);\r\n if (alt) formData.append(\"alt\", alt);\r\n if (title) formData.append(\"title\", title);\r\n formData.append(\"file\", file);\r\n\r\n return handleApiRequest(\"POST\", `${this.baseUrl}/upload`, {\r\n token,\r\n body: formData,\r\n cache: this.defaultCache,\r\n ...options,\r\n });\r\n }\r\n\r\n /**\r\n * Upload multiple files (max 20)\r\n * POST /api/media/upload-multiple\r\n *\r\n * @example\r\n * const files = Array.from(event.target.files);\r\n * const result = await mediaApi.uploadMultiple({\r\n * token: 'xxx',\r\n * files,\r\n * folder: 'products'\r\n * });\r\n */\r\n async uploadMultiple({\r\n token,\r\n files,\r\n folder,\r\n options = {},\r\n }: {\r\n token: string;\r\n files: (File | Blob)[];\r\n folder?: MediaFolder;\r\n options?: FetchOptions;\r\n }): Promise<ApiResponse<Media[]>> {\r\n const formData = new FormData();\r\n // Fields MUST come before files for Fastify multipart streaming parser\r\n if (folder) formData.append(\"folder\", folder);\r\n files.forEach((file) => formData.append(\"files[]\", file));\r\n\r\n return handleApiRequest(\"POST\", `${this.baseUrl}/upload-multiple`, {\r\n token,\r\n body: formData,\r\n cache: this.defaultCache,\r\n ...options,\r\n });\r\n }\r\n\r\n /**\r\n * Get all media with filtering\r\n * GET /api/media\r\n *\r\n * @example\r\n * const result = await mediaApi.getAll({\r\n * token: 'xxx',\r\n * params: {\r\n * folder: 'products',\r\n * search: 'shirt',\r\n * limit: 20,\r\n * sort: '-createdAt'\r\n * }\r\n * });\r\n */\r\n async getAll({\r\n token,\r\n params = {},\r\n options = {},\r\n }: {\r\n token: string;\r\n params?: MediaQueryParams;\r\n options?: FetchOptions;\r\n } = {} as any): Promise<PaginatedResponse<Media>> {\r\n // Filter out undefined/null values before creating query string\r\n const cleanParams = Object.fromEntries(\r\n Object.entries(params).filter(([, v]) => v !== undefined && v !== null)\r\n );\r\n const queryString = new URLSearchParams(\r\n cleanParams as Record<string, string>\r\n ).toString();\r\n const url = queryString ? `${this.baseUrl}?${queryString}` : this.baseUrl;\r\n\r\n return handleApiRequest(\"GET\", url, {\r\n token,\r\n cache: this.defaultCache,\r\n ...options,\r\n });\r\n }\r\n\r\n /**\r\n * Get single media by ID\r\n * GET /api/media/:id\r\n *\r\n * @example\r\n * const result = await mediaApi.getById({ token: 'xxx', id: '123abc' });\r\n */\r\n async getById({\r\n token,\r\n id,\r\n options = {},\r\n }: {\r\n token: string;\r\n id: string;\r\n options?: FetchOptions;\r\n }): Promise<ApiResponse<Media>> {\r\n if (!id) throw new Error(\"ID is required\");\r\n\r\n return handleApiRequest(\"GET\", `${this.baseUrl}/${id}`, {\r\n token,\r\n cache: this.defaultCache,\r\n ...options,\r\n });\r\n }\r\n\r\n /**\r\n * Update media metadata (alt, title)\r\n * PATCH /api/media/:id\r\n *\r\n * @example\r\n * await mediaApi.update({\r\n * token: 'xxx',\r\n * id: '123',\r\n * data: { alt: 'New alt text', title: 'New title' }\r\n * });\r\n */\r\n async update({\r\n token,\r\n id,\r\n data,\r\n options = {},\r\n }: {\r\n token: string;\r\n id: string;\r\n data: UpdateMediaPayload;\r\n options?: FetchOptions;\r\n }): Promise<ApiResponse<Media>> {\r\n if (!id) throw new Error(\"ID is required\");\r\n\r\n return handleApiRequest(\"PATCH\", `${this.baseUrl}/${id}`, {\r\n token,\r\n body: data,\r\n cache: this.defaultCache,\r\n ...options,\r\n });\r\n }\r\n\r\n /**\r\n * Delete single media\r\n * DELETE /api/media/:id\r\n *\r\n * @example\r\n * await mediaApi.delete({ token: 'xxx', id: '123' });\r\n */\r\n async delete({\r\n token,\r\n id,\r\n options = {},\r\n }: {\r\n token: string;\r\n id: string;\r\n options?: FetchOptions;\r\n }): Promise<DeleteResponse> {\r\n if (!id) throw new Error(\"ID is required\");\r\n\r\n return handleApiRequest(\"DELETE\", `${this.baseUrl}/${id}`, {\r\n token,\r\n cache: this.defaultCache,\r\n ...options,\r\n });\r\n }\r\n\r\n /**\r\n * Bulk delete multiple files\r\n * POST /api/media/bulk-delete\r\n *\r\n * @example\r\n * await mediaApi.bulkDelete({\r\n * token: 'xxx',\r\n * ids: ['123', '456', '789']\r\n * });\r\n */\r\n async bulkDelete({\r\n token,\r\n ids,\r\n options = {},\r\n }: {\r\n token: string;\r\n ids: string[];\r\n options?: FetchOptions;\r\n }): Promise<ApiResponse<BulkDeleteResult>> {\r\n if (!ids.length) throw new Error(\"IDs array is required\");\r\n\r\n return handleApiRequest(\"POST\", `${this.baseUrl}/bulk-delete`, {\r\n token,\r\n body: { ids },\r\n cache: this.defaultCache,\r\n ...options,\r\n });\r\n }\r\n\r\n /**\r\n * Move files to different folder\r\n * POST /api/media/move\r\n *\r\n * @example\r\n * await mediaApi.moveToFolder({\r\n * token: 'xxx',\r\n * ids: ['123', '456'],\r\n * targetFolder: 'banners'\r\n * });\r\n */\r\n async moveToFolder({\r\n token,\r\n data,\r\n options = {},\r\n }: {\r\n token: string;\r\n data: MoveFilesPayload;\r\n options?: FetchOptions;\r\n }): Promise<ApiResponse<MoveFilesResult>> {\r\n if (!data.ids.length) throw new Error(\"IDs array is required\");\r\n if (!data.targetFolder) throw new Error(\"Target folder is required\");\r\n\r\n return handleApiRequest(\"POST\", `${this.baseUrl}/move`, {\r\n token,\r\n body: data,\r\n cache: this.defaultCache,\r\n ...options,\r\n });\r\n }\r\n\r\n /**\r\n * Get allowed folders\r\n * GET /api/media/folders\r\n *\r\n * @example\r\n * const result = await mediaApi.getFolders({ token: 'xxx' });\r\n * // result.data = ['general', 'products', 'categories', ...]\r\n */\r\n async getFolders({\r\n token,\r\n options = {},\r\n }: {\r\n token: string;\r\n options?: FetchOptions;\r\n }): Promise<ApiResponse<MediaFolder[]>> {\r\n return handleApiRequest(\"GET\", `${this.baseUrl}/folders`, {\r\n token,\r\n cache: this.defaultCache,\r\n ...options,\r\n });\r\n }\r\n\r\n /**\r\n * Helper: Get variant URL by name\r\n * Falls back to original URL if variant not found\r\n *\r\n * @example\r\n * const thumbnailUrl = mediaApi.getVariantUrl(media, 'thumbnail');\r\n * <img src={thumbnailUrl} />\r\n */\r\n getVariantUrl(media: Media, variantName: string): string {\r\n return (\r\n media.variants?.find((v) => v.name === variantName)?.url || media.url\r\n );\r\n }\r\n\r\n /**\r\n * Helper: Get thumbnail URL\r\n */\r\n getThumbnailUrl(media: Media): string {\r\n return this.getVariantUrl(media, \"thumbnail\");\r\n }\r\n\r\n /**\r\n * Helper: Get medium URL\r\n */\r\n getMediumUrl(media: Media): string {\r\n return this.getVariantUrl(media, \"medium\");\r\n }\r\n\r\n}\r\n\r\n// Create and export a singleton instance\r\nexport const mediaApi = new MediaApi();\r\nexport { MediaApi };\r\n","\"use client\";\n\nimport { useQuery, useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport { mediaApi } from \"../api/media\";\nimport { getToastHandler } from \"../../core/react/mutation.factory\";\nimport type {\n Media,\n MediaFolder,\n MediaQueryParams,\n UpdateMediaPayload,\n BulkDeleteResult,\n MoveFilesPayload,\n MoveFilesResult,\n} from \"../types/media\";\n\n// ============================================\n// Query Keys\n// ============================================\n\nexport const MEDIA_KEYS = {\n all: [\"media\"] as const,\n lists: () => [...MEDIA_KEYS.all, \"list\"] as const,\n list: (params: MediaQueryParams) => [...MEDIA_KEYS.lists(), params] as const,\n details: () => [...MEDIA_KEYS.all, \"detail\"] as const,\n detail: (id: string) => [...MEDIA_KEYS.details(), id] as const,\n folders: () => [...MEDIA_KEYS.all, \"folders\"] as const,\n};\n\n// ============================================\n// Types\n// ============================================\n\nexport interface UseMediaListOptions {\n enabled?: boolean;\n staleTime?: number;\n gcTime?: number;\n refetchOnWindowFocus?: boolean;\n}\n\nexport interface UseMediaUploadReturn {\n upload: (params: { files: (File | Blob)[]; folder?: MediaFolder }) => void;\n uploadAsync: (params: { files: (File | Blob)[]; folder?: MediaFolder }) => Promise<Media[]>;\n isUploading: boolean;\n error: Error | null;\n}\n\nexport interface UseMediaBulkDeleteReturn {\n bulkDelete: (ids: string[]) => void;\n bulkDeleteAsync: (ids: string[]) => Promise<BulkDeleteResult>;\n isDeleting: boolean;\n error: Error | null;\n}\n\nexport interface UseMediaMoveReturn {\n move: (data: MoveFilesPayload) => void;\n moveAsync: (data: MoveFilesPayload) => Promise<MoveFilesResult>;\n isMoving: boolean;\n error: Error | null;\n}\n\nexport interface UseMediaUpdateReturn {\n update: (params: { id: string; data: UpdateMediaPayload }) => void;\n updateAsync: (params: { id: string; data: UpdateMediaPayload }) => Promise<Media>;\n isUpdating: boolean;\n error: Error | null;\n}\n\n// ============================================\n// Query Hooks\n// ============================================\n\n/**\n * Hook to get media list with folder filtering\n *\n * @param token - Auth token (required)\n * @param params - Query params (folder, search, limit, etc.)\n * @param options - React Query options\n *\n * @example\n * ```tsx\n * function MediaLibrary() {\n * const { data, isLoading } = useMediaList(token, {\n * folder: 'products',\n * limit: 20,\n * });\n *\n * if (isLoading) return <Spinner />;\n *\n * return (\n * <div className=\"grid\">\n * {data?.docs?.map(media => (\n * <img key={media._id} src={media.variants?.thumbnail?.url || media.url} />\n * ))}\n * </div>\n * );\n * }\n * ```\n */\nexport function useMediaList(\n token: string | null,\n params: MediaQueryParams = {},\n options: UseMediaListOptions = {}\n) {\n const {\n enabled = true,\n staleTime = 2 * 60 * 1000,\n gcTime = 5 * 60 * 1000,\n refetchOnWindowFocus = false,\n } = options;\n\n return useQuery({\n queryKey: MEDIA_KEYS.list(params),\n queryFn: () => mediaApi.getAll({ token: token!, params }),\n enabled: enabled && !!token,\n staleTime,\n gcTime,\n refetchOnWindowFocus,\n });\n}\n\n/**\n * Hook to get single media by ID\n *\n * @param token - Auth token (required)\n * @param id - Media ID\n * @param options - React Query options\n *\n * @example\n * ```tsx\n * function MediaDetail({ id }) {\n * const { data, isLoading } = useMediaDetail(token, id);\n *\n * if (isLoading) return <Spinner />;\n *\n * return (\n * <div>\n * <img src={data?.data?.url} alt={data?.data?.alt} />\n * <p>{data?.data?.title}</p>\n * </div>\n * );\n * }\n * ```\n */\nexport function useMediaDetail(\n token: string | null,\n id: string | null,\n options: UseMediaListOptions = {}\n) {\n const { enabled = true, staleTime = 2 * 60 * 1000, gcTime = 5 * 60 * 1000 } = options;\n\n return useQuery({\n queryKey: MEDIA_KEYS.detail(id!),\n queryFn: () => mediaApi.getById({ token: token!, id: id! }),\n enabled: enabled && !!token && !!id,\n staleTime,\n gcTime,\n });\n}\n\n/**\n * Hook to get allowed folders\n *\n * @param token - Auth token (required)\n * @param options - React Query options\n *\n * @example\n * ```tsx\n * function FolderSelector() {\n * const { data, isLoading } = useMediaFolders(token);\n *\n * return (\n * <Select>\n * {data?.data?.map(folder => (\n * <SelectItem key={folder} value={folder}>{folder}</SelectItem>\n * ))}\n * </Select>\n * );\n * }\n * ```\n */\nexport function useMediaFolders(token: string | null, options: UseMediaListOptions = {}) {\n const { enabled = true, staleTime = 10 * 60 * 1000, gcTime = 15 * 60 * 1000 } = options;\n\n return useQuery({\n queryKey: MEDIA_KEYS.folders(),\n queryFn: () => mediaApi.getFolders({ token: token! }),\n enabled: enabled && !!token,\n staleTime,\n gcTime,\n });\n}\n\n// ============================================\n// Mutation Hooks\n// ============================================\n\n/**\n * Hook to upload multiple media files\n *\n * @param token - Auth token (required)\n *\n * @example\n * ```tsx\n * function MediaUploader() {\n * const { upload, isUploading } = useMediaUpload(token);\n *\n * const handleFiles = (e) => {\n * const files = Array.from(e.target.files);\n * upload({ files, folder: 'products' });\n * };\n *\n * return (\n * <input type=\"file\" multiple onChange={handleFiles} disabled={isUploading} />\n * );\n * }\n * ```\n */\nexport function useMediaUpload(token: string): UseMediaUploadReturn {\n const queryClient = useQueryClient();\n const toast = getToastHandler();\n\n const mutation = useMutation({\n mutationFn: async ({ files, folder }: { files: (File | Blob)[]; folder?: MediaFolder }) => {\n const response = await mediaApi.uploadMultiple({ token, files, folder });\n return response.data as Media[];\n },\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: MEDIA_KEYS.lists() });\n toast.success(\"Media uploaded successfully\");\n },\n onError: (error: Error) => {\n toast.error(error.message || \"Failed to upload media\");\n },\n });\n\n return {\n upload: (params) => mutation.mutate(params),\n uploadAsync: (params) => mutation.mutateAsync(params),\n isUploading: mutation.isPending,\n error: mutation.error,\n };\n}\n\n/**\n * Hook to bulk delete media with optimistic updates\n *\n * @param token - Auth token (required)\n *\n * @example\n * ```tsx\n * function MediaManager({ selectedIds }) {\n * const { bulkDelete, isDeleting } = useMediaBulkDelete(token);\n *\n * return (\n * <Button\n * variant=\"destructive\"\n * onClick={() => bulkDelete(selectedIds)}\n * disabled={isDeleting}\n * >\n * Delete Selected\n * </Button>\n * );\n * }\n * ```\n */\nexport function useMediaBulkDelete(token: string): UseMediaBulkDeleteReturn {\n const queryClient = useQueryClient();\n const toast = getToastHandler();\n\n const mutation = useMutation<\n BulkDeleteResult,\n Error,\n string[],\n { previousLists: [unknown, unknown][] }\n >({\n mutationFn: async (ids: string[]) => {\n const response = await mediaApi.bulkDelete({ token, ids });\n return response.data as BulkDeleteResult;\n },\n onMutate: async (ids) => {\n await queryClient.cancelQueries({ queryKey: MEDIA_KEYS.lists() });\n\n const previousLists = queryClient.getQueriesData({ queryKey: MEDIA_KEYS.lists() });\n\n // Optimistically update cache\n queryClient.setQueriesData({ queryKey: MEDIA_KEYS.lists() }, (old: unknown) => {\n const data = old as { docs?: Media[]; total?: number } | undefined;\n if (!data?.docs) return old;\n return {\n ...data,\n docs: data.docs.filter((item: Media) => !ids.includes(item._id)),\n total: (data.total || 0) - ids.length,\n };\n });\n\n return { previousLists: previousLists as [unknown, unknown][] };\n },\n onError: (_error, _ids, context) => {\n if (context?.previousLists) {\n context.previousLists.forEach(([queryKey, data]) => {\n queryClient.setQueryData(queryKey as unknown[], data);\n });\n }\n toast.error(\"Failed to delete media\");\n },\n onSuccess: (data) => {\n const deletedCount = data.success.length;\n toast.success(`${deletedCount} item(s) deleted successfully`);\n },\n onSettled: () => {\n queryClient.invalidateQueries({ queryKey: MEDIA_KEYS.lists() });\n },\n });\n\n return {\n bulkDelete: (ids) => mutation.mutate(ids),\n bulkDeleteAsync: (ids) => mutation.mutateAsync(ids),\n isDeleting: mutation.isPending,\n error: mutation.error,\n };\n}\n\n/**\n * Hook to move media to different folder\n *\n * @param token - Auth token (required)\n *\n * @example\n * ```tsx\n * function MoveDialog({ selectedIds }) {\n * const { move, isMoving } = useMediaMove(token);\n *\n * const handleMove = (folder) => {\n * move({ ids: selectedIds, targetFolder: folder });\n * };\n *\n * return <FolderSelect onSelect={handleMove} disabled={isMoving} />;\n * }\n * ```\n */\nexport function useMediaMove(token: string): UseMediaMoveReturn {\n const queryClient = useQueryClient();\n const toast = getToastHandler();\n\n const mutation = useMutation<MoveFilesResult, Error, MoveFilesPayload>({\n mutationFn: async (data: MoveFilesPayload) => {\n const response = await mediaApi.moveToFolder({ token, data });\n return response.data as MoveFilesResult;\n },\n onSuccess: (data, variables) => {\n queryClient.invalidateQueries({ queryKey: MEDIA_KEYS.lists() });\n toast.success(`${data.modifiedCount} item(s) moved to ${variables.targetFolder}`);\n },\n onError: (error: Error) => {\n toast.error(error.message || \"Failed to move media\");\n },\n });\n\n return {\n move: (data) => mutation.mutate(data),\n moveAsync: (data) => mutation.mutateAsync(data),\n isMoving: mutation.isPending,\n error: mutation.error,\n };\n}\n\n/**\n * Hook to update media metadata\n *\n * @param token - Auth token (required)\n *\n * @example\n * ```tsx\n * function MediaEdit({ media }) {\n * const { update, isUpdating } = useMediaUpdate(token);\n * const [alt, setAlt] = useState(media.alt);\n *\n * const handleSave = () => {\n * update({ id: media._id, data: { alt } });\n * };\n *\n * return (\n * <>\n * <Input value={alt} onChange={e => setAlt(e.target.value)} />\n * <Button onClick={handleSave} disabled={isUpdating}>Save</Button>\n * </>\n * );\n * }\n * ```\n */\nexport function useMediaUpdate(token: string): UseMediaUpdateReturn {\n const queryClient = useQueryClient();\n const toast = getToastHandler();\n\n const mutation = useMutation({\n mutationFn: async ({ id, data }: { id: string; data: UpdateMediaPayload }) => {\n const response = await mediaApi.update({ token, id, data });\n return response.data as Media;\n },\n onMutate: async ({ id, data }) => {\n await queryClient.cancelQueries({ queryKey: MEDIA_KEYS.detail(id) });\n\n const previous = queryClient.getQueryData(MEDIA_KEYS.detail(id));\n\n queryClient.setQueryData(MEDIA_KEYS.detail(id), (old: unknown) => ({\n ...(old as object),\n ...data,\n }));\n\n return { previous, id };\n },\n onError: (_error, _variables, context) => {\n if (context?.previous) {\n queryClient.setQueryData(MEDIA_KEYS.detail(context.id), context.previous);\n }\n toast.error(\"Failed to update media\");\n },\n onSuccess: () => {\n toast.success(\"Media updated successfully\");\n },\n onSettled: (_data, _error, variables) => {\n queryClient.invalidateQueries({ queryKey: MEDIA_KEYS.detail(variables.id) });\n queryClient.invalidateQueries({ queryKey: MEDIA_KEYS.lists() });\n },\n });\n\n return {\n update: (params) => mutation.mutate(params),\n updateAsync: (params) => mutation.mutateAsync(params),\n isUpdating: mutation.isPending,\n error: mutation.error,\n };\n}\n\n// ============================================\n// Helper Functions (re-exported from API)\n// ============================================\n\n/**\n * Get variant URL by name, falls back to original URL\n */\nexport function getMediaVariantUrl(media: Media, variantName: string): string {\n return media.variants?.find((v) => v.name === variantName)?.url || media.url;\n}\n\n/**\n * Get thumbnail URL\n */\nexport function getMediaThumbnailUrl(media: Media): string {\n return getMediaVariantUrl(media, \"thumbnail\");\n}\n\n/**\n * Get medium URL\n */\nexport function getMediaMediumUrl(media: Media): string {\n return getMediaVariantUrl(media, \"medium\");\n}\n","\"use client\";\r\n\r\n/**\r\n * CMS Hooks\r\n *\r\n * React hooks for CMS page operations.\r\n */\r\n\r\nimport { useQuery, useMutation, useQueryClient } from \"@tanstack/react-query\";\r\nimport { getCmsPage, updateCmsPage } from \"../api/cms\";\r\nimport { getToastHandler } from \"../../core/react/mutation.factory\";\r\nimport type { CMSPage, CMSPagePayload } from \"../types/cms\";\r\n\r\n// ============================================\r\n// Query Keys\r\n// ============================================\r\n\r\nexport const CMS_KEYS = {\r\n all: [\"cms\"] as const,\r\n page: (slug: string) => [...CMS_KEYS.all, slug] as const,\r\n};\r\n\r\n// ============================================\r\n// Types\r\n// ============================================\r\n\r\ninterface QueryOptions {\r\n enabled?: boolean;\r\n staleTime?: number;\r\n}\r\n\r\nexport interface UseCMSPageReturn {\r\n page: CMSPage | null;\r\n isLoading: boolean;\r\n isFetching: boolean;\r\n error: Error | null;\r\n refetch: () => void;\r\n}\r\n\r\nexport interface UseCMSUpdateReturn {\r\n updatePage: (params: { slug: string; data: CMSPagePayload }) => Promise<CMSPage>;\r\n isUpdating: boolean;\r\n}\r\n\r\n// ============================================\r\n// Hooks\r\n// ============================================\r\n\r\n/**\r\n * Fetch CMS page by slug\r\n *\r\n * @param slug - Page slug (e.g., 'about', 'terms', 'privacy')\r\n * @param options - Query options\r\n *\r\n * @example\r\n * ```tsx\r\n * function AboutPage() {\r\n * const { page, isLoading } = useCMSPage('about');\r\n *\r\n * if (isLoading) return <Skeleton />;\r\n * if (!page) return <StaticAboutPage />;\r\n *\r\n * return (\r\n * <article>\r\n * <h1>{page.title}</h1>\r\n * <div dangerouslySetInnerHTML={{ __html: page.content }} />\r\n * </article>\r\n * );\r\n * }\r\n * ```\r\n */\r\nexport function useCMSPage(\r\n slug: string,\r\n options: QueryOptions = {}\r\n): UseCMSPageReturn {\r\n const { data, isLoading, isFetching, error, refetch } = useQuery({\r\n queryKey: CMS_KEYS.page(slug),\r\n queryFn: async () => {\r\n const response = await getCmsPage({ slug });\r\n return response.data;\r\n },\r\n enabled: !!slug && options.enabled !== false,\r\n staleTime: options.staleTime ?? 5 * 60 * 1000, // 5 minutes\r\n });\r\n\r\n return {\r\n page: data || null,\r\n isLoading,\r\n isFetching,\r\n error: error as Error | null,\r\n refetch,\r\n };\r\n}\r\n\r\n/**\r\n * Update CMS page (admin)\r\n * Backend auto-creates if page doesn't exist\r\n *\r\n * @param token - Admin token\r\n *\r\n * @example\r\n * ```tsx\r\n * function CMSEditor({ slug }) {\r\n * const { page } = useCMSPage(slug);\r\n * const { updatePage, isUpdating } = useCMSUpdate(token);\r\n *\r\n * const handleSave = async (content: string) => {\r\n * await updatePage({\r\n * slug,\r\n * data: { content, title: page?.title },\r\n * });\r\n * };\r\n *\r\n * return (\r\n * <Editor\r\n * content={page?.content || ''}\r\n * onSave={handleSave}\r\n * isSaving={isUpdating}\r\n * />\r\n * );\r\n * }\r\n * ```\r\n */\r\nexport function useCMSUpdate(token: string): UseCMSUpdateReturn {\r\n const queryClient = useQueryClient();\r\n const toast = getToastHandler();\r\n\r\n const mutation = useMutation({\r\n mutationFn: async ({ slug, data }: { slug: string; data: CMSPagePayload }) => {\r\n const response = await updateCmsPage({ slug, token, data });\r\n return response.data as CMSPage;\r\n },\r\n onSuccess: (_, { slug }) => {\r\n queryClient.invalidateQueries({ queryKey: CMS_KEYS.page(slug) });\r\n toast.success(\"Page saved successfully\");\r\n\r\n // Trigger ISR revalidation (optional)\r\n if (typeof window !== \"undefined\") {\r\n fetch(\"/revalidate\", {\r\n method: \"POST\",\r\n headers: { \"Content-Type\": \"application/json\" },\r\n body: JSON.stringify({ slug, type: \"both\" }),\r\n }).catch(() => {\r\n // Silently ignore revalidation errors\r\n });\r\n }\r\n },\r\n onError: (error: Error) => {\r\n toast.error(error.message || \"Failed to save page\");\r\n },\r\n });\r\n\r\n return {\r\n updatePage: mutation.mutateAsync,\r\n isUpdating: mutation.isPending,\r\n };\r\n}\r\n"]}