@rovela-ai/sdk 0.3.6 → 0.3.7

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 (64) hide show
  1. package/dist/admin/components/CategoryForm.d.ts.map +1 -1
  2. package/dist/admin/components/CategoryForm.js +54 -65
  3. package/dist/admin/components/CategoryForm.js.map +1 -1
  4. package/dist/admin/components/CategorySelect.d.ts.map +1 -1
  5. package/dist/admin/components/CategorySelect.js +9 -14
  6. package/dist/admin/components/CategorySelect.js.map +1 -1
  7. package/dist/admin/components/CustomerDetails.js +3 -3
  8. package/dist/admin/components/CustomerDetails.js.map +1 -1
  9. package/dist/admin/components/CustomerTable.js +3 -3
  10. package/dist/admin/components/CustomerTable.js.map +1 -1
  11. package/dist/admin/components/LogoUpload.d.ts.map +1 -1
  12. package/dist/admin/components/LogoUpload.js +49 -67
  13. package/dist/admin/components/LogoUpload.js.map +1 -1
  14. package/dist/admin/components/OrderDetails.d.ts.map +1 -1
  15. package/dist/admin/components/OrderDetails.js +34 -65
  16. package/dist/admin/components/OrderDetails.js.map +1 -1
  17. package/dist/admin/components/OrderTable.js +2 -2
  18. package/dist/admin/components/OrderTable.js.map +1 -1
  19. package/dist/admin/components/PaymentSettings.d.ts.map +1 -1
  20. package/dist/admin/components/PaymentSettings.js +28 -51
  21. package/dist/admin/components/PaymentSettings.js.map +1 -1
  22. package/dist/admin/components/ProductTable.js +3 -3
  23. package/dist/admin/components/ProductTable.js.map +1 -1
  24. package/dist/admin/components/ShippingSettings.d.ts.map +1 -1
  25. package/dist/admin/components/ShippingSettings.js +177 -244
  26. package/dist/admin/components/ShippingSettings.js.map +1 -1
  27. package/dist/admin/components/StoreSettings.d.ts.map +1 -1
  28. package/dist/admin/components/StoreSettings.js +28 -42
  29. package/dist/admin/components/StoreSettings.js.map +1 -1
  30. package/dist/admin/components/TaxSettings.d.ts.map +1 -1
  31. package/dist/admin/components/TaxSettings.js +49 -81
  32. package/dist/admin/components/TaxSettings.js.map +1 -1
  33. package/dist/admin/hooks/fetchAdminApi.d.ts +65 -0
  34. package/dist/admin/hooks/fetchAdminApi.d.ts.map +1 -0
  35. package/dist/admin/hooks/fetchAdminApi.js +96 -0
  36. package/dist/admin/hooks/fetchAdminApi.js.map +1 -0
  37. package/dist/admin/hooks/useAdminCategories.d.ts +0 -27
  38. package/dist/admin/hooks/useAdminCategories.d.ts.map +1 -1
  39. package/dist/admin/hooks/useAdminCategories.js +39 -147
  40. package/dist/admin/hooks/useAdminCategories.js.map +1 -1
  41. package/dist/admin/hooks/useAdminCustomers.d.ts +0 -38
  42. package/dist/admin/hooks/useAdminCustomers.d.ts.map +1 -1
  43. package/dist/admin/hooks/useAdminCustomers.js +66 -172
  44. package/dist/admin/hooks/useAdminCustomers.js.map +1 -1
  45. package/dist/admin/hooks/useAdminMe.d.ts.map +1 -1
  46. package/dist/admin/hooks/useAdminMe.js +31 -59
  47. package/dist/admin/hooks/useAdminMe.js.map +1 -1
  48. package/dist/admin/hooks/useAdminOrders.d.ts +0 -38
  49. package/dist/admin/hooks/useAdminOrders.d.ts.map +1 -1
  50. package/dist/admin/hooks/useAdminOrders.js +66 -208
  51. package/dist/admin/hooks/useAdminOrders.js.map +1 -1
  52. package/dist/admin/hooks/useAdminProducts.d.ts +0 -36
  53. package/dist/admin/hooks/useAdminProducts.d.ts.map +1 -1
  54. package/dist/admin/hooks/useAdminProducts.js +61 -180
  55. package/dist/admin/hooks/useAdminProducts.js.map +1 -1
  56. package/dist/admin/hooks/useAdminStats.d.ts.map +1 -1
  57. package/dist/admin/hooks/useAdminStats.js +26 -38
  58. package/dist/admin/hooks/useAdminStats.js.map +1 -1
  59. package/dist/admin/hooks/useAdminUsers.d.ts.map +1 -1
  60. package/dist/admin/hooks/useAdminUsers.js +82 -145
  61. package/dist/admin/hooks/useAdminUsers.js.map +1 -1
  62. package/dist/admin/types.d.ts +4 -4
  63. package/dist/admin/types.d.ts.map +1 -1
  64. package/package.json +1 -1
