@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.
- package/dist/admin/components/CategoryForm.d.ts.map +1 -1
- package/dist/admin/components/CategoryForm.js +54 -65
- package/dist/admin/components/CategoryForm.js.map +1 -1
- package/dist/admin/components/CategorySelect.d.ts.map +1 -1
- package/dist/admin/components/CategorySelect.js +9 -14
- package/dist/admin/components/CategorySelect.js.map +1 -1
- package/dist/admin/components/CustomerDetails.js +3 -3
- package/dist/admin/components/CustomerDetails.js.map +1 -1
- package/dist/admin/components/CustomerTable.js +3 -3
- package/dist/admin/components/CustomerTable.js.map +1 -1
- package/dist/admin/components/LogoUpload.d.ts.map +1 -1
- package/dist/admin/components/LogoUpload.js +49 -67
- package/dist/admin/components/LogoUpload.js.map +1 -1
- package/dist/admin/components/OrderDetails.d.ts.map +1 -1
- package/dist/admin/components/OrderDetails.js +34 -65
- package/dist/admin/components/OrderDetails.js.map +1 -1
- package/dist/admin/components/OrderTable.js +2 -2
- package/dist/admin/components/OrderTable.js.map +1 -1
- package/dist/admin/components/PaymentSettings.d.ts.map +1 -1
- package/dist/admin/components/PaymentSettings.js +28 -51
- package/dist/admin/components/PaymentSettings.js.map +1 -1
- package/dist/admin/components/ProductTable.js +3 -3
- package/dist/admin/components/ProductTable.js.map +1 -1
- package/dist/admin/components/ShippingSettings.d.ts.map +1 -1
- package/dist/admin/components/ShippingSettings.js +177 -244
- package/dist/admin/components/ShippingSettings.js.map +1 -1
- package/dist/admin/components/StoreSettings.d.ts.map +1 -1
- package/dist/admin/components/StoreSettings.js +28 -42
- package/dist/admin/components/StoreSettings.js.map +1 -1
- package/dist/admin/components/TaxSettings.d.ts.map +1 -1
- package/dist/admin/components/TaxSettings.js +49 -81
- package/dist/admin/components/TaxSettings.js.map +1 -1
- package/dist/admin/hooks/fetchAdminApi.d.ts +65 -0
- package/dist/admin/hooks/fetchAdminApi.d.ts.map +1 -0
- package/dist/admin/hooks/fetchAdminApi.js +96 -0
- package/dist/admin/hooks/fetchAdminApi.js.map +1 -0
- package/dist/admin/hooks/useAdminCategories.d.ts +0 -27
- package/dist/admin/hooks/useAdminCategories.d.ts.map +1 -1
- package/dist/admin/hooks/useAdminCategories.js +39 -147
- package/dist/admin/hooks/useAdminCategories.js.map +1 -1
- package/dist/admin/hooks/useAdminCustomers.d.ts +0 -38
- package/dist/admin/hooks/useAdminCustomers.d.ts.map +1 -1
- package/dist/admin/hooks/useAdminCustomers.js +66 -172
- package/dist/admin/hooks/useAdminCustomers.js.map +1 -1
- package/dist/admin/hooks/useAdminMe.d.ts.map +1 -1
- package/dist/admin/hooks/useAdminMe.js +31 -59
- package/dist/admin/hooks/useAdminMe.js.map +1 -1
- package/dist/admin/hooks/useAdminOrders.d.ts +0 -38
- package/dist/admin/hooks/useAdminOrders.d.ts.map +1 -1
- package/dist/admin/hooks/useAdminOrders.js +66 -208
- package/dist/admin/hooks/useAdminOrders.js.map +1 -1
- package/dist/admin/hooks/useAdminProducts.d.ts +0 -36
- package/dist/admin/hooks/useAdminProducts.d.ts.map +1 -1
- package/dist/admin/hooks/useAdminProducts.js +61 -180
- package/dist/admin/hooks/useAdminProducts.js.map +1 -1
- package/dist/admin/hooks/useAdminStats.d.ts.map +1 -1
- package/dist/admin/hooks/useAdminStats.js +26 -38
- package/dist/admin/hooks/useAdminStats.js.map +1 -1
- package/dist/admin/hooks/useAdminUsers.d.ts.map +1 -1
- package/dist/admin/hooks/useAdminUsers.js +82 -145
- package/dist/admin/hooks/useAdminUsers.js.map +1 -1
- package/dist/admin/types.d.ts +4 -4
- package/dist/admin/types.d.ts.map +1 -1
- 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
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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
|
-
|
|
131
|
-
|
|
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
|
-
|
|
138
|
-
showToast('error',
|
|
132
|
+
else {
|
|
133
|
+
showToast('error', settingsRes.error);
|
|
134
|
+
}
|
|
135
|
+
if (carriersRes.ok) {
|
|
136
|
+
setCarriers(carriersRes.data.data || []);
|
|
139
137
|
}
|
|
140
|
-
|
|
141
|
-
|
|
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
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
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
|
-
|
|
185
|
-
|
|
186
|
-
|
|
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',
|
|
183
|
+
showToast('error', res.error);
|
|
184
|
+
return;
|
|
201
185
|
}
|
|
202
|
-
|
|
203
|
-
|
|
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
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
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
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
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
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
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',
|
|
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
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
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
|
-
|
|
351
|
-
|
|
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
|
-
|
|
354
|
-
|
|
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
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
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
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
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
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
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))]'
|