@lodashventure/medusa-campaign 1.4.3 → 1.4.6
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/.medusa/server/src/admin/index.js +1560 -1560
- package/.medusa/server/src/admin/index.mjs +1562 -1562
- package/package.json +14 -14
|
@@ -201,102 +201,99 @@ const CampaignDetailWidget = ({
|
|
|
201
201
|
adminSdk.defineWidgetConfig({
|
|
202
202
|
zone: "promotion.details.side.after"
|
|
203
203
|
});
|
|
204
|
-
const
|
|
204
|
+
const useCoupons = ({ limit, offset }) => {
|
|
205
205
|
const [data, setData] = React.useState(null);
|
|
206
206
|
const [isLoading, setIsLoading] = React.useState(true);
|
|
207
207
|
const [error, setError] = React.useState(null);
|
|
208
|
-
const
|
|
208
|
+
const fetchCoupons = async () => {
|
|
209
209
|
setIsLoading(true);
|
|
210
210
|
setError(null);
|
|
211
211
|
try {
|
|
212
|
-
const response = await axios__default.default.get("/admin/
|
|
213
|
-
params:
|
|
212
|
+
const response = await axios__default.default.get("/admin/coupons", {
|
|
213
|
+
params: { limit, offset }
|
|
214
214
|
});
|
|
215
215
|
setData(response.data);
|
|
216
216
|
} catch (err) {
|
|
217
|
-
setError(
|
|
218
|
-
err instanceof Error ? err : new Error("Failed to fetch flash sales")
|
|
219
|
-
);
|
|
217
|
+
setError(err instanceof Error ? err : new Error("Failed to fetch coupons"));
|
|
220
218
|
} finally {
|
|
221
219
|
setIsLoading(false);
|
|
222
220
|
}
|
|
223
221
|
};
|
|
224
222
|
React.useEffect(() => {
|
|
225
|
-
|
|
226
|
-
}, [
|
|
223
|
+
fetchCoupons();
|
|
224
|
+
}, [limit, offset]);
|
|
227
225
|
return {
|
|
228
226
|
data,
|
|
229
227
|
isLoading,
|
|
230
228
|
error,
|
|
231
|
-
refetch:
|
|
229
|
+
refetch: fetchCoupons
|
|
232
230
|
};
|
|
233
231
|
};
|
|
234
|
-
const
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
pageSize: limit,
|
|
239
|
-
pageIndex: 0
|
|
240
|
-
});
|
|
241
|
-
const offset = React.useMemo(() => {
|
|
242
|
-
return pagination.pageIndex * limit;
|
|
243
|
-
}, [pagination]);
|
|
244
|
-
const columnHelper2 = ui.createDataTableColumnHelper();
|
|
245
|
-
const columns2 = [
|
|
246
|
-
columnHelper2.accessor("name", {
|
|
232
|
+
const columnHelper$1 = ui.createDataTableColumnHelper();
|
|
233
|
+
const useCouponColumns = () => React.useMemo(
|
|
234
|
+
() => [
|
|
235
|
+
columnHelper$1.accessor("name", {
|
|
247
236
|
header: "Name",
|
|
248
237
|
cell: (info) => info.getValue()
|
|
249
238
|
}),
|
|
250
|
-
|
|
239
|
+
columnHelper$1.display({
|
|
240
|
+
id: "code",
|
|
241
|
+
header: "Code",
|
|
242
|
+
cell: (info) => {
|
|
243
|
+
var _a, _b;
|
|
244
|
+
return ((_b = (_a = info.row.original.promotions) == null ? void 0 : _a[0]) == null ? void 0 : _b.code) ?? "-";
|
|
245
|
+
}
|
|
246
|
+
}),
|
|
247
|
+
columnHelper$1.display({
|
|
248
|
+
id: "status",
|
|
251
249
|
header: "Status",
|
|
252
250
|
cell: (info) => {
|
|
253
|
-
const
|
|
254
|
-
if (!
|
|
251
|
+
const endsAt = info.row.original.ends_at;
|
|
252
|
+
if (!endsAt) {
|
|
255
253
|
return "";
|
|
256
254
|
}
|
|
257
|
-
const isActive = dayjs__default.default(
|
|
255
|
+
const isActive = dayjs__default.default(endsAt).isAfter(dayjs__default.default());
|
|
258
256
|
return /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { color: isActive ? "green" : "grey", children: isActive ? "Active" : "Inactive" });
|
|
259
257
|
}
|
|
260
258
|
}),
|
|
261
|
-
|
|
259
|
+
columnHelper$1.accessor("starts_at", {
|
|
262
260
|
header: "Start Date",
|
|
263
|
-
cell: (info) =>
|
|
264
|
-
const date = info.getValue();
|
|
265
|
-
if (!date) {
|
|
266
|
-
return "";
|
|
267
|
-
}
|
|
268
|
-
return dayjs__default.default(date).format("YYYY-MM-DD HH:mm");
|
|
269
|
-
}
|
|
261
|
+
cell: (info) => info.getValue() ? dayjs__default.default(info.getValue()).format("YYYY-MM-DD HH:mm") : ""
|
|
270
262
|
}),
|
|
271
|
-
|
|
263
|
+
columnHelper$1.accessor("ends_at", {
|
|
272
264
|
header: "End Date",
|
|
273
|
-
cell: (info) =>
|
|
274
|
-
const date = info.getValue();
|
|
275
|
-
if (!date) {
|
|
276
|
-
return "";
|
|
277
|
-
}
|
|
278
|
-
return dayjs__default.default(date).format("YYYY-MM-DD HH:mm");
|
|
279
|
-
}
|
|
265
|
+
cell: (info) => info.getValue() ? dayjs__default.default(info.getValue()).format("YYYY-MM-DD HH:mm") : ""
|
|
280
266
|
})
|
|
281
|
-
]
|
|
282
|
-
|
|
267
|
+
],
|
|
268
|
+
[]
|
|
269
|
+
);
|
|
270
|
+
const CouponPage = () => {
|
|
271
|
+
const navigate = reactRouterDom.useNavigate();
|
|
272
|
+
const limit = 20;
|
|
273
|
+
const [pagination, setPagination] = React.useState({
|
|
274
|
+
pageSize: limit,
|
|
275
|
+
pageIndex: 0
|
|
276
|
+
});
|
|
277
|
+
const offset = pagination.pageIndex * limit;
|
|
278
|
+
const { data } = useCoupons({ limit, offset });
|
|
279
|
+
const columns2 = useCouponColumns();
|
|
283
280
|
const table = ui.useDataTable({
|
|
284
|
-
data: (data == null ? void 0 : data.campaigns)
|
|
281
|
+
data: (data == null ? void 0 : data.campaigns) ?? [],
|
|
285
282
|
columns: columns2,
|
|
286
|
-
getRowId: (
|
|
283
|
+
getRowId: (row) => row.id,
|
|
287
284
|
pagination: {
|
|
288
285
|
state: pagination,
|
|
289
286
|
onPaginationChange: setPagination
|
|
290
287
|
},
|
|
291
|
-
rowCount: (data == null ? void 0 : data.count)
|
|
288
|
+
rowCount: (data == null ? void 0 : data.count) ?? 0,
|
|
292
289
|
onRowClick: (_, row) => {
|
|
293
|
-
navigate(`/
|
|
290
|
+
navigate(`/coupons/${row.id}`);
|
|
294
291
|
}
|
|
295
292
|
});
|
|
296
293
|
return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { children: [
|
|
297
294
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-center", children: [
|
|
298
|
-
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-xl font-semibold", children: "
|
|
299
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Button, { onClick: () => navigate("/
|
|
295
|
+
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-xl font-semibold", children: "Coupons" }),
|
|
296
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Button, { onClick: () => navigate("/coupons/create"), children: "Create Coupon" })
|
|
300
297
|
] }),
|
|
301
298
|
/* @__PURE__ */ jsxRuntime.jsxs(ui.DataTable, { instance: table, children: [
|
|
302
299
|
/* @__PURE__ */ jsxRuntime.jsx(ui.DataTable.Table, {}),
|
|
@@ -304,106 +301,109 @@ const FlashSalePage = () => {
|
|
|
304
301
|
] })
|
|
305
302
|
] });
|
|
306
303
|
};
|
|
307
|
-
const
|
|
308
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
304
|
+
const Coupons = () => {
|
|
305
|
+
return /* @__PURE__ */ jsxRuntime.jsx(CouponPage, {});
|
|
309
306
|
};
|
|
310
307
|
const config$3 = adminSdk.defineRouteConfig({
|
|
311
|
-
label: "
|
|
312
|
-
icon: icons.
|
|
308
|
+
label: "Coupons",
|
|
309
|
+
icon: icons.Tag
|
|
313
310
|
});
|
|
314
|
-
const
|
|
311
|
+
const useFlashSales = (pagination) => {
|
|
315
312
|
const [data, setData] = React.useState(null);
|
|
316
313
|
const [isLoading, setIsLoading] = React.useState(true);
|
|
317
314
|
const [error, setError] = React.useState(null);
|
|
318
|
-
const
|
|
315
|
+
const fetchFlashSales = async () => {
|
|
319
316
|
setIsLoading(true);
|
|
320
317
|
setError(null);
|
|
321
318
|
try {
|
|
322
|
-
const response = await axios__default.default.get("/admin/
|
|
323
|
-
params:
|
|
319
|
+
const response = await axios__default.default.get("/admin/flash-sales", {
|
|
320
|
+
params: pagination
|
|
324
321
|
});
|
|
325
322
|
setData(response.data);
|
|
326
323
|
} catch (err) {
|
|
327
|
-
setError(
|
|
324
|
+
setError(
|
|
325
|
+
err instanceof Error ? err : new Error("Failed to fetch flash sales")
|
|
326
|
+
);
|
|
328
327
|
} finally {
|
|
329
328
|
setIsLoading(false);
|
|
330
329
|
}
|
|
331
330
|
};
|
|
332
331
|
React.useEffect(() => {
|
|
333
|
-
|
|
334
|
-
}, [limit, offset]);
|
|
332
|
+
fetchFlashSales();
|
|
333
|
+
}, [pagination.limit, pagination.offset]);
|
|
335
334
|
return {
|
|
336
335
|
data,
|
|
337
336
|
isLoading,
|
|
338
337
|
error,
|
|
339
|
-
refetch:
|
|
338
|
+
refetch: fetchFlashSales
|
|
340
339
|
};
|
|
341
340
|
};
|
|
342
|
-
const
|
|
343
|
-
const
|
|
344
|
-
|
|
345
|
-
|
|
341
|
+
const FlashSalePage = () => {
|
|
342
|
+
const navigate = reactRouterDom.useNavigate();
|
|
343
|
+
const limit = 20;
|
|
344
|
+
const [pagination, setPagination] = React.useState({
|
|
345
|
+
pageSize: limit,
|
|
346
|
+
pageIndex: 0
|
|
347
|
+
});
|
|
348
|
+
const offset = React.useMemo(() => {
|
|
349
|
+
return pagination.pageIndex * limit;
|
|
350
|
+
}, [pagination]);
|
|
351
|
+
const columnHelper2 = ui.createDataTableColumnHelper();
|
|
352
|
+
const columns2 = [
|
|
353
|
+
columnHelper2.accessor("name", {
|
|
346
354
|
header: "Name",
|
|
347
355
|
cell: (info) => info.getValue()
|
|
348
356
|
}),
|
|
349
|
-
|
|
350
|
-
id: "code",
|
|
351
|
-
header: "Code",
|
|
352
|
-
cell: (info) => {
|
|
353
|
-
var _a, _b;
|
|
354
|
-
return ((_b = (_a = info.row.original.promotions) == null ? void 0 : _a[0]) == null ? void 0 : _b.code) ?? "-";
|
|
355
|
-
}
|
|
356
|
-
}),
|
|
357
|
-
columnHelper$1.display({
|
|
358
|
-
id: "status",
|
|
357
|
+
columnHelper2.accessor("ends_at", {
|
|
359
358
|
header: "Status",
|
|
360
359
|
cell: (info) => {
|
|
361
|
-
const
|
|
362
|
-
if (!
|
|
360
|
+
const date = info.getValue();
|
|
361
|
+
if (!date) {
|
|
363
362
|
return "";
|
|
364
363
|
}
|
|
365
|
-
const isActive = dayjs__default.default(
|
|
364
|
+
const isActive = dayjs__default.default(date).isAfter(dayjs__default.default());
|
|
366
365
|
return /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { color: isActive ? "green" : "grey", children: isActive ? "Active" : "Inactive" });
|
|
367
366
|
}
|
|
368
367
|
}),
|
|
369
|
-
|
|
368
|
+
columnHelper2.accessor("starts_at", {
|
|
370
369
|
header: "Start Date",
|
|
371
|
-
cell: (info) =>
|
|
370
|
+
cell: (info) => {
|
|
371
|
+
const date = info.getValue();
|
|
372
|
+
if (!date) {
|
|
373
|
+
return "";
|
|
374
|
+
}
|
|
375
|
+
return dayjs__default.default(date).format("YYYY-MM-DD HH:mm");
|
|
376
|
+
}
|
|
372
377
|
}),
|
|
373
|
-
|
|
378
|
+
columnHelper2.accessor("ends_at", {
|
|
374
379
|
header: "End Date",
|
|
375
|
-
cell: (info) =>
|
|
380
|
+
cell: (info) => {
|
|
381
|
+
const date = info.getValue();
|
|
382
|
+
if (!date) {
|
|
383
|
+
return "";
|
|
384
|
+
}
|
|
385
|
+
return dayjs__default.default(date).format("YYYY-MM-DD HH:mm");
|
|
386
|
+
}
|
|
376
387
|
})
|
|
377
|
-
]
|
|
378
|
-
|
|
379
|
-
);
|
|
380
|
-
const CouponPage = () => {
|
|
381
|
-
const navigate = reactRouterDom.useNavigate();
|
|
382
|
-
const limit = 20;
|
|
383
|
-
const [pagination, setPagination] = React.useState({
|
|
384
|
-
pageSize: limit,
|
|
385
|
-
pageIndex: 0
|
|
386
|
-
});
|
|
387
|
-
const offset = pagination.pageIndex * limit;
|
|
388
|
-
const { data } = useCoupons({ limit, offset });
|
|
389
|
-
const columns2 = useCouponColumns();
|
|
388
|
+
];
|
|
389
|
+
const { data } = useFlashSales({ limit, offset });
|
|
390
390
|
const table = ui.useDataTable({
|
|
391
|
-
data: (data == null ? void 0 : data.campaigns)
|
|
391
|
+
data: (data == null ? void 0 : data.campaigns) || [],
|
|
392
392
|
columns: columns2,
|
|
393
|
-
getRowId: (
|
|
393
|
+
getRowId: (campaign) => campaign.id,
|
|
394
394
|
pagination: {
|
|
395
395
|
state: pagination,
|
|
396
396
|
onPaginationChange: setPagination
|
|
397
397
|
},
|
|
398
|
-
rowCount: (data == null ? void 0 : data.count)
|
|
398
|
+
rowCount: (data == null ? void 0 : data.count) || 0,
|
|
399
399
|
onRowClick: (_, row) => {
|
|
400
|
-
navigate(`/
|
|
400
|
+
navigate(`/flash-sales/${row.id}`);
|
|
401
401
|
}
|
|
402
402
|
});
|
|
403
403
|
return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { children: [
|
|
404
404
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-center", children: [
|
|
405
|
-
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-xl font-semibold", children: "
|
|
406
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Button, { onClick: () => navigate("/
|
|
405
|
+
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-xl font-semibold", children: "Campaigns" }),
|
|
406
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Button, { onClick: () => navigate("/flash-sales/create"), children: "Create Campaign" })
|
|
407
407
|
] }),
|
|
408
408
|
/* @__PURE__ */ jsxRuntime.jsxs(ui.DataTable, { instance: table, children: [
|
|
409
409
|
/* @__PURE__ */ jsxRuntime.jsx(ui.DataTable.Table, {}),
|
|
@@ -411,12 +411,12 @@ const CouponPage = () => {
|
|
|
411
411
|
] })
|
|
412
412
|
] });
|
|
413
413
|
};
|
|
414
|
-
const
|
|
415
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
414
|
+
const FlashSale = () => {
|
|
415
|
+
return /* @__PURE__ */ jsxRuntime.jsx(FlashSalePage, {});
|
|
416
416
|
};
|
|
417
417
|
const config$2 = adminSdk.defineRouteConfig({
|
|
418
|
-
label: "
|
|
419
|
-
icon: icons.
|
|
418
|
+
label: "Flash Sale",
|
|
419
|
+
icon: icons.Sparkles
|
|
420
420
|
});
|
|
421
421
|
var isCheckBoxInput = (element) => element.type === "checkbox";
|
|
422
422
|
var isDateObject = (value) => value instanceof Date;
|
|
@@ -2299,134 +2299,64 @@ var n = function(e2, o2) {
|
|
|
2299
2299
|
}
|
|
2300
2300
|
};
|
|
2301
2301
|
};
|
|
2302
|
-
const
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2302
|
+
const couponSchema = z__default.default.object({
|
|
2303
|
+
name: z__default.default.string().min(1, "Name is required"),
|
|
2304
|
+
description: z__default.default.string().min(1, "Description is required"),
|
|
2305
|
+
type: z__default.default.literal("coupon"),
|
|
2306
|
+
code: z__default.default.string().min(1, "Coupon code is required"),
|
|
2307
|
+
discount_type: z__default.default.enum(["percentage", "fixed"]),
|
|
2308
|
+
discount_value: z__default.default.number().positive("Discount must be positive"),
|
|
2309
|
+
currency_code: z__default.default.string().optional(),
|
|
2310
|
+
starts_at: z__default.default.string().min(1, "Start date is required"),
|
|
2311
|
+
ends_at: z__default.default.string().min(1, "End date is required"),
|
|
2312
|
+
allocation: z__default.default.enum(["each", "total"]).optional(),
|
|
2313
|
+
target_type: z__default.default.enum(["order", "items"]).optional()
|
|
2314
|
+
}).superRefine((data, ctx) => {
|
|
2315
|
+
if (new Date(data.ends_at) < new Date(data.starts_at)) {
|
|
2316
|
+
ctx.addIssue({
|
|
2317
|
+
code: z__default.default.ZodIssueCode.custom,
|
|
2318
|
+
path: ["ends_at"],
|
|
2319
|
+
message: "End date must be after start date"
|
|
2320
|
+
});
|
|
2321
|
+
}
|
|
2322
|
+
if (data.discount_type === "fixed" && !data.currency_code) {
|
|
2323
|
+
ctx.addIssue({
|
|
2324
|
+
code: z__default.default.ZodIssueCode.custom,
|
|
2325
|
+
path: ["currency_code"],
|
|
2326
|
+
message: "Currency is required for fixed discount"
|
|
2327
|
+
});
|
|
2307
2328
|
}
|
|
2308
2329
|
});
|
|
2309
|
-
const
|
|
2310
|
-
const columns = [
|
|
2311
|
-
columnHelper.accessor("title", {
|
|
2312
|
-
header: "Title",
|
|
2313
|
-
cell: (info) => /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "large", className: "py-2", children: info.getValue() })
|
|
2314
|
-
}),
|
|
2315
|
-
columnHelper.accessor("description", {
|
|
2316
|
-
header: "Description",
|
|
2317
|
-
cell: (info) => /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "large", className: "py-2", children: info.getValue() })
|
|
2318
|
-
})
|
|
2319
|
-
];
|
|
2320
|
-
const ProductSelector = ({
|
|
2321
|
-
selectedProductIds,
|
|
2322
|
-
onSelectProduct
|
|
2323
|
-
}) => {
|
|
2324
|
-
const [search, setSearch] = React.useState("");
|
|
2325
|
-
const debouncedSearchHandler = lodash.debounce((value) => {
|
|
2326
|
-
setSearch(value);
|
|
2327
|
-
}, 500);
|
|
2328
|
-
const [products, setProducts] = React.useState(null);
|
|
2329
|
-
const [isLoading, setIsLoading] = React.useState(true);
|
|
2330
|
-
React.useEffect(() => {
|
|
2331
|
-
const fetchProducts = async () => {
|
|
2332
|
-
setIsLoading(true);
|
|
2333
|
-
try {
|
|
2334
|
-
const result = await sdk.admin.product.list({
|
|
2335
|
-
q: search,
|
|
2336
|
-
limit: 100
|
|
2337
|
-
});
|
|
2338
|
-
setProducts(result);
|
|
2339
|
-
} catch (error) {
|
|
2340
|
-
console.error("Failed to fetch products:", error);
|
|
2341
|
-
} finally {
|
|
2342
|
-
setIsLoading(false);
|
|
2343
|
-
}
|
|
2344
|
-
};
|
|
2345
|
-
fetchProducts();
|
|
2346
|
-
}, [selectedProductIds, search]);
|
|
2347
|
-
const selectableProducts = React.useMemo(() => {
|
|
2348
|
-
var _a;
|
|
2349
|
-
return (_a = products == null ? void 0 : products.products) == null ? void 0 : _a.filter(
|
|
2350
|
-
(product) => !selectedProductIds.includes(product.id)
|
|
2351
|
-
);
|
|
2352
|
-
}, [products == null ? void 0 : products.products, selectedProductIds]);
|
|
2353
|
-
const table = ui.useDataTable({
|
|
2354
|
-
data: selectableProducts || [],
|
|
2355
|
-
columns,
|
|
2356
|
-
getRowId: (product) => product.id,
|
|
2357
|
-
onRowClick: (_, row) => {
|
|
2358
|
-
onSelectProduct == null ? void 0 : onSelectProduct(row.original);
|
|
2359
|
-
}
|
|
2360
|
-
});
|
|
2361
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { children: [
|
|
2362
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2363
|
-
ui.Input,
|
|
2364
|
-
{
|
|
2365
|
-
className: "text-lg py-2",
|
|
2366
|
-
placeholder: "Search products...",
|
|
2367
|
-
onChange: (e2) => debouncedSearchHandler(e2.target.value)
|
|
2368
|
-
}
|
|
2369
|
-
),
|
|
2370
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.DataTable, { instance: table, children: /* @__PURE__ */ jsxRuntime.jsx(ui.DataTable.Table, {}) })
|
|
2371
|
-
] });
|
|
2372
|
-
};
|
|
2373
|
-
const customCampaignSchema = z__default.default.object({
|
|
2374
|
-
name: z__default.default.string().min(1, "Name is required"),
|
|
2375
|
-
description: z__default.default.string().min(1, "Description is required"),
|
|
2376
|
-
type: z__default.default.literal("flash-sale"),
|
|
2377
|
-
starts_at: z__default.default.string().min(1, "Start date is required"),
|
|
2378
|
-
ends_at: z__default.default.string().min(1, "End date is required")
|
|
2379
|
-
});
|
|
2380
|
-
const campaignProductSchema = z__default.default.object({
|
|
2381
|
-
product: z__default.default.object({
|
|
2382
|
-
id: z__default.default.string(),
|
|
2383
|
-
title: z__default.default.string()
|
|
2384
|
-
}),
|
|
2385
|
-
discountType: z__default.default.enum([
|
|
2386
|
-
"percentage"
|
|
2387
|
-
// , "fixed"
|
|
2388
|
-
]),
|
|
2389
|
-
discountValue: z__default.default.number().min(1),
|
|
2390
|
-
limit: z__default.default.number().min(1),
|
|
2391
|
-
maxQty: z__default.default.number().min(1)
|
|
2392
|
-
});
|
|
2393
|
-
const campaignDataSchema = customCampaignSchema.extend({
|
|
2394
|
-
products: z__default.default.array(campaignProductSchema).min(1, "At least one product is required")
|
|
2395
|
-
}).refine(
|
|
2396
|
-
(data) => new Date(data.starts_at) < new Date(data.ends_at),
|
|
2397
|
-
"End date must be after start date"
|
|
2398
|
-
);
|
|
2399
|
-
const FlashSaleForm = ({
|
|
2330
|
+
const CouponForm = ({
|
|
2400
2331
|
initialData,
|
|
2401
|
-
initialProducts,
|
|
2402
2332
|
onSubmit,
|
|
2403
2333
|
onCancel,
|
|
2404
2334
|
disabled = false
|
|
2405
2335
|
}) => {
|
|
2406
|
-
var _a;
|
|
2407
2336
|
const {
|
|
2408
|
-
control,
|
|
2409
2337
|
register,
|
|
2338
|
+
control,
|
|
2410
2339
|
handleSubmit,
|
|
2411
2340
|
watch,
|
|
2412
2341
|
setValue,
|
|
2413
2342
|
formState: { errors }
|
|
2414
2343
|
} = useForm({
|
|
2415
|
-
resolver: t(
|
|
2344
|
+
resolver: t(couponSchema),
|
|
2416
2345
|
defaultValues: {
|
|
2417
|
-
name: (initialData == null ? void 0 : initialData.name)
|
|
2418
|
-
description: (initialData == null ? void 0 : initialData.description)
|
|
2419
|
-
type: "
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2346
|
+
name: (initialData == null ? void 0 : initialData.name) ?? "",
|
|
2347
|
+
description: (initialData == null ? void 0 : initialData.description) ?? "",
|
|
2348
|
+
type: "coupon",
|
|
2349
|
+
code: (initialData == null ? void 0 : initialData.code) ?? "",
|
|
2350
|
+
discount_type: (initialData == null ? void 0 : initialData.discount_type) ?? "percentage",
|
|
2351
|
+
discount_value: (initialData == null ? void 0 : initialData.discount_value) ?? 0,
|
|
2352
|
+
currency_code: (initialData == null ? void 0 : initialData.currency_code) ?? "",
|
|
2353
|
+
starts_at: (initialData == null ? void 0 : initialData.starts_at) ?? dayjs__default.default().startOf("day").format("YYYY-MM-DDTHH:mm"),
|
|
2354
|
+
ends_at: (initialData == null ? void 0 : initialData.ends_at) ?? dayjs__default.default().endOf("day").format("YYYY-MM-DDTHH:mm"),
|
|
2355
|
+
allocation: (initialData == null ? void 0 : initialData.allocation) ?? "total",
|
|
2356
|
+
target_type: (initialData == null ? void 0 : initialData.target_type) ?? "order"
|
|
2423
2357
|
}
|
|
2424
2358
|
});
|
|
2425
|
-
const
|
|
2426
|
-
control,
|
|
2427
|
-
name: "products"
|
|
2428
|
-
});
|
|
2429
|
-
const [openProductModal, setOpenProductModal] = React.useState(false);
|
|
2359
|
+
const discountType = watch("discount_type");
|
|
2430
2360
|
const startsAt = watch("starts_at");
|
|
2431
2361
|
const endsAt = watch("ends_at");
|
|
2432
2362
|
const handleDateTimeChange = (field, type, value) => {
|
|
@@ -2443,42 +2373,34 @@ const FlashSaleForm = ({
|
|
|
2443
2373
|
onSubmit(data);
|
|
2444
2374
|
};
|
|
2445
2375
|
const onFormError = () => {
|
|
2446
|
-
const errorMessages = Object.
|
|
2447
|
-
|
|
2448
|
-
if (key === "products" && Array.isArray(value)) {
|
|
2449
|
-
return value.map(
|
|
2450
|
-
(item, index) => item ? `Product ${index + 1}: ${Object.values(item).join(", ")}` : ""
|
|
2451
|
-
).filter(Boolean).join(", ");
|
|
2452
|
-
}
|
|
2453
|
-
return (value == null ? void 0 : value.message) || ((_a2 = value == null ? void 0 : value.root) == null ? void 0 : _a2.message);
|
|
2454
|
-
}).filter(Boolean).join(", ");
|
|
2455
|
-
ui.toast.error("Invalid data", {
|
|
2376
|
+
const errorMessages = Object.values(errors).map((err) => err == null ? void 0 : err.message).filter(Boolean).join(", ");
|
|
2377
|
+
ui.toast.error("Invalid coupon data", {
|
|
2456
2378
|
description: errorMessages
|
|
2457
2379
|
});
|
|
2458
2380
|
};
|
|
2459
2381
|
return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { children: [
|
|
2460
2382
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
|
|
2461
|
-
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-xl font-semibold", children: "
|
|
2383
|
+
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-xl font-semibold", children: "Coupon" }),
|
|
2462
2384
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "transparent", onClick: onCancel, children: "Cancel" })
|
|
2463
2385
|
] }),
|
|
2464
2386
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
2465
2387
|
"form",
|
|
2466
2388
|
{
|
|
2467
2389
|
onSubmit: handleSubmit(onFormSubmit, onFormError),
|
|
2468
|
-
className: "space-y-
|
|
2390
|
+
className: "space-y-6 my-8",
|
|
2469
2391
|
children: [
|
|
2470
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
2392
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
2471
2393
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { children: "Name" }),
|
|
2472
2394
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Input, { ...register("name"), disabled }),
|
|
2473
2395
|
errors.name && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-red-500 text-sm mt-1", children: errors.name.message })
|
|
2474
2396
|
] }),
|
|
2475
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
2397
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
2476
2398
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { children: "Description" }),
|
|
2477
2399
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Textarea, { ...register("description"), disabled }),
|
|
2478
2400
|
errors.description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-red-500 text-sm mt-1", children: errors.description.message })
|
|
2479
2401
|
] }),
|
|
2480
2402
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
|
|
2481
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
2403
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
2482
2404
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { children: "Start Date" }),
|
|
2483
2405
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
|
|
2484
2406
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -2498,13 +2420,13 @@ const FlashSaleForm = ({
|
|
|
2498
2420
|
value: startsAt ? dayjs__default.default(startsAt).format("HH:mm") : "",
|
|
2499
2421
|
onChange: (e2) => handleDateTimeChange("starts_at", "time", e2.target.value),
|
|
2500
2422
|
disabled,
|
|
2501
|
-
className: "
|
|
2423
|
+
className: "flex-1"
|
|
2502
2424
|
}
|
|
2503
2425
|
)
|
|
2504
2426
|
] }),
|
|
2505
2427
|
errors.starts_at && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-red-500 text-sm mt-1", children: errors.starts_at.message })
|
|
2506
2428
|
] }),
|
|
2507
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
2429
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
2508
2430
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { children: "End Date" }),
|
|
2509
2431
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
|
|
2510
2432
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -2524,1450 +2446,1493 @@ const FlashSaleForm = ({
|
|
|
2524
2446
|
value: endsAt ? dayjs__default.default(endsAt).format("HH:mm") : "",
|
|
2525
2447
|
onChange: (e2) => handleDateTimeChange("ends_at", "time", e2.target.value),
|
|
2526
2448
|
disabled,
|
|
2527
|
-
className: "
|
|
2449
|
+
className: "flex-1"
|
|
2528
2450
|
}
|
|
2529
2451
|
)
|
|
2530
2452
|
] }),
|
|
2531
2453
|
errors.ends_at && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-red-500 text-sm mt-1", children: errors.ends_at.message })
|
|
2532
2454
|
] })
|
|
2533
2455
|
] }),
|
|
2534
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "
|
|
2535
|
-
/* @__PURE__ */ jsxRuntime.
|
|
2536
|
-
|
|
2537
|
-
ui.
|
|
2538
|
-
{
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
children: "Add Product"
|
|
2544
|
-
}
|
|
2545
|
-
)
|
|
2546
|
-
] }),
|
|
2547
|
-
((_a = errors.products) == null ? void 0 : _a.root) && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-red-500 text-sm", children: errors.products.root.message }),
|
|
2548
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ui.Table, { children: [
|
|
2549
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Header, { children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
|
|
2550
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "Product" }),
|
|
2551
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "Discount Type" }),
|
|
2552
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "Discount Value" }),
|
|
2553
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "Limit" }),
|
|
2554
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "Max Qty per Order" }),
|
|
2555
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "Actions" })
|
|
2556
|
-
] }) }),
|
|
2557
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Body, { children: fields.map((field, index) => /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
|
|
2558
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: field.product.title }),
|
|
2559
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2456
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
|
|
2457
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
2458
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { children: "Coupon Code" }),
|
|
2459
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Input, { ...register("code"), disabled }),
|
|
2460
|
+
errors.code && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-red-500 text-sm mt-1", children: errors.code.message })
|
|
2461
|
+
] }),
|
|
2462
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
2463
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { children: "Discount Type" }),
|
|
2464
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2560
2465
|
Controller,
|
|
2561
2466
|
{
|
|
2562
|
-
name:
|
|
2467
|
+
name: "discount_type",
|
|
2563
2468
|
control,
|
|
2564
|
-
render: ({ field
|
|
2469
|
+
render: ({ field }) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2565
2470
|
ui.Select,
|
|
2566
2471
|
{
|
|
2567
|
-
value:
|
|
2568
|
-
onValueChange:
|
|
2472
|
+
value: field.value,
|
|
2473
|
+
onValueChange: field.onChange,
|
|
2569
2474
|
disabled,
|
|
2570
2475
|
children: [
|
|
2571
2476
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder: "Select discount type" }) }),
|
|
2572
|
-
/* @__PURE__ */ jsxRuntime.
|
|
2477
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Select.Content, { children: [
|
|
2478
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: "percentage", children: "Percentage" }),
|
|
2479
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: "fixed", children: "Fixed Amount" })
|
|
2480
|
+
] })
|
|
2573
2481
|
]
|
|
2574
2482
|
}
|
|
2575
2483
|
)
|
|
2576
2484
|
}
|
|
2577
|
-
)
|
|
2578
|
-
|
|
2485
|
+
)
|
|
2486
|
+
] })
|
|
2487
|
+
] }),
|
|
2488
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
|
|
2489
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
2490
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { children: "Discount Value" }),
|
|
2491
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2579
2492
|
Controller,
|
|
2580
2493
|
{
|
|
2581
|
-
name:
|
|
2494
|
+
name: "discount_value",
|
|
2582
2495
|
control,
|
|
2583
|
-
render: ({ field
|
|
2496
|
+
render: ({ field }) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
2584
2497
|
ui.Input,
|
|
2585
2498
|
{
|
|
2586
2499
|
type: "number",
|
|
2587
|
-
value:
|
|
2588
|
-
|
|
2500
|
+
value: field.value,
|
|
2501
|
+
min: 0,
|
|
2502
|
+
step: field.value % 1 === 0 ? 1 : 0.01,
|
|
2503
|
+
onChange: (e2) => field.onChange(Number(e2.target.value)),
|
|
2589
2504
|
disabled
|
|
2590
2505
|
}
|
|
2591
2506
|
)
|
|
2592
2507
|
}
|
|
2593
|
-
)
|
|
2594
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2508
|
+
),
|
|
2509
|
+
errors.discount_value && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-red-500 text-sm mt-1", children: errors.discount_value.message })
|
|
2510
|
+
] }),
|
|
2511
|
+
discountType === "fixed" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
2512
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { children: "Currency Code" }),
|
|
2513
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Input, { ...register("currency_code"), disabled }),
|
|
2514
|
+
errors.currency_code && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-red-500 text-sm mt-1", children: errors.currency_code.message })
|
|
2515
|
+
] })
|
|
2516
|
+
] }),
|
|
2517
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
|
|
2518
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
2519
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { children: "Allocation" }),
|
|
2520
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2595
2521
|
Controller,
|
|
2596
2522
|
{
|
|
2597
|
-
name:
|
|
2523
|
+
name: "allocation",
|
|
2598
2524
|
control,
|
|
2599
|
-
render: ({ field
|
|
2600
|
-
ui.
|
|
2525
|
+
render: ({ field }) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2526
|
+
ui.Select,
|
|
2601
2527
|
{
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2528
|
+
value: field.value,
|
|
2529
|
+
onValueChange: field.onChange,
|
|
2530
|
+
disabled,
|
|
2531
|
+
children: [
|
|
2532
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder: "Select allocation" }) }),
|
|
2533
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Select.Content, { children: [
|
|
2534
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: "total", children: "Order Total" }),
|
|
2535
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: "each", children: "Each Item" })
|
|
2536
|
+
] })
|
|
2537
|
+
]
|
|
2606
2538
|
}
|
|
2607
2539
|
)
|
|
2608
2540
|
}
|
|
2609
|
-
)
|
|
2610
|
-
|
|
2541
|
+
)
|
|
2542
|
+
] }),
|
|
2543
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
2544
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { children: "Target Type" }),
|
|
2545
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2611
2546
|
Controller,
|
|
2612
2547
|
{
|
|
2613
|
-
name:
|
|
2548
|
+
name: "target_type",
|
|
2614
2549
|
control,
|
|
2615
|
-
render: ({ field
|
|
2616
|
-
ui.
|
|
2550
|
+
render: ({ field }) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2551
|
+
ui.Select,
|
|
2617
2552
|
{
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2553
|
+
value: field.value,
|
|
2554
|
+
onValueChange: field.onChange,
|
|
2555
|
+
disabled,
|
|
2556
|
+
children: [
|
|
2557
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder: "Select target" }) }),
|
|
2558
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Select.Content, { children: [
|
|
2559
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: "order", children: "Order" }),
|
|
2560
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: "items", children: "Items" })
|
|
2561
|
+
] })
|
|
2562
|
+
]
|
|
2622
2563
|
}
|
|
2623
2564
|
)
|
|
2624
2565
|
}
|
|
2625
|
-
)
|
|
2626
|
-
|
|
2627
|
-
ui.Button,
|
|
2628
|
-
{
|
|
2629
|
-
type: "button",
|
|
2630
|
-
variant: "danger",
|
|
2631
|
-
onClick: () => remove(index),
|
|
2632
|
-
disabled,
|
|
2633
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(icons.Trash, {})
|
|
2634
|
-
}
|
|
2635
|
-
) })
|
|
2636
|
-
] }, field.id)) })
|
|
2566
|
+
)
|
|
2567
|
+
] })
|
|
2637
2568
|
] }),
|
|
2638
|
-
/* @__PURE__ */ jsxRuntime.
|
|
2569
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-end gap-2", children: [
|
|
2570
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "secondary", type: "button", onClick: onCancel, children: "Cancel" }),
|
|
2571
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Button, { type: "submit", disabled, children: "Save Coupon" })
|
|
2572
|
+
] })
|
|
2639
2573
|
]
|
|
2640
2574
|
}
|
|
2641
|
-
)
|
|
2642
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.FocusModal, { open: openProductModal, onOpenChange: setOpenProductModal, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.FocusModal.Content, { children: [
|
|
2643
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.FocusModal.Header, { title: "Add Product" }),
|
|
2644
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.FocusModal.Body, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2645
|
-
ProductSelector,
|
|
2646
|
-
{
|
|
2647
|
-
selectedProductIds: fields.map((f2) => f2.product.id),
|
|
2648
|
-
onSelectProduct: (product) => {
|
|
2649
|
-
append({
|
|
2650
|
-
product,
|
|
2651
|
-
discountType: "percentage",
|
|
2652
|
-
discountValue: 10,
|
|
2653
|
-
limit: 10,
|
|
2654
|
-
maxQty: 1
|
|
2655
|
-
});
|
|
2656
|
-
setOpenProductModal(false);
|
|
2657
|
-
}
|
|
2658
|
-
}
|
|
2659
|
-
) })
|
|
2660
|
-
] }) })
|
|
2575
|
+
)
|
|
2661
2576
|
] });
|
|
2662
2577
|
};
|
|
2663
|
-
const
|
|
2664
|
-
const
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
starts_at: new Date(campaignData.starts_at).toUTCString(),
|
|
2671
|
-
ends_at: new Date(campaignData.ends_at).toUTCString()
|
|
2672
|
-
});
|
|
2673
|
-
ui.toast.success("Flash sale created successfully");
|
|
2674
|
-
navigate("/flash-sales");
|
|
2675
|
-
} catch (error) {
|
|
2676
|
-
let message;
|
|
2677
|
-
if (axios__default.default.isAxiosError(error)) {
|
|
2678
|
-
console.log(error);
|
|
2679
|
-
message = (_a = error.response) == null ? void 0 : _a.data.message;
|
|
2680
|
-
} else if (error instanceof Error) {
|
|
2681
|
-
message = error.message;
|
|
2682
|
-
} else {
|
|
2683
|
-
message = "Failed to create flash sale";
|
|
2684
|
-
}
|
|
2685
|
-
ui.toast.error("Failed to create flash sale", {
|
|
2686
|
-
description: message
|
|
2687
|
-
});
|
|
2578
|
+
const useCouponById = (id) => {
|
|
2579
|
+
const [data, setData] = React.useState(null);
|
|
2580
|
+
const [isLoading, setIsLoading] = React.useState(true);
|
|
2581
|
+
const [error, setError] = React.useState(null);
|
|
2582
|
+
const fetchCoupon = async () => {
|
|
2583
|
+
if (!id) {
|
|
2584
|
+
return;
|
|
2688
2585
|
}
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2586
|
+
setIsLoading(true);
|
|
2587
|
+
setError(null);
|
|
2588
|
+
try {
|
|
2589
|
+
const response = await axios__default.default.get(`/admin/coupons/${id}`);
|
|
2590
|
+
setData(response.data);
|
|
2591
|
+
} catch (err) {
|
|
2592
|
+
setError(err instanceof Error ? err : new Error("Failed to fetch coupon"));
|
|
2593
|
+
} finally {
|
|
2594
|
+
setIsLoading(false);
|
|
2695
2595
|
}
|
|
2696
|
-
|
|
2596
|
+
};
|
|
2597
|
+
React.useEffect(() => {
|
|
2598
|
+
fetchCoupon();
|
|
2599
|
+
}, [id]);
|
|
2600
|
+
return {
|
|
2601
|
+
data,
|
|
2602
|
+
isLoading,
|
|
2603
|
+
error,
|
|
2604
|
+
refetch: fetchCoupon
|
|
2605
|
+
};
|
|
2697
2606
|
};
|
|
2698
|
-
const
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
starts_at: z__default.default.string().min(1, "Start date is required"),
|
|
2707
|
-
ends_at: z__default.default.string().min(1, "End date is required"),
|
|
2708
|
-
allocation: z__default.default.enum(["each", "total"]).optional(),
|
|
2709
|
-
target_type: z__default.default.enum(["order", "items"]).optional()
|
|
2710
|
-
}).superRefine((data, ctx) => {
|
|
2711
|
-
if (new Date(data.ends_at) < new Date(data.starts_at)) {
|
|
2712
|
-
ctx.addIssue({
|
|
2713
|
-
code: z__default.default.ZodIssueCode.custom,
|
|
2714
|
-
path: ["ends_at"],
|
|
2715
|
-
message: "End date must be after start date"
|
|
2716
|
-
});
|
|
2717
|
-
}
|
|
2718
|
-
if (data.discount_type === "fixed" && !data.currency_code) {
|
|
2719
|
-
ctx.addIssue({
|
|
2720
|
-
code: z__default.default.ZodIssueCode.custom,
|
|
2721
|
-
path: ["currency_code"],
|
|
2722
|
-
message: "Currency is required for fixed discount"
|
|
2723
|
-
});
|
|
2724
|
-
}
|
|
2725
|
-
});
|
|
2726
|
-
const CouponForm = ({
|
|
2727
|
-
initialData,
|
|
2728
|
-
onSubmit,
|
|
2729
|
-
onCancel,
|
|
2730
|
-
disabled = false
|
|
2607
|
+
const MarkdownEditor = ({
|
|
2608
|
+
label,
|
|
2609
|
+
value,
|
|
2610
|
+
onChange,
|
|
2611
|
+
placeholder,
|
|
2612
|
+
helpText,
|
|
2613
|
+
rows = 10,
|
|
2614
|
+
showPreview = true
|
|
2731
2615
|
}) => {
|
|
2732
|
-
const
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
ends_at: (initialData == null ? void 0 : initialData.ends_at) ?? dayjs__default.default().endOf("day").format("YYYY-MM-DDTHH:mm"),
|
|
2751
|
-
allocation: (initialData == null ? void 0 : initialData.allocation) ?? "total",
|
|
2752
|
-
target_type: (initialData == null ? void 0 : initialData.target_type) ?? "order"
|
|
2753
|
-
}
|
|
2754
|
-
});
|
|
2755
|
-
const discountType = watch("discount_type");
|
|
2756
|
-
const startsAt = watch("starts_at");
|
|
2757
|
-
const endsAt = watch("ends_at");
|
|
2758
|
-
const handleDateTimeChange = (field, type, value) => {
|
|
2759
|
-
const currentValue = watch(field);
|
|
2760
|
-
if (type === "date") {
|
|
2761
|
-
const time = currentValue ? dayjs__default.default(currentValue).format("HH:mm") : field === "starts_at" ? "00:00" : "23:59";
|
|
2762
|
-
setValue(field, `${value}T${time}`);
|
|
2763
|
-
} else {
|
|
2764
|
-
const date = currentValue ? dayjs__default.default(currentValue).format("YYYY-MM-DD") : dayjs__default.default().format("YYYY-MM-DD");
|
|
2765
|
-
setValue(field, `${date}T${value}`);
|
|
2766
|
-
}
|
|
2767
|
-
};
|
|
2768
|
-
const onFormSubmit = (data) => {
|
|
2769
|
-
onSubmit(data);
|
|
2616
|
+
const [activeTab, setActiveTab] = React.useState("write");
|
|
2617
|
+
const insertMarkdown = (before, after = "") => {
|
|
2618
|
+
const textarea = document.getElementById(
|
|
2619
|
+
`markdown-${label}`
|
|
2620
|
+
);
|
|
2621
|
+
if (!textarea) return;
|
|
2622
|
+
const start = textarea.selectionStart;
|
|
2623
|
+
const end = textarea.selectionEnd;
|
|
2624
|
+
const selectedText = value.substring(start, end);
|
|
2625
|
+
const newText = value.substring(0, start) + before + selectedText + after + value.substring(end);
|
|
2626
|
+
onChange(newText);
|
|
2627
|
+
setTimeout(() => {
|
|
2628
|
+
textarea.focus();
|
|
2629
|
+
textarea.setSelectionRange(
|
|
2630
|
+
start + before.length,
|
|
2631
|
+
start + before.length + selectedText.length
|
|
2632
|
+
);
|
|
2633
|
+
}, 0);
|
|
2770
2634
|
};
|
|
2771
|
-
const
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2635
|
+
const renderMarkdownPreview = (markdown) => {
|
|
2636
|
+
let html = markdown;
|
|
2637
|
+
html = html.replace(/^### (.*$)/gim, "<h3>$1</h3>");
|
|
2638
|
+
html = html.replace(/^## (.*$)/gim, "<h2>$1</h2>");
|
|
2639
|
+
html = html.replace(/^# (.*$)/gim, "<h1>$1</h1>");
|
|
2640
|
+
html = html.replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>");
|
|
2641
|
+
html = html.replace(/__(.+?)__/g, "<strong>$1</strong>");
|
|
2642
|
+
html = html.replace(/\*(.+?)\*/g, "<em>$1</em>");
|
|
2643
|
+
html = html.replace(/_(.+?)_/g, "<em>$1</em>");
|
|
2644
|
+
html = html.replace(
|
|
2645
|
+
/```(\w+)?\n([\s\S]+?)```/g,
|
|
2646
|
+
'<pre class="bg-ui-bg-subtle p-4 rounded-lg overflow-x-auto"><code class="language-$1">$2</code></pre>'
|
|
2647
|
+
);
|
|
2648
|
+
html = html.replace(
|
|
2649
|
+
/`([^`]+)`/g,
|
|
2650
|
+
'<code class="bg-ui-bg-subtle px-1 py-0.5 rounded text-sm">$1</code>'
|
|
2651
|
+
);
|
|
2652
|
+
html = html.replace(
|
|
2653
|
+
/\[([^\]]+)\]\(([^)]+)\)/g,
|
|
2654
|
+
'<a href="$2" class="text-ui-fg-interactive underline">$1</a>'
|
|
2655
|
+
);
|
|
2656
|
+
html = html.replace(
|
|
2657
|
+
/!\[([^\]]*)\]\(([^)]+)\)/g,
|
|
2658
|
+
'<img src="$2" alt="$1" class="max-w-full h-auto rounded-lg my-2" />'
|
|
2659
|
+
);
|
|
2660
|
+
html = html.replace(/^\* (.+)$/gim, "<li>$1</li>");
|
|
2661
|
+
html = html.replace(/^\- (.+)$/gim, "<li>$1</li>");
|
|
2662
|
+
html = html.replace(
|
|
2663
|
+
/(<li>.*<\/li>)/s,
|
|
2664
|
+
"<ul class='list-disc ml-6'>$1</ul>"
|
|
2665
|
+
);
|
|
2666
|
+
html = html.replace(/\n\n/g, "</p><p>");
|
|
2667
|
+
html = `<p>${html}</p>`;
|
|
2668
|
+
return html;
|
|
2776
2669
|
};
|
|
2777
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2670
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
2778
2671
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
|
|
2779
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2780
|
-
/* @__PURE__ */ jsxRuntime.
|
|
2672
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: `markdown-${label}`, className: "font-medium", children: label }),
|
|
2673
|
+
helpText && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1 text-xs text-ui-fg-subtle", children: [
|
|
2674
|
+
/* @__PURE__ */ jsxRuntime.jsx(icons.InformationCircle, { className: "h-4 w-4" }),
|
|
2675
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: helpText })
|
|
2676
|
+
] })
|
|
2781
2677
|
] }),
|
|
2782
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
)
|
|
2678
|
+
showPreview ? /* @__PURE__ */ jsxRuntime.jsxs(ui.Tabs, { value: activeTab, onValueChange: (v) => setActiveTab(v), children: [
|
|
2679
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Tabs.List, { children: [
|
|
2680
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Tabs.Trigger, { value: "write", children: "Write" }),
|
|
2681
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Tabs.Trigger, { value: "preview", children: "Preview" })
|
|
2682
|
+
] }),
|
|
2683
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Tabs.Content, { value: "write", className: "mt-4", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
2684
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap gap-2 border-b pb-2", children: [
|
|
2685
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2686
|
+
ui.Button,
|
|
2687
|
+
{
|
|
2688
|
+
type: "button",
|
|
2689
|
+
variant: "secondary",
|
|
2690
|
+
size: "small",
|
|
2691
|
+
onClick: () => insertMarkdown("**", "**"),
|
|
2692
|
+
title: "Bold",
|
|
2693
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("strong", { children: "B" })
|
|
2694
|
+
}
|
|
2695
|
+
),
|
|
2696
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2697
|
+
ui.Button,
|
|
2698
|
+
{
|
|
2699
|
+
type: "button",
|
|
2700
|
+
variant: "secondary",
|
|
2701
|
+
size: "small",
|
|
2702
|
+
onClick: () => insertMarkdown("*", "*"),
|
|
2703
|
+
title: "Italic",
|
|
2704
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("em", { children: "I" })
|
|
2705
|
+
}
|
|
2706
|
+
),
|
|
2707
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2708
|
+
ui.Button,
|
|
2709
|
+
{
|
|
2710
|
+
type: "button",
|
|
2711
|
+
variant: "secondary",
|
|
2712
|
+
size: "small",
|
|
2713
|
+
onClick: () => insertMarkdown("`", "`"),
|
|
2714
|
+
title: "Inline Code",
|
|
2715
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(icons.CommandLine, { className: "h-4 w-4" })
|
|
2716
|
+
}
|
|
2717
|
+
),
|
|
2718
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2719
|
+
ui.Button,
|
|
2720
|
+
{
|
|
2721
|
+
type: "button",
|
|
2722
|
+
variant: "secondary",
|
|
2723
|
+
size: "small",
|
|
2724
|
+
onClick: () => insertMarkdown("## "),
|
|
2725
|
+
title: "Heading",
|
|
2726
|
+
children: "H2"
|
|
2727
|
+
}
|
|
2728
|
+
),
|
|
2729
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2730
|
+
ui.Button,
|
|
2731
|
+
{
|
|
2732
|
+
type: "button",
|
|
2733
|
+
variant: "secondary",
|
|
2734
|
+
size: "small",
|
|
2735
|
+
onClick: () => insertMarkdown("### "),
|
|
2736
|
+
title: "Heading",
|
|
2737
|
+
children: "H3"
|
|
2738
|
+
}
|
|
2739
|
+
),
|
|
2740
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2741
|
+
ui.Button,
|
|
2742
|
+
{
|
|
2743
|
+
type: "button",
|
|
2744
|
+
variant: "secondary",
|
|
2745
|
+
size: "small",
|
|
2746
|
+
onClick: () => insertMarkdown("[", "](url)"),
|
|
2747
|
+
title: "Link",
|
|
2748
|
+
children: "Link"
|
|
2749
|
+
}
|
|
2750
|
+
),
|
|
2751
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2752
|
+
ui.Button,
|
|
2753
|
+
{
|
|
2754
|
+
type: "button",
|
|
2755
|
+
variant: "secondary",
|
|
2756
|
+
size: "small",
|
|
2757
|
+
onClick: () => insertMarkdown(""),
|
|
2758
|
+
title: "Image",
|
|
2759
|
+
children: "Image"
|
|
2760
|
+
}
|
|
2761
|
+
),
|
|
2762
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2763
|
+
ui.Button,
|
|
2764
|
+
{
|
|
2765
|
+
type: "button",
|
|
2766
|
+
variant: "secondary",
|
|
2767
|
+
size: "small",
|
|
2768
|
+
onClick: () => insertMarkdown("```\n", "\n```"),
|
|
2769
|
+
title: "Code Block",
|
|
2770
|
+
children: "Code"
|
|
2771
|
+
}
|
|
2772
|
+
),
|
|
2773
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2774
|
+
ui.Button,
|
|
2775
|
+
{
|
|
2776
|
+
type: "button",
|
|
2777
|
+
variant: "secondary",
|
|
2778
|
+
size: "small",
|
|
2779
|
+
onClick: () => insertMarkdown("- "),
|
|
2780
|
+
title: "List",
|
|
2781
|
+
children: "List"
|
|
2782
|
+
}
|
|
2783
|
+
)
|
|
2784
|
+
] }),
|
|
2785
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2786
|
+
ui.Textarea,
|
|
2787
|
+
{
|
|
2788
|
+
id: `markdown-${label}`,
|
|
2789
|
+
value,
|
|
2790
|
+
onChange: (e2) => onChange(e2.target.value),
|
|
2791
|
+
placeholder,
|
|
2792
|
+
rows,
|
|
2793
|
+
className: "font-mono text-sm"
|
|
2794
|
+
}
|
|
2795
|
+
)
|
|
2796
|
+
] }) }),
|
|
2797
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Tabs.Content, { value: "preview", className: "mt-4", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2798
|
+
"div",
|
|
2799
|
+
{
|
|
2800
|
+
className: ui.clx(
|
|
2801
|
+
"min-h-[200px] rounded-lg border border-ui-border-base bg-ui-bg-subtle p-4",
|
|
2802
|
+
"prose prose-sm max-w-none"
|
|
2803
|
+
),
|
|
2804
|
+
dangerouslySetInnerHTML: {
|
|
2805
|
+
__html: value ? renderMarkdownPreview(value) : '<p class="text-ui-fg-subtle">Nothing to preview</p>'
|
|
2806
|
+
}
|
|
2807
|
+
}
|
|
2808
|
+
) })
|
|
2809
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
2810
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap gap-2 border-b pb-2", children: [
|
|
2811
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2812
|
+
ui.Button,
|
|
2813
|
+
{
|
|
2814
|
+
type: "button",
|
|
2815
|
+
variant: "secondary",
|
|
2816
|
+
size: "small",
|
|
2817
|
+
onClick: () => insertMarkdown("**", "**"),
|
|
2818
|
+
title: "Bold",
|
|
2819
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("strong", { children: "B" })
|
|
2820
|
+
}
|
|
2821
|
+
),
|
|
2822
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2823
|
+
ui.Button,
|
|
2824
|
+
{
|
|
2825
|
+
type: "button",
|
|
2826
|
+
variant: "secondary",
|
|
2827
|
+
size: "small",
|
|
2828
|
+
onClick: () => insertMarkdown("*", "*"),
|
|
2829
|
+
title: "Italic",
|
|
2830
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("em", { children: "I" })
|
|
2831
|
+
}
|
|
2832
|
+
),
|
|
2833
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2834
|
+
ui.Button,
|
|
2835
|
+
{
|
|
2836
|
+
type: "button",
|
|
2837
|
+
variant: "secondary",
|
|
2838
|
+
size: "small",
|
|
2839
|
+
onClick: () => insertMarkdown("`", "`"),
|
|
2840
|
+
title: "Inline Code",
|
|
2841
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(icons.CommandLine, { className: "h-4 w-4" })
|
|
2842
|
+
}
|
|
2843
|
+
),
|
|
2844
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2845
|
+
ui.Button,
|
|
2846
|
+
{
|
|
2847
|
+
type: "button",
|
|
2848
|
+
variant: "secondary",
|
|
2849
|
+
size: "small",
|
|
2850
|
+
onClick: () => insertMarkdown("```\n", "\n```"),
|
|
2851
|
+
title: "Code Block",
|
|
2852
|
+
children: "Code"
|
|
2853
|
+
}
|
|
2854
|
+
)
|
|
2855
|
+
] }),
|
|
2856
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2857
|
+
ui.Textarea,
|
|
2858
|
+
{
|
|
2859
|
+
id: `markdown-${label}`,
|
|
2860
|
+
value,
|
|
2861
|
+
onChange: (e2) => onChange(e2.target.value),
|
|
2862
|
+
placeholder,
|
|
2863
|
+
rows,
|
|
2864
|
+
className: "font-mono text-sm"
|
|
2865
|
+
}
|
|
2866
|
+
)
|
|
2867
|
+
] }),
|
|
2868
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-ui-fg-subtle", children: "Supports Markdown formatting: **bold**, *italic*, `code`, ```code blocks```, [links](url), , and lists" })
|
|
2972
2869
|
] });
|
|
2973
2870
|
};
|
|
2974
|
-
const
|
|
2975
|
-
|
|
2976
|
-
|
|
2871
|
+
const CampaignImageUploader = ({
|
|
2872
|
+
campaignId,
|
|
2873
|
+
imageType,
|
|
2874
|
+
currentImageUrl,
|
|
2875
|
+
onClose,
|
|
2876
|
+
onSuccess
|
|
2877
|
+
}) => {
|
|
2878
|
+
const [displayImage, setDisplayImage] = React.useState(currentImageUrl || null);
|
|
2879
|
+
const [isUploading, setIsUploading] = React.useState(false);
|
|
2880
|
+
const [isDragging, setIsDragging] = React.useState(false);
|
|
2977
2881
|
const [error, setError] = React.useState(null);
|
|
2978
|
-
const
|
|
2979
|
-
|
|
2980
|
-
|
|
2882
|
+
const [previewUrl, setPreviewUrl] = React.useState(null);
|
|
2883
|
+
const isThumbnail = imageType === "thumbnail";
|
|
2884
|
+
const maxSize = 5;
|
|
2885
|
+
const allowedTypes = ["image/jpeg", "image/png", "image/gif", "image/webp"];
|
|
2886
|
+
const formatFileTypes = () => {
|
|
2887
|
+
return "JPEG, PNG, GIF, or WebP";
|
|
2888
|
+
};
|
|
2889
|
+
const validateFile = (file) => {
|
|
2890
|
+
if (!allowedTypes.includes(file.type)) {
|
|
2891
|
+
setError(`Please upload a valid image file (${formatFileTypes()})`);
|
|
2892
|
+
return false;
|
|
2981
2893
|
}
|
|
2982
|
-
|
|
2894
|
+
if (file.size > maxSize * 1024 * 1024) {
|
|
2895
|
+
setError(`File size must be less than ${maxSize}MB`);
|
|
2896
|
+
return false;
|
|
2897
|
+
}
|
|
2898
|
+
return true;
|
|
2899
|
+
};
|
|
2900
|
+
const handleFileUpload = async (file) => {
|
|
2901
|
+
var _a, _b;
|
|
2902
|
+
if (!validateFile(file)) return;
|
|
2983
2903
|
setError(null);
|
|
2904
|
+
const reader = new FileReader();
|
|
2905
|
+
reader.onloadend = () => {
|
|
2906
|
+
setPreviewUrl(reader.result);
|
|
2907
|
+
};
|
|
2908
|
+
reader.readAsDataURL(file);
|
|
2909
|
+
setIsUploading(true);
|
|
2910
|
+
const formData = new FormData();
|
|
2911
|
+
formData.append("file", file);
|
|
2984
2912
|
try {
|
|
2985
|
-
const response = await
|
|
2986
|
-
|
|
2913
|
+
const response = await fetch(
|
|
2914
|
+
`/admin/campaigns/${campaignId}/${imageType}`,
|
|
2915
|
+
{
|
|
2916
|
+
method: "POST",
|
|
2917
|
+
body: formData,
|
|
2918
|
+
credentials: "include"
|
|
2919
|
+
}
|
|
2920
|
+
);
|
|
2921
|
+
if (response.ok) {
|
|
2922
|
+
const result = await response.json();
|
|
2923
|
+
const newImageUrl = imageType === "image" ? (_a = result.campaign_detail) == null ? void 0 : _a.image_url : (_b = result.campaign_detail) == null ? void 0 : _b.thumbnail_url;
|
|
2924
|
+
setDisplayImage(newImageUrl);
|
|
2925
|
+
setPreviewUrl(null);
|
|
2926
|
+
onSuccess == null ? void 0 : onSuccess();
|
|
2927
|
+
setTimeout(() => {
|
|
2928
|
+
onClose();
|
|
2929
|
+
}, 1e3);
|
|
2930
|
+
} else {
|
|
2931
|
+
const errorData = await response.json();
|
|
2932
|
+
setError(errorData.error || `Failed to upload ${imageType}`);
|
|
2933
|
+
setPreviewUrl(null);
|
|
2934
|
+
}
|
|
2987
2935
|
} catch (err) {
|
|
2988
|
-
setError(
|
|
2936
|
+
setError(`Error uploading ${imageType}`);
|
|
2937
|
+
setPreviewUrl(null);
|
|
2938
|
+
console.error(`Error uploading ${imageType}:`, err);
|
|
2989
2939
|
} finally {
|
|
2990
|
-
|
|
2940
|
+
setIsUploading(false);
|
|
2991
2941
|
}
|
|
2992
2942
|
};
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
label,
|
|
3005
|
-
value,
|
|
3006
|
-
onChange,
|
|
3007
|
-
placeholder,
|
|
3008
|
-
helpText,
|
|
3009
|
-
rows = 10,
|
|
3010
|
-
showPreview = true
|
|
3011
|
-
}) => {
|
|
3012
|
-
const [activeTab, setActiveTab] = React.useState("write");
|
|
3013
|
-
const insertMarkdown = (before, after = "") => {
|
|
3014
|
-
const textarea = document.getElementById(
|
|
3015
|
-
`markdown-${label}`
|
|
3016
|
-
);
|
|
3017
|
-
if (!textarea) return;
|
|
3018
|
-
const start = textarea.selectionStart;
|
|
3019
|
-
const end = textarea.selectionEnd;
|
|
3020
|
-
const selectedText = value.substring(start, end);
|
|
3021
|
-
const newText = value.substring(0, start) + before + selectedText + after + value.substring(end);
|
|
3022
|
-
onChange(newText);
|
|
3023
|
-
setTimeout(() => {
|
|
3024
|
-
textarea.focus();
|
|
3025
|
-
textarea.setSelectionRange(
|
|
3026
|
-
start + before.length,
|
|
3027
|
-
start + before.length + selectedText.length
|
|
2943
|
+
const handleDelete = async () => {
|
|
2944
|
+
if (!confirm(`Are you sure you want to delete the ${imageType}?`)) return;
|
|
2945
|
+
setIsUploading(true);
|
|
2946
|
+
setError(null);
|
|
2947
|
+
try {
|
|
2948
|
+
const response = await fetch(
|
|
2949
|
+
`/admin/campaigns/${campaignId}/${imageType}`,
|
|
2950
|
+
{
|
|
2951
|
+
method: "DELETE",
|
|
2952
|
+
credentials: "include"
|
|
2953
|
+
}
|
|
3028
2954
|
);
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
);
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
2955
|
+
if (response.ok) {
|
|
2956
|
+
setDisplayImage(null);
|
|
2957
|
+
setPreviewUrl(null);
|
|
2958
|
+
onSuccess == null ? void 0 : onSuccess();
|
|
2959
|
+
setTimeout(() => {
|
|
2960
|
+
onClose();
|
|
2961
|
+
}, 500);
|
|
2962
|
+
} else {
|
|
2963
|
+
const errorData = await response.json();
|
|
2964
|
+
setError(errorData.error || `Failed to delete ${imageType}`);
|
|
2965
|
+
}
|
|
2966
|
+
} catch (err) {
|
|
2967
|
+
setError(`Error deleting ${imageType}`);
|
|
2968
|
+
console.error(`Error deleting ${imageType}:`, err);
|
|
2969
|
+
} finally {
|
|
2970
|
+
setIsUploading(false);
|
|
2971
|
+
}
|
|
2972
|
+
};
|
|
2973
|
+
const handleDragEnter = React.useCallback((e2) => {
|
|
2974
|
+
e2.preventDefault();
|
|
2975
|
+
e2.stopPropagation();
|
|
2976
|
+
setIsDragging(true);
|
|
2977
|
+
}, []);
|
|
2978
|
+
const handleDragLeave = React.useCallback((e2) => {
|
|
2979
|
+
e2.preventDefault();
|
|
2980
|
+
e2.stopPropagation();
|
|
2981
|
+
setIsDragging(false);
|
|
2982
|
+
}, []);
|
|
2983
|
+
const handleDragOver = React.useCallback((e2) => {
|
|
2984
|
+
e2.preventDefault();
|
|
2985
|
+
e2.stopPropagation();
|
|
2986
|
+
}, []);
|
|
2987
|
+
const handleDrop = React.useCallback((e2) => {
|
|
2988
|
+
var _a;
|
|
2989
|
+
e2.preventDefault();
|
|
2990
|
+
e2.stopPropagation();
|
|
2991
|
+
setIsDragging(false);
|
|
2992
|
+
const file = (_a = e2.dataTransfer.files) == null ? void 0 : _a[0];
|
|
2993
|
+
if (file) {
|
|
2994
|
+
handleFileUpload(file);
|
|
2995
|
+
}
|
|
2996
|
+
}, []);
|
|
2997
|
+
const imageToDisplay = previewUrl || displayImage;
|
|
2998
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/50", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full max-w-2xl rounded-lg bg-ui-bg-base p-6", children: [
|
|
2999
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-6 flex items-center justify-between", children: [
|
|
3000
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
3001
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Heading, { level: "h2", children: [
|
|
3002
|
+
"Upload Campaign ",
|
|
3003
|
+
isThumbnail ? "Thumbnail" : "Image"
|
|
3004
|
+
] }),
|
|
3005
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-subtle", children: isThumbnail ? "Thumbnail for campaign listing" : "Main campaign image" })
|
|
3078
3006
|
] }),
|
|
3079
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
variant: "secondary",
|
|
3086
|
-
size: "small",
|
|
3087
|
-
onClick: () => insertMarkdown("**", "**"),
|
|
3088
|
-
title: "Bold",
|
|
3089
|
-
children: /* @__PURE__ */ jsxRuntime.jsx("strong", { children: "B" })
|
|
3090
|
-
}
|
|
3091
|
-
),
|
|
3092
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3093
|
-
ui.Button,
|
|
3094
|
-
{
|
|
3095
|
-
type: "button",
|
|
3096
|
-
variant: "secondary",
|
|
3097
|
-
size: "small",
|
|
3098
|
-
onClick: () => insertMarkdown("*", "*"),
|
|
3099
|
-
title: "Italic",
|
|
3100
|
-
children: /* @__PURE__ */ jsxRuntime.jsx("em", { children: "I" })
|
|
3101
|
-
}
|
|
3102
|
-
),
|
|
3103
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3104
|
-
ui.Button,
|
|
3105
|
-
{
|
|
3106
|
-
type: "button",
|
|
3107
|
-
variant: "secondary",
|
|
3108
|
-
size: "small",
|
|
3109
|
-
onClick: () => insertMarkdown("`", "`"),
|
|
3110
|
-
title: "Inline Code",
|
|
3111
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(icons.CommandLine, { className: "h-4 w-4" })
|
|
3112
|
-
}
|
|
3113
|
-
),
|
|
3114
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3115
|
-
ui.Button,
|
|
3116
|
-
{
|
|
3117
|
-
type: "button",
|
|
3118
|
-
variant: "secondary",
|
|
3119
|
-
size: "small",
|
|
3120
|
-
onClick: () => insertMarkdown("## "),
|
|
3121
|
-
title: "Heading",
|
|
3122
|
-
children: "H2"
|
|
3123
|
-
}
|
|
3124
|
-
),
|
|
3125
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3126
|
-
ui.Button,
|
|
3127
|
-
{
|
|
3128
|
-
type: "button",
|
|
3129
|
-
variant: "secondary",
|
|
3130
|
-
size: "small",
|
|
3131
|
-
onClick: () => insertMarkdown("### "),
|
|
3132
|
-
title: "Heading",
|
|
3133
|
-
children: "H3"
|
|
3134
|
-
}
|
|
3135
|
-
),
|
|
3136
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3137
|
-
ui.Button,
|
|
3138
|
-
{
|
|
3139
|
-
type: "button",
|
|
3140
|
-
variant: "secondary",
|
|
3141
|
-
size: "small",
|
|
3142
|
-
onClick: () => insertMarkdown("[", "](url)"),
|
|
3143
|
-
title: "Link",
|
|
3144
|
-
children: "Link"
|
|
3145
|
-
}
|
|
3146
|
-
),
|
|
3007
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "secondary", size: "small", onClick: onClose, children: /* @__PURE__ */ jsxRuntime.jsx(icons.X, {}) })
|
|
3008
|
+
] }),
|
|
3009
|
+
error && /* @__PURE__ */ jsxRuntime.jsx(ui.Alert, { variant: "error", dismissible: true, className: "mb-4", children: error }),
|
|
3010
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
|
|
3011
|
+
imageToDisplay ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
|
|
3012
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative overflow-hidden rounded-lg border border-ui-border-base bg-ui-bg-subtle", children: [
|
|
3147
3013
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3148
|
-
|
|
3014
|
+
"img",
|
|
3149
3015
|
{
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3016
|
+
src: imageToDisplay,
|
|
3017
|
+
alt: `Campaign ${imageType}`,
|
|
3018
|
+
className: ui.clx(
|
|
3019
|
+
"w-full object-contain",
|
|
3020
|
+
isThumbnail ? "h-32" : "h-64"
|
|
3021
|
+
)
|
|
3156
3022
|
}
|
|
3157
3023
|
),
|
|
3158
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3024
|
+
isUploading && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 flex items-center justify-center bg-black/50", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-white", children: "Uploading..." }) })
|
|
3025
|
+
] }),
|
|
3026
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
|
|
3027
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
3159
3028
|
ui.Button,
|
|
3160
3029
|
{
|
|
3161
|
-
type: "button",
|
|
3162
3030
|
variant: "secondary",
|
|
3163
|
-
|
|
3164
|
-
onClick: () =>
|
|
3165
|
-
|
|
3166
|
-
|
|
3031
|
+
disabled: isUploading,
|
|
3032
|
+
onClick: () => {
|
|
3033
|
+
var _a;
|
|
3034
|
+
return (_a = document.getElementById(`${imageType}-file-input`)) == null ? void 0 : _a.click();
|
|
3035
|
+
},
|
|
3036
|
+
children: [
|
|
3037
|
+
/* @__PURE__ */ jsxRuntime.jsx(icons.CloudArrowUp, { className: "mr-2" }),
|
|
3038
|
+
"Replace"
|
|
3039
|
+
]
|
|
3167
3040
|
}
|
|
3168
3041
|
),
|
|
3169
|
-
/* @__PURE__ */ jsxRuntime.
|
|
3042
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
3170
3043
|
ui.Button,
|
|
3171
3044
|
{
|
|
3172
|
-
|
|
3173
|
-
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
|
|
3045
|
+
variant: "danger",
|
|
3046
|
+
disabled: isUploading,
|
|
3047
|
+
onClick: handleDelete,
|
|
3048
|
+
children: [
|
|
3049
|
+
/* @__PURE__ */ jsxRuntime.jsx(icons.Trash, { className: "mr-2" }),
|
|
3050
|
+
"Delete"
|
|
3051
|
+
]
|
|
3178
3052
|
}
|
|
3179
3053
|
)
|
|
3054
|
+
] })
|
|
3055
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3056
|
+
"div",
|
|
3057
|
+
{
|
|
3058
|
+
className: ui.clx(
|
|
3059
|
+
"flex flex-col items-center justify-center rounded-lg border-2 border-dashed transition-colors",
|
|
3060
|
+
isThumbnail ? "h-48" : "h-64",
|
|
3061
|
+
isDragging ? "border-ui-border-interactive bg-ui-bg-highlight" : "border-ui-border-base bg-ui-bg-subtle",
|
|
3062
|
+
!isUploading && "cursor-pointer"
|
|
3063
|
+
),
|
|
3064
|
+
onDragEnter: handleDragEnter,
|
|
3065
|
+
onDragLeave: handleDragLeave,
|
|
3066
|
+
onDragOver: handleDragOver,
|
|
3067
|
+
onDrop: handleDrop,
|
|
3068
|
+
onClick: () => {
|
|
3069
|
+
var _a;
|
|
3070
|
+
return !isUploading && ((_a = document.getElementById(`${imageType}-file-input`)) == null ? void 0 : _a.click());
|
|
3071
|
+
},
|
|
3072
|
+
children: [
|
|
3073
|
+
/* @__PURE__ */ jsxRuntime.jsx(icons.PhotoSolid, { className: "mb-4 h-12 w-12 text-ui-fg-subtle" }),
|
|
3074
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "mb-2 text-lg font-medium", children: isDragging ? `Drop ${imageType} here` : `Upload ${isThumbnail ? "Thumbnail" : "Image"}` }),
|
|
3075
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "mb-4 text-center text-ui-fg-subtle", children: "Drag and drop an image here, or click to select" }),
|
|
3076
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { className: "text-sm text-ui-fg-subtle", children: [
|
|
3077
|
+
formatFileTypes(),
|
|
3078
|
+
" • Max ",
|
|
3079
|
+
maxSize,
|
|
3080
|
+
"MB"
|
|
3081
|
+
] }),
|
|
3082
|
+
isThumbnail && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "mt-2 text-xs text-ui-fg-subtle", children: "Recommended: 16:9 aspect ratio, minimum 400x225px" }),
|
|
3083
|
+
!isThumbnail && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "mt-2 text-xs text-ui-fg-subtle", children: "Recommended: High resolution, minimum 1200x600px" }),
|
|
3084
|
+
isUploading && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-4", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { children: "Uploading..." }) })
|
|
3085
|
+
]
|
|
3086
|
+
}
|
|
3087
|
+
),
|
|
3088
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3089
|
+
"input",
|
|
3090
|
+
{
|
|
3091
|
+
id: `${imageType}-file-input`,
|
|
3092
|
+
type: "file",
|
|
3093
|
+
accept: allowedTypes.join(","),
|
|
3094
|
+
onChange: (e2) => {
|
|
3095
|
+
var _a;
|
|
3096
|
+
const file = (_a = e2.target.files) == null ? void 0 : _a[0];
|
|
3097
|
+
if (file) handleFileUpload(file);
|
|
3098
|
+
e2.target.value = "";
|
|
3099
|
+
},
|
|
3100
|
+
className: "hidden"
|
|
3101
|
+
}
|
|
3102
|
+
)
|
|
3103
|
+
] }),
|
|
3104
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-6 flex items-center justify-between border-t pt-4", children: [
|
|
3105
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-sm text-ui-fg-subtle", children: isThumbnail ? "Thumbnail will be displayed in campaign listings" : "Main image for the campaign detail page" }),
|
|
3106
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "secondary", onClick: onClose, children: "Close" })
|
|
3107
|
+
] })
|
|
3108
|
+
] }) });
|
|
3109
|
+
};
|
|
3110
|
+
const CampaignDetailForm = ({
|
|
3111
|
+
campaignId,
|
|
3112
|
+
campaignName
|
|
3113
|
+
}) => {
|
|
3114
|
+
const [campaignDetail, setCampaignDetail] = React.useState(
|
|
3115
|
+
null
|
|
3116
|
+
);
|
|
3117
|
+
const [isLoading, setIsLoading] = React.useState(true);
|
|
3118
|
+
const [isSaving, setIsSaving] = React.useState(false);
|
|
3119
|
+
const [showImageUploader, setShowImageUploader] = React.useState(null);
|
|
3120
|
+
const [formData, setFormData] = React.useState({
|
|
3121
|
+
detail_content: "",
|
|
3122
|
+
terms_and_conditions: "",
|
|
3123
|
+
meta_title: "",
|
|
3124
|
+
meta_description: "",
|
|
3125
|
+
meta_keywords: "",
|
|
3126
|
+
link_url: "",
|
|
3127
|
+
link_text: "",
|
|
3128
|
+
display_order: 0
|
|
3129
|
+
});
|
|
3130
|
+
React.useEffect(() => {
|
|
3131
|
+
fetchCampaignDetail();
|
|
3132
|
+
}, [campaignId]);
|
|
3133
|
+
const fetchCampaignDetail = async () => {
|
|
3134
|
+
setIsLoading(true);
|
|
3135
|
+
try {
|
|
3136
|
+
const response = await fetch(`/admin/campaigns/${campaignId}/detail`, {
|
|
3137
|
+
credentials: "include"
|
|
3138
|
+
});
|
|
3139
|
+
if (response.ok) {
|
|
3140
|
+
const data = await response.json();
|
|
3141
|
+
if (data.campaign_detail) {
|
|
3142
|
+
console.log("Campaign detail loaded:", {
|
|
3143
|
+
image_url: data.campaign_detail.image_url,
|
|
3144
|
+
thumbnail_url: data.campaign_detail.thumbnail_url,
|
|
3145
|
+
decoded_image: data.campaign_detail.image_url ? decodeURIComponent(data.campaign_detail.image_url) : null,
|
|
3146
|
+
decoded_thumbnail: data.campaign_detail.thumbnail_url ? decodeURIComponent(data.campaign_detail.thumbnail_url) : null
|
|
3147
|
+
});
|
|
3148
|
+
setCampaignDetail(data.campaign_detail);
|
|
3149
|
+
setFormData({
|
|
3150
|
+
detail_content: data.campaign_detail.detail_content || "",
|
|
3151
|
+
terms_and_conditions: data.campaign_detail.terms_and_conditions || "",
|
|
3152
|
+
meta_title: data.campaign_detail.meta_title || "",
|
|
3153
|
+
meta_description: data.campaign_detail.meta_description || "",
|
|
3154
|
+
meta_keywords: data.campaign_detail.meta_keywords || "",
|
|
3155
|
+
link_url: data.campaign_detail.link_url || "",
|
|
3156
|
+
link_text: data.campaign_detail.link_text || "",
|
|
3157
|
+
display_order: data.campaign_detail.display_order || 0
|
|
3158
|
+
});
|
|
3159
|
+
}
|
|
3160
|
+
} else if (response.status !== 404) {
|
|
3161
|
+
console.error("Failed to fetch campaign detail");
|
|
3162
|
+
}
|
|
3163
|
+
} catch (error) {
|
|
3164
|
+
console.error("Error fetching campaign detail:", error);
|
|
3165
|
+
} finally {
|
|
3166
|
+
setIsLoading(false);
|
|
3167
|
+
}
|
|
3168
|
+
};
|
|
3169
|
+
const handleSave = async () => {
|
|
3170
|
+
setIsSaving(true);
|
|
3171
|
+
try {
|
|
3172
|
+
const response = await fetch(`/admin/campaigns/${campaignId}/detail`, {
|
|
3173
|
+
method: "POST",
|
|
3174
|
+
headers: {
|
|
3175
|
+
"Content-Type": "application/json"
|
|
3176
|
+
},
|
|
3177
|
+
body: JSON.stringify(formData),
|
|
3178
|
+
credentials: "include"
|
|
3179
|
+
});
|
|
3180
|
+
if (response.ok) {
|
|
3181
|
+
const data = await response.json();
|
|
3182
|
+
setCampaignDetail(data.campaign_detail);
|
|
3183
|
+
ui.toast.success("Campaign details saved successfully");
|
|
3184
|
+
} else {
|
|
3185
|
+
const errorData = await response.json();
|
|
3186
|
+
ui.toast.error(errorData.error || "Failed to save campaign details");
|
|
3187
|
+
}
|
|
3188
|
+
} catch (error) {
|
|
3189
|
+
console.error("Error saving campaign detail:", error);
|
|
3190
|
+
ui.toast.error("Error saving campaign details");
|
|
3191
|
+
} finally {
|
|
3192
|
+
setIsSaving(false);
|
|
3193
|
+
}
|
|
3194
|
+
};
|
|
3195
|
+
if (isLoading) {
|
|
3196
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ui.Container, { children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "py-8 text-center", children: "Loading campaign details..." }) });
|
|
3197
|
+
}
|
|
3198
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
3199
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "space-y-6", children: [
|
|
3200
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
|
|
3201
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
3202
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: "Campaign Details" }),
|
|
3203
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-ui-fg-subtle", children: campaignName })
|
|
3180
3204
|
] }),
|
|
3181
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
|
|
3205
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Button, { onClick: handleSave, isLoading: isSaving, children: "Save Details" })
|
|
3206
|
+
] }),
|
|
3207
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
|
|
3208
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h3", children: "Images" }),
|
|
3209
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
|
|
3210
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
3211
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { children: "Main Campaign Image" }),
|
|
3212
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3213
|
+
"div",
|
|
3214
|
+
{
|
|
3215
|
+
className: ui.clx(
|
|
3216
|
+
"relative flex h-64 items-center justify-center overflow-hidden rounded-lg border-2 border-dashed",
|
|
3217
|
+
(campaignDetail == null ? void 0 : campaignDetail.image_url) ? "border-ui-border-base" : "border-ui-border-base bg-ui-bg-subtle"
|
|
3218
|
+
),
|
|
3219
|
+
children: (campaignDetail == null ? void 0 : campaignDetail.image_url) ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
3220
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3221
|
+
"img",
|
|
3222
|
+
{
|
|
3223
|
+
src: campaignDetail.image_url,
|
|
3224
|
+
alt: "Campaign",
|
|
3225
|
+
className: "h-full w-full object-cover",
|
|
3226
|
+
onError: (e2) => {
|
|
3227
|
+
console.error(
|
|
3228
|
+
"Failed to load image:",
|
|
3229
|
+
campaignDetail.image_url
|
|
3230
|
+
);
|
|
3231
|
+
e2.currentTarget.style.display = "none";
|
|
3232
|
+
}
|
|
3233
|
+
}
|
|
3234
|
+
),
|
|
3235
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
3236
|
+
ui.Button,
|
|
3237
|
+
{
|
|
3238
|
+
variant: "secondary",
|
|
3239
|
+
size: "small",
|
|
3240
|
+
className: "absolute bottom-2 right-2",
|
|
3241
|
+
onClick: () => setShowImageUploader("image"),
|
|
3242
|
+
children: [
|
|
3243
|
+
/* @__PURE__ */ jsxRuntime.jsx(icons.PencilSquare, { className: "mr-1" }),
|
|
3244
|
+
"Change"
|
|
3245
|
+
]
|
|
3246
|
+
}
|
|
3247
|
+
)
|
|
3248
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3249
|
+
ui.Button,
|
|
3250
|
+
{
|
|
3251
|
+
variant: "secondary",
|
|
3252
|
+
onClick: () => setShowImageUploader("image"),
|
|
3253
|
+
children: [
|
|
3254
|
+
/* @__PURE__ */ jsxRuntime.jsx(icons.PhotoSolid, { className: "mr-2" }),
|
|
3255
|
+
"Upload Image"
|
|
3256
|
+
]
|
|
3257
|
+
}
|
|
3258
|
+
)
|
|
3259
|
+
}
|
|
3260
|
+
)
|
|
3261
|
+
] }),
|
|
3262
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
3263
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { children: "Thumbnail" }),
|
|
3264
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3265
|
+
"div",
|
|
3266
|
+
{
|
|
3267
|
+
className: ui.clx(
|
|
3268
|
+
"relative flex h-64 items-center justify-center overflow-hidden rounded-lg border-2 border-dashed",
|
|
3269
|
+
(campaignDetail == null ? void 0 : campaignDetail.thumbnail_url) ? "border-ui-border-base" : "border-ui-border-base bg-ui-bg-subtle"
|
|
3270
|
+
),
|
|
3271
|
+
children: (campaignDetail == null ? void 0 : campaignDetail.thumbnail_url) ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
3272
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3273
|
+
"img",
|
|
3274
|
+
{
|
|
3275
|
+
src: campaignDetail.thumbnail_url,
|
|
3276
|
+
alt: "Thumbnail",
|
|
3277
|
+
className: "h-full w-full object-cover",
|
|
3278
|
+
onError: (e2) => {
|
|
3279
|
+
console.error(
|
|
3280
|
+
"Failed to load thumbnail:",
|
|
3281
|
+
campaignDetail.thumbnail_url
|
|
3282
|
+
);
|
|
3283
|
+
e2.currentTarget.style.display = "none";
|
|
3284
|
+
}
|
|
3285
|
+
}
|
|
3286
|
+
),
|
|
3287
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
3288
|
+
ui.Button,
|
|
3289
|
+
{
|
|
3290
|
+
variant: "secondary",
|
|
3291
|
+
size: "small",
|
|
3292
|
+
className: "absolute bottom-2 right-2",
|
|
3293
|
+
onClick: () => setShowImageUploader("thumbnail"),
|
|
3294
|
+
children: [
|
|
3295
|
+
/* @__PURE__ */ jsxRuntime.jsx(icons.PencilSquare, { className: "mr-1" }),
|
|
3296
|
+
"Change"
|
|
3297
|
+
]
|
|
3298
|
+
}
|
|
3299
|
+
)
|
|
3300
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3301
|
+
ui.Button,
|
|
3302
|
+
{
|
|
3303
|
+
variant: "secondary",
|
|
3304
|
+
onClick: () => setShowImageUploader("thumbnail"),
|
|
3305
|
+
children: [
|
|
3306
|
+
/* @__PURE__ */ jsxRuntime.jsx(icons.PhotoSolid, { className: "mr-2" }),
|
|
3307
|
+
"Upload Thumbnail"
|
|
3308
|
+
]
|
|
3309
|
+
}
|
|
3310
|
+
)
|
|
3311
|
+
}
|
|
3312
|
+
)
|
|
3313
|
+
] })
|
|
3314
|
+
] })
|
|
3315
|
+
] }),
|
|
3316
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-4", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3317
|
+
MarkdownEditor,
|
|
3195
3318
|
{
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
|
|
3319
|
+
label: "Campaign Detail Content",
|
|
3320
|
+
value: formData.detail_content,
|
|
3321
|
+
onChange: (value) => setFormData({ ...formData, detail_content: value }),
|
|
3322
|
+
placeholder: "Enter campaign details in Markdown format. You can include images, links, code blocks, and more...",
|
|
3323
|
+
helpText: "Supports Markdown and code syntax",
|
|
3324
|
+
rows: 12,
|
|
3325
|
+
showPreview: true
|
|
3203
3326
|
}
|
|
3204
|
-
) })
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3208
|
-
ui.Button,
|
|
3209
|
-
{
|
|
3210
|
-
type: "button",
|
|
3211
|
-
variant: "secondary",
|
|
3212
|
-
size: "small",
|
|
3213
|
-
onClick: () => insertMarkdown("**", "**"),
|
|
3214
|
-
title: "Bold",
|
|
3215
|
-
children: /* @__PURE__ */ jsxRuntime.jsx("strong", { children: "B" })
|
|
3216
|
-
}
|
|
3217
|
-
),
|
|
3218
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3219
|
-
ui.Button,
|
|
3220
|
-
{
|
|
3221
|
-
type: "button",
|
|
3222
|
-
variant: "secondary",
|
|
3223
|
-
size: "small",
|
|
3224
|
-
onClick: () => insertMarkdown("*", "*"),
|
|
3225
|
-
title: "Italic",
|
|
3226
|
-
children: /* @__PURE__ */ jsxRuntime.jsx("em", { children: "I" })
|
|
3227
|
-
}
|
|
3228
|
-
),
|
|
3229
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3230
|
-
ui.Button,
|
|
3231
|
-
{
|
|
3232
|
-
type: "button",
|
|
3233
|
-
variant: "secondary",
|
|
3234
|
-
size: "small",
|
|
3235
|
-
onClick: () => insertMarkdown("`", "`"),
|
|
3236
|
-
title: "Inline Code",
|
|
3237
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(icons.CommandLine, { className: "h-4 w-4" })
|
|
3238
|
-
}
|
|
3239
|
-
),
|
|
3240
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3241
|
-
ui.Button,
|
|
3242
|
-
{
|
|
3243
|
-
type: "button",
|
|
3244
|
-
variant: "secondary",
|
|
3245
|
-
size: "small",
|
|
3246
|
-
onClick: () => insertMarkdown("```\n", "\n```"),
|
|
3247
|
-
title: "Code Block",
|
|
3248
|
-
children: "Code"
|
|
3249
|
-
}
|
|
3250
|
-
)
|
|
3251
|
-
] }),
|
|
3252
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3253
|
-
ui.Textarea,
|
|
3327
|
+
) }),
|
|
3328
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-4", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3329
|
+
MarkdownEditor,
|
|
3254
3330
|
{
|
|
3255
|
-
|
|
3256
|
-
value,
|
|
3257
|
-
onChange: (
|
|
3258
|
-
placeholder,
|
|
3259
|
-
|
|
3260
|
-
|
|
3331
|
+
label: "Terms and Conditions",
|
|
3332
|
+
value: formData.terms_and_conditions,
|
|
3333
|
+
onChange: (value) => setFormData({ ...formData, terms_and_conditions: value }),
|
|
3334
|
+
placeholder: "Enter terms and conditions for this campaign...",
|
|
3335
|
+
helpText: "Optional - Campaign specific terms",
|
|
3336
|
+
rows: 8,
|
|
3337
|
+
showPreview: true
|
|
3261
3338
|
}
|
|
3262
|
-
)
|
|
3339
|
+
) }),
|
|
3340
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
|
|
3341
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h3", children: "Call to Action Link" }),
|
|
3342
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
|
|
3343
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
3344
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "link-url", children: "Link URL" }),
|
|
3345
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3346
|
+
ui.Input,
|
|
3347
|
+
{
|
|
3348
|
+
id: "link-url",
|
|
3349
|
+
type: "url",
|
|
3350
|
+
value: formData.link_url,
|
|
3351
|
+
onChange: (e2) => setFormData({ ...formData, link_url: e2.target.value }),
|
|
3352
|
+
placeholder: "https://example.com/campaign"
|
|
3353
|
+
}
|
|
3354
|
+
)
|
|
3355
|
+
] }),
|
|
3356
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
3357
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "link-text", children: "Link Text" }),
|
|
3358
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3359
|
+
ui.Input,
|
|
3360
|
+
{
|
|
3361
|
+
id: "link-text",
|
|
3362
|
+
value: formData.link_text,
|
|
3363
|
+
onChange: (e2) => setFormData({ ...formData, link_text: e2.target.value }),
|
|
3364
|
+
placeholder: "Shop Now"
|
|
3365
|
+
}
|
|
3366
|
+
)
|
|
3367
|
+
] })
|
|
3368
|
+
] })
|
|
3369
|
+
] }),
|
|
3370
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
|
|
3371
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h3", children: "SEO & Metadata" }),
|
|
3372
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
|
|
3373
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
3374
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "meta-title", children: "Meta Title" }),
|
|
3375
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3376
|
+
ui.Input,
|
|
3377
|
+
{
|
|
3378
|
+
id: "meta-title",
|
|
3379
|
+
value: formData.meta_title,
|
|
3380
|
+
onChange: (e2) => setFormData({ ...formData, meta_title: e2.target.value }),
|
|
3381
|
+
placeholder: "Campaign meta title for SEO"
|
|
3382
|
+
}
|
|
3383
|
+
)
|
|
3384
|
+
] }),
|
|
3385
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
3386
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "meta-description", children: "Meta Description" }),
|
|
3387
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3388
|
+
ui.Input,
|
|
3389
|
+
{
|
|
3390
|
+
id: "meta-description",
|
|
3391
|
+
value: formData.meta_description,
|
|
3392
|
+
onChange: (e2) => setFormData({
|
|
3393
|
+
...formData,
|
|
3394
|
+
meta_description: e2.target.value
|
|
3395
|
+
}),
|
|
3396
|
+
placeholder: "Campaign meta description for SEO"
|
|
3397
|
+
}
|
|
3398
|
+
)
|
|
3399
|
+
] }),
|
|
3400
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
3401
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "meta-keywords", children: "Meta Keywords" }),
|
|
3402
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3403
|
+
ui.Input,
|
|
3404
|
+
{
|
|
3405
|
+
id: "meta-keywords",
|
|
3406
|
+
value: formData.meta_keywords,
|
|
3407
|
+
onChange: (e2) => setFormData({
|
|
3408
|
+
...formData,
|
|
3409
|
+
meta_keywords: e2.target.value
|
|
3410
|
+
}),
|
|
3411
|
+
placeholder: "keyword1, keyword2, keyword3"
|
|
3412
|
+
}
|
|
3413
|
+
),
|
|
3414
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-ui-fg-subtle", children: "Comma-separated keywords for SEO" })
|
|
3415
|
+
] }),
|
|
3416
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
3417
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "display-order", children: "Display Order" }),
|
|
3418
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3419
|
+
ui.Input,
|
|
3420
|
+
{
|
|
3421
|
+
id: "display-order",
|
|
3422
|
+
type: "number",
|
|
3423
|
+
value: formData.display_order,
|
|
3424
|
+
onChange: (e2) => setFormData({
|
|
3425
|
+
...formData,
|
|
3426
|
+
display_order: parseInt(e2.target.value) || 0
|
|
3427
|
+
}),
|
|
3428
|
+
placeholder: "0"
|
|
3429
|
+
}
|
|
3430
|
+
),
|
|
3431
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-ui-fg-subtle", children: "Lower numbers appear first in listings" })
|
|
3432
|
+
] })
|
|
3433
|
+
] })
|
|
3434
|
+
] }),
|
|
3435
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-end border-t pt-4", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { onClick: handleSave, isLoading: isSaving, children: "Save Campaign Details" }) })
|
|
3263
3436
|
] }),
|
|
3264
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3437
|
+
showImageUploader && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3438
|
+
CampaignImageUploader,
|
|
3439
|
+
{
|
|
3440
|
+
campaignId,
|
|
3441
|
+
imageType: showImageUploader,
|
|
3442
|
+
currentImageUrl: showImageUploader === "image" ? campaignDetail == null ? void 0 : campaignDetail.image_url : campaignDetail == null ? void 0 : campaignDetail.thumbnail_url,
|
|
3443
|
+
onClose: () => setShowImageUploader(null),
|
|
3444
|
+
onSuccess: fetchCampaignDetail
|
|
3445
|
+
}
|
|
3446
|
+
)
|
|
3265
3447
|
] });
|
|
3266
3448
|
};
|
|
3267
|
-
const
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
const
|
|
3280
|
-
const
|
|
3281
|
-
const
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
}
|
|
3294
|
-
return true;
|
|
3449
|
+
const CouponDetail = () => {
|
|
3450
|
+
var _a, _b, _c, _d, _e, _f;
|
|
3451
|
+
const { id } = reactRouterDom.useParams();
|
|
3452
|
+
const navigate = reactRouterDom.useNavigate();
|
|
3453
|
+
const [isEditing, setIsEditing] = React.useState(false);
|
|
3454
|
+
const { data, isLoading, refetch } = useCouponById(id ?? "");
|
|
3455
|
+
if (isLoading) {
|
|
3456
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ui.Container, { className: "flex items-center justify-center h-screen", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "animate-spin h-10 w-10 border-4 border-primary rounded-full border-t-transparent" }) });
|
|
3457
|
+
}
|
|
3458
|
+
if (!(data == null ? void 0 : data.campaign)) {
|
|
3459
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ui.Container, { children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Coupon not found" }) });
|
|
3460
|
+
}
|
|
3461
|
+
const campaign = data.campaign;
|
|
3462
|
+
const promotion = (_a = campaign.promotions) == null ? void 0 : _a[0];
|
|
3463
|
+
const initialValues = {
|
|
3464
|
+
name: campaign.name ?? "",
|
|
3465
|
+
description: campaign.description ?? "",
|
|
3466
|
+
type: "coupon",
|
|
3467
|
+
code: (promotion == null ? void 0 : promotion.code) ?? "",
|
|
3468
|
+
discount_type: ((_b = promotion == null ? void 0 : promotion.application_method) == null ? void 0 : _b.type) ?? "percentage",
|
|
3469
|
+
discount_value: Number(((_c = promotion == null ? void 0 : promotion.application_method) == null ? void 0 : _c.value) ?? 0),
|
|
3470
|
+
currency_code: ((_d = promotion == null ? void 0 : promotion.application_method) == null ? void 0 : _d.currency_code) ?? void 0,
|
|
3471
|
+
starts_at: campaign.starts_at ? dayjs__default.default(campaign.starts_at).format("YYYY-MM-DDTHH:mm") : void 0,
|
|
3472
|
+
ends_at: campaign.ends_at ? dayjs__default.default(campaign.ends_at).format("YYYY-MM-DDTHH:mm") : void 0,
|
|
3473
|
+
allocation: ((_e = promotion == null ? void 0 : promotion.application_method) == null ? void 0 : _e.allocation) ?? "total",
|
|
3474
|
+
target_type: ((_f = promotion == null ? void 0 : promotion.application_method) == null ? void 0 : _f.target_type) ?? "order"
|
|
3295
3475
|
};
|
|
3296
|
-
const
|
|
3297
|
-
var
|
|
3298
|
-
if (!
|
|
3299
|
-
|
|
3300
|
-
const reader = new FileReader();
|
|
3301
|
-
reader.onloadend = () => {
|
|
3302
|
-
setPreviewUrl(reader.result);
|
|
3303
|
-
};
|
|
3304
|
-
reader.readAsDataURL(file);
|
|
3305
|
-
setIsUploading(true);
|
|
3306
|
-
const formData = new FormData();
|
|
3307
|
-
formData.append("file", file);
|
|
3308
|
-
try {
|
|
3309
|
-
const response = await fetch(
|
|
3310
|
-
`/admin/campaigns/${campaignId}/${imageType}`,
|
|
3311
|
-
{
|
|
3312
|
-
method: "POST",
|
|
3313
|
-
body: formData,
|
|
3314
|
-
credentials: "include"
|
|
3315
|
-
}
|
|
3316
|
-
);
|
|
3317
|
-
if (response.ok) {
|
|
3318
|
-
const result = await response.json();
|
|
3319
|
-
const newImageUrl = imageType === "image" ? (_a = result.campaign_detail) == null ? void 0 : _a.image_url : (_b = result.campaign_detail) == null ? void 0 : _b.thumbnail_url;
|
|
3320
|
-
setDisplayImage(newImageUrl);
|
|
3321
|
-
setPreviewUrl(null);
|
|
3322
|
-
onSuccess == null ? void 0 : onSuccess();
|
|
3323
|
-
setTimeout(() => {
|
|
3324
|
-
onClose();
|
|
3325
|
-
}, 1e3);
|
|
3326
|
-
} else {
|
|
3327
|
-
const errorData = await response.json();
|
|
3328
|
-
setError(errorData.error || `Failed to upload ${imageType}`);
|
|
3329
|
-
setPreviewUrl(null);
|
|
3330
|
-
}
|
|
3331
|
-
} catch (err) {
|
|
3332
|
-
setError(`Error uploading ${imageType}`);
|
|
3333
|
-
setPreviewUrl(null);
|
|
3334
|
-
console.error(`Error uploading ${imageType}:`, err);
|
|
3335
|
-
} finally {
|
|
3336
|
-
setIsUploading(false);
|
|
3476
|
+
const handleSubmit = async (formData) => {
|
|
3477
|
+
var _a2, _b2;
|
|
3478
|
+
if (!id) {
|
|
3479
|
+
return;
|
|
3337
3480
|
}
|
|
3338
|
-
};
|
|
3339
|
-
const handleDelete = async () => {
|
|
3340
|
-
if (!confirm(`Are you sure you want to delete the ${imageType}?`)) return;
|
|
3341
|
-
setIsUploading(true);
|
|
3342
|
-
setError(null);
|
|
3343
3481
|
try {
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
);
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
setError(`Error deleting ${imageType}`);
|
|
3364
|
-
console.error(`Error deleting ${imageType}:`, err);
|
|
3365
|
-
} finally {
|
|
3366
|
-
setIsUploading(false);
|
|
3482
|
+
await axios__default.default.put(`/admin/coupons/${id}`, {
|
|
3483
|
+
...formData,
|
|
3484
|
+
starts_at: new Date(formData.starts_at).toUTCString(),
|
|
3485
|
+
ends_at: new Date(formData.ends_at).toUTCString(),
|
|
3486
|
+
currency_code: formData.discount_type === "fixed" ? formData.currency_code : void 0
|
|
3487
|
+
});
|
|
3488
|
+
ui.toast.success("Coupon updated successfully");
|
|
3489
|
+
setIsEditing(false);
|
|
3490
|
+
refetch();
|
|
3491
|
+
} catch (error) {
|
|
3492
|
+
let message = "Failed to update coupon";
|
|
3493
|
+
if (axios__default.default.isAxiosError(error)) {
|
|
3494
|
+
message = ((_b2 = (_a2 = error.response) == null ? void 0 : _a2.data) == null ? void 0 : _b2.message) ?? message;
|
|
3495
|
+
} else if (error instanceof Error) {
|
|
3496
|
+
message = error.message;
|
|
3497
|
+
}
|
|
3498
|
+
ui.toast.error("Failed to update coupon", {
|
|
3499
|
+
description: message
|
|
3500
|
+
});
|
|
3367
3501
|
}
|
|
3368
3502
|
};
|
|
3369
|
-
|
|
3370
|
-
e2.preventDefault();
|
|
3371
|
-
e2.stopPropagation();
|
|
3372
|
-
setIsDragging(true);
|
|
3373
|
-
}, []);
|
|
3374
|
-
const handleDragLeave = React.useCallback((e2) => {
|
|
3375
|
-
e2.preventDefault();
|
|
3376
|
-
e2.stopPropagation();
|
|
3377
|
-
setIsDragging(false);
|
|
3378
|
-
}, []);
|
|
3379
|
-
const handleDragOver = React.useCallback((e2) => {
|
|
3380
|
-
e2.preventDefault();
|
|
3381
|
-
e2.stopPropagation();
|
|
3382
|
-
}, []);
|
|
3383
|
-
const handleDrop = React.useCallback((e2) => {
|
|
3384
|
-
var _a;
|
|
3385
|
-
e2.preventDefault();
|
|
3386
|
-
e2.stopPropagation();
|
|
3387
|
-
setIsDragging(false);
|
|
3388
|
-
const file = (_a = e2.dataTransfer.files) == null ? void 0 : _a[0];
|
|
3389
|
-
if (file) {
|
|
3390
|
-
handleFileUpload(file);
|
|
3391
|
-
}
|
|
3392
|
-
}, []);
|
|
3393
|
-
const imageToDisplay = previewUrl || displayImage;
|
|
3394
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/50", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full max-w-2xl rounded-lg bg-ui-bg-base p-6", children: [
|
|
3503
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { children: [
|
|
3395
3504
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-6 flex items-center justify-between", children: [
|
|
3396
3505
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
3397
|
-
/* @__PURE__ */ jsxRuntime.
|
|
3398
|
-
|
|
3399
|
-
isThumbnail ? "Thumbnail" : "Image"
|
|
3400
|
-
] }),
|
|
3401
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-subtle", children: isThumbnail ? "Thumbnail for campaign listing" : "Main campaign image" })
|
|
3506
|
+
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-2xl font-semibold mb-1", children: campaign.name }),
|
|
3507
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-ui-fg-subtle", children: "Manage coupon configuration and content" })
|
|
3402
3508
|
] }),
|
|
3403
|
-
/* @__PURE__ */ jsxRuntime.
|
|
3509
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
|
|
3510
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "secondary", onClick: () => navigate("/coupons"), children: "Back to Coupons" }),
|
|
3511
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Button, { onClick: () => setIsEditing((prev) => !prev), children: isEditing ? "View Mode" : "Edit Mode" })
|
|
3512
|
+
] })
|
|
3404
3513
|
] }),
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
/* @__PURE__ */ jsxRuntime.
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
src: imageToDisplay,
|
|
3413
|
-
alt: `Campaign ${imageType}`,
|
|
3414
|
-
className: ui.clx(
|
|
3415
|
-
"w-full object-contain",
|
|
3416
|
-
isThumbnail ? "h-32" : "h-64"
|
|
3417
|
-
)
|
|
3418
|
-
}
|
|
3419
|
-
),
|
|
3420
|
-
isUploading && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 flex items-center justify-center bg-black/50", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-white", children: "Uploading..." }) })
|
|
3421
|
-
] }),
|
|
3422
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
|
|
3423
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
3424
|
-
ui.Button,
|
|
3425
|
-
{
|
|
3426
|
-
variant: "secondary",
|
|
3427
|
-
disabled: isUploading,
|
|
3428
|
-
onClick: () => {
|
|
3429
|
-
var _a;
|
|
3430
|
-
return (_a = document.getElementById(`${imageType}-file-input`)) == null ? void 0 : _a.click();
|
|
3431
|
-
},
|
|
3432
|
-
children: [
|
|
3433
|
-
/* @__PURE__ */ jsxRuntime.jsx(icons.CloudArrowUp, { className: "mr-2" }),
|
|
3434
|
-
"Replace"
|
|
3435
|
-
]
|
|
3436
|
-
}
|
|
3437
|
-
),
|
|
3438
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
3439
|
-
ui.Button,
|
|
3440
|
-
{
|
|
3441
|
-
variant: "danger",
|
|
3442
|
-
disabled: isUploading,
|
|
3443
|
-
onClick: handleDelete,
|
|
3444
|
-
children: [
|
|
3445
|
-
/* @__PURE__ */ jsxRuntime.jsx(icons.Trash, { className: "mr-2" }),
|
|
3446
|
-
"Delete"
|
|
3447
|
-
]
|
|
3448
|
-
}
|
|
3449
|
-
)
|
|
3450
|
-
] })
|
|
3451
|
-
] }) : /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3452
|
-
"div",
|
|
3514
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Tabs, { defaultValue: "settings", children: [
|
|
3515
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Tabs.List, { children: [
|
|
3516
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Tabs.Trigger, { value: "settings", children: "Coupon Settings" }),
|
|
3517
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Tabs.Trigger, { value: "details", children: "Coupon Details" })
|
|
3518
|
+
] }),
|
|
3519
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Tabs.Content, { value: "settings", className: "mt-6", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3520
|
+
CouponForm,
|
|
3453
3521
|
{
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
!isUploading && "cursor-pointer"
|
|
3459
|
-
),
|
|
3460
|
-
onDragEnter: handleDragEnter,
|
|
3461
|
-
onDragLeave: handleDragLeave,
|
|
3462
|
-
onDragOver: handleDragOver,
|
|
3463
|
-
onDrop: handleDrop,
|
|
3464
|
-
onClick: () => {
|
|
3465
|
-
var _a;
|
|
3466
|
-
return !isUploading && ((_a = document.getElementById(`${imageType}-file-input`)) == null ? void 0 : _a.click());
|
|
3467
|
-
},
|
|
3468
|
-
children: [
|
|
3469
|
-
/* @__PURE__ */ jsxRuntime.jsx(icons.PhotoSolid, { className: "mb-4 h-12 w-12 text-ui-fg-subtle" }),
|
|
3470
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "mb-2 text-lg font-medium", children: isDragging ? `Drop ${imageType} here` : `Upload ${isThumbnail ? "Thumbnail" : "Image"}` }),
|
|
3471
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "mb-4 text-center text-ui-fg-subtle", children: "Drag and drop an image here, or click to select" }),
|
|
3472
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { className: "text-sm text-ui-fg-subtle", children: [
|
|
3473
|
-
formatFileTypes(),
|
|
3474
|
-
" • Max ",
|
|
3475
|
-
maxSize,
|
|
3476
|
-
"MB"
|
|
3477
|
-
] }),
|
|
3478
|
-
isThumbnail && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "mt-2 text-xs text-ui-fg-subtle", children: "Recommended: 16:9 aspect ratio, minimum 400x225px" }),
|
|
3479
|
-
!isThumbnail && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "mt-2 text-xs text-ui-fg-subtle", children: "Recommended: High resolution, minimum 1200x600px" }),
|
|
3480
|
-
isUploading && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-4", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { children: "Uploading..." }) })
|
|
3481
|
-
]
|
|
3522
|
+
initialData: initialValues,
|
|
3523
|
+
onSubmit: handleSubmit,
|
|
3524
|
+
onCancel: () => navigate("/coupons"),
|
|
3525
|
+
disabled: !isEditing
|
|
3482
3526
|
}
|
|
3483
|
-
),
|
|
3484
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3485
|
-
|
|
3527
|
+
) }),
|
|
3528
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Tabs.Content, { value: "details", className: "mt-6", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3529
|
+
CampaignDetailForm,
|
|
3486
3530
|
{
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
accept: allowedTypes.join(","),
|
|
3490
|
-
onChange: (e2) => {
|
|
3491
|
-
var _a;
|
|
3492
|
-
const file = (_a = e2.target.files) == null ? void 0 : _a[0];
|
|
3493
|
-
if (file) handleFileUpload(file);
|
|
3494
|
-
e2.target.value = "";
|
|
3495
|
-
},
|
|
3496
|
-
className: "hidden"
|
|
3531
|
+
campaignId: campaign.id,
|
|
3532
|
+
campaignName: campaign.name ?? ""
|
|
3497
3533
|
}
|
|
3498
|
-
)
|
|
3499
|
-
] }),
|
|
3500
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-6 flex items-center justify-between border-t pt-4", children: [
|
|
3501
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-sm text-ui-fg-subtle", children: isThumbnail ? "Thumbnail will be displayed in campaign listings" : "Main image for the campaign detail page" }),
|
|
3502
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "secondary", onClick: onClose, children: "Close" })
|
|
3534
|
+
) })
|
|
3503
3535
|
] })
|
|
3504
|
-
] })
|
|
3536
|
+
] });
|
|
3537
|
+
};
|
|
3538
|
+
const config$1 = adminSdk.defineRouteConfig({
|
|
3539
|
+
label: "Coupon Detail",
|
|
3540
|
+
icon: icons.Tag
|
|
3541
|
+
});
|
|
3542
|
+
const CouponCreate = () => {
|
|
3543
|
+
const navigate = reactRouterDom.useNavigate();
|
|
3544
|
+
const handleSubmit = async (formData) => {
|
|
3545
|
+
var _a, _b;
|
|
3546
|
+
try {
|
|
3547
|
+
await axios__default.default.post("/admin/coupons", {
|
|
3548
|
+
...formData,
|
|
3549
|
+
starts_at: new Date(formData.starts_at).toUTCString(),
|
|
3550
|
+
ends_at: new Date(formData.ends_at).toUTCString(),
|
|
3551
|
+
currency_code: formData.discount_type === "fixed" ? formData.currency_code : void 0
|
|
3552
|
+
});
|
|
3553
|
+
ui.toast.success("Coupon created successfully");
|
|
3554
|
+
navigate("/coupons");
|
|
3555
|
+
} catch (error) {
|
|
3556
|
+
let message = "Failed to create coupon";
|
|
3557
|
+
if (axios__default.default.isAxiosError(error)) {
|
|
3558
|
+
message = ((_b = (_a = error.response) == null ? void 0 : _a.data) == null ? void 0 : _b.message) ?? message;
|
|
3559
|
+
} else if (error instanceof Error) {
|
|
3560
|
+
message = error.message;
|
|
3561
|
+
}
|
|
3562
|
+
ui.toast.error("Failed to create coupon", {
|
|
3563
|
+
description: message
|
|
3564
|
+
});
|
|
3565
|
+
}
|
|
3566
|
+
};
|
|
3567
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3568
|
+
CouponForm,
|
|
3569
|
+
{
|
|
3570
|
+
onSubmit: handleSubmit,
|
|
3571
|
+
onCancel: () => navigate("/coupons")
|
|
3572
|
+
}
|
|
3573
|
+
);
|
|
3574
|
+
};
|
|
3575
|
+
const sdk = new Medusa__default.default({
|
|
3576
|
+
baseUrl: "/",
|
|
3577
|
+
debug: false,
|
|
3578
|
+
auth: {
|
|
3579
|
+
type: "session"
|
|
3580
|
+
}
|
|
3581
|
+
});
|
|
3582
|
+
const columnHelper = ui.createDataTableColumnHelper();
|
|
3583
|
+
const columns = [
|
|
3584
|
+
columnHelper.accessor("title", {
|
|
3585
|
+
header: "Title",
|
|
3586
|
+
cell: (info) => /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "large", className: "py-2", children: info.getValue() })
|
|
3587
|
+
}),
|
|
3588
|
+
columnHelper.accessor("description", {
|
|
3589
|
+
header: "Description",
|
|
3590
|
+
cell: (info) => /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "large", className: "py-2", children: info.getValue() })
|
|
3591
|
+
})
|
|
3592
|
+
];
|
|
3593
|
+
const ProductSelector = ({
|
|
3594
|
+
selectedProductIds,
|
|
3595
|
+
onSelectProduct
|
|
3596
|
+
}) => {
|
|
3597
|
+
const [search, setSearch] = React.useState("");
|
|
3598
|
+
const debouncedSearchHandler = lodash.debounce((value) => {
|
|
3599
|
+
setSearch(value);
|
|
3600
|
+
}, 500);
|
|
3601
|
+
const [products, setProducts] = React.useState(null);
|
|
3602
|
+
const [isLoading, setIsLoading] = React.useState(true);
|
|
3603
|
+
React.useEffect(() => {
|
|
3604
|
+
const fetchProducts = async () => {
|
|
3605
|
+
setIsLoading(true);
|
|
3606
|
+
try {
|
|
3607
|
+
const result = await sdk.admin.product.list({
|
|
3608
|
+
q: search,
|
|
3609
|
+
limit: 100
|
|
3610
|
+
});
|
|
3611
|
+
setProducts(result);
|
|
3612
|
+
} catch (error) {
|
|
3613
|
+
console.error("Failed to fetch products:", error);
|
|
3614
|
+
} finally {
|
|
3615
|
+
setIsLoading(false);
|
|
3616
|
+
}
|
|
3617
|
+
};
|
|
3618
|
+
fetchProducts();
|
|
3619
|
+
}, [selectedProductIds, search]);
|
|
3620
|
+
const selectableProducts = React.useMemo(() => {
|
|
3621
|
+
var _a;
|
|
3622
|
+
return (_a = products == null ? void 0 : products.products) == null ? void 0 : _a.filter(
|
|
3623
|
+
(product) => !selectedProductIds.includes(product.id)
|
|
3624
|
+
);
|
|
3625
|
+
}, [products == null ? void 0 : products.products, selectedProductIds]);
|
|
3626
|
+
const table = ui.useDataTable({
|
|
3627
|
+
data: selectableProducts || [],
|
|
3628
|
+
columns,
|
|
3629
|
+
getRowId: (product) => product.id,
|
|
3630
|
+
onRowClick: (_, row) => {
|
|
3631
|
+
onSelectProduct == null ? void 0 : onSelectProduct(row.original);
|
|
3632
|
+
}
|
|
3633
|
+
});
|
|
3634
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { children: [
|
|
3635
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3636
|
+
ui.Input,
|
|
3637
|
+
{
|
|
3638
|
+
className: "text-lg py-2",
|
|
3639
|
+
placeholder: "Search products...",
|
|
3640
|
+
onChange: (e2) => debouncedSearchHandler(e2.target.value)
|
|
3641
|
+
}
|
|
3642
|
+
),
|
|
3643
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.DataTable, { instance: table, children: /* @__PURE__ */ jsxRuntime.jsx(ui.DataTable.Table, {}) })
|
|
3644
|
+
] });
|
|
3505
3645
|
};
|
|
3506
|
-
const
|
|
3507
|
-
|
|
3508
|
-
|
|
3646
|
+
const customCampaignSchema = z__default.default.object({
|
|
3647
|
+
name: z__default.default.string().min(1, "Name is required"),
|
|
3648
|
+
description: z__default.default.string().min(1, "Description is required"),
|
|
3649
|
+
type: z__default.default.literal("flash-sale"),
|
|
3650
|
+
starts_at: z__default.default.string().min(1, "Start date is required"),
|
|
3651
|
+
ends_at: z__default.default.string().min(1, "End date is required")
|
|
3652
|
+
});
|
|
3653
|
+
const campaignProductSchema = z__default.default.object({
|
|
3654
|
+
product: z__default.default.object({
|
|
3655
|
+
id: z__default.default.string(),
|
|
3656
|
+
title: z__default.default.string()
|
|
3657
|
+
}),
|
|
3658
|
+
discountType: z__default.default.enum([
|
|
3659
|
+
"percentage"
|
|
3660
|
+
// , "fixed"
|
|
3661
|
+
]),
|
|
3662
|
+
discountValue: z__default.default.number().min(1),
|
|
3663
|
+
limit: z__default.default.number().min(1),
|
|
3664
|
+
maxQty: z__default.default.number().min(1)
|
|
3665
|
+
});
|
|
3666
|
+
const campaignDataSchema = customCampaignSchema.extend({
|
|
3667
|
+
products: z__default.default.array(campaignProductSchema).min(1, "At least one product is required")
|
|
3668
|
+
}).refine(
|
|
3669
|
+
(data) => new Date(data.starts_at) < new Date(data.ends_at),
|
|
3670
|
+
"End date must be after start date"
|
|
3671
|
+
);
|
|
3672
|
+
const FlashSaleForm = ({
|
|
3673
|
+
initialData,
|
|
3674
|
+
initialProducts,
|
|
3675
|
+
onSubmit,
|
|
3676
|
+
onCancel,
|
|
3677
|
+
disabled = false
|
|
3509
3678
|
}) => {
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
|
|
3516
|
-
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
|
|
3679
|
+
var _a;
|
|
3680
|
+
const {
|
|
3681
|
+
control,
|
|
3682
|
+
register,
|
|
3683
|
+
handleSubmit,
|
|
3684
|
+
watch,
|
|
3685
|
+
setValue,
|
|
3686
|
+
formState: { errors }
|
|
3687
|
+
} = useForm({
|
|
3688
|
+
resolver: t(campaignDataSchema),
|
|
3689
|
+
defaultValues: {
|
|
3690
|
+
name: (initialData == null ? void 0 : initialData.name) || "",
|
|
3691
|
+
description: (initialData == null ? void 0 : initialData.description) || "",
|
|
3692
|
+
type: "flash-sale",
|
|
3693
|
+
starts_at: (initialData == null ? void 0 : initialData.starts_at) || "",
|
|
3694
|
+
ends_at: (initialData == null ? void 0 : initialData.ends_at) || "",
|
|
3695
|
+
products: initialProducts ? Array.from(initialProducts.values()) : []
|
|
3696
|
+
}
|
|
3525
3697
|
});
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
decoded_image: data.campaign_detail.image_url ? decodeURIComponent(data.campaign_detail.image_url) : null,
|
|
3542
|
-
decoded_thumbnail: data.campaign_detail.thumbnail_url ? decodeURIComponent(data.campaign_detail.thumbnail_url) : null
|
|
3543
|
-
});
|
|
3544
|
-
setCampaignDetail(data.campaign_detail);
|
|
3545
|
-
setFormData({
|
|
3546
|
-
detail_content: data.campaign_detail.detail_content || "",
|
|
3547
|
-
terms_and_conditions: data.campaign_detail.terms_and_conditions || "",
|
|
3548
|
-
meta_title: data.campaign_detail.meta_title || "",
|
|
3549
|
-
meta_description: data.campaign_detail.meta_description || "",
|
|
3550
|
-
meta_keywords: data.campaign_detail.meta_keywords || "",
|
|
3551
|
-
link_url: data.campaign_detail.link_url || "",
|
|
3552
|
-
link_text: data.campaign_detail.link_text || "",
|
|
3553
|
-
display_order: data.campaign_detail.display_order || 0
|
|
3554
|
-
});
|
|
3555
|
-
}
|
|
3556
|
-
} else if (response.status !== 404) {
|
|
3557
|
-
console.error("Failed to fetch campaign detail");
|
|
3558
|
-
}
|
|
3559
|
-
} catch (error) {
|
|
3560
|
-
console.error("Error fetching campaign detail:", error);
|
|
3561
|
-
} finally {
|
|
3562
|
-
setIsLoading(false);
|
|
3698
|
+
const { fields, append, remove } = useFieldArray({
|
|
3699
|
+
control,
|
|
3700
|
+
name: "products"
|
|
3701
|
+
});
|
|
3702
|
+
const [openProductModal, setOpenProductModal] = React.useState(false);
|
|
3703
|
+
const startsAt = watch("starts_at");
|
|
3704
|
+
const endsAt = watch("ends_at");
|
|
3705
|
+
const handleDateTimeChange = (field, type, value) => {
|
|
3706
|
+
const currentValue = watch(field);
|
|
3707
|
+
if (type === "date") {
|
|
3708
|
+
const time = currentValue ? dayjs__default.default(currentValue).format("HH:mm") : field === "starts_at" ? "00:00" : "23:59";
|
|
3709
|
+
setValue(field, `${value}T${time}`);
|
|
3710
|
+
} else {
|
|
3711
|
+
const date = currentValue ? dayjs__default.default(currentValue).format("YYYY-MM-DD") : dayjs__default.default().format("YYYY-MM-DD");
|
|
3712
|
+
setValue(field, `${date}T${value}`);
|
|
3563
3713
|
}
|
|
3564
3714
|
};
|
|
3565
|
-
const
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
});
|
|
3576
|
-
if (response.ok) {
|
|
3577
|
-
const data = await response.json();
|
|
3578
|
-
setCampaignDetail(data.campaign_detail);
|
|
3579
|
-
ui.toast.success("Campaign details saved successfully");
|
|
3580
|
-
} else {
|
|
3581
|
-
const errorData = await response.json();
|
|
3582
|
-
ui.toast.error(errorData.error || "Failed to save campaign details");
|
|
3715
|
+
const onFormSubmit = (data) => {
|
|
3716
|
+
onSubmit(data);
|
|
3717
|
+
};
|
|
3718
|
+
const onFormError = () => {
|
|
3719
|
+
const errorMessages = Object.entries(errors).map(([key, value]) => {
|
|
3720
|
+
var _a2;
|
|
3721
|
+
if (key === "products" && Array.isArray(value)) {
|
|
3722
|
+
return value.map(
|
|
3723
|
+
(item, index) => item ? `Product ${index + 1}: ${Object.values(item).join(", ")}` : ""
|
|
3724
|
+
).filter(Boolean).join(", ");
|
|
3583
3725
|
}
|
|
3584
|
-
|
|
3585
|
-
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
}
|
|
3726
|
+
return (value == null ? void 0 : value.message) || ((_a2 = value == null ? void 0 : value.root) == null ? void 0 : _a2.message);
|
|
3727
|
+
}).filter(Boolean).join(", ");
|
|
3728
|
+
ui.toast.error("Invalid data", {
|
|
3729
|
+
description: errorMessages
|
|
3730
|
+
});
|
|
3590
3731
|
};
|
|
3591
|
-
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
}
|
|
3629
|
-
}
|
|
3630
|
-
),
|
|
3631
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
3632
|
-
ui.Button,
|
|
3633
|
-
{
|
|
3634
|
-
variant: "secondary",
|
|
3635
|
-
size: "small",
|
|
3636
|
-
className: "absolute bottom-2 right-2",
|
|
3637
|
-
onClick: () => setShowImageUploader("image"),
|
|
3638
|
-
children: [
|
|
3639
|
-
/* @__PURE__ */ jsxRuntime.jsx(icons.PencilSquare, { className: "mr-1" }),
|
|
3640
|
-
"Change"
|
|
3641
|
-
]
|
|
3642
|
-
}
|
|
3643
|
-
)
|
|
3644
|
-
] }) : /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3645
|
-
ui.Button,
|
|
3732
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { children: [
|
|
3733
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
|
|
3734
|
+
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-xl font-semibold", children: "Flash sale campaign" }),
|
|
3735
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "transparent", onClick: onCancel, children: "Cancel" })
|
|
3736
|
+
] }),
|
|
3737
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
3738
|
+
"form",
|
|
3739
|
+
{
|
|
3740
|
+
onSubmit: handleSubmit(onFormSubmit, onFormError),
|
|
3741
|
+
className: "space-y-4 my-8",
|
|
3742
|
+
children: [
|
|
3743
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
3744
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { children: "Name" }),
|
|
3745
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Input, { ...register("name"), disabled }),
|
|
3746
|
+
errors.name && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-red-500 text-sm mt-1", children: errors.name.message })
|
|
3747
|
+
] }),
|
|
3748
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
3749
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { children: "Description" }),
|
|
3750
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Textarea, { ...register("description"), disabled }),
|
|
3751
|
+
errors.description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-red-500 text-sm mt-1", children: errors.description.message })
|
|
3752
|
+
] }),
|
|
3753
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
|
|
3754
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
3755
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { children: "Start Date" }),
|
|
3756
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
|
|
3757
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3758
|
+
ui.Input,
|
|
3759
|
+
{
|
|
3760
|
+
type: "date",
|
|
3761
|
+
value: startsAt ? dayjs__default.default(startsAt).format("YYYY-MM-DD") : "",
|
|
3762
|
+
onChange: (e2) => handleDateTimeChange("starts_at", "date", e2.target.value),
|
|
3763
|
+
disabled,
|
|
3764
|
+
className: "flex-1"
|
|
3765
|
+
}
|
|
3766
|
+
),
|
|
3767
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3768
|
+
ui.Input,
|
|
3646
3769
|
{
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
]
|
|
3770
|
+
type: "time",
|
|
3771
|
+
value: startsAt ? dayjs__default.default(startsAt).format("HH:mm") : "",
|
|
3772
|
+
onChange: (e2) => handleDateTimeChange("starts_at", "time", e2.target.value),
|
|
3773
|
+
disabled,
|
|
3774
|
+
className: "w-32"
|
|
3653
3775
|
}
|
|
3654
3776
|
)
|
|
3655
|
-
}
|
|
3656
|
-
|
|
3657
|
-
|
|
3658
|
-
|
|
3659
|
-
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
3777
|
+
] }),
|
|
3778
|
+
errors.starts_at && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-red-500 text-sm mt-1", children: errors.starts_at.message })
|
|
3779
|
+
] }),
|
|
3780
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
3781
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { children: "End Date" }),
|
|
3782
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
|
|
3783
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3784
|
+
ui.Input,
|
|
3785
|
+
{
|
|
3786
|
+
type: "date",
|
|
3787
|
+
value: endsAt ? dayjs__default.default(endsAt).format("YYYY-MM-DD") : "",
|
|
3788
|
+
onChange: (e2) => handleDateTimeChange("ends_at", "date", e2.target.value),
|
|
3789
|
+
disabled,
|
|
3790
|
+
className: "flex-1"
|
|
3791
|
+
}
|
|
3666
3792
|
),
|
|
3667
|
-
|
|
3668
|
-
|
|
3669
|
-
"img",
|
|
3670
|
-
{
|
|
3671
|
-
src: campaignDetail.thumbnail_url,
|
|
3672
|
-
alt: "Thumbnail",
|
|
3673
|
-
className: "h-full w-full object-cover",
|
|
3674
|
-
onError: (e2) => {
|
|
3675
|
-
console.error(
|
|
3676
|
-
"Failed to load thumbnail:",
|
|
3677
|
-
campaignDetail.thumbnail_url
|
|
3678
|
-
);
|
|
3679
|
-
e2.currentTarget.style.display = "none";
|
|
3680
|
-
}
|
|
3681
|
-
}
|
|
3682
|
-
),
|
|
3683
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
3684
|
-
ui.Button,
|
|
3685
|
-
{
|
|
3686
|
-
variant: "secondary",
|
|
3687
|
-
size: "small",
|
|
3688
|
-
className: "absolute bottom-2 right-2",
|
|
3689
|
-
onClick: () => setShowImageUploader("thumbnail"),
|
|
3690
|
-
children: [
|
|
3691
|
-
/* @__PURE__ */ jsxRuntime.jsx(icons.PencilSquare, { className: "mr-1" }),
|
|
3692
|
-
"Change"
|
|
3693
|
-
]
|
|
3694
|
-
}
|
|
3695
|
-
)
|
|
3696
|
-
] }) : /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3697
|
-
ui.Button,
|
|
3793
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3794
|
+
ui.Input,
|
|
3698
3795
|
{
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
|
|
3702
|
-
|
|
3703
|
-
|
|
3704
|
-
]
|
|
3796
|
+
type: "time",
|
|
3797
|
+
value: endsAt ? dayjs__default.default(endsAt).format("HH:mm") : "",
|
|
3798
|
+
onChange: (e2) => handleDateTimeChange("ends_at", "time", e2.target.value),
|
|
3799
|
+
disabled,
|
|
3800
|
+
className: "w-32"
|
|
3705
3801
|
}
|
|
3706
3802
|
)
|
|
3707
|
-
}
|
|
3708
|
-
|
|
3709
|
-
|
|
3710
|
-
] })
|
|
3711
|
-
] }),
|
|
3712
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-4", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3713
|
-
MarkdownEditor,
|
|
3714
|
-
{
|
|
3715
|
-
label: "Campaign Detail Content",
|
|
3716
|
-
value: formData.detail_content,
|
|
3717
|
-
onChange: (value) => setFormData({ ...formData, detail_content: value }),
|
|
3718
|
-
placeholder: "Enter campaign details in Markdown format. You can include images, links, code blocks, and more...",
|
|
3719
|
-
helpText: "Supports Markdown and code syntax",
|
|
3720
|
-
rows: 12,
|
|
3721
|
-
showPreview: true
|
|
3722
|
-
}
|
|
3723
|
-
) }),
|
|
3724
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-4", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3725
|
-
MarkdownEditor,
|
|
3726
|
-
{
|
|
3727
|
-
label: "Terms and Conditions",
|
|
3728
|
-
value: formData.terms_and_conditions,
|
|
3729
|
-
onChange: (value) => setFormData({ ...formData, terms_and_conditions: value }),
|
|
3730
|
-
placeholder: "Enter terms and conditions for this campaign...",
|
|
3731
|
-
helpText: "Optional - Campaign specific terms",
|
|
3732
|
-
rows: 8,
|
|
3733
|
-
showPreview: true
|
|
3734
|
-
}
|
|
3735
|
-
) }),
|
|
3736
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
|
|
3737
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h3", children: "Call to Action Link" }),
|
|
3738
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
|
|
3739
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
3740
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "link-url", children: "Link URL" }),
|
|
3741
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3742
|
-
ui.Input,
|
|
3743
|
-
{
|
|
3744
|
-
id: "link-url",
|
|
3745
|
-
type: "url",
|
|
3746
|
-
value: formData.link_url,
|
|
3747
|
-
onChange: (e2) => setFormData({ ...formData, link_url: e2.target.value }),
|
|
3748
|
-
placeholder: "https://example.com/campaign"
|
|
3749
|
-
}
|
|
3750
|
-
)
|
|
3751
|
-
] }),
|
|
3752
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
3753
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "link-text", children: "Link Text" }),
|
|
3754
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3755
|
-
ui.Input,
|
|
3756
|
-
{
|
|
3757
|
-
id: "link-text",
|
|
3758
|
-
value: formData.link_text,
|
|
3759
|
-
onChange: (e2) => setFormData({ ...formData, link_text: e2.target.value }),
|
|
3760
|
-
placeholder: "Shop Now"
|
|
3761
|
-
}
|
|
3762
|
-
)
|
|
3763
|
-
] })
|
|
3764
|
-
] })
|
|
3765
|
-
] }),
|
|
3766
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
|
|
3767
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h3", children: "SEO & Metadata" }),
|
|
3768
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
|
|
3769
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
3770
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "meta-title", children: "Meta Title" }),
|
|
3771
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3772
|
-
ui.Input,
|
|
3773
|
-
{
|
|
3774
|
-
id: "meta-title",
|
|
3775
|
-
value: formData.meta_title,
|
|
3776
|
-
onChange: (e2) => setFormData({ ...formData, meta_title: e2.target.value }),
|
|
3777
|
-
placeholder: "Campaign meta title for SEO"
|
|
3778
|
-
}
|
|
3779
|
-
)
|
|
3803
|
+
] }),
|
|
3804
|
+
errors.ends_at && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-red-500 text-sm mt-1", children: errors.ends_at.message })
|
|
3805
|
+
] })
|
|
3780
3806
|
] }),
|
|
3781
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "
|
|
3782
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, {
|
|
3807
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-center", children: [
|
|
3808
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { children: "Products" }),
|
|
3783
3809
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3784
|
-
ui.
|
|
3810
|
+
ui.Button,
|
|
3785
3811
|
{
|
|
3786
|
-
|
|
3787
|
-
|
|
3788
|
-
|
|
3789
|
-
|
|
3790
|
-
|
|
3791
|
-
}),
|
|
3792
|
-
placeholder: "Campaign meta description for SEO"
|
|
3812
|
+
type: "button",
|
|
3813
|
+
variant: "secondary",
|
|
3814
|
+
onClick: () => setOpenProductModal(true),
|
|
3815
|
+
disabled,
|
|
3816
|
+
children: "Add Product"
|
|
3793
3817
|
}
|
|
3794
3818
|
)
|
|
3795
3819
|
] }),
|
|
3796
|
-
/* @__PURE__ */ jsxRuntime.
|
|
3797
|
-
|
|
3798
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3799
|
-
ui.
|
|
3800
|
-
{
|
|
3801
|
-
|
|
3802
|
-
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
|
|
3806
|
-
|
|
3807
|
-
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
|
|
3820
|
+
((_a = errors.products) == null ? void 0 : _a.root) && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-red-500 text-sm", children: errors.products.root.message }),
|
|
3821
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Table, { children: [
|
|
3822
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Header, { children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
|
|
3823
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "Product" }),
|
|
3824
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "Discount Type" }),
|
|
3825
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "Discount Value" }),
|
|
3826
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "Limit" }),
|
|
3827
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "Max Qty per Order" }),
|
|
3828
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "Actions" })
|
|
3829
|
+
] }) }),
|
|
3830
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Body, { children: fields.map((field, index) => /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
|
|
3831
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: field.product.title }),
|
|
3832
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3833
|
+
Controller,
|
|
3834
|
+
{
|
|
3835
|
+
name: `products.${index}.discountType`,
|
|
3836
|
+
control,
|
|
3837
|
+
render: ({ field: field2 }) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3838
|
+
ui.Select,
|
|
3839
|
+
{
|
|
3840
|
+
value: field2.value,
|
|
3841
|
+
onValueChange: field2.onChange,
|
|
3842
|
+
disabled,
|
|
3843
|
+
children: [
|
|
3844
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder: "Select discount type" }) }),
|
|
3845
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Content, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: "percentage", children: "Percentage" }) })
|
|
3846
|
+
]
|
|
3847
|
+
}
|
|
3848
|
+
)
|
|
3849
|
+
}
|
|
3850
|
+
) }),
|
|
3851
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3852
|
+
Controller,
|
|
3853
|
+
{
|
|
3854
|
+
name: `products.${index}.discountValue`,
|
|
3855
|
+
control,
|
|
3856
|
+
render: ({ field: field2 }) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
3857
|
+
ui.Input,
|
|
3858
|
+
{
|
|
3859
|
+
type: "number",
|
|
3860
|
+
value: field2.value,
|
|
3861
|
+
onChange: (e2) => field2.onChange(Number(e2.target.value)),
|
|
3862
|
+
disabled
|
|
3863
|
+
}
|
|
3864
|
+
)
|
|
3865
|
+
}
|
|
3866
|
+
) }),
|
|
3867
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3868
|
+
Controller,
|
|
3869
|
+
{
|
|
3870
|
+
name: `products.${index}.limit`,
|
|
3871
|
+
control,
|
|
3872
|
+
render: ({ field: field2 }) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
3873
|
+
ui.Input,
|
|
3874
|
+
{
|
|
3875
|
+
type: "number",
|
|
3876
|
+
value: field2.value,
|
|
3877
|
+
onChange: (e2) => field2.onChange(Number(e2.target.value)),
|
|
3878
|
+
disabled
|
|
3879
|
+
}
|
|
3880
|
+
)
|
|
3881
|
+
}
|
|
3882
|
+
) }),
|
|
3883
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3884
|
+
Controller,
|
|
3885
|
+
{
|
|
3886
|
+
name: `products.${index}.maxQty`,
|
|
3887
|
+
control,
|
|
3888
|
+
render: ({ field: field2 }) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
3889
|
+
ui.Input,
|
|
3890
|
+
{
|
|
3891
|
+
type: "number",
|
|
3892
|
+
value: field2.value,
|
|
3893
|
+
onChange: (e2) => field2.onChange(Number(e2.target.value)),
|
|
3894
|
+
disabled
|
|
3895
|
+
}
|
|
3896
|
+
)
|
|
3897
|
+
}
|
|
3898
|
+
) }),
|
|
3899
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3900
|
+
ui.Button,
|
|
3901
|
+
{
|
|
3902
|
+
type: "button",
|
|
3903
|
+
variant: "danger",
|
|
3904
|
+
onClick: () => remove(index),
|
|
3905
|
+
disabled,
|
|
3906
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(icons.Trash, {})
|
|
3907
|
+
}
|
|
3908
|
+
) })
|
|
3909
|
+
] }, field.id)) })
|
|
3811
3910
|
] }),
|
|
3812
|
-
/* @__PURE__ */ jsxRuntime.
|
|
3813
|
-
|
|
3814
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3815
|
-
ui.Input,
|
|
3816
|
-
{
|
|
3817
|
-
id: "display-order",
|
|
3818
|
-
type: "number",
|
|
3819
|
-
value: formData.display_order,
|
|
3820
|
-
onChange: (e2) => setFormData({
|
|
3821
|
-
...formData,
|
|
3822
|
-
display_order: parseInt(e2.target.value) || 0
|
|
3823
|
-
}),
|
|
3824
|
-
placeholder: "0"
|
|
3825
|
-
}
|
|
3826
|
-
),
|
|
3827
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-ui-fg-subtle", children: "Lower numbers appear first in listings" })
|
|
3828
|
-
] })
|
|
3829
|
-
] })
|
|
3830
|
-
] }),
|
|
3831
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-end border-t pt-4", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { onClick: handleSave, isLoading: isSaving, children: "Save Campaign Details" }) })
|
|
3832
|
-
] }),
|
|
3833
|
-
showImageUploader && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3834
|
-
CampaignImageUploader,
|
|
3835
|
-
{
|
|
3836
|
-
campaignId,
|
|
3837
|
-
imageType: showImageUploader,
|
|
3838
|
-
currentImageUrl: showImageUploader === "image" ? campaignDetail == null ? void 0 : campaignDetail.image_url : campaignDetail == null ? void 0 : campaignDetail.thumbnail_url,
|
|
3839
|
-
onClose: () => setShowImageUploader(null),
|
|
3840
|
-
onSuccess: fetchCampaignDetail
|
|
3841
|
-
}
|
|
3842
|
-
)
|
|
3843
|
-
] });
|
|
3844
|
-
};
|
|
3845
|
-
const CouponDetail = () => {
|
|
3846
|
-
var _a, _b, _c, _d, _e, _f;
|
|
3847
|
-
const { id } = reactRouterDom.useParams();
|
|
3848
|
-
const navigate = reactRouterDom.useNavigate();
|
|
3849
|
-
const [isEditing, setIsEditing] = React.useState(false);
|
|
3850
|
-
const { data, isLoading, refetch } = useCouponById(id ?? "");
|
|
3851
|
-
if (isLoading) {
|
|
3852
|
-
return /* @__PURE__ */ jsxRuntime.jsx(ui.Container, { className: "flex items-center justify-center h-screen", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "animate-spin h-10 w-10 border-4 border-primary rounded-full border-t-transparent" }) });
|
|
3853
|
-
}
|
|
3854
|
-
if (!(data == null ? void 0 : data.campaign)) {
|
|
3855
|
-
return /* @__PURE__ */ jsxRuntime.jsx(ui.Container, { children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Coupon not found" }) });
|
|
3856
|
-
}
|
|
3857
|
-
const campaign = data.campaign;
|
|
3858
|
-
const promotion = (_a = campaign.promotions) == null ? void 0 : _a[0];
|
|
3859
|
-
const initialValues = {
|
|
3860
|
-
name: campaign.name ?? "",
|
|
3861
|
-
description: campaign.description ?? "",
|
|
3862
|
-
type: "coupon",
|
|
3863
|
-
code: (promotion == null ? void 0 : promotion.code) ?? "",
|
|
3864
|
-
discount_type: ((_b = promotion == null ? void 0 : promotion.application_method) == null ? void 0 : _b.type) ?? "percentage",
|
|
3865
|
-
discount_value: Number(((_c = promotion == null ? void 0 : promotion.application_method) == null ? void 0 : _c.value) ?? 0),
|
|
3866
|
-
currency_code: ((_d = promotion == null ? void 0 : promotion.application_method) == null ? void 0 : _d.currency_code) ?? void 0,
|
|
3867
|
-
starts_at: campaign.starts_at ? dayjs__default.default(campaign.starts_at).format("YYYY-MM-DDTHH:mm") : void 0,
|
|
3868
|
-
ends_at: campaign.ends_at ? dayjs__default.default(campaign.ends_at).format("YYYY-MM-DDTHH:mm") : void 0,
|
|
3869
|
-
allocation: ((_e = promotion == null ? void 0 : promotion.application_method) == null ? void 0 : _e.allocation) ?? "total",
|
|
3870
|
-
target_type: ((_f = promotion == null ? void 0 : promotion.application_method) == null ? void 0 : _f.target_type) ?? "order"
|
|
3871
|
-
};
|
|
3872
|
-
const handleSubmit = async (formData) => {
|
|
3873
|
-
var _a2, _b2;
|
|
3874
|
-
if (!id) {
|
|
3875
|
-
return;
|
|
3876
|
-
}
|
|
3877
|
-
try {
|
|
3878
|
-
await axios__default.default.put(`/admin/coupons/${id}`, {
|
|
3879
|
-
...formData,
|
|
3880
|
-
starts_at: new Date(formData.starts_at).toUTCString(),
|
|
3881
|
-
ends_at: new Date(formData.ends_at).toUTCString(),
|
|
3882
|
-
currency_code: formData.discount_type === "fixed" ? formData.currency_code : void 0
|
|
3883
|
-
});
|
|
3884
|
-
ui.toast.success("Coupon updated successfully");
|
|
3885
|
-
setIsEditing(false);
|
|
3886
|
-
refetch();
|
|
3887
|
-
} catch (error) {
|
|
3888
|
-
let message = "Failed to update coupon";
|
|
3889
|
-
if (axios__default.default.isAxiosError(error)) {
|
|
3890
|
-
message = ((_b2 = (_a2 = error.response) == null ? void 0 : _a2.data) == null ? void 0 : _b2.message) ?? message;
|
|
3891
|
-
} else if (error instanceof Error) {
|
|
3892
|
-
message = error.message;
|
|
3911
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Button, { type: "submit", disabled, children: "Save" })
|
|
3912
|
+
]
|
|
3893
3913
|
}
|
|
3894
|
-
|
|
3895
|
-
|
|
3896
|
-
})
|
|
3897
|
-
|
|
3898
|
-
|
|
3899
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { children: [
|
|
3900
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-6 flex items-center justify-between", children: [
|
|
3901
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
3902
|
-
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-2xl font-semibold mb-1", children: campaign.name }),
|
|
3903
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-ui-fg-subtle", children: "Manage coupon configuration and content" })
|
|
3904
|
-
] }),
|
|
3905
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
|
|
3906
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "secondary", onClick: () => navigate("/coupons"), children: "Back to Coupons" }),
|
|
3907
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Button, { onClick: () => setIsEditing((prev) => !prev), children: isEditing ? "View Mode" : "Edit Mode" })
|
|
3908
|
-
] })
|
|
3909
|
-
] }),
|
|
3910
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ui.Tabs, { defaultValue: "settings", children: [
|
|
3911
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ui.Tabs.List, { children: [
|
|
3912
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Tabs.Trigger, { value: "settings", children: "Coupon Settings" }),
|
|
3913
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Tabs.Trigger, { value: "details", children: "Coupon Details" })
|
|
3914
|
-
] }),
|
|
3915
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Tabs.Content, { value: "settings", className: "mt-6", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3916
|
-
CouponForm,
|
|
3917
|
-
{
|
|
3918
|
-
initialData: initialValues,
|
|
3919
|
-
onSubmit: handleSubmit,
|
|
3920
|
-
onCancel: () => navigate("/coupons"),
|
|
3921
|
-
disabled: !isEditing
|
|
3922
|
-
}
|
|
3923
|
-
) }),
|
|
3924
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Tabs.Content, { value: "details", className: "mt-6", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3925
|
-
CampaignDetailForm,
|
|
3914
|
+
),
|
|
3915
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.FocusModal, { open: openProductModal, onOpenChange: setOpenProductModal, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.FocusModal.Content, { children: [
|
|
3916
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.FocusModal.Header, { title: "Add Product" }),
|
|
3917
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.FocusModal.Body, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3918
|
+
ProductSelector,
|
|
3926
3919
|
{
|
|
3927
|
-
|
|
3928
|
-
|
|
3920
|
+
selectedProductIds: fields.map((f2) => f2.product.id),
|
|
3921
|
+
onSelectProduct: (product) => {
|
|
3922
|
+
append({
|
|
3923
|
+
product,
|
|
3924
|
+
discountType: "percentage",
|
|
3925
|
+
discountValue: 10,
|
|
3926
|
+
limit: 10,
|
|
3927
|
+
maxQty: 1
|
|
3928
|
+
});
|
|
3929
|
+
setOpenProductModal(false);
|
|
3930
|
+
}
|
|
3929
3931
|
}
|
|
3930
3932
|
) })
|
|
3931
|
-
] })
|
|
3933
|
+
] }) })
|
|
3932
3934
|
] });
|
|
3933
3935
|
};
|
|
3934
|
-
const config$1 = adminSdk.defineRouteConfig({
|
|
3935
|
-
label: "Coupon Detail",
|
|
3936
|
-
icon: icons.Tag
|
|
3937
|
-
});
|
|
3938
|
-
const CouponCreate = () => {
|
|
3939
|
-
const navigate = reactRouterDom.useNavigate();
|
|
3940
|
-
const handleSubmit = async (formData) => {
|
|
3941
|
-
var _a, _b;
|
|
3942
|
-
try {
|
|
3943
|
-
await axios__default.default.post("/admin/coupons", {
|
|
3944
|
-
...formData,
|
|
3945
|
-
starts_at: new Date(formData.starts_at).toUTCString(),
|
|
3946
|
-
ends_at: new Date(formData.ends_at).toUTCString(),
|
|
3947
|
-
currency_code: formData.discount_type === "fixed" ? formData.currency_code : void 0
|
|
3948
|
-
});
|
|
3949
|
-
ui.toast.success("Coupon created successfully");
|
|
3950
|
-
navigate("/coupons");
|
|
3951
|
-
} catch (error) {
|
|
3952
|
-
let message = "Failed to create coupon";
|
|
3953
|
-
if (axios__default.default.isAxiosError(error)) {
|
|
3954
|
-
message = ((_b = (_a = error.response) == null ? void 0 : _a.data) == null ? void 0 : _b.message) ?? message;
|
|
3955
|
-
} else if (error instanceof Error) {
|
|
3956
|
-
message = error.message;
|
|
3957
|
-
}
|
|
3958
|
-
ui.toast.error("Failed to create coupon", {
|
|
3959
|
-
description: message
|
|
3960
|
-
});
|
|
3961
|
-
}
|
|
3962
|
-
};
|
|
3963
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3964
|
-
CouponForm,
|
|
3965
|
-
{
|
|
3966
|
-
onSubmit: handleSubmit,
|
|
3967
|
-
onCancel: () => navigate("/coupons")
|
|
3968
|
-
}
|
|
3969
|
-
);
|
|
3970
|
-
};
|
|
3971
3936
|
const useFlashSaleById = (id) => {
|
|
3972
3937
|
const [data, setData] = React.useState(null);
|
|
3973
3938
|
const [isLoading, setIsLoading] = React.useState(true);
|
|
@@ -4086,6 +4051,41 @@ const config = adminSdk.defineRouteConfig({
|
|
|
4086
4051
|
label: "Flash Sale Detail",
|
|
4087
4052
|
icon: icons.Sparkles
|
|
4088
4053
|
});
|
|
4054
|
+
const FlashSaleCreate = () => {
|
|
4055
|
+
const navigate = reactRouterDom.useNavigate();
|
|
4056
|
+
async function handleSubmit(campaignData) {
|
|
4057
|
+
var _a;
|
|
4058
|
+
try {
|
|
4059
|
+
await axios__default.default.post("/admin/flash-sales", {
|
|
4060
|
+
...campaignData,
|
|
4061
|
+
starts_at: new Date(campaignData.starts_at).toUTCString(),
|
|
4062
|
+
ends_at: new Date(campaignData.ends_at).toUTCString()
|
|
4063
|
+
});
|
|
4064
|
+
ui.toast.success("Flash sale created successfully");
|
|
4065
|
+
navigate("/flash-sales");
|
|
4066
|
+
} catch (error) {
|
|
4067
|
+
let message;
|
|
4068
|
+
if (axios__default.default.isAxiosError(error)) {
|
|
4069
|
+
console.log(error);
|
|
4070
|
+
message = (_a = error.response) == null ? void 0 : _a.data.message;
|
|
4071
|
+
} else if (error instanceof Error) {
|
|
4072
|
+
message = error.message;
|
|
4073
|
+
} else {
|
|
4074
|
+
message = "Failed to create flash sale";
|
|
4075
|
+
}
|
|
4076
|
+
ui.toast.error("Failed to create flash sale", {
|
|
4077
|
+
description: message
|
|
4078
|
+
});
|
|
4079
|
+
}
|
|
4080
|
+
}
|
|
4081
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
4082
|
+
FlashSaleForm,
|
|
4083
|
+
{
|
|
4084
|
+
onSubmit: handleSubmit,
|
|
4085
|
+
onCancel: () => navigate("/flash-sales")
|
|
4086
|
+
}
|
|
4087
|
+
);
|
|
4088
|
+
};
|
|
4089
4089
|
const widgetModule = { widgets: [
|
|
4090
4090
|
{
|
|
4091
4091
|
Component: CampaignDetailWidget,
|
|
@@ -4094,17 +4094,13 @@ const widgetModule = { widgets: [
|
|
|
4094
4094
|
] };
|
|
4095
4095
|
const routeModule = {
|
|
4096
4096
|
routes: [
|
|
4097
|
-
{
|
|
4098
|
-
Component: FlashSale,
|
|
4099
|
-
path: "/flash-sales"
|
|
4100
|
-
},
|
|
4101
4097
|
{
|
|
4102
4098
|
Component: Coupons,
|
|
4103
4099
|
path: "/coupons"
|
|
4104
4100
|
},
|
|
4105
4101
|
{
|
|
4106
|
-
Component:
|
|
4107
|
-
path: "/flash-sales
|
|
4102
|
+
Component: FlashSale,
|
|
4103
|
+
path: "/flash-sales"
|
|
4108
4104
|
},
|
|
4109
4105
|
{
|
|
4110
4106
|
Component: CouponDetail,
|
|
@@ -4117,20 +4113,24 @@ const routeModule = {
|
|
|
4117
4113
|
{
|
|
4118
4114
|
Component: FlashSaleDetail,
|
|
4119
4115
|
path: "/flash-sales/:id"
|
|
4116
|
+
},
|
|
4117
|
+
{
|
|
4118
|
+
Component: FlashSaleCreate,
|
|
4119
|
+
path: "/flash-sales/create"
|
|
4120
4120
|
}
|
|
4121
4121
|
]
|
|
4122
4122
|
};
|
|
4123
4123
|
const menuItemModule = {
|
|
4124
4124
|
menuItems: [
|
|
4125
4125
|
{
|
|
4126
|
-
label: config$
|
|
4127
|
-
icon: config$
|
|
4126
|
+
label: config$3.label,
|
|
4127
|
+
icon: config$3.icon,
|
|
4128
4128
|
path: "/coupons",
|
|
4129
4129
|
nested: void 0
|
|
4130
4130
|
},
|
|
4131
4131
|
{
|
|
4132
|
-
label: config$
|
|
4133
|
-
icon: config$
|
|
4132
|
+
label: config$2.label,
|
|
4133
|
+
icon: config$2.icon,
|
|
4134
4134
|
path: "/flash-sales",
|
|
4135
4135
|
nested: void 0
|
|
4136
4136
|
},
|