@@ -1,6 +1,7 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import { useState, useCallback, useEffect } from 'react';
4
+ import { fetchAdminApi } from '../hooks/fetchAdminApi';
4
5
  import { Package, Plus, Edit2, Trash2, Globe, ChevronDown, ChevronUp, Plug, MapPin, Check, Truck, Box, RefreshCw, Key, Loader2, ExternalLink, } from 'lucide-react';
5
6
  import { DeleteConfirmDialog } from './DeleteConfirmDialog';
6
7
  import { AdminSelect } from './AdminSelect';
@@ -108,37 +109,36 @@ export function ShippingSettings({ className }) {
108
109
  // Data Fetching
109
110
  // ---------------------------------------------------------------------------
110
111
  const fetchData = useCallback(async () => {
111
- try {
112
- // Fetch settings
113
- const settingsRes = await fetch('/api/admin/settings');
114
- if (settingsRes.ok) {
115
- const settingsData = await settingsRes.json();
116
- const settings = settingsData.settings || {};
117
- setShippingEnabled(settings.shippingEnabled ?? true);
118
- setFreeShippingThreshold(String(settings.freeShippingThreshold ?? 0));
119
- setShippingCountries(settings.shippingCountries || ['US', 'CA']);
120
- // Shippo settings
121
- setShippingMode(settings.shippingMode || 'manual');
122
- setShippoTestMode(settings.shippoTestMode ?? true);
123
- if (settings.shippoFromAddress) {
124
- setShippoFromAddress(settings.shippoFromAddress);
125
- }
126
- if (settings.shippoDefaultParcel) {
127
- setShippoDefaultParcel(settings.shippoDefaultParcel);
128
- }
112
+ // Fetch settings and carriers in parallel.
113
+ const [settingsRes, carriersRes] = await Promise.all([
114
+ fetchAdminApi('/api/admin/settings'),
115
+ fetchAdminApi('/api/admin/shipping/carriers'),
116
+ ]);
117
+ setIsFetching(false);
118
+ if (settingsRes.ok) {
119
+ const settings = settingsRes.data.settings || {};
120
+ setShippingEnabled(settings.shippingEnabled ?? true);
121
+ setFreeShippingThreshold(String(settings.freeShippingThreshold ?? 0));
122
+ setShippingCountries(settings.shippingCountries || ['US', 'CA']);
123
+ setShippingMode(settings.shippingMode || 'manual');
124
+ setShippoTestMode(settings.shippoTestMode ?? true);
125
+ if (settings.shippoFromAddress) {
126
+ setShippoFromAddress(settings.shippoFromAddress);
129
127
  }
130
- // Fetch carriers (includes nested zones and rates)
131
- const carriersRes = await fetch('/api/admin/shipping/carriers');
132
- if (carriersRes.ok) {
133
- const carriersData = await carriersRes.json();
134
- setCarriers(carriersData.data || []);
128
+ if (settings.shippoDefaultParcel) {
129
+ setShippoDefaultParcel(settings.shippoDefaultParcel);
135
130
  }
136
131
  }
137
- catch (err) {
138
- showToast('error', err instanceof Error ? err.message : 'Failed to load shipping data');
132
+ else {
133
+ showToast('error', settingsRes.error);
134
+ }
135
+ if (carriersRes.ok) {
136
+ setCarriers(carriersRes.data.data || []);
139
137
  }
140
- finally {
141
- setIsFetching(false);
138
+ else if (settingsRes.ok) {
139
+ // Only surface the carriers error if settings loaded OK — otherwise
140
+ // the settings error above is the more actionable message.
141
+ showToast('error', carriersRes.error);
142
142
  }
143
143
  }, [showToast]);
144
144
  useEffect(() => {
@@ -150,57 +150,44 @@ export function ShippingSettings({ className }) {
150
150
  const handleSaveSettings = useCallback(async () => {
151
151
  setToast(null);
152
152
  setIsLoading(true);
153
- try {
154
- const response = await fetch('/api/admin/settings', {
155
- method: 'PUT',
156
- headers: { 'Content-Type': 'application/json' },
157
- body: JSON.stringify({
158
- shippingEnabled,
159
- freeShippingThreshold: parseFloat(freeShippingThreshold) || 0,
160
- shippingCountries,
161
- // Shippo settings
162
- shippingMode,
163
- shippoTestMode,
164
- shippoFromAddress,
165
- shippoDefaultParcel,
166
- }),
167
- });
168
- if (!response.ok) {
169
- const result = await response.json();
170
- throw new Error(result.error || 'Failed to save settings');
171
- }
172
- showToast('success', 'Settings saved successfully!');
173
- }
174
- catch (err) {
175
- showToast('error', err instanceof Error ? err.message : 'Failed to save settings');
176
- }
177
- finally {
178
- setIsLoading(false);
153
+ const res = await fetchAdminApi('/api/admin/settings', {
154
+ method: 'PUT',
155
+ headers: { 'Content-Type': 'application/json' },
156
+ body: JSON.stringify({
157
+ shippingEnabled,
158
+ freeShippingThreshold: parseFloat(freeShippingThreshold) || 0,
159
+ shippingCountries,
160
+ // Shippo settings
161
+ shippingMode,
162
+ shippoTestMode,
163
+ shippoFromAddress,
164
+ shippoDefaultParcel,
165
+ }),
166
+ });
167
+ setIsLoading(false);
168
+ if (!res.ok) {
169
+ showToast('error', res.error);
170
+ return;
179
171
  }
172
+ showToast('success', 'Settings saved successfully!');
180
173
  }, [shippingEnabled, freeShippingThreshold, shippingCountries, shippingMode, shippoTestMode, shippoFromAddress, shippoDefaultParcel, showToast]);
181
174
  const handleValidateShippoKey = useCallback(async () => {
182
175
  setIsValidatingShippo(true);
183
176
  setShippoKeyValid(null);
184
- try {
185
- const response = await fetch('/api/admin/shipping/shippo/validate', {
186
- method: 'POST',
187
- });
188
- const result = await response.json();
189
- setShippoKeyValid(result.valid);
190
- setHasStoredKey(result.hasStoredKey ?? false);
191
- setMaskedKey(result.maskedKey ?? null);
192
- if (!result.valid && result.error) {
193
- showToast('error', result.error);
194
- }
195
- }
196
- catch (err) {
177
+ const res = await fetchAdminApi('/api/admin/shipping/shippo/validate', { method: 'POST' });
178
+ setIsValidatingShippo(false);
179
+ if (!res.ok) {
197
180
  setShippoKeyValid(false);
198
181
  setHasStoredKey(false);
199
182
  setMaskedKey(null);
200
- showToast('error', err instanceof Error ? err.message : 'Failed to validate Shippo API key');
183
+ showToast('error', res.error);
184
+ return;
201
185
  }
202
- finally {
203
- setIsValidatingShippo(false);
186
+ setShippoKeyValid(res.data.valid ?? false);
187
+ setHasStoredKey(res.data.hasStoredKey ?? false);
188
+ setMaskedKey(res.data.maskedKey ?? null);
189
+ if (!res.data.valid && res.data.error) {
190
+ showToast('error', res.data.error);
204
191
  }
205
192
  }, [showToast]);
206
193
  // Auto-validate Shippo API key when mode is 'shippo' after initial load
@@ -214,56 +201,38 @@ export function ShippingSettings({ className }) {
214
201
  return;
215
202
  setIsSavingApiKey(true);
216
203
  setToast(null);
217
- try {
218
- const response = await fetch('/api/admin/shipping/shippo/api-key', {
219
- method: 'POST',
220
- headers: { 'Content-Type': 'application/json' },
221
- body: JSON.stringify({ apiKey: shippoApiKeyInput }),
222
- });
223
- const result = await response.json();
224
- if (!response.ok) {
225
- showToast('error', result.error || 'Failed to save API key');
226
- return;
227
- }
228
- // Success - update state
229
- setHasStoredKey(true);
230
- setMaskedKey(result.maskedKey);
231
- setShippoApiKeyInput('');
232
- setShippoKeyValid(true);
233
- showToast('success', 'API key saved and verified!');
234
- }
235
- catch (err) {
236
- showToast('error', err instanceof Error ? err.message : 'Failed to save API key');
237
- }
238
- finally {
239
- setIsSavingApiKey(false);
204
+ const res = await fetchAdminApi('/api/admin/shipping/shippo/api-key', {
205
+ method: 'POST',
206
+ headers: { 'Content-Type': 'application/json' },
207
+ body: JSON.stringify({ apiKey: shippoApiKeyInput }),
208
+ });
209
+ setIsSavingApiKey(false);
210
+ if (!res.ok) {
211
+ showToast('error', res.error);
212
+ return;
240
213
  }
214
+ setHasStoredKey(true);
215
+ setMaskedKey(res.data.maskedKey ?? null);
216
+ setShippoApiKeyInput('');
217
+ setShippoKeyValid(true);
218
+ showToast('success', 'API key saved and verified!');
241
219
  }, [shippoApiKeyInput, showToast]);
242
220
  const handleRemoveApiKey = useCallback(async () => {
243
221
  setIsDeletingApiKey(true);
244
222
  setToast(null);
245
- try {
246
- const response = await fetch('/api/admin/shipping/shippo/api-key', {
247
- method: 'DELETE',
248
- });
249
- const result = await response.json();
250
- if (!response.ok) {
251
- showToast('error', result.error || 'Failed to remove API key');
252
- return;
253
- }
254
- // Success - reset state
255
- setHasStoredKey(false);
256
- setMaskedKey(null);
257
- showToast('success', 'API key removed');
258
- // Re-validate to check if env var exists
259
- handleValidateShippoKey();
260
- }
261
- catch (err) {
262
- showToast('error', err instanceof Error ? err.message : 'Failed to remove API key');
263
- }
264
- finally {
265
- setIsDeletingApiKey(false);
223
+ const res = await fetchAdminApi('/api/admin/shipping/shippo/api-key', {
224
+ method: 'DELETE',
225
+ });
226
+ setIsDeletingApiKey(false);
227
+ if (!res.ok) {
228
+ showToast('error', res.error);
229
+ return;
266
230
  }
231
+ setHasStoredKey(false);
232
+ setMaskedKey(null);
233
+ showToast('success', 'API key removed');
234
+ // Re-validate to check if env var exists
235
+ handleValidateShippoKey();
267
236
  }, [handleValidateShippoKey, showToast]);
268
237
  const handleAddCountry = useCallback(() => {
269
238
  const code = countryInput.toUpperCase().trim();
@@ -279,26 +248,18 @@ export function ShippingSettings({ className }) {
279
248
  const handleToggleShippingEnabled = useCallback(async () => {
280
249
  const newValue = !shippingEnabled;
281
250
  setShippingEnabled(newValue);
282
- try {
283
- const response = await fetch('/api/admin/settings', {
284
- method: 'PUT',
285
- headers: { 'Content-Type': 'application/json' },
286
- body: JSON.stringify({ shippingEnabled: newValue }),
287
- });
288
- if (!response.ok) {
289
- // Revert on error
290
- setShippingEnabled(!newValue);
291
- const result = await response.json();
292
- showToast('error', result.error || 'Failed to update shipping status');
293
- return;
294
- }
295
- showToast('success', newValue ? 'Shipping enabled' : 'Shipping disabled');
296
- }
297
- catch (err) {
251
+ const res = await fetchAdminApi('/api/admin/settings', {
252
+ method: 'PUT',
253
+ headers: { 'Content-Type': 'application/json' },
254
+ body: JSON.stringify({ shippingEnabled: newValue }),
255
+ });
256
+ if (!res.ok) {
298
257
  // Revert on error
299
258
  setShippingEnabled(!newValue);
300
- showToast('error', err instanceof Error ? err.message : 'Failed to update shipping status');
259
+ showToast('error', res.error);
260
+ return;
301
261
  }
262
+ showToast('success', newValue ? 'Shipping enabled' : 'Shipping disabled');
302
263
  }, [shippingEnabled, showToast]);
303
264
  // Carrier handlers - open delete dialog
304
265
  const handleDeleteCarrierClick = useCallback((carrier) => {
@@ -318,41 +279,34 @@ export function ShippingSettings({ className }) {
318
279
  return;
319
280
  setIsDeleting(true);
320
281
  setDeleteError(null);
321
- try {
322
- let endpoint;
323
- switch (deleteTarget.type) {
324
- case 'carrier':
325
- endpoint = `/api/admin/shipping/carriers/${deleteTarget.id}`;
326
- break;
327
- case 'zone':
328
- endpoint = `/api/admin/shipping/zones/${deleteTarget.id}`;
329
- break;
330
- case 'rate':
331
- endpoint = `/api/admin/shipping/rates/${deleteTarget.id}`;
332
- break;
333
- }
334
- const response = await fetch(endpoint, { method: 'DELETE' });
335
- if (!response.ok) {
336
- const result = await response.json();
337
- throw new Error(result.error || `Failed to delete ${deleteTarget.type}`);
338
- }
339
- // Update local state or refresh data
340
- if (deleteTarget.type === 'carrier') {
341
- setCarriers((prev) => prev.filter((c) => c.id !== deleteTarget.id));
342
- }
343
- else {
344
- // For zones and rates, refresh the full carrier list to reflect nested changes
345
- await fetchData();
346
- }
347
- // Close dialog on success
348
- setDeleteTarget(null);
282
+ let endpoint;
283
+ switch (deleteTarget.type) {
284
+ case 'carrier':
285
+ endpoint = `/api/admin/shipping/carriers/${deleteTarget.id}`;
286
+ break;
287
+ case 'zone':
288
+ endpoint = `/api/admin/shipping/zones/${deleteTarget.id}`;
289
+ break;
290
+ case 'rate':
291
+ endpoint = `/api/admin/shipping/rates/${deleteTarget.id}`;
292
+ break;
293
+ }
294
+ const res = await fetchAdminApi(endpoint, { method: 'DELETE' });
295
+ setIsDeleting(false);
296
+ if (!res.ok) {
297
+ setDeleteError(res.error);
298
+ return;
349
299
  }
350
- catch (err) {
351
- setDeleteError(err instanceof Error ? err.message : `Failed to delete ${deleteTarget.type}`);
300
+ // Update local state or refresh data
301
+ if (deleteTarget.type === 'carrier') {
302
+ setCarriers((prev) => prev.filter((c) => c.id !== deleteTarget.id));
352
303
  }
353
- finally {
354
- setIsDeleting(false);
304
+ else {
305
+ // For zones and rates, refresh the full carrier list to reflect nested changes
306
+ await fetchData();
355
307
  }
308
+ // Close dialog on success
309
+ setDeleteTarget(null);
356
310
  }, [deleteTarget, fetchData]);
357
311
  const handleDeleteCancel = useCallback(() => {
358
312
  setDeleteTarget(null);
@@ -465,33 +419,26 @@ function CarrierFormModal({ carrier, onClose, onSuccess, }) {
465
419
  e.preventDefault();
466
420
  setError(null);
467
421
  setIsLoading(true);
468
- try {
469
- const url = isEdit
470
- ? `/api/admin/shipping/carriers/${carrier.id}`
471
- : '/api/admin/shipping/carriers';
472
- const method = isEdit ? 'PUT' : 'POST';
473
- const response = await fetch(url, {
474
- method,
475
- headers: { 'Content-Type': 'application/json' },
476
- body: JSON.stringify({
477
- name,
478
- code,
479
- description: description || undefined,
480
- isActive,
481
- }),
482
- });
483
- if (!response.ok) {
484
- const result = await response.json();
485
- throw new Error(result.error || 'Failed to save carrier');
486
- }
487
- onSuccess();
488
- }
489
- catch (err) {
490
- setError(err instanceof Error ? err.message : 'Failed to save carrier');
491
- }
492
- finally {
493
- setIsLoading(false);
422
+ const url = isEdit
423
+ ? `/api/admin/shipping/carriers/${carrier.id}`
424
+ : '/api/admin/shipping/carriers';
425
+ const method = isEdit ? 'PUT' : 'POST';
426
+ const res = await fetchAdminApi(url, {
427
+ method,
428
+ headers: { 'Content-Type': 'application/json' },
429
+ body: JSON.stringify({
430
+ name,
431
+ code,
432
+ description: description || undefined,
433
+ isActive,
434
+ }),
435
+ });
436
+ setIsLoading(false);
437
+ if (!res.ok) {
438
+ setError(res.error);
439
+ return;
494
440
  }
441
+ onSuccess();
495
442
  };
496
443
  return (_jsxs("div", { className: "fixed inset-0 z-50 flex items-center justify-center", children: [_jsx("div", { className: "absolute inset-0 bg-black/50", onClick: onClose }), _jsx("div", { className: "relative bg-[hsl(var(--admin-card))] rounded-lg shadow-xl w-full max-w-md mx-4 max-h-[90vh] overflow-y-auto", children: _jsxs("form", { onSubmit: handleSubmit, children: [_jsxs("div", { className: "p-6", children: [_jsx("h3", { className: "admin-card-title mb-4", children: isEdit ? 'Edit Shipping Carrier' : 'Add Shipping Carrier' }), error && (_jsx("div", { className: "admin-alert admin-alert-error mb-4", children: _jsx("div", { className: "admin-alert-description", children: error }) })), _jsxs("div", { className: "space-y-4", children: [_jsxs("div", { children: [_jsx("label", { htmlFor: "carrierName", className: "admin-label admin-label-required", children: "Carrier Name" }), _jsx("input", { type: "text", id: "carrierName", value: name, onChange: (e) => handleNameChange(e.target.value), className: "admin-input mt-1 w-full", placeholder: "e.g., USPS, UPS, FedEx", required: true })] }), _jsxs("div", { children: [_jsx("label", { htmlFor: "carrierCode", className: "admin-label admin-label-required", children: "Carrier Code" }), _jsx("input", { type: "text", id: "carrierCode", value: code, onChange: (e) => setCode(e.target.value.toLowerCase().replace(/[^a-z0-9-]/g, '')), className: "admin-input mt-1 w-full", placeholder: "e.g., usps, ups, fedex", required: true, maxLength: 50 }), _jsx("p", { className: "admin-helper-text mt-1", children: "Unique identifier (lowercase, letters, numbers, hyphens only)" })] }), _jsxs("div", { children: [_jsx("label", { htmlFor: "carrierDescription", className: "admin-label", children: "Description" }), _jsx("input", { type: "text", id: "carrierDescription", value: description, onChange: (e) => setDescription(e.target.value), className: "admin-input mt-1 w-full", placeholder: "e.g., United States Postal Service", maxLength: 500 })] }), _jsxs("div", { className: "flex items-center justify-between", children: [_jsx("label", { className: "admin-label", children: "Active" }), _jsx("button", { type: "button", role: "switch", "aria-checked": isActive, onClick: () => setIsActive(!isActive), className: `relative inline-flex h-6 w-11 items-center rounded-full transition-colors ${isActive
497
444
  ? 'bg-[hsl(var(--admin-primary))]'
@@ -513,33 +460,26 @@ function ZoneFormModal({ carrierId, zone, onClose, onSuccess, }) {
513
460
  e.preventDefault();
514
461
  setError(null);
515
462
  setIsLoading(true);
516
- try {
517
- const url = isEdit
518
- ? `/api/admin/shipping/zones/${zone.id}`
519
- : '/api/admin/shipping/zones';
520
- const method = isEdit ? 'PUT' : 'POST';
521
- const response = await fetch(url, {
522
- method,
523
- headers: { 'Content-Type': 'application/json' },
524
- body: JSON.stringify({
525
- carrierId, // Required for carrier-first architecture
526
- name,
527
- countries: isAllCountries ? ['*'] : countries,
528
- isActive,
529
- }),
530
- });
531
- if (!response.ok) {
532
- const result = await response.json();
533
- throw new Error(result.error || 'Failed to save zone');
534
- }
535
- onSuccess();
536
- }
537
- catch (err) {
538
- setError(err instanceof Error ? err.message : 'Failed to save zone');
539
- }
540
- finally {
541
- setIsLoading(false);
463
+ const url = isEdit
464
+ ? `/api/admin/shipping/zones/${zone.id}`
465
+ : '/api/admin/shipping/zones';
466
+ const method = isEdit ? 'PUT' : 'POST';
467
+ const res = await fetchAdminApi(url, {
468
+ method,
469
+ headers: { 'Content-Type': 'application/json' },
470
+ body: JSON.stringify({
471
+ carrierId, // Required for carrier-first architecture
472
+ name,
473
+ countries: isAllCountries ? ['*'] : countries,
474
+ isActive,
475
+ }),
476
+ });
477
+ setIsLoading(false);
478
+ if (!res.ok) {
479
+ setError(res.error);
480
+ return;
542
481
  }
482
+ onSuccess();
543
483
  };
544
484
  const handleAddCountry = () => {
545
485
  const code = countryInput.toUpperCase().trim();
@@ -575,37 +515,30 @@ function RateFormModal({ zoneId, rate, onClose, onSuccess, currencySymbol, }) {
575
515
  e.preventDefault();
576
516
  setError(null);
577
517
  setIsLoading(true);
578
- try {
579
- const url = isEdit
580
- ? `/api/admin/shipping/rates/${rate.id}`
581
- : '/api/admin/shipping/rates';
582
- const method = isEdit ? 'PUT' : 'POST';
583
- const response = await fetch(url, {
584
- method,
585
- headers: { 'Content-Type': 'application/json' },
586
- body: JSON.stringify({
587
- zoneId,
588
- name,
589
- description: description || undefined,
590
- price: parseFloat(price) || 0,
591
- estimatedDays: estimatedDays || undefined,
592
- minOrderAmount: minOrderAmount ? parseFloat(minOrderAmount) : undefined,
593
- maxOrderAmount: maxOrderAmount ? parseFloat(maxOrderAmount) : undefined,
594
- isActive,
595
- }),
596
- });
597
- if (!response.ok) {
598
- const result = await response.json();
599
- throw new Error(result.error || 'Failed to save rate');
600
- }
601
- onSuccess();
602
- }
603
- catch (err) {
604
- setError(err instanceof Error ? err.message : 'Failed to save rate');
605
- }
606
- finally {
607
- setIsLoading(false);
518
+ const url = isEdit
519
+ ? `/api/admin/shipping/rates/${rate.id}`
520
+ : '/api/admin/shipping/rates';
521
+ const method = isEdit ? 'PUT' : 'POST';
522
+ const res = await fetchAdminApi(url, {
523
+ method,
524
+ headers: { 'Content-Type': 'application/json' },
525
+ body: JSON.stringify({
526
+ zoneId,
527
+ name,
528
+ description: description || undefined,
529
+ price: parseFloat(price) || 0,
530
+ estimatedDays: estimatedDays || undefined,
531
+ minOrderAmount: minOrderAmount ? parseFloat(minOrderAmount) : undefined,
532
+ maxOrderAmount: maxOrderAmount ? parseFloat(maxOrderAmount) : undefined,
533
+ isActive,
534
+ }),
535
+ });
536
+ setIsLoading(false);
537
+ if (!res.ok) {
538
+ setError(res.error);
539
+ return;
608
540
  }
541
+ onSuccess();
609
542
  };
610
543
  return (_jsxs("div", { className: "fixed inset-0 z-50 flex items-center justify-center", children: [_jsx("div", { className: "absolute inset-0 bg-black/50", onClick: onClose }), _jsx("div", { className: "relative bg-[hsl(var(--admin-card))] rounded-lg shadow-xl w-full max-w-md mx-4 max-h-[90vh] overflow-y-auto", children: _jsxs("form", { onSubmit: handleSubmit, children: [_jsxs("div", { className: "p-6", children: [_jsx("h3", { className: "admin-card-title mb-4", children: isEdit ? 'Edit Shipping Rate' : 'Add Shipping Rate' }), error && (_jsx("div", { className: "admin-alert admin-alert-error mb-4", children: _jsx("div", { className: "admin-alert-description", children: error }) })), _jsxs("div", { className: "space-y-4", children: [_jsxs("div", { children: [_jsx("label", { htmlFor: "rateName", className: "admin-label admin-label-required", children: "Rate Name" }), _jsx("input", { type: "text", id: "rateName", value: name, onChange: (e) => setName(e.target.value), className: "admin-input mt-1 w-full", placeholder: "e.g., Standard Shipping", required: true })] }), _jsxs("div", { children: [_jsx("label", { htmlFor: "rateDescription", className: "admin-label", children: "Description" }), _jsx("input", { type: "text", id: "rateDescription", value: description, onChange: (e) => setDescription(e.target.value), className: "admin-input mt-1 w-full", placeholder: "e.g., Delivery in 5-7 business days" })] }), _jsxs("div", { children: [_jsx("label", { htmlFor: "ratePrice", className: "admin-label admin-label-required", children: "Price" }), _jsxs("div", { className: "admin-input-with-addon mt-1", children: [_jsx("span", { className: "admin-input-addon-left", children: currencySymbol }), _jsx("input", { type: "number", id: "ratePrice", value: price, onChange: (e) => setPrice(e.target.value), min: "0", step: "0.01", className: "admin-input w-full pl-7", placeholder: "0.00", required: true })] })] }), _jsxs("div", { children: [_jsx("label", { htmlFor: "estimatedDays", className: "admin-label", children: "Estimated Delivery (days)" }), _jsx("input", { type: "text", id: "estimatedDays", value: estimatedDays, onChange: (e) => setEstimatedDays(e.target.value), className: "admin-input mt-1 w-full", placeholder: "e.g., 3-5" }), _jsx("p", { className: "admin-helper-text mt-1", children: "Format: single number (5) or range (3-5)" })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { children: [_jsx("label", { htmlFor: "minOrderAmount", className: "admin-label", children: "Min Order Amount" }), _jsxs("div", { className: "admin-input-with-addon mt-1", children: [_jsx("span", { className: "admin-input-addon-left", children: currencySymbol }), _jsx("input", { type: "number", id: "minOrderAmount", value: minOrderAmount, onChange: (e) => setMinOrderAmount(e.target.value), min: "0", step: "0.01", className: "admin-input w-full pl-7", placeholder: "0.00" })] })] }), _jsxs("div", { children: [_jsx("label", { htmlFor: "maxOrderAmount", className: "admin-label", children: "Max Order Amount" }), _jsxs("div", { className: "admin-input-with-addon mt-1", children: [_jsx("span", { className: "admin-input-addon-left", children: currencySymbol }), _jsx("input", { type: "number", id: "maxOrderAmount", value: maxOrderAmount, onChange: (e) => setMaxOrderAmount(e.target.value), min: "0", step: "0.01", className: "admin-input w-full pl-7", placeholder: "No limit" })] })] })] }), _jsx("p", { className: "admin-helper-text", children: "Leave empty for no restrictions" }), _jsxs("div", { className: "flex items-center justify-between", children: [_jsx("label", { className: "admin-label", children: "Active" }), _jsx("button", { type: "button", role: "switch", "aria-checked": isActive, onClick: () => setIsActive(!isActive), className: `relative inline-flex h-6 w-11 items-center rounded-full transition-colors ${isActive
611
544
  ? 'bg-[hsl(var(--admin-primary))]'