@lodashventure/medusa-campaign 1.4.16 → 1.4.17

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.
@@ -0,0 +1,2281 @@
1
+ import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
+ import { defineWidgetConfig, defineRouteConfig } from "@medusajs/admin-sdk";
3
+ import { Container, Heading, Text, Alert, Badge, Button, createDataTableColumnHelper, useDataTable, DataTable, Label, Input, Textarea, Select, toast, Tabs, clx, Table, FocusModal } from "@medusajs/ui";
4
+ import { Sparkles, PhotoSolid, PencilSquare, Eye, Tag, InformationCircle, CommandLine, X, CloudArrowUp, Trash } from "@medusajs/icons";
5
+ import { useState, useEffect, useMemo, useCallback } from "react";
6
+ import { useNavigate, useParams } from "react-router-dom";
7
+ import dayjs from "dayjs";
8
+ import axios from "axios";
9
+ import { zodResolver } from "@hookform/resolvers/zod";
10
+ import { useForm, Controller, useFieldArray } from "react-hook-form";
11
+ import z from "zod";
12
+ import { debounce } from "lodash";
13
+ import Medusa from "@medusajs/js-sdk";
14
+ import "@medusajs/admin-shared";
15
+ const CampaignDetailWidget = ({
16
+ data: promotion
17
+ }) => {
18
+ const navigate = useNavigate();
19
+ const [campaign, setCampaign] = useState(null);
20
+ const [campaignDetail, setCampaignDetail] = useState(
21
+ null
22
+ );
23
+ const [loading, setLoading] = useState(true);
24
+ const [error, setError] = useState(null);
25
+ useEffect(() => {
26
+ if (promotion == null ? void 0 : promotion.campaign_id) {
27
+ fetchCampaignAndDetail();
28
+ } else {
29
+ setLoading(false);
30
+ }
31
+ }, [promotion == null ? void 0 : promotion.campaign_id]);
32
+ const fetchCampaignAndDetail = async () => {
33
+ if (!(promotion == null ? void 0 : promotion.campaign_id)) return;
34
+ setLoading(true);
35
+ setError(null);
36
+ try {
37
+ const campaignResponse = await fetch(
38
+ `/admin/flash-sales/${promotion.campaign_id}`,
39
+ {
40
+ credentials: "include"
41
+ }
42
+ );
43
+ if (campaignResponse.ok) {
44
+ const campaignData = await campaignResponse.json();
45
+ setCampaign(campaignData);
46
+ if (campaignData.campaign_detail) {
47
+ setCampaignDetail(campaignData.campaign_detail);
48
+ }
49
+ }
50
+ } catch (err) {
51
+ console.error("Error fetching campaign:", err);
52
+ setError("Failed to load campaign information");
53
+ } finally {
54
+ setLoading(false);
55
+ }
56
+ };
57
+ const handleEditCampaign = () => {
58
+ if (campaign == null ? void 0 : campaign.id) {
59
+ navigate(`/flash-sales/${campaign.id}`);
60
+ }
61
+ };
62
+ const handleViewCampaign = () => {
63
+ if (campaign == null ? void 0 : campaign.id) {
64
+ navigate(`/flash-sales/${campaign.id}#details`);
65
+ }
66
+ };
67
+ if (!(promotion == null ? void 0 : promotion.campaign_id)) {
68
+ return null;
69
+ }
70
+ if (loading) {
71
+ return /* @__PURE__ */ jsx(Container, { className: "divide-y px-0 pb-0 pt-0", children: /* @__PURE__ */ jsxs("div", { className: "px-6 py-6", children: [
72
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 mb-4", children: [
73
+ /* @__PURE__ */ jsx(Sparkles, { className: "text-ui-fg-subtle" }),
74
+ /* @__PURE__ */ jsx(Heading, { level: "h2", children: "Campaign" })
75
+ ] }),
76
+ /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-subtle", children: "Loading campaign..." })
77
+ ] }) });
78
+ }
79
+ if (error) {
80
+ return /* @__PURE__ */ jsx(Container, { className: "divide-y px-0 pb-0 pt-0", children: /* @__PURE__ */ jsxs("div", { className: "px-6 py-6", children: [
81
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 mb-4", children: [
82
+ /* @__PURE__ */ jsx(Sparkles, { className: "text-ui-fg-subtle" }),
83
+ /* @__PURE__ */ jsx(Heading, { level: "h2", children: "Campaign" })
84
+ ] }),
85
+ /* @__PURE__ */ jsx(Alert, { variant: "error", children: error })
86
+ ] }) });
87
+ }
88
+ if (!campaign) {
89
+ return /* @__PURE__ */ jsx(Container, { className: "divide-y px-0 pb-0 pt-0", children: /* @__PURE__ */ jsxs("div", { className: "px-6 py-6", children: [
90
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 mb-4", children: [
91
+ /* @__PURE__ */ jsx(Sparkles, { className: "text-ui-fg-subtle" }),
92
+ /* @__PURE__ */ jsx(Heading, { level: "h2", children: "Campaign" })
93
+ ] }),
94
+ /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-subtle", children: "This promotion is not part of a campaign" })
95
+ ] }) });
96
+ }
97
+ const hasCampaignDetail = !!campaignDetail;
98
+ const hasImages = (campaignDetail == null ? void 0 : campaignDetail.image_url) || (campaignDetail == null ? void 0 : campaignDetail.thumbnail_url);
99
+ const hasContent = (campaignDetail == null ? void 0 : campaignDetail.detail_content) || (campaignDetail == null ? void 0 : campaignDetail.terms_and_conditions);
100
+ const hasSEO = (campaignDetail == null ? void 0 : campaignDetail.meta_title) || (campaignDetail == null ? void 0 : campaignDetail.meta_description);
101
+ return /* @__PURE__ */ jsx(Container, { className: "divide-y px-0 pb-0 pt-0", children: /* @__PURE__ */ jsxs("div", { className: "px-6 py-6", children: [
102
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 mb-4", children: [
103
+ /* @__PURE__ */ jsx(Sparkles, { className: "text-ui-fg-subtle" }),
104
+ /* @__PURE__ */ jsx(Heading, { level: "h2", children: "Campaign" })
105
+ ] }),
106
+ /* @__PURE__ */ jsxs("div", { className: "mb-4 p-4 rounded-lg border bg-ui-bg-subtle", children: [
107
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between mb-3", children: [
108
+ /* @__PURE__ */ jsxs("div", { className: "flex-1", children: [
109
+ /* @__PURE__ */ jsx(Text, { className: "font-semibold text-lg mb-1", children: campaign.name }),
110
+ campaign.description && /* @__PURE__ */ jsx(Text, { className: "text-sm text-ui-fg-subtle line-clamp-2", children: campaign.description })
111
+ ] }),
112
+ /* @__PURE__ */ jsxs(Badge, { color: "purple", size: "small", children: [
113
+ /* @__PURE__ */ jsx(Sparkles, { className: "mr-1" }),
114
+ "Campaign"
115
+ ] })
116
+ ] }),
117
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-2 mt-3 pt-3 border-t", children: [
118
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
119
+ /* @__PURE__ */ jsx(
120
+ PhotoSolid,
121
+ {
122
+ className: `h-4 w-4 ${hasImages ? "text-green-500" : "text-ui-fg-muted"}`
123
+ }
124
+ ),
125
+ /* @__PURE__ */ jsx(Text, { className: "text-xs", children: hasImages ? "Images added" : "No images" })
126
+ ] }),
127
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
128
+ /* @__PURE__ */ jsx(
129
+ PencilSquare,
130
+ {
131
+ className: `h-4 w-4 ${hasContent ? "text-green-500" : "text-ui-fg-muted"}`
132
+ }
133
+ ),
134
+ /* @__PURE__ */ jsx(Text, { className: "text-xs", children: hasContent ? "Content added" : "No content" })
135
+ ] })
136
+ ] })
137
+ ] }),
138
+ hasCampaignDetail && /* @__PURE__ */ jsxs("div", { className: "space-y-3 mb-4", children: [
139
+ campaignDetail.thumbnail_url && /* @__PURE__ */ jsxs("div", { children: [
140
+ /* @__PURE__ */ jsx(Text, { className: "text-xs font-medium text-ui-fg-subtle mb-2", children: "Thumbnail" }),
141
+ /* @__PURE__ */ jsx(
142
+ "img",
143
+ {
144
+ src: campaignDetail.thumbnail_url,
145
+ alt: "Campaign thumbnail",
146
+ className: "w-full h-32 object-cover rounded-lg border"
147
+ }
148
+ )
149
+ ] }),
150
+ campaignDetail.detail_content && /* @__PURE__ */ jsxs("div", { children: [
151
+ /* @__PURE__ */ jsx(Text, { className: "text-xs font-medium text-ui-fg-subtle mb-1", children: "Content Preview" }),
152
+ /* @__PURE__ */ jsxs(Text, { className: "text-sm line-clamp-3 text-ui-fg-muted", children: [
153
+ campaignDetail.detail_content.substring(0, 150),
154
+ "..."
155
+ ] })
156
+ ] }),
157
+ campaignDetail.link_url && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-xs", children: [
158
+ /* @__PURE__ */ jsx(Badge, { size: "xsmall", color: "blue", children: "CTA Link" }),
159
+ /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-subtle truncate", children: campaignDetail.link_text || campaignDetail.link_url })
160
+ ] }),
161
+ hasSEO && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-xs", children: [
162
+ /* @__PURE__ */ jsx(Badge, { size: "xsmall", color: "green", children: "SEO" }),
163
+ /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-subtle", children: "Meta fields configured" })
164
+ ] })
165
+ ] }),
166
+ !hasCampaignDetail && /* @__PURE__ */ jsx(Alert, { variant: "info", className: "mb-4", children: /* @__PURE__ */ jsx(Text, { className: "text-sm", children: "No campaign details added yet. Add images, content, and SEO to enhance this campaign." }) }),
167
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
168
+ /* @__PURE__ */ jsxs(Button, { variant: "secondary", size: "small", onClick: handleEditCampaign, children: [
169
+ /* @__PURE__ */ jsx(PencilSquare, { className: "mr-1" }),
170
+ "Edit Campaign"
171
+ ] }),
172
+ hasCampaignDetail && /* @__PURE__ */ jsxs(
173
+ Button,
174
+ {
175
+ variant: "secondary",
176
+ size: "small",
177
+ onClick: handleViewCampaign,
178
+ children: [
179
+ /* @__PURE__ */ jsx(Eye, { className: "mr-1" }),
180
+ "View Details"
181
+ ]
182
+ }
183
+ )
184
+ ] }),
185
+ campaignDetail && /* @__PURE__ */ jsxs("div", { className: "mt-4 pt-4 border-t grid grid-cols-2 gap-3 text-xs", children: [
186
+ /* @__PURE__ */ jsxs("div", { children: [
187
+ /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-subtle", children: "Display Order" }),
188
+ /* @__PURE__ */ jsx(Text, { className: "font-medium", children: campaignDetail.display_order })
189
+ ] }),
190
+ /* @__PURE__ */ jsxs("div", { children: [
191
+ /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-subtle", children: "Status" }),
192
+ /* @__PURE__ */ jsx(Badge, { color: hasCampaignDetail ? "green" : "grey", size: "xsmall", children: hasCampaignDetail ? "Complete" : "Incomplete" })
193
+ ] })
194
+ ] })
195
+ ] }) });
196
+ };
197
+ defineWidgetConfig({
198
+ zone: "promotion.details.side.after"
199
+ });
200
+ const useCoupons = ({ limit, offset }) => {
201
+ const [data, setData] = useState(null);
202
+ const [isLoading, setIsLoading] = useState(true);
203
+ const [error, setError] = useState(null);
204
+ const fetchCoupons = async () => {
205
+ setIsLoading(true);
206
+ setError(null);
207
+ try {
208
+ const response = await axios.get("/admin/coupons", {
209
+ params: { limit, offset }
210
+ });
211
+ setData(response.data);
212
+ } catch (err) {
213
+ setError(err instanceof Error ? err : new Error("Failed to fetch coupons"));
214
+ } finally {
215
+ setIsLoading(false);
216
+ }
217
+ };
218
+ useEffect(() => {
219
+ fetchCoupons();
220
+ }, [limit, offset]);
221
+ return {
222
+ data,
223
+ isLoading,
224
+ error,
225
+ refetch: fetchCoupons
226
+ };
227
+ };
228
+ const columnHelper$1 = createDataTableColumnHelper();
229
+ const useCouponColumns = () => useMemo(
230
+ () => [
231
+ columnHelper$1.accessor("name", {
232
+ header: "Name",
233
+ cell: (info) => info.getValue()
234
+ }),
235
+ columnHelper$1.display({
236
+ id: "code",
237
+ header: "Code",
238
+ cell: (info) => {
239
+ var _a, _b;
240
+ return ((_b = (_a = info.row.original.promotions) == null ? void 0 : _a[0]) == null ? void 0 : _b.code) ?? "-";
241
+ }
242
+ }),
243
+ columnHelper$1.display({
244
+ id: "status",
245
+ header: "Status",
246
+ cell: (info) => {
247
+ const endsAt = info.row.original.ends_at;
248
+ if (!endsAt) {
249
+ return "";
250
+ }
251
+ const isActive = dayjs(endsAt).isAfter(dayjs());
252
+ return /* @__PURE__ */ jsx(Badge, { color: isActive ? "green" : "grey", children: isActive ? "Active" : "Inactive" });
253
+ }
254
+ }),
255
+ columnHelper$1.accessor("starts_at", {
256
+ header: "Start Date",
257
+ cell: (info) => info.getValue() ? dayjs(info.getValue()).format("YYYY-MM-DD HH:mm") : ""
258
+ }),
259
+ columnHelper$1.accessor("ends_at", {
260
+ header: "End Date",
261
+ cell: (info) => info.getValue() ? dayjs(info.getValue()).format("YYYY-MM-DD HH:mm") : ""
262
+ })
263
+ ],
264
+ []
265
+ );
266
+ const CouponPage = () => {
267
+ const navigate = useNavigate();
268
+ const limit = 20;
269
+ const [pagination, setPagination] = useState({
270
+ pageSize: limit,
271
+ pageIndex: 0
272
+ });
273
+ const offset = pagination.pageIndex * limit;
274
+ const { data } = useCoupons({ limit, offset });
275
+ const columns2 = useCouponColumns();
276
+ const table = useDataTable({
277
+ data: (data == null ? void 0 : data.campaigns) ?? [],
278
+ columns: columns2,
279
+ getRowId: (row) => row.id,
280
+ pagination: {
281
+ state: pagination,
282
+ onPaginationChange: setPagination
283
+ },
284
+ rowCount: (data == null ? void 0 : data.count) ?? 0,
285
+ onRowClick: (_, row) => {
286
+ navigate(`/coupons/${row.id}`);
287
+ }
288
+ });
289
+ return /* @__PURE__ */ jsxs(Container, { children: [
290
+ /* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center", children: [
291
+ /* @__PURE__ */ jsx("h1", { className: "text-xl font-semibold", children: "Coupons" }),
292
+ /* @__PURE__ */ jsx(Button, { onClick: () => navigate("/coupons/create"), children: "Create Coupon" })
293
+ ] }),
294
+ /* @__PURE__ */ jsxs(DataTable, { instance: table, children: [
295
+ /* @__PURE__ */ jsx(DataTable.Table, {}),
296
+ /* @__PURE__ */ jsx(DataTable.Pagination, {})
297
+ ] })
298
+ ] });
299
+ };
300
+ const Coupons = () => {
301
+ return /* @__PURE__ */ jsx(CouponPage, {});
302
+ };
303
+ const config$3 = defineRouteConfig({
304
+ label: "Coupons",
305
+ icon: Tag
306
+ });
307
+ const useFlashSales = (pagination) => {
308
+ const [data, setData] = useState(null);
309
+ const [isLoading, setIsLoading] = useState(true);
310
+ const [error, setError] = useState(null);
311
+ const fetchFlashSales = async () => {
312
+ setIsLoading(true);
313
+ setError(null);
314
+ try {
315
+ const response = await axios.get("/admin/flash-sales", {
316
+ params: pagination
317
+ });
318
+ setData(response.data);
319
+ } catch (err) {
320
+ setError(
321
+ err instanceof Error ? err : new Error("Failed to fetch flash sales")
322
+ );
323
+ } finally {
324
+ setIsLoading(false);
325
+ }
326
+ };
327
+ useEffect(() => {
328
+ fetchFlashSales();
329
+ }, [pagination.limit, pagination.offset]);
330
+ return {
331
+ data,
332
+ isLoading,
333
+ error,
334
+ refetch: fetchFlashSales
335
+ };
336
+ };
337
+ const FlashSalePage = () => {
338
+ const navigate = useNavigate();
339
+ const limit = 20;
340
+ const [pagination, setPagination] = useState({
341
+ pageSize: limit,
342
+ pageIndex: 0
343
+ });
344
+ const offset = useMemo(() => {
345
+ return pagination.pageIndex * limit;
346
+ }, [pagination]);
347
+ const columnHelper2 = createDataTableColumnHelper();
348
+ const columns2 = [
349
+ columnHelper2.accessor("name", {
350
+ header: "Name",
351
+ cell: (info) => info.getValue()
352
+ }),
353
+ columnHelper2.accessor("ends_at", {
354
+ header: "Status",
355
+ cell: (info) => {
356
+ const date = info.getValue();
357
+ if (!date) {
358
+ return "";
359
+ }
360
+ const isActive = dayjs(date).isAfter(dayjs());
361
+ return /* @__PURE__ */ jsx(Badge, { color: isActive ? "green" : "grey", children: isActive ? "Active" : "Inactive" });
362
+ }
363
+ }),
364
+ columnHelper2.accessor("starts_at", {
365
+ header: "Start Date",
366
+ cell: (info) => {
367
+ const date = info.getValue();
368
+ if (!date) {
369
+ return "";
370
+ }
371
+ return dayjs(date).format("YYYY-MM-DD HH:mm");
372
+ }
373
+ }),
374
+ columnHelper2.accessor("ends_at", {
375
+ header: "End Date",
376
+ cell: (info) => {
377
+ const date = info.getValue();
378
+ if (!date) {
379
+ return "";
380
+ }
381
+ return dayjs(date).format("YYYY-MM-DD HH:mm");
382
+ }
383
+ })
384
+ ];
385
+ const { data } = useFlashSales({ limit, offset });
386
+ const table = useDataTable({
387
+ data: (data == null ? void 0 : data.campaigns) || [],
388
+ columns: columns2,
389
+ getRowId: (campaign) => campaign.id,
390
+ pagination: {
391
+ state: pagination,
392
+ onPaginationChange: setPagination
393
+ },
394
+ rowCount: (data == null ? void 0 : data.count) || 0,
395
+ onRowClick: (_, row) => {
396
+ navigate(`/flash-sales/${row.id}`);
397
+ }
398
+ });
399
+ return /* @__PURE__ */ jsxs(Container, { children: [
400
+ /* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center", children: [
401
+ /* @__PURE__ */ jsx("h1", { className: "text-xl font-semibold", children: "Campaigns" }),
402
+ /* @__PURE__ */ jsx(Button, { onClick: () => navigate("/flash-sales/create"), children: "Create Campaign" })
403
+ ] }),
404
+ /* @__PURE__ */ jsxs(DataTable, { instance: table, children: [
405
+ /* @__PURE__ */ jsx(DataTable.Table, {}),
406
+ /* @__PURE__ */ jsx(DataTable.Pagination, {})
407
+ ] })
408
+ ] });
409
+ };
410
+ const FlashSale = () => {
411
+ return /* @__PURE__ */ jsx(FlashSalePage, {});
412
+ };
413
+ const config$2 = defineRouteConfig({
414
+ label: "Flash Sale",
415
+ icon: Sparkles
416
+ });
417
+ const couponSchema = z.object({
418
+ name: z.string().min(1, "Name is required"),
419
+ description: z.string().min(1, "Description is required"),
420
+ type: z.literal("coupon"),
421
+ code: z.string().min(1, "Coupon code is required"),
422
+ discount_type: z.enum(["percentage", "fixed"]),
423
+ discount_value: z.number().positive("Discount must be positive"),
424
+ currency_code: z.string().optional(),
425
+ starts_at: z.string().min(1, "Start date is required"),
426
+ ends_at: z.string().min(1, "End date is required"),
427
+ allocation: z.enum(["each", "total"]).optional(),
428
+ target_type: z.enum(["order", "items"]).optional()
429
+ }).superRefine((data, ctx) => {
430
+ if (new Date(data.ends_at) < new Date(data.starts_at)) {
431
+ ctx.addIssue({
432
+ code: z.ZodIssueCode.custom,
433
+ path: ["ends_at"],
434
+ message: "End date must be after start date"
435
+ });
436
+ }
437
+ if (data.discount_type === "fixed" && !data.currency_code) {
438
+ ctx.addIssue({
439
+ code: z.ZodIssueCode.custom,
440
+ path: ["currency_code"],
441
+ message: "Currency is required for fixed discount"
442
+ });
443
+ }
444
+ });
445
+ const CouponForm = ({
446
+ initialData,
447
+ onSubmit,
448
+ onCancel,
449
+ disabled = false
450
+ }) => {
451
+ const {
452
+ register,
453
+ control,
454
+ handleSubmit,
455
+ watch,
456
+ setValue,
457
+ formState: { errors }
458
+ } = useForm({
459
+ resolver: zodResolver(couponSchema),
460
+ defaultValues: {
461
+ name: (initialData == null ? void 0 : initialData.name) ?? "",
462
+ description: (initialData == null ? void 0 : initialData.description) ?? "",
463
+ type: "coupon",
464
+ code: (initialData == null ? void 0 : initialData.code) ?? "",
465
+ discount_type: (initialData == null ? void 0 : initialData.discount_type) ?? "percentage",
466
+ discount_value: (initialData == null ? void 0 : initialData.discount_value) ?? 0,
467
+ currency_code: (initialData == null ? void 0 : initialData.currency_code) ?? "",
468
+ starts_at: (initialData == null ? void 0 : initialData.starts_at) ?? dayjs().startOf("day").format("YYYY-MM-DDTHH:mm"),
469
+ ends_at: (initialData == null ? void 0 : initialData.ends_at) ?? dayjs().endOf("day").format("YYYY-MM-DDTHH:mm"),
470
+ allocation: (initialData == null ? void 0 : initialData.allocation) ?? "total",
471
+ target_type: (initialData == null ? void 0 : initialData.target_type) ?? "order"
472
+ }
473
+ });
474
+ const discountType = watch("discount_type");
475
+ const startsAt = watch("starts_at");
476
+ const endsAt = watch("ends_at");
477
+ const handleDateTimeChange = (field, type, value) => {
478
+ const currentValue = watch(field);
479
+ if (type === "date") {
480
+ const time = currentValue ? dayjs(currentValue).format("HH:mm") : field === "starts_at" ? "00:00" : "23:59";
481
+ setValue(field, `${value}T${time}`);
482
+ } else {
483
+ const date = currentValue ? dayjs(currentValue).format("YYYY-MM-DD") : dayjs().format("YYYY-MM-DD");
484
+ setValue(field, `${date}T${value}`);
485
+ }
486
+ };
487
+ const onFormSubmit = (data) => {
488
+ onSubmit(data);
489
+ };
490
+ const onFormError = () => {
491
+ const errorMessages = Object.values(errors).map((err) => err == null ? void 0 : err.message).filter(Boolean).join(", ");
492
+ toast.error("Invalid coupon data", {
493
+ description: errorMessages
494
+ });
495
+ };
496
+ return /* @__PURE__ */ jsxs(Container, { children: [
497
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
498
+ /* @__PURE__ */ jsx("h1", { className: "text-xl font-semibold", children: "Coupon" }),
499
+ /* @__PURE__ */ jsx(Button, { variant: "transparent", onClick: onCancel, children: "Cancel" })
500
+ ] }),
501
+ /* @__PURE__ */ jsxs(
502
+ "form",
503
+ {
504
+ onSubmit: handleSubmit(onFormSubmit, onFormError),
505
+ className: "space-y-6 my-8",
506
+ children: [
507
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
508
+ /* @__PURE__ */ jsx(Label, { children: "Name" }),
509
+ /* @__PURE__ */ jsx(Input, { ...register("name"), disabled }),
510
+ errors.name && /* @__PURE__ */ jsx("p", { className: "text-red-500 text-sm mt-1", children: errors.name.message })
511
+ ] }),
512
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
513
+ /* @__PURE__ */ jsx(Label, { children: "Description" }),
514
+ /* @__PURE__ */ jsx(Textarea, { ...register("description"), disabled }),
515
+ errors.description && /* @__PURE__ */ jsx("p", { className: "text-red-500 text-sm mt-1", children: errors.description.message })
516
+ ] }),
517
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
518
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
519
+ /* @__PURE__ */ jsx(Label, { children: "Start Date" }),
520
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
521
+ /* @__PURE__ */ jsx(
522
+ Input,
523
+ {
524
+ type: "date",
525
+ value: startsAt ? dayjs(startsAt).format("YYYY-MM-DD") : "",
526
+ onChange: (e) => handleDateTimeChange("starts_at", "date", e.target.value),
527
+ disabled,
528
+ className: "flex-1"
529
+ }
530
+ ),
531
+ /* @__PURE__ */ jsx(
532
+ Input,
533
+ {
534
+ type: "time",
535
+ value: startsAt ? dayjs(startsAt).format("HH:mm") : "",
536
+ onChange: (e) => handleDateTimeChange("starts_at", "time", e.target.value),
537
+ disabled,
538
+ className: "flex-1"
539
+ }
540
+ )
541
+ ] }),
542
+ errors.starts_at && /* @__PURE__ */ jsx("p", { className: "text-red-500 text-sm mt-1", children: errors.starts_at.message })
543
+ ] }),
544
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
545
+ /* @__PURE__ */ jsx(Label, { children: "End Date" }),
546
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
547
+ /* @__PURE__ */ jsx(
548
+ Input,
549
+ {
550
+ type: "date",
551
+ value: endsAt ? dayjs(endsAt).format("YYYY-MM-DD") : "",
552
+ onChange: (e) => handleDateTimeChange("ends_at", "date", e.target.value),
553
+ disabled,
554
+ className: "flex-1"
555
+ }
556
+ ),
557
+ /* @__PURE__ */ jsx(
558
+ Input,
559
+ {
560
+ type: "time",
561
+ value: endsAt ? dayjs(endsAt).format("HH:mm") : "",
562
+ onChange: (e) => handleDateTimeChange("ends_at", "time", e.target.value),
563
+ disabled,
564
+ className: "flex-1"
565
+ }
566
+ )
567
+ ] }),
568
+ errors.ends_at && /* @__PURE__ */ jsx("p", { className: "text-red-500 text-sm mt-1", children: errors.ends_at.message })
569
+ ] })
570
+ ] }),
571
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
572
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
573
+ /* @__PURE__ */ jsx(Label, { children: "Coupon Code" }),
574
+ /* @__PURE__ */ jsx(Input, { ...register("code"), disabled }),
575
+ errors.code && /* @__PURE__ */ jsx("p", { className: "text-red-500 text-sm mt-1", children: errors.code.message })
576
+ ] }),
577
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
578
+ /* @__PURE__ */ jsx(Label, { children: "Discount Type" }),
579
+ /* @__PURE__ */ jsx(
580
+ Controller,
581
+ {
582
+ name: "discount_type",
583
+ control,
584
+ render: ({ field }) => /* @__PURE__ */ jsxs(
585
+ Select,
586
+ {
587
+ value: field.value,
588
+ onValueChange: field.onChange,
589
+ disabled,
590
+ children: [
591
+ /* @__PURE__ */ jsx(Select.Trigger, { children: /* @__PURE__ */ jsx(Select.Value, { placeholder: "Select discount type" }) }),
592
+ /* @__PURE__ */ jsxs(Select.Content, { children: [
593
+ /* @__PURE__ */ jsx(Select.Item, { value: "percentage", children: "Percentage" }),
594
+ /* @__PURE__ */ jsx(Select.Item, { value: "fixed", children: "Fixed Amount" })
595
+ ] })
596
+ ]
597
+ }
598
+ )
599
+ }
600
+ )
601
+ ] })
602
+ ] }),
603
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
604
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
605
+ /* @__PURE__ */ jsx(Label, { children: "Discount Value" }),
606
+ /* @__PURE__ */ jsx(
607
+ Controller,
608
+ {
609
+ name: "discount_value",
610
+ control,
611
+ render: ({ field }) => /* @__PURE__ */ jsx(
612
+ Input,
613
+ {
614
+ type: "number",
615
+ value: field.value,
616
+ min: 0,
617
+ step: field.value % 1 === 0 ? 1 : 0.01,
618
+ onChange: (e) => field.onChange(Number(e.target.value)),
619
+ disabled
620
+ }
621
+ )
622
+ }
623
+ ),
624
+ errors.discount_value && /* @__PURE__ */ jsx("p", { className: "text-red-500 text-sm mt-1", children: errors.discount_value.message })
625
+ ] }),
626
+ discountType === "fixed" && /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
627
+ /* @__PURE__ */ jsx(Label, { children: "Currency Code" }),
628
+ /* @__PURE__ */ jsx(Input, { ...register("currency_code"), disabled }),
629
+ errors.currency_code && /* @__PURE__ */ jsx("p", { className: "text-red-500 text-sm mt-1", children: errors.currency_code.message })
630
+ ] })
631
+ ] }),
632
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
633
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
634
+ /* @__PURE__ */ jsx(Label, { children: "Allocation" }),
635
+ /* @__PURE__ */ jsx(
636
+ Controller,
637
+ {
638
+ name: "allocation",
639
+ control,
640
+ render: ({ field }) => /* @__PURE__ */ jsxs(
641
+ Select,
642
+ {
643
+ value: field.value,
644
+ onValueChange: field.onChange,
645
+ disabled,
646
+ children: [
647
+ /* @__PURE__ */ jsx(Select.Trigger, { children: /* @__PURE__ */ jsx(Select.Value, { placeholder: "Select allocation" }) }),
648
+ /* @__PURE__ */ jsxs(Select.Content, { children: [
649
+ /* @__PURE__ */ jsx(Select.Item, { value: "total", children: "Order Total" }),
650
+ /* @__PURE__ */ jsx(Select.Item, { value: "each", children: "Each Item" })
651
+ ] })
652
+ ]
653
+ }
654
+ )
655
+ }
656
+ )
657
+ ] }),
658
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
659
+ /* @__PURE__ */ jsx(Label, { children: "Target Type" }),
660
+ /* @__PURE__ */ jsx(
661
+ Controller,
662
+ {
663
+ name: "target_type",
664
+ control,
665
+ render: ({ field }) => /* @__PURE__ */ jsxs(
666
+ Select,
667
+ {
668
+ value: field.value,
669
+ onValueChange: field.onChange,
670
+ disabled,
671
+ children: [
672
+ /* @__PURE__ */ jsx(Select.Trigger, { children: /* @__PURE__ */ jsx(Select.Value, { placeholder: "Select target" }) }),
673
+ /* @__PURE__ */ jsxs(Select.Content, { children: [
674
+ /* @__PURE__ */ jsx(Select.Item, { value: "order", children: "Order" }),
675
+ /* @__PURE__ */ jsx(Select.Item, { value: "items", children: "Items" })
676
+ ] })
677
+ ]
678
+ }
679
+ )
680
+ }
681
+ )
682
+ ] })
683
+ ] }),
684
+ /* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-2", children: [
685
+ /* @__PURE__ */ jsx(Button, { variant: "secondary", type: "button", onClick: onCancel, children: "Cancel" }),
686
+ /* @__PURE__ */ jsx(Button, { type: "submit", disabled, children: "Save Coupon" })
687
+ ] })
688
+ ]
689
+ }
690
+ )
691
+ ] });
692
+ };
693
+ const useCouponById = (id) => {
694
+ const [data, setData] = useState(null);
695
+ const [isLoading, setIsLoading] = useState(true);
696
+ const [error, setError] = useState(null);
697
+ const fetchCoupon = async () => {
698
+ if (!id) {
699
+ return;
700
+ }
701
+ setIsLoading(true);
702
+ setError(null);
703
+ try {
704
+ const response = await axios.get(`/admin/coupons/${id}`);
705
+ setData(response.data);
706
+ } catch (err) {
707
+ setError(err instanceof Error ? err : new Error("Failed to fetch coupon"));
708
+ } finally {
709
+ setIsLoading(false);
710
+ }
711
+ };
712
+ useEffect(() => {
713
+ fetchCoupon();
714
+ }, [id]);
715
+ return {
716
+ data,
717
+ isLoading,
718
+ error,
719
+ refetch: fetchCoupon
720
+ };
721
+ };
722
+ const MarkdownEditor = ({
723
+ label,
724
+ value,
725
+ onChange,
726
+ placeholder,
727
+ helpText,
728
+ rows = 10,
729
+ showPreview = true
730
+ }) => {
731
+ const [activeTab, setActiveTab] = useState("write");
732
+ const insertMarkdown = (before, after = "") => {
733
+ const textarea = document.getElementById(
734
+ `markdown-${label}`
735
+ );
736
+ if (!textarea) return;
737
+ const start = textarea.selectionStart;
738
+ const end = textarea.selectionEnd;
739
+ const selectedText = value.substring(start, end);
740
+ const newText = value.substring(0, start) + before + selectedText + after + value.substring(end);
741
+ onChange(newText);
742
+ setTimeout(() => {
743
+ textarea.focus();
744
+ textarea.setSelectionRange(
745
+ start + before.length,
746
+ start + before.length + selectedText.length
747
+ );
748
+ }, 0);
749
+ };
750
+ const renderMarkdownPreview = (markdown) => {
751
+ let html = markdown;
752
+ html = html.replace(/^### (.*$)/gim, "<h3>$1</h3>");
753
+ html = html.replace(/^## (.*$)/gim, "<h2>$1</h2>");
754
+ html = html.replace(/^# (.*$)/gim, "<h1>$1</h1>");
755
+ html = html.replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>");
756
+ html = html.replace(/__(.+?)__/g, "<strong>$1</strong>");
757
+ html = html.replace(/\*(.+?)\*/g, "<em>$1</em>");
758
+ html = html.replace(/_(.+?)_/g, "<em>$1</em>");
759
+ html = html.replace(
760
+ /```(\w+)?\n([\s\S]+?)```/g,
761
+ '<pre class="bg-ui-bg-subtle p-4 rounded-lg overflow-x-auto"><code class="language-$1">$2</code></pre>'
762
+ );
763
+ html = html.replace(
764
+ /`([^`]+)`/g,
765
+ '<code class="bg-ui-bg-subtle px-1 py-0.5 rounded text-sm">$1</code>'
766
+ );
767
+ html = html.replace(
768
+ /\[([^\]]+)\]\(([^)]+)\)/g,
769
+ '<a href="$2" class="text-ui-fg-interactive underline">$1</a>'
770
+ );
771
+ html = html.replace(
772
+ /!\[([^\]]*)\]\(([^)]+)\)/g,
773
+ '<img src="$2" alt="$1" class="max-w-full h-auto rounded-lg my-2" />'
774
+ );
775
+ html = html.replace(/^\* (.+)$/gim, "<li>$1</li>");
776
+ html = html.replace(/^\- (.+)$/gim, "<li>$1</li>");
777
+ html = html.replace(
778
+ /(<li>.*<\/li>)/s,
779
+ "<ul class='list-disc ml-6'>$1</ul>"
780
+ );
781
+ html = html.replace(/\n\n/g, "</p><p>");
782
+ html = `<p>${html}</p>`;
783
+ return html;
784
+ };
785
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
786
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
787
+ /* @__PURE__ */ jsx(Label, { htmlFor: `markdown-${label}`, className: "font-medium", children: label }),
788
+ helpText && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 text-xs text-ui-fg-subtle", children: [
789
+ /* @__PURE__ */ jsx(InformationCircle, { className: "h-4 w-4" }),
790
+ /* @__PURE__ */ jsx("span", { children: helpText })
791
+ ] })
792
+ ] }),
793
+ showPreview ? /* @__PURE__ */ jsxs(Tabs, { value: activeTab, onValueChange: (v) => setActiveTab(v), children: [
794
+ /* @__PURE__ */ jsxs(Tabs.List, { children: [
795
+ /* @__PURE__ */ jsx(Tabs.Trigger, { value: "write", children: "Write" }),
796
+ /* @__PURE__ */ jsx(Tabs.Trigger, { value: "preview", children: "Preview" })
797
+ ] }),
798
+ /* @__PURE__ */ jsx(Tabs.Content, { value: "write", className: "mt-4", children: /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
799
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-2 border-b pb-2", children: [
800
+ /* @__PURE__ */ jsx(
801
+ Button,
802
+ {
803
+ type: "button",
804
+ variant: "secondary",
805
+ size: "small",
806
+ onClick: () => insertMarkdown("**", "**"),
807
+ title: "Bold",
808
+ children: /* @__PURE__ */ jsx("strong", { children: "B" })
809
+ }
810
+ ),
811
+ /* @__PURE__ */ jsx(
812
+ Button,
813
+ {
814
+ type: "button",
815
+ variant: "secondary",
816
+ size: "small",
817
+ onClick: () => insertMarkdown("*", "*"),
818
+ title: "Italic",
819
+ children: /* @__PURE__ */ jsx("em", { children: "I" })
820
+ }
821
+ ),
822
+ /* @__PURE__ */ jsx(
823
+ Button,
824
+ {
825
+ type: "button",
826
+ variant: "secondary",
827
+ size: "small",
828
+ onClick: () => insertMarkdown("`", "`"),
829
+ title: "Inline Code",
830
+ children: /* @__PURE__ */ jsx(CommandLine, { className: "h-4 w-4" })
831
+ }
832
+ ),
833
+ /* @__PURE__ */ jsx(
834
+ Button,
835
+ {
836
+ type: "button",
837
+ variant: "secondary",
838
+ size: "small",
839
+ onClick: () => insertMarkdown("## "),
840
+ title: "Heading",
841
+ children: "H2"
842
+ }
843
+ ),
844
+ /* @__PURE__ */ jsx(
845
+ Button,
846
+ {
847
+ type: "button",
848
+ variant: "secondary",
849
+ size: "small",
850
+ onClick: () => insertMarkdown("### "),
851
+ title: "Heading",
852
+ children: "H3"
853
+ }
854
+ ),
855
+ /* @__PURE__ */ jsx(
856
+ Button,
857
+ {
858
+ type: "button",
859
+ variant: "secondary",
860
+ size: "small",
861
+ onClick: () => insertMarkdown("[", "](url)"),
862
+ title: "Link",
863
+ children: "Link"
864
+ }
865
+ ),
866
+ /* @__PURE__ */ jsx(
867
+ Button,
868
+ {
869
+ type: "button",
870
+ variant: "secondary",
871
+ size: "small",
872
+ onClick: () => insertMarkdown("![alt](", ")"),
873
+ title: "Image",
874
+ children: "Image"
875
+ }
876
+ ),
877
+ /* @__PURE__ */ jsx(
878
+ Button,
879
+ {
880
+ type: "button",
881
+ variant: "secondary",
882
+ size: "small",
883
+ onClick: () => insertMarkdown("```\n", "\n```"),
884
+ title: "Code Block",
885
+ children: "Code"
886
+ }
887
+ ),
888
+ /* @__PURE__ */ jsx(
889
+ Button,
890
+ {
891
+ type: "button",
892
+ variant: "secondary",
893
+ size: "small",
894
+ onClick: () => insertMarkdown("- "),
895
+ title: "List",
896
+ children: "List"
897
+ }
898
+ )
899
+ ] }),
900
+ /* @__PURE__ */ jsx(
901
+ Textarea,
902
+ {
903
+ id: `markdown-${label}`,
904
+ value,
905
+ onChange: (e) => onChange(e.target.value),
906
+ placeholder,
907
+ rows,
908
+ className: "font-mono text-sm"
909
+ }
910
+ )
911
+ ] }) }),
912
+ /* @__PURE__ */ jsx(Tabs.Content, { value: "preview", className: "mt-4", children: /* @__PURE__ */ jsx(
913
+ "div",
914
+ {
915
+ className: clx(
916
+ "min-h-[200px] rounded-lg border border-ui-border-base bg-ui-bg-subtle p-4",
917
+ "prose prose-sm max-w-none"
918
+ ),
919
+ dangerouslySetInnerHTML: {
920
+ __html: value ? renderMarkdownPreview(value) : '<p class="text-ui-fg-subtle">Nothing to preview</p>'
921
+ }
922
+ }
923
+ ) })
924
+ ] }) : /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
925
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-2 border-b pb-2", children: [
926
+ /* @__PURE__ */ jsx(
927
+ Button,
928
+ {
929
+ type: "button",
930
+ variant: "secondary",
931
+ size: "small",
932
+ onClick: () => insertMarkdown("**", "**"),
933
+ title: "Bold",
934
+ children: /* @__PURE__ */ jsx("strong", { children: "B" })
935
+ }
936
+ ),
937
+ /* @__PURE__ */ jsx(
938
+ Button,
939
+ {
940
+ type: "button",
941
+ variant: "secondary",
942
+ size: "small",
943
+ onClick: () => insertMarkdown("*", "*"),
944
+ title: "Italic",
945
+ children: /* @__PURE__ */ jsx("em", { children: "I" })
946
+ }
947
+ ),
948
+ /* @__PURE__ */ jsx(
949
+ Button,
950
+ {
951
+ type: "button",
952
+ variant: "secondary",
953
+ size: "small",
954
+ onClick: () => insertMarkdown("`", "`"),
955
+ title: "Inline Code",
956
+ children: /* @__PURE__ */ jsx(CommandLine, { className: "h-4 w-4" })
957
+ }
958
+ ),
959
+ /* @__PURE__ */ jsx(
960
+ Button,
961
+ {
962
+ type: "button",
963
+ variant: "secondary",
964
+ size: "small",
965
+ onClick: () => insertMarkdown("```\n", "\n```"),
966
+ title: "Code Block",
967
+ children: "Code"
968
+ }
969
+ )
970
+ ] }),
971
+ /* @__PURE__ */ jsx(
972
+ Textarea,
973
+ {
974
+ id: `markdown-${label}`,
975
+ value,
976
+ onChange: (e) => onChange(e.target.value),
977
+ placeholder,
978
+ rows,
979
+ className: "font-mono text-sm"
980
+ }
981
+ )
982
+ ] }),
983
+ /* @__PURE__ */ jsx("div", { className: "text-xs text-ui-fg-subtle", children: "Supports Markdown formatting: **bold**, *italic*, `code`, ```code blocks```, [links](url), ![images](url), and lists" })
984
+ ] });
985
+ };
986
+ const CampaignImageUploader = ({
987
+ campaignId,
988
+ imageType,
989
+ currentImageUrl,
990
+ onClose,
991
+ onSuccess
992
+ }) => {
993
+ const [displayImage, setDisplayImage] = useState(currentImageUrl || null);
994
+ const [isUploading, setIsUploading] = useState(false);
995
+ const [isDragging, setIsDragging] = useState(false);
996
+ const [error, setError] = useState(null);
997
+ const [previewUrl, setPreviewUrl] = useState(null);
998
+ const isThumbnail = imageType === "thumbnail";
999
+ const maxSize = 5;
1000
+ const allowedTypes = ["image/jpeg", "image/png", "image/gif", "image/webp"];
1001
+ const formatFileTypes = () => {
1002
+ return "JPEG, PNG, GIF, or WebP";
1003
+ };
1004
+ const validateFile = (file) => {
1005
+ if (!allowedTypes.includes(file.type)) {
1006
+ setError(`Please upload a valid image file (${formatFileTypes()})`);
1007
+ return false;
1008
+ }
1009
+ if (file.size > maxSize * 1024 * 1024) {
1010
+ setError(`File size must be less than ${maxSize}MB`);
1011
+ return false;
1012
+ }
1013
+ return true;
1014
+ };
1015
+ const handleFileUpload = async (file) => {
1016
+ var _a, _b;
1017
+ if (!validateFile(file)) return;
1018
+ setError(null);
1019
+ const reader = new FileReader();
1020
+ reader.onloadend = () => {
1021
+ setPreviewUrl(reader.result);
1022
+ };
1023
+ reader.readAsDataURL(file);
1024
+ setIsUploading(true);
1025
+ const formData = new FormData();
1026
+ formData.append("file", file);
1027
+ try {
1028
+ const response = await fetch(
1029
+ `/admin/campaigns/${campaignId}/${imageType}`,
1030
+ {
1031
+ method: "POST",
1032
+ body: formData,
1033
+ credentials: "include"
1034
+ }
1035
+ );
1036
+ if (response.ok) {
1037
+ const result = await response.json();
1038
+ const newImageUrl = imageType === "image" ? (_a = result.campaign_detail) == null ? void 0 : _a.image_url : (_b = result.campaign_detail) == null ? void 0 : _b.thumbnail_url;
1039
+ setDisplayImage(newImageUrl);
1040
+ setPreviewUrl(null);
1041
+ onSuccess == null ? void 0 : onSuccess();
1042
+ setTimeout(() => {
1043
+ onClose();
1044
+ }, 1e3);
1045
+ } else {
1046
+ const errorData = await response.json();
1047
+ setError(errorData.error || `Failed to upload ${imageType}`);
1048
+ setPreviewUrl(null);
1049
+ }
1050
+ } catch (err) {
1051
+ setError(`Error uploading ${imageType}`);
1052
+ setPreviewUrl(null);
1053
+ console.error(`Error uploading ${imageType}:`, err);
1054
+ } finally {
1055
+ setIsUploading(false);
1056
+ }
1057
+ };
1058
+ const handleDelete = async () => {
1059
+ if (!confirm(`Are you sure you want to delete the ${imageType}?`)) return;
1060
+ setIsUploading(true);
1061
+ setError(null);
1062
+ try {
1063
+ const response = await fetch(
1064
+ `/admin/campaigns/${campaignId}/${imageType}`,
1065
+ {
1066
+ method: "DELETE",
1067
+ credentials: "include"
1068
+ }
1069
+ );
1070
+ if (response.ok) {
1071
+ setDisplayImage(null);
1072
+ setPreviewUrl(null);
1073
+ onSuccess == null ? void 0 : onSuccess();
1074
+ setTimeout(() => {
1075
+ onClose();
1076
+ }, 500);
1077
+ } else {
1078
+ const errorData = await response.json();
1079
+ setError(errorData.error || `Failed to delete ${imageType}`);
1080
+ }
1081
+ } catch (err) {
1082
+ setError(`Error deleting ${imageType}`);
1083
+ console.error(`Error deleting ${imageType}:`, err);
1084
+ } finally {
1085
+ setIsUploading(false);
1086
+ }
1087
+ };
1088
+ const handleDragEnter = useCallback((e) => {
1089
+ e.preventDefault();
1090
+ e.stopPropagation();
1091
+ setIsDragging(true);
1092
+ }, []);
1093
+ const handleDragLeave = useCallback((e) => {
1094
+ e.preventDefault();
1095
+ e.stopPropagation();
1096
+ setIsDragging(false);
1097
+ }, []);
1098
+ const handleDragOver = useCallback((e) => {
1099
+ e.preventDefault();
1100
+ e.stopPropagation();
1101
+ }, []);
1102
+ const handleDrop = useCallback((e) => {
1103
+ var _a;
1104
+ e.preventDefault();
1105
+ e.stopPropagation();
1106
+ setIsDragging(false);
1107
+ const file = (_a = e.dataTransfer.files) == null ? void 0 : _a[0];
1108
+ if (file) {
1109
+ handleFileUpload(file);
1110
+ }
1111
+ }, []);
1112
+ const imageToDisplay = previewUrl || displayImage;
1113
+ return /* @__PURE__ */ jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/50", children: /* @__PURE__ */ jsxs("div", { className: "w-full max-w-2xl rounded-lg bg-ui-bg-base p-6", children: [
1114
+ /* @__PURE__ */ jsxs("div", { className: "mb-6 flex items-center justify-between", children: [
1115
+ /* @__PURE__ */ jsxs("div", { children: [
1116
+ /* @__PURE__ */ jsxs(Heading, { level: "h2", children: [
1117
+ "Upload Campaign ",
1118
+ isThumbnail ? "Thumbnail" : "Image"
1119
+ ] }),
1120
+ /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-subtle", children: isThumbnail ? "Thumbnail for campaign listing" : "Main campaign image" })
1121
+ ] }),
1122
+ /* @__PURE__ */ jsx(Button, { variant: "secondary", size: "small", onClick: onClose, children: /* @__PURE__ */ jsx(X, {}) })
1123
+ ] }),
1124
+ error && /* @__PURE__ */ jsx(Alert, { variant: "error", dismissible: true, className: "mb-4", children: error }),
1125
+ /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
1126
+ imageToDisplay ? /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
1127
+ /* @__PURE__ */ jsxs("div", { className: "relative overflow-hidden rounded-lg border border-ui-border-base bg-ui-bg-subtle", children: [
1128
+ /* @__PURE__ */ jsx(
1129
+ "img",
1130
+ {
1131
+ src: imageToDisplay,
1132
+ alt: `Campaign ${imageType}`,
1133
+ className: clx(
1134
+ "w-full object-contain",
1135
+ isThumbnail ? "h-32" : "h-64"
1136
+ )
1137
+ }
1138
+ ),
1139
+ isUploading && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center justify-center bg-black/50", children: /* @__PURE__ */ jsx("div", { className: "text-white", children: "Uploading..." }) })
1140
+ ] }),
1141
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
1142
+ /* @__PURE__ */ jsxs(
1143
+ Button,
1144
+ {
1145
+ variant: "secondary",
1146
+ disabled: isUploading,
1147
+ onClick: () => {
1148
+ var _a;
1149
+ return (_a = document.getElementById(`${imageType}-file-input`)) == null ? void 0 : _a.click();
1150
+ },
1151
+ children: [
1152
+ /* @__PURE__ */ jsx(CloudArrowUp, { className: "mr-2" }),
1153
+ "Replace"
1154
+ ]
1155
+ }
1156
+ ),
1157
+ /* @__PURE__ */ jsxs(
1158
+ Button,
1159
+ {
1160
+ variant: "danger",
1161
+ disabled: isUploading,
1162
+ onClick: handleDelete,
1163
+ children: [
1164
+ /* @__PURE__ */ jsx(Trash, { className: "mr-2" }),
1165
+ "Delete"
1166
+ ]
1167
+ }
1168
+ )
1169
+ ] })
1170
+ ] }) : /* @__PURE__ */ jsxs(
1171
+ "div",
1172
+ {
1173
+ className: clx(
1174
+ "flex flex-col items-center justify-center rounded-lg border-2 border-dashed transition-colors",
1175
+ isThumbnail ? "h-48" : "h-64",
1176
+ isDragging ? "border-ui-border-interactive bg-ui-bg-highlight" : "border-ui-border-base bg-ui-bg-subtle",
1177
+ !isUploading && "cursor-pointer"
1178
+ ),
1179
+ onDragEnter: handleDragEnter,
1180
+ onDragLeave: handleDragLeave,
1181
+ onDragOver: handleDragOver,
1182
+ onDrop: handleDrop,
1183
+ onClick: () => {
1184
+ var _a;
1185
+ return !isUploading && ((_a = document.getElementById(`${imageType}-file-input`)) == null ? void 0 : _a.click());
1186
+ },
1187
+ children: [
1188
+ /* @__PURE__ */ jsx(PhotoSolid, { className: "mb-4 h-12 w-12 text-ui-fg-subtle" }),
1189
+ /* @__PURE__ */ jsx(Text, { className: "mb-2 text-lg font-medium", children: isDragging ? `Drop ${imageType} here` : `Upload ${isThumbnail ? "Thumbnail" : "Image"}` }),
1190
+ /* @__PURE__ */ jsx(Text, { className: "mb-4 text-center text-ui-fg-subtle", children: "Drag and drop an image here, or click to select" }),
1191
+ /* @__PURE__ */ jsxs(Text, { className: "text-sm text-ui-fg-subtle", children: [
1192
+ formatFileTypes(),
1193
+ " • Max ",
1194
+ maxSize,
1195
+ "MB"
1196
+ ] }),
1197
+ isThumbnail && /* @__PURE__ */ jsx(Text, { className: "mt-2 text-xs text-ui-fg-subtle", children: "Recommended: 16:9 aspect ratio, minimum 400x225px" }),
1198
+ !isThumbnail && /* @__PURE__ */ jsx(Text, { className: "mt-2 text-xs text-ui-fg-subtle", children: "Recommended: High resolution, minimum 1200x600px" }),
1199
+ isUploading && /* @__PURE__ */ jsx("div", { className: "mt-4", children: /* @__PURE__ */ jsx(Text, { children: "Uploading..." }) })
1200
+ ]
1201
+ }
1202
+ ),
1203
+ /* @__PURE__ */ jsx(
1204
+ "input",
1205
+ {
1206
+ id: `${imageType}-file-input`,
1207
+ type: "file",
1208
+ accept: allowedTypes.join(","),
1209
+ onChange: (e) => {
1210
+ var _a;
1211
+ const file = (_a = e.target.files) == null ? void 0 : _a[0];
1212
+ if (file) handleFileUpload(file);
1213
+ e.target.value = "";
1214
+ },
1215
+ className: "hidden"
1216
+ }
1217
+ )
1218
+ ] }),
1219
+ /* @__PURE__ */ jsxs("div", { className: "mt-6 flex items-center justify-between border-t pt-4", children: [
1220
+ /* @__PURE__ */ jsx(Text, { className: "text-sm text-ui-fg-subtle", children: isThumbnail ? "Thumbnail will be displayed in campaign listings" : "Main image for the campaign detail page" }),
1221
+ /* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: onClose, children: "Close" })
1222
+ ] })
1223
+ ] }) });
1224
+ };
1225
+ const CampaignDetailForm = ({
1226
+ campaignId,
1227
+ campaignName
1228
+ }) => {
1229
+ const [campaignDetail, setCampaignDetail] = useState(
1230
+ null
1231
+ );
1232
+ const [isLoading, setIsLoading] = useState(true);
1233
+ const [isSaving, setIsSaving] = useState(false);
1234
+ const [showImageUploader, setShowImageUploader] = useState(null);
1235
+ const [formData, setFormData] = useState({
1236
+ detail_content: "",
1237
+ terms_and_conditions: "",
1238
+ meta_title: "",
1239
+ meta_description: "",
1240
+ meta_keywords: "",
1241
+ link_url: "",
1242
+ link_text: "",
1243
+ display_order: 0
1244
+ });
1245
+ useEffect(() => {
1246
+ fetchCampaignDetail();
1247
+ }, [campaignId]);
1248
+ const fetchCampaignDetail = async () => {
1249
+ setIsLoading(true);
1250
+ try {
1251
+ const response = await fetch(`/admin/campaigns/${campaignId}/detail`, {
1252
+ credentials: "include"
1253
+ });
1254
+ if (response.ok) {
1255
+ const data = await response.json();
1256
+ if (data.campaign_detail) {
1257
+ console.log("Campaign detail loaded:", {
1258
+ image_url: data.campaign_detail.image_url,
1259
+ thumbnail_url: data.campaign_detail.thumbnail_url,
1260
+ decoded_image: data.campaign_detail.image_url ? decodeURIComponent(data.campaign_detail.image_url) : null,
1261
+ decoded_thumbnail: data.campaign_detail.thumbnail_url ? decodeURIComponent(data.campaign_detail.thumbnail_url) : null
1262
+ });
1263
+ setCampaignDetail(data.campaign_detail);
1264
+ setFormData({
1265
+ detail_content: data.campaign_detail.detail_content || "",
1266
+ terms_and_conditions: data.campaign_detail.terms_and_conditions || "",
1267
+ meta_title: data.campaign_detail.meta_title || "",
1268
+ meta_description: data.campaign_detail.meta_description || "",
1269
+ meta_keywords: data.campaign_detail.meta_keywords || "",
1270
+ link_url: data.campaign_detail.link_url || "",
1271
+ link_text: data.campaign_detail.link_text || "",
1272
+ display_order: data.campaign_detail.display_order || 0
1273
+ });
1274
+ }
1275
+ } else if (response.status !== 404) {
1276
+ console.error("Failed to fetch campaign detail");
1277
+ }
1278
+ } catch (error) {
1279
+ console.error("Error fetching campaign detail:", error);
1280
+ } finally {
1281
+ setIsLoading(false);
1282
+ }
1283
+ };
1284
+ const handleSave = async () => {
1285
+ setIsSaving(true);
1286
+ try {
1287
+ const response = await fetch(`/admin/campaigns/${campaignId}/detail`, {
1288
+ method: "POST",
1289
+ headers: {
1290
+ "Content-Type": "application/json"
1291
+ },
1292
+ body: JSON.stringify(formData),
1293
+ credentials: "include"
1294
+ });
1295
+ if (response.ok) {
1296
+ const data = await response.json();
1297
+ setCampaignDetail(data.campaign_detail);
1298
+ toast.success("Campaign details saved successfully");
1299
+ } else {
1300
+ const errorData = await response.json();
1301
+ toast.error(errorData.error || "Failed to save campaign details");
1302
+ }
1303
+ } catch (error) {
1304
+ console.error("Error saving campaign detail:", error);
1305
+ toast.error("Error saving campaign details");
1306
+ } finally {
1307
+ setIsSaving(false);
1308
+ }
1309
+ };
1310
+ if (isLoading) {
1311
+ return /* @__PURE__ */ jsx(Container, { children: /* @__PURE__ */ jsx("div", { className: "py-8 text-center", children: "Loading campaign details..." }) });
1312
+ }
1313
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1314
+ /* @__PURE__ */ jsxs(Container, { className: "space-y-6", children: [
1315
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
1316
+ /* @__PURE__ */ jsxs("div", { children: [
1317
+ /* @__PURE__ */ jsx(Heading, { level: "h2", children: "Campaign Details" }),
1318
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-ui-fg-subtle", children: campaignName })
1319
+ ] }),
1320
+ /* @__PURE__ */ jsx(Button, { onClick: handleSave, isLoading: isSaving, children: "Save Details" })
1321
+ ] }),
1322
+ /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
1323
+ /* @__PURE__ */ jsx(Heading, { level: "h3", children: "Images" }),
1324
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
1325
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1326
+ /* @__PURE__ */ jsx(Label, { children: "Main Campaign Image" }),
1327
+ /* @__PURE__ */ jsx(
1328
+ "div",
1329
+ {
1330
+ className: clx(
1331
+ "relative flex h-64 items-center justify-center overflow-hidden rounded-lg border-2 border-dashed",
1332
+ (campaignDetail == null ? void 0 : campaignDetail.image_url) ? "border-ui-border-base" : "border-ui-border-base bg-ui-bg-subtle"
1333
+ ),
1334
+ children: (campaignDetail == null ? void 0 : campaignDetail.image_url) ? /* @__PURE__ */ jsxs(Fragment, { children: [
1335
+ /* @__PURE__ */ jsx(
1336
+ "img",
1337
+ {
1338
+ src: campaignDetail.image_url,
1339
+ alt: "Campaign",
1340
+ className: "h-full w-full object-cover",
1341
+ onError: (e) => {
1342
+ console.error(
1343
+ "Failed to load image:",
1344
+ campaignDetail.image_url
1345
+ );
1346
+ e.currentTarget.style.display = "none";
1347
+ }
1348
+ }
1349
+ ),
1350
+ /* @__PURE__ */ jsxs(
1351
+ Button,
1352
+ {
1353
+ variant: "secondary",
1354
+ size: "small",
1355
+ className: "absolute bottom-2 right-2",
1356
+ onClick: () => setShowImageUploader("image"),
1357
+ children: [
1358
+ /* @__PURE__ */ jsx(PencilSquare, { className: "mr-1" }),
1359
+ "Change"
1360
+ ]
1361
+ }
1362
+ )
1363
+ ] }) : /* @__PURE__ */ jsxs(
1364
+ Button,
1365
+ {
1366
+ variant: "secondary",
1367
+ onClick: () => setShowImageUploader("image"),
1368
+ children: [
1369
+ /* @__PURE__ */ jsx(PhotoSolid, { className: "mr-2" }),
1370
+ "Upload Image"
1371
+ ]
1372
+ }
1373
+ )
1374
+ }
1375
+ )
1376
+ ] }),
1377
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1378
+ /* @__PURE__ */ jsx(Label, { children: "Thumbnail" }),
1379
+ /* @__PURE__ */ jsx(
1380
+ "div",
1381
+ {
1382
+ className: clx(
1383
+ "relative flex h-64 items-center justify-center overflow-hidden rounded-lg border-2 border-dashed",
1384
+ (campaignDetail == null ? void 0 : campaignDetail.thumbnail_url) ? "border-ui-border-base" : "border-ui-border-base bg-ui-bg-subtle"
1385
+ ),
1386
+ children: (campaignDetail == null ? void 0 : campaignDetail.thumbnail_url) ? /* @__PURE__ */ jsxs(Fragment, { children: [
1387
+ /* @__PURE__ */ jsx(
1388
+ "img",
1389
+ {
1390
+ src: campaignDetail.thumbnail_url,
1391
+ alt: "Thumbnail",
1392
+ className: "h-full w-full object-cover",
1393
+ onError: (e) => {
1394
+ console.error(
1395
+ "Failed to load thumbnail:",
1396
+ campaignDetail.thumbnail_url
1397
+ );
1398
+ e.currentTarget.style.display = "none";
1399
+ }
1400
+ }
1401
+ ),
1402
+ /* @__PURE__ */ jsxs(
1403
+ Button,
1404
+ {
1405
+ variant: "secondary",
1406
+ size: "small",
1407
+ className: "absolute bottom-2 right-2",
1408
+ onClick: () => setShowImageUploader("thumbnail"),
1409
+ children: [
1410
+ /* @__PURE__ */ jsx(PencilSquare, { className: "mr-1" }),
1411
+ "Change"
1412
+ ]
1413
+ }
1414
+ )
1415
+ ] }) : /* @__PURE__ */ jsxs(
1416
+ Button,
1417
+ {
1418
+ variant: "secondary",
1419
+ onClick: () => setShowImageUploader("thumbnail"),
1420
+ children: [
1421
+ /* @__PURE__ */ jsx(PhotoSolid, { className: "mr-2" }),
1422
+ "Upload Thumbnail"
1423
+ ]
1424
+ }
1425
+ )
1426
+ }
1427
+ )
1428
+ ] })
1429
+ ] })
1430
+ ] }),
1431
+ /* @__PURE__ */ jsx("div", { className: "space-y-4", children: /* @__PURE__ */ jsx(
1432
+ MarkdownEditor,
1433
+ {
1434
+ label: "Campaign Detail Content",
1435
+ value: formData.detail_content,
1436
+ onChange: (value) => setFormData({ ...formData, detail_content: value }),
1437
+ placeholder: "Enter campaign details in Markdown format. You can include images, links, code blocks, and more...",
1438
+ helpText: "Supports Markdown and code syntax",
1439
+ rows: 12,
1440
+ showPreview: true
1441
+ }
1442
+ ) }),
1443
+ /* @__PURE__ */ jsx("div", { className: "space-y-4", children: /* @__PURE__ */ jsx(
1444
+ MarkdownEditor,
1445
+ {
1446
+ label: "Terms and Conditions",
1447
+ value: formData.terms_and_conditions,
1448
+ onChange: (value) => setFormData({ ...formData, terms_and_conditions: value }),
1449
+ placeholder: "Enter terms and conditions for this campaign...",
1450
+ helpText: "Optional - Campaign specific terms",
1451
+ rows: 8,
1452
+ showPreview: true
1453
+ }
1454
+ ) }),
1455
+ /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
1456
+ /* @__PURE__ */ jsx(Heading, { level: "h3", children: "Call to Action Link" }),
1457
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
1458
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1459
+ /* @__PURE__ */ jsx(Label, { htmlFor: "link-url", children: "Link URL" }),
1460
+ /* @__PURE__ */ jsx(
1461
+ Input,
1462
+ {
1463
+ id: "link-url",
1464
+ type: "url",
1465
+ value: formData.link_url,
1466
+ onChange: (e) => setFormData({ ...formData, link_url: e.target.value }),
1467
+ placeholder: "https://example.com/campaign"
1468
+ }
1469
+ )
1470
+ ] }),
1471
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1472
+ /* @__PURE__ */ jsx(Label, { htmlFor: "link-text", children: "Link Text" }),
1473
+ /* @__PURE__ */ jsx(
1474
+ Input,
1475
+ {
1476
+ id: "link-text",
1477
+ value: formData.link_text,
1478
+ onChange: (e) => setFormData({ ...formData, link_text: e.target.value }),
1479
+ placeholder: "Shop Now"
1480
+ }
1481
+ )
1482
+ ] })
1483
+ ] })
1484
+ ] }),
1485
+ /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
1486
+ /* @__PURE__ */ jsx(Heading, { level: "h3", children: "SEO & Metadata" }),
1487
+ /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
1488
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1489
+ /* @__PURE__ */ jsx(Label, { htmlFor: "meta-title", children: "Meta Title" }),
1490
+ /* @__PURE__ */ jsx(
1491
+ Input,
1492
+ {
1493
+ id: "meta-title",
1494
+ value: formData.meta_title,
1495
+ onChange: (e) => setFormData({ ...formData, meta_title: e.target.value }),
1496
+ placeholder: "Campaign meta title for SEO"
1497
+ }
1498
+ )
1499
+ ] }),
1500
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1501
+ /* @__PURE__ */ jsx(Label, { htmlFor: "meta-description", children: "Meta Description" }),
1502
+ /* @__PURE__ */ jsx(
1503
+ Input,
1504
+ {
1505
+ id: "meta-description",
1506
+ value: formData.meta_description,
1507
+ onChange: (e) => setFormData({
1508
+ ...formData,
1509
+ meta_description: e.target.value
1510
+ }),
1511
+ placeholder: "Campaign meta description for SEO"
1512
+ }
1513
+ )
1514
+ ] }),
1515
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1516
+ /* @__PURE__ */ jsx(Label, { htmlFor: "meta-keywords", children: "Meta Keywords" }),
1517
+ /* @__PURE__ */ jsx(
1518
+ Input,
1519
+ {
1520
+ id: "meta-keywords",
1521
+ value: formData.meta_keywords,
1522
+ onChange: (e) => setFormData({
1523
+ ...formData,
1524
+ meta_keywords: e.target.value
1525
+ }),
1526
+ placeholder: "keyword1, keyword2, keyword3"
1527
+ }
1528
+ ),
1529
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-ui-fg-subtle", children: "Comma-separated keywords for SEO" })
1530
+ ] }),
1531
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1532
+ /* @__PURE__ */ jsx(Label, { htmlFor: "display-order", children: "Display Order" }),
1533
+ /* @__PURE__ */ jsx(
1534
+ Input,
1535
+ {
1536
+ id: "display-order",
1537
+ type: "number",
1538
+ value: formData.display_order,
1539
+ onChange: (e) => setFormData({
1540
+ ...formData,
1541
+ display_order: parseInt(e.target.value) || 0
1542
+ }),
1543
+ placeholder: "0"
1544
+ }
1545
+ ),
1546
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-ui-fg-subtle", children: "Lower numbers appear first in listings" })
1547
+ ] })
1548
+ ] })
1549
+ ] }),
1550
+ /* @__PURE__ */ jsx("div", { className: "flex justify-end border-t pt-4", children: /* @__PURE__ */ jsx(Button, { onClick: handleSave, isLoading: isSaving, children: "Save Campaign Details" }) })
1551
+ ] }),
1552
+ showImageUploader && /* @__PURE__ */ jsx(
1553
+ CampaignImageUploader,
1554
+ {
1555
+ campaignId,
1556
+ imageType: showImageUploader,
1557
+ currentImageUrl: showImageUploader === "image" ? campaignDetail == null ? void 0 : campaignDetail.image_url : campaignDetail == null ? void 0 : campaignDetail.thumbnail_url,
1558
+ onClose: () => setShowImageUploader(null),
1559
+ onSuccess: fetchCampaignDetail
1560
+ }
1561
+ )
1562
+ ] });
1563
+ };
1564
+ const CouponDetail = () => {
1565
+ var _a, _b, _c, _d, _e, _f;
1566
+ const { id } = useParams();
1567
+ const navigate = useNavigate();
1568
+ const [isEditing, setIsEditing] = useState(false);
1569
+ const { data, isLoading, refetch } = useCouponById(id ?? "");
1570
+ if (isLoading) {
1571
+ return /* @__PURE__ */ jsx(Container, { className: "flex items-center justify-center h-screen", children: /* @__PURE__ */ jsx("div", { className: "animate-spin h-10 w-10 border-4 border-primary rounded-full border-t-transparent" }) });
1572
+ }
1573
+ if (!(data == null ? void 0 : data.campaign)) {
1574
+ return /* @__PURE__ */ jsx(Container, { children: /* @__PURE__ */ jsx("p", { children: "Coupon not found" }) });
1575
+ }
1576
+ const campaign = data.campaign;
1577
+ const promotion = (_a = campaign.promotions) == null ? void 0 : _a[0];
1578
+ const initialValues = {
1579
+ name: campaign.name ?? "",
1580
+ description: campaign.description ?? "",
1581
+ type: "coupon",
1582
+ code: (promotion == null ? void 0 : promotion.code) ?? "",
1583
+ discount_type: ((_b = promotion == null ? void 0 : promotion.application_method) == null ? void 0 : _b.type) ?? "percentage",
1584
+ discount_value: Number(((_c = promotion == null ? void 0 : promotion.application_method) == null ? void 0 : _c.value) ?? 0),
1585
+ currency_code: ((_d = promotion == null ? void 0 : promotion.application_method) == null ? void 0 : _d.currency_code) ?? void 0,
1586
+ starts_at: campaign.starts_at ? dayjs(campaign.starts_at).format("YYYY-MM-DDTHH:mm") : void 0,
1587
+ ends_at: campaign.ends_at ? dayjs(campaign.ends_at).format("YYYY-MM-DDTHH:mm") : void 0,
1588
+ allocation: ((_e = promotion == null ? void 0 : promotion.application_method) == null ? void 0 : _e.allocation) ?? "total",
1589
+ target_type: ((_f = promotion == null ? void 0 : promotion.application_method) == null ? void 0 : _f.target_type) ?? "order"
1590
+ };
1591
+ const handleSubmit = async (formData) => {
1592
+ var _a2, _b2;
1593
+ if (!id) {
1594
+ return;
1595
+ }
1596
+ try {
1597
+ await axios.put(`/admin/coupons/${id}`, {
1598
+ ...formData,
1599
+ starts_at: new Date(formData.starts_at).toUTCString(),
1600
+ ends_at: new Date(formData.ends_at).toUTCString(),
1601
+ currency_code: formData.discount_type === "fixed" ? formData.currency_code : void 0
1602
+ });
1603
+ toast.success("Coupon updated successfully");
1604
+ setIsEditing(false);
1605
+ refetch();
1606
+ } catch (error) {
1607
+ let message = "Failed to update coupon";
1608
+ if (axios.isAxiosError(error)) {
1609
+ message = ((_b2 = (_a2 = error.response) == null ? void 0 : _a2.data) == null ? void 0 : _b2.message) ?? message;
1610
+ } else if (error instanceof Error) {
1611
+ message = error.message;
1612
+ }
1613
+ toast.error("Failed to update coupon", {
1614
+ description: message
1615
+ });
1616
+ }
1617
+ };
1618
+ return /* @__PURE__ */ jsxs(Container, { children: [
1619
+ /* @__PURE__ */ jsxs("div", { className: "mb-6 flex items-center justify-between", children: [
1620
+ /* @__PURE__ */ jsxs("div", { children: [
1621
+ /* @__PURE__ */ jsx("h1", { className: "text-2xl font-semibold mb-1", children: campaign.name }),
1622
+ /* @__PURE__ */ jsx("p", { className: "text-ui-fg-subtle", children: "Manage coupon configuration and content" })
1623
+ ] }),
1624
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
1625
+ /* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: () => navigate("/coupons"), children: "Back to Coupons" }),
1626
+ /* @__PURE__ */ jsx(Button, { onClick: () => setIsEditing((prev) => !prev), children: isEditing ? "View Mode" : "Edit Mode" })
1627
+ ] })
1628
+ ] }),
1629
+ /* @__PURE__ */ jsxs(Tabs, { defaultValue: "settings", children: [
1630
+ /* @__PURE__ */ jsxs(Tabs.List, { children: [
1631
+ /* @__PURE__ */ jsx(Tabs.Trigger, { value: "settings", children: "Coupon Settings" }),
1632
+ /* @__PURE__ */ jsx(Tabs.Trigger, { value: "details", children: "Coupon Details" })
1633
+ ] }),
1634
+ /* @__PURE__ */ jsx(Tabs.Content, { value: "settings", className: "mt-6", children: /* @__PURE__ */ jsx(
1635
+ CouponForm,
1636
+ {
1637
+ initialData: initialValues,
1638
+ onSubmit: handleSubmit,
1639
+ onCancel: () => navigate("/coupons"),
1640
+ disabled: !isEditing
1641
+ }
1642
+ ) }),
1643
+ /* @__PURE__ */ jsx(Tabs.Content, { value: "details", className: "mt-6", children: /* @__PURE__ */ jsx(
1644
+ CampaignDetailForm,
1645
+ {
1646
+ campaignId: campaign.id,
1647
+ campaignName: campaign.name ?? ""
1648
+ }
1649
+ ) })
1650
+ ] })
1651
+ ] });
1652
+ };
1653
+ const config$1 = defineRouteConfig({
1654
+ label: "Coupon Detail",
1655
+ icon: Tag
1656
+ });
1657
+ const CouponCreate = () => {
1658
+ const navigate = useNavigate();
1659
+ const handleSubmit = async (formData) => {
1660
+ var _a, _b;
1661
+ try {
1662
+ await axios.post("/admin/coupons", {
1663
+ ...formData,
1664
+ starts_at: new Date(formData.starts_at).toUTCString(),
1665
+ ends_at: new Date(formData.ends_at).toUTCString(),
1666
+ currency_code: formData.discount_type === "fixed" ? formData.currency_code : void 0
1667
+ });
1668
+ toast.success("Coupon created successfully");
1669
+ navigate("/coupons");
1670
+ } catch (error) {
1671
+ let message = "Failed to create coupon";
1672
+ if (axios.isAxiosError(error)) {
1673
+ message = ((_b = (_a = error.response) == null ? void 0 : _a.data) == null ? void 0 : _b.message) ?? message;
1674
+ } else if (error instanceof Error) {
1675
+ message = error.message;
1676
+ }
1677
+ toast.error("Failed to create coupon", {
1678
+ description: message
1679
+ });
1680
+ }
1681
+ };
1682
+ return /* @__PURE__ */ jsx(
1683
+ CouponForm,
1684
+ {
1685
+ onSubmit: handleSubmit,
1686
+ onCancel: () => navigate("/coupons")
1687
+ }
1688
+ );
1689
+ };
1690
+ const sdk = new Medusa({
1691
+ baseUrl: "/",
1692
+ debug: false,
1693
+ auth: {
1694
+ type: "session"
1695
+ }
1696
+ });
1697
+ const columnHelper = createDataTableColumnHelper();
1698
+ const columns = [
1699
+ columnHelper.accessor("title", {
1700
+ header: "Title",
1701
+ cell: (info) => /* @__PURE__ */ jsx(Text, { size: "large", className: "py-2", children: info.getValue() })
1702
+ }),
1703
+ columnHelper.accessor("description", {
1704
+ header: "Description",
1705
+ cell: (info) => /* @__PURE__ */ jsx(Text, { size: "large", className: "py-2", children: info.getValue() })
1706
+ })
1707
+ ];
1708
+ const ProductSelector = ({
1709
+ selectedProductIds,
1710
+ onSelectProduct
1711
+ }) => {
1712
+ const [search, setSearch] = useState("");
1713
+ const debouncedSearchHandler = debounce((value) => {
1714
+ setSearch(value);
1715
+ }, 500);
1716
+ const [products, setProducts] = useState(null);
1717
+ const [isLoading, setIsLoading] = useState(true);
1718
+ useEffect(() => {
1719
+ const fetchProducts = async () => {
1720
+ setIsLoading(true);
1721
+ try {
1722
+ const result = await sdk.admin.product.list({
1723
+ q: search,
1724
+ limit: 100
1725
+ });
1726
+ setProducts(result);
1727
+ } catch (error) {
1728
+ console.error("Failed to fetch products:", error);
1729
+ } finally {
1730
+ setIsLoading(false);
1731
+ }
1732
+ };
1733
+ fetchProducts();
1734
+ }, [selectedProductIds, search]);
1735
+ const selectableProducts = useMemo(() => {
1736
+ var _a;
1737
+ return (_a = products == null ? void 0 : products.products) == null ? void 0 : _a.filter(
1738
+ (product) => !selectedProductIds.includes(product.id)
1739
+ );
1740
+ }, [products == null ? void 0 : products.products, selectedProductIds]);
1741
+ const table = useDataTable({
1742
+ data: selectableProducts || [],
1743
+ columns,
1744
+ getRowId: (product) => product.id,
1745
+ onRowClick: (_, row) => {
1746
+ onSelectProduct == null ? void 0 : onSelectProduct(row.original);
1747
+ }
1748
+ });
1749
+ return /* @__PURE__ */ jsxs(Container, { children: [
1750
+ /* @__PURE__ */ jsx(
1751
+ Input,
1752
+ {
1753
+ className: "text-lg py-2",
1754
+ placeholder: "Search products...",
1755
+ onChange: (e) => debouncedSearchHandler(e.target.value)
1756
+ }
1757
+ ),
1758
+ /* @__PURE__ */ jsx(DataTable, { instance: table, children: /* @__PURE__ */ jsx(DataTable.Table, {}) })
1759
+ ] });
1760
+ };
1761
+ const customCampaignSchema = z.object({
1762
+ name: z.string().min(1, "Name is required"),
1763
+ description: z.string().min(1, "Description is required"),
1764
+ type: z.literal("flash-sale"),
1765
+ starts_at: z.string().min(1, "Start date is required"),
1766
+ ends_at: z.string().min(1, "End date is required")
1767
+ });
1768
+ const campaignProductSchema = z.object({
1769
+ product: z.object({
1770
+ id: z.string(),
1771
+ title: z.string()
1772
+ }),
1773
+ discountType: z.enum([
1774
+ "percentage"
1775
+ // , "fixed"
1776
+ ]),
1777
+ discountValue: z.number().min(1),
1778
+ limit: z.number().min(1),
1779
+ maxQty: z.number().min(1)
1780
+ });
1781
+ const campaignDataSchema = customCampaignSchema.extend({
1782
+ products: z.array(campaignProductSchema).min(1, "At least one product is required")
1783
+ }).refine(
1784
+ (data) => new Date(data.starts_at) < new Date(data.ends_at),
1785
+ "End date must be after start date"
1786
+ );
1787
+ const FlashSaleForm = ({
1788
+ initialData,
1789
+ initialProducts,
1790
+ onSubmit,
1791
+ onCancel,
1792
+ disabled = false
1793
+ }) => {
1794
+ var _a;
1795
+ const {
1796
+ control,
1797
+ register,
1798
+ handleSubmit,
1799
+ watch,
1800
+ setValue,
1801
+ formState: { errors }
1802
+ } = useForm({
1803
+ resolver: zodResolver(campaignDataSchema),
1804
+ defaultValues: {
1805
+ name: (initialData == null ? void 0 : initialData.name) || "",
1806
+ description: (initialData == null ? void 0 : initialData.description) || "",
1807
+ type: "flash-sale",
1808
+ starts_at: (initialData == null ? void 0 : initialData.starts_at) || "",
1809
+ ends_at: (initialData == null ? void 0 : initialData.ends_at) || "",
1810
+ products: initialProducts ? Array.from(initialProducts.values()) : []
1811
+ }
1812
+ });
1813
+ const { fields, append, remove, update } = useFieldArray({
1814
+ control,
1815
+ name: "products"
1816
+ });
1817
+ const [openProductModal, setOpenProductModal] = useState(false);
1818
+ const startsAt = watch("starts_at");
1819
+ const endsAt = watch("ends_at");
1820
+ const handleDateTimeChange = (field, type, value) => {
1821
+ const currentValue = watch(field);
1822
+ if (type === "date") {
1823
+ const time = currentValue ? dayjs(currentValue).format("HH:mm") : field === "starts_at" ? "00:00" : "23:59";
1824
+ setValue(field, `${value}T${time}`);
1825
+ } else {
1826
+ const date = currentValue ? dayjs(currentValue).format("YYYY-MM-DD") : dayjs().format("YYYY-MM-DD");
1827
+ setValue(field, `${date}T${value}`);
1828
+ }
1829
+ };
1830
+ const onFormSubmit = (data) => {
1831
+ onSubmit(data);
1832
+ };
1833
+ const onFormError = () => {
1834
+ const errorMessages = Object.entries(errors).map(([key, value]) => {
1835
+ var _a2;
1836
+ if (key === "products" && Array.isArray(value)) {
1837
+ return value.map(
1838
+ (item, index) => item ? `Product ${index + 1}: ${Object.values(item).join(", ")}` : ""
1839
+ ).filter(Boolean).join(", ");
1840
+ }
1841
+ return (value == null ? void 0 : value.message) || ((_a2 = value == null ? void 0 : value.root) == null ? void 0 : _a2.message);
1842
+ }).filter(Boolean).join(", ");
1843
+ toast.error("Invalid data", {
1844
+ description: errorMessages
1845
+ });
1846
+ };
1847
+ return /* @__PURE__ */ jsxs(Container, { children: [
1848
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
1849
+ /* @__PURE__ */ jsx("h1", { className: "text-xl font-semibold", children: "Flash sale campaign" }),
1850
+ /* @__PURE__ */ jsx(Button, { variant: "transparent", onClick: onCancel, children: "Cancel" })
1851
+ ] }),
1852
+ /* @__PURE__ */ jsxs(
1853
+ "form",
1854
+ {
1855
+ onSubmit: handleSubmit(onFormSubmit, onFormError),
1856
+ className: "space-y-4 my-8",
1857
+ children: [
1858
+ /* @__PURE__ */ jsxs("div", { children: [
1859
+ /* @__PURE__ */ jsx(Label, { children: "Name" }),
1860
+ /* @__PURE__ */ jsx(Input, { ...register("name"), disabled }),
1861
+ errors.name && /* @__PURE__ */ jsx("p", { className: "text-red-500 text-sm mt-1", children: errors.name.message })
1862
+ ] }),
1863
+ /* @__PURE__ */ jsxs("div", { children: [
1864
+ /* @__PURE__ */ jsx(Label, { children: "Description" }),
1865
+ /* @__PURE__ */ jsx(Textarea, { ...register("description"), disabled }),
1866
+ errors.description && /* @__PURE__ */ jsx("p", { className: "text-red-500 text-sm mt-1", children: errors.description.message })
1867
+ ] }),
1868
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
1869
+ /* @__PURE__ */ jsxs("div", { children: [
1870
+ /* @__PURE__ */ jsx(Label, { children: "Start Date" }),
1871
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
1872
+ /* @__PURE__ */ jsx(
1873
+ Input,
1874
+ {
1875
+ type: "date",
1876
+ value: startsAt ? dayjs(startsAt).format("YYYY-MM-DD") : "",
1877
+ onChange: (e) => handleDateTimeChange("starts_at", "date", e.target.value),
1878
+ disabled,
1879
+ className: "flex-1"
1880
+ }
1881
+ ),
1882
+ /* @__PURE__ */ jsx(
1883
+ Input,
1884
+ {
1885
+ type: "time",
1886
+ value: startsAt ? dayjs(startsAt).format("HH:mm") : "",
1887
+ onChange: (e) => handleDateTimeChange("starts_at", "time", e.target.value),
1888
+ disabled,
1889
+ className: "w-32"
1890
+ }
1891
+ )
1892
+ ] }),
1893
+ errors.starts_at && /* @__PURE__ */ jsx("p", { className: "text-red-500 text-sm mt-1", children: errors.starts_at.message })
1894
+ ] }),
1895
+ /* @__PURE__ */ jsxs("div", { children: [
1896
+ /* @__PURE__ */ jsx(Label, { children: "End Date" }),
1897
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
1898
+ /* @__PURE__ */ jsx(
1899
+ Input,
1900
+ {
1901
+ type: "date",
1902
+ value: endsAt ? dayjs(endsAt).format("YYYY-MM-DD") : "",
1903
+ onChange: (e) => handleDateTimeChange("ends_at", "date", e.target.value),
1904
+ disabled,
1905
+ className: "flex-1"
1906
+ }
1907
+ ),
1908
+ /* @__PURE__ */ jsx(
1909
+ Input,
1910
+ {
1911
+ type: "time",
1912
+ value: endsAt ? dayjs(endsAt).format("HH:mm") : "",
1913
+ onChange: (e) => handleDateTimeChange("ends_at", "time", e.target.value),
1914
+ disabled,
1915
+ className: "w-32"
1916
+ }
1917
+ )
1918
+ ] }),
1919
+ errors.ends_at && /* @__PURE__ */ jsx("p", { className: "text-red-500 text-sm mt-1", children: errors.ends_at.message })
1920
+ ] })
1921
+ ] }),
1922
+ /* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center", children: [
1923
+ /* @__PURE__ */ jsx(Label, { children: "Products" }),
1924
+ /* @__PURE__ */ jsx(
1925
+ Button,
1926
+ {
1927
+ type: "button",
1928
+ variant: "secondary",
1929
+ onClick: () => setOpenProductModal(true),
1930
+ disabled,
1931
+ children: "Add Product"
1932
+ }
1933
+ )
1934
+ ] }),
1935
+ ((_a = errors.products) == null ? void 0 : _a.root) && /* @__PURE__ */ jsx("p", { className: "text-red-500 text-sm", children: errors.products.root.message }),
1936
+ /* @__PURE__ */ jsxs(Table, { children: [
1937
+ /* @__PURE__ */ jsx(Table.Header, { children: /* @__PURE__ */ jsxs(Table.Row, { children: [
1938
+ /* @__PURE__ */ jsx(Table.HeaderCell, { children: "Product" }),
1939
+ /* @__PURE__ */ jsx(Table.HeaderCell, { children: "Discount Type" }),
1940
+ /* @__PURE__ */ jsx(Table.HeaderCell, { children: "Discount Value" }),
1941
+ /* @__PURE__ */ jsx(Table.HeaderCell, { children: "Limit" }),
1942
+ /* @__PURE__ */ jsx(Table.HeaderCell, { children: "Max Qty per Order" }),
1943
+ /* @__PURE__ */ jsx(Table.HeaderCell, { children: "Actions" })
1944
+ ] }) }),
1945
+ /* @__PURE__ */ jsx(Table.Body, { children: fields.map((field, index) => /* @__PURE__ */ jsxs(Table.Row, { children: [
1946
+ /* @__PURE__ */ jsx(Table.Cell, { children: field.product.title }),
1947
+ /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(
1948
+ Controller,
1949
+ {
1950
+ name: `products.${index}.discountType`,
1951
+ control,
1952
+ render: ({ field: field2 }) => /* @__PURE__ */ jsxs(
1953
+ Select,
1954
+ {
1955
+ value: field2.value,
1956
+ onValueChange: field2.onChange,
1957
+ disabled,
1958
+ children: [
1959
+ /* @__PURE__ */ jsx(Select.Trigger, { children: /* @__PURE__ */ jsx(Select.Value, { placeholder: "Select discount type" }) }),
1960
+ /* @__PURE__ */ jsx(Select.Content, { children: /* @__PURE__ */ jsx(Select.Item, { value: "percentage", children: "Percentage" }) })
1961
+ ]
1962
+ }
1963
+ )
1964
+ }
1965
+ ) }),
1966
+ /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(
1967
+ Controller,
1968
+ {
1969
+ name: `products.${index}.discountValue`,
1970
+ control,
1971
+ render: ({ field: field2 }) => /* @__PURE__ */ jsx(
1972
+ Input,
1973
+ {
1974
+ type: "number",
1975
+ value: field2.value,
1976
+ onChange: (e) => field2.onChange(Number(e.target.value)),
1977
+ disabled
1978
+ }
1979
+ )
1980
+ }
1981
+ ) }),
1982
+ /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(
1983
+ Controller,
1984
+ {
1985
+ name: `products.${index}.limit`,
1986
+ control,
1987
+ render: ({ field: field2 }) => /* @__PURE__ */ jsx(
1988
+ Input,
1989
+ {
1990
+ type: "number",
1991
+ value: field2.value,
1992
+ onChange: (e) => field2.onChange(Number(e.target.value)),
1993
+ disabled
1994
+ }
1995
+ )
1996
+ }
1997
+ ) }),
1998
+ /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(
1999
+ Controller,
2000
+ {
2001
+ name: `products.${index}.maxQty`,
2002
+ control,
2003
+ render: ({ field: field2 }) => /* @__PURE__ */ jsx(
2004
+ Input,
2005
+ {
2006
+ type: "number",
2007
+ value: field2.value,
2008
+ onChange: (e) => field2.onChange(Number(e.target.value)),
2009
+ disabled
2010
+ }
2011
+ )
2012
+ }
2013
+ ) }),
2014
+ /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(
2015
+ Button,
2016
+ {
2017
+ type: "button",
2018
+ variant: "danger",
2019
+ onClick: () => remove(index),
2020
+ disabled,
2021
+ children: /* @__PURE__ */ jsx(Trash, {})
2022
+ }
2023
+ ) })
2024
+ ] }, field.id)) })
2025
+ ] }),
2026
+ /* @__PURE__ */ jsx(Button, { type: "submit", disabled, children: "Save" })
2027
+ ]
2028
+ }
2029
+ ),
2030
+ /* @__PURE__ */ jsx(FocusModal, { open: openProductModal, onOpenChange: setOpenProductModal, children: /* @__PURE__ */ jsxs(FocusModal.Content, { children: [
2031
+ /* @__PURE__ */ jsx(FocusModal.Header, { title: "Add Product" }),
2032
+ /* @__PURE__ */ jsx(FocusModal.Body, { children: /* @__PURE__ */ jsx(
2033
+ ProductSelector,
2034
+ {
2035
+ selectedProductIds: fields.map((f) => f.product.id),
2036
+ onSelectProduct: (product) => {
2037
+ append({
2038
+ product,
2039
+ discountType: "percentage",
2040
+ discountValue: 10,
2041
+ limit: 10,
2042
+ maxQty: 1
2043
+ });
2044
+ setOpenProductModal(false);
2045
+ }
2046
+ }
2047
+ ) })
2048
+ ] }) })
2049
+ ] });
2050
+ };
2051
+ const useFlashSaleById = (id) => {
2052
+ const [data, setData] = useState(null);
2053
+ const [isLoading, setIsLoading] = useState(true);
2054
+ const [error, setError] = useState(null);
2055
+ const fetchFlashSale = async () => {
2056
+ if (!id) {
2057
+ setIsLoading(false);
2058
+ return;
2059
+ }
2060
+ setIsLoading(true);
2061
+ setError(null);
2062
+ try {
2063
+ const response = await axios.get(`/admin/flash-sales/${id}`);
2064
+ setData(response.data);
2065
+ } catch (err) {
2066
+ setError(
2067
+ err instanceof Error ? err : new Error("Failed to fetch flash sale")
2068
+ );
2069
+ } finally {
2070
+ setIsLoading(false);
2071
+ }
2072
+ };
2073
+ useEffect(() => {
2074
+ fetchFlashSale();
2075
+ }, [id]);
2076
+ return {
2077
+ data,
2078
+ isLoading,
2079
+ error,
2080
+ refetch: fetchFlashSale
2081
+ };
2082
+ };
2083
+ const FlashSaleDetail = () => {
2084
+ const { id } = useParams();
2085
+ const navigate = useNavigate();
2086
+ const { data, isLoading, refetch } = useFlashSaleById(id || "");
2087
+ const [isEditing, setIsEditing] = useState(false);
2088
+ async function handleSubmit(campaignData2) {
2089
+ var _a;
2090
+ try {
2091
+ await axios.put(`/admin/flash-sales/${id}`, {
2092
+ ...campaignData2,
2093
+ starts_at: new Date(campaignData2.starts_at).toUTCString(),
2094
+ ends_at: new Date(campaignData2.ends_at).toUTCString()
2095
+ });
2096
+ toast.success("Flash sale updated successfully");
2097
+ refetch();
2098
+ navigate("/flash-sales");
2099
+ } catch (error) {
2100
+ let message;
2101
+ if (axios.isAxiosError(error)) {
2102
+ console.log(error);
2103
+ message = (_a = error.response) == null ? void 0 : _a.data.message;
2104
+ } else if (error instanceof Error) {
2105
+ message = error.message;
2106
+ } else {
2107
+ message = "Failed to update flash sale";
2108
+ }
2109
+ toast.error("Failed to update flash sale", {
2110
+ description: message
2111
+ });
2112
+ }
2113
+ }
2114
+ if (isLoading) {
2115
+ return /* @__PURE__ */ jsx(Container, { className: "flex items-center justify-center h-screen", children: /* @__PURE__ */ jsx("div", { className: "animate-spin h-10 w-10 border-4 border-primary rounded-full border-t-transparent" }) });
2116
+ }
2117
+ if (!data) {
2118
+ return /* @__PURE__ */ jsx(Container, { children: /* @__PURE__ */ jsx("p", { children: "Flash sale not found" }) });
2119
+ }
2120
+ const campaignData = {
2121
+ name: data.name || "",
2122
+ description: data.description || "",
2123
+ type: "flash-sale",
2124
+ starts_at: data.starts_at,
2125
+ ends_at: data.ends_at
2126
+ };
2127
+ return /* @__PURE__ */ jsxs(Container, { children: [
2128
+ /* @__PURE__ */ jsxs("div", { className: "mb-6 flex items-center justify-between", children: [
2129
+ /* @__PURE__ */ jsxs("div", { children: [
2130
+ /* @__PURE__ */ jsx("h1", { className: "text-2xl font-semibold mb-1", children: data.name }),
2131
+ /* @__PURE__ */ jsx("p", { className: "text-ui-fg-subtle", children: "Manage campaign settings and content" })
2132
+ ] }),
2133
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
2134
+ /* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: () => navigate("/flash-sales"), children: "Back to Campaigns" }),
2135
+ /* @__PURE__ */ jsx(Button, { onClick: () => setIsEditing((prev) => !prev), children: isEditing ? "View Mode" : "Edit Mode" })
2136
+ ] })
2137
+ ] }),
2138
+ /* @__PURE__ */ jsxs(Tabs, { defaultValue: "settings", children: [
2139
+ /* @__PURE__ */ jsxs(Tabs.List, { children: [
2140
+ /* @__PURE__ */ jsx(Tabs.Trigger, { value: "settings", children: "Campaign Settings" }),
2141
+ /* @__PURE__ */ jsx(Tabs.Trigger, { value: "details", children: "Campaign Details" })
2142
+ ] }),
2143
+ /* @__PURE__ */ jsx(Tabs.Content, { value: "settings", className: "mt-6", children: /* @__PURE__ */ jsx(
2144
+ FlashSaleForm,
2145
+ {
2146
+ initialData: campaignData,
2147
+ initialProducts: new Map(
2148
+ data.products.map((product) => [product.product.id, product])
2149
+ ),
2150
+ onSubmit: handleSubmit,
2151
+ onCancel: () => navigate("/flash-sales"),
2152
+ disabled: !isEditing
2153
+ }
2154
+ ) }),
2155
+ /* @__PURE__ */ jsx(Tabs.Content, { value: "details", className: "mt-6", children: /* @__PURE__ */ jsx(
2156
+ CampaignDetailForm,
2157
+ {
2158
+ campaignId: id || "",
2159
+ campaignName: data.name || ""
2160
+ }
2161
+ ) })
2162
+ ] })
2163
+ ] });
2164
+ };
2165
+ const config = defineRouteConfig({
2166
+ label: "Flash Sale Detail",
2167
+ icon: Sparkles
2168
+ });
2169
+ const FlashSaleCreate = () => {
2170
+ const navigate = useNavigate();
2171
+ async function handleSubmit(campaignData) {
2172
+ var _a;
2173
+ try {
2174
+ await axios.post("/admin/flash-sales", {
2175
+ ...campaignData,
2176
+ starts_at: new Date(campaignData.starts_at).toUTCString(),
2177
+ ends_at: new Date(campaignData.ends_at).toUTCString()
2178
+ });
2179
+ toast.success("Flash sale created successfully");
2180
+ navigate("/flash-sales");
2181
+ } catch (error) {
2182
+ let message;
2183
+ if (axios.isAxiosError(error)) {
2184
+ console.log(error);
2185
+ message = (_a = error.response) == null ? void 0 : _a.data.message;
2186
+ } else if (error instanceof Error) {
2187
+ message = error.message;
2188
+ } else {
2189
+ message = "Failed to create flash sale";
2190
+ }
2191
+ toast.error("Failed to create flash sale", {
2192
+ description: message
2193
+ });
2194
+ }
2195
+ }
2196
+ return /* @__PURE__ */ jsx(
2197
+ FlashSaleForm,
2198
+ {
2199
+ onSubmit: handleSubmit,
2200
+ onCancel: () => navigate("/flash-sales")
2201
+ }
2202
+ );
2203
+ };
2204
+ const widgetModule = { widgets: [
2205
+ {
2206
+ Component: CampaignDetailWidget,
2207
+ zone: ["promotion.details.side.after"]
2208
+ }
2209
+ ] };
2210
+ const routeModule = {
2211
+ routes: [
2212
+ {
2213
+ Component: Coupons,
2214
+ path: "/coupons"
2215
+ },
2216
+ {
2217
+ Component: FlashSale,
2218
+ path: "/flash-sales"
2219
+ },
2220
+ {
2221
+ Component: CouponDetail,
2222
+ path: "/coupons/:id"
2223
+ },
2224
+ {
2225
+ Component: CouponCreate,
2226
+ path: "/coupons/create"
2227
+ },
2228
+ {
2229
+ Component: FlashSaleDetail,
2230
+ path: "/flash-sales/:id"
2231
+ },
2232
+ {
2233
+ Component: FlashSaleCreate,
2234
+ path: "/flash-sales/create"
2235
+ }
2236
+ ]
2237
+ };
2238
+ const menuItemModule = {
2239
+ menuItems: [
2240
+ {
2241
+ label: config$3.label,
2242
+ icon: config$3.icon,
2243
+ path: "/coupons",
2244
+ nested: void 0
2245
+ },
2246
+ {
2247
+ label: config$2.label,
2248
+ icon: config$2.icon,
2249
+ path: "/flash-sales",
2250
+ nested: void 0
2251
+ },
2252
+ {
2253
+ label: config$1.label,
2254
+ icon: config$1.icon,
2255
+ path: "/coupons/:id",
2256
+ nested: void 0
2257
+ },
2258
+ {
2259
+ label: config.label,
2260
+ icon: config.icon,
2261
+ path: "/flash-sales/:id",
2262
+ nested: void 0
2263
+ }
2264
+ ]
2265
+ };
2266
+ const formModule = { customFields: {} };
2267
+ const displayModule = {
2268
+ displays: {}
2269
+ };
2270
+ const i18nModule = { resources: {} };
2271
+ const plugin = {
2272
+ widgetModule,
2273
+ routeModule,
2274
+ menuItemModule,
2275
+ formModule,
2276
+ displayModule,
2277
+ i18nModule
2278
+ };
2279
+ export {
2280
+ plugin as default
2281
+ };