@lodashventure/medusa-campaign 1.4.2 → 1.4.3

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.
@@ -201,99 +201,102 @@ const CampaignDetailWidget = ({
201
201
  adminSdk.defineWidgetConfig({
202
202
  zone: "promotion.details.side.after"
203
203
  });
204
- const useCoupons = ({ limit, offset }) => {
204
+ const useFlashSales = (pagination) => {
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 fetchCoupons = async () => {
208
+ const fetchFlashSales = async () => {
209
209
  setIsLoading(true);
210
210
  setError(null);
211
211
  try {
212
- const response = await axios__default.default.get("/admin/coupons", {
213
- params: { limit, offset }
212
+ const response = await axios__default.default.get("/admin/flash-sales", {
213
+ params: pagination
214
214
  });
215
215
  setData(response.data);
216
216
  } catch (err) {
217
- setError(err instanceof Error ? err : new Error("Failed to fetch coupons"));
217
+ setError(
218
+ err instanceof Error ? err : new Error("Failed to fetch flash sales")
219
+ );
218
220
  } finally {
219
221
  setIsLoading(false);
220
222
  }
221
223
  };
222
224
  React.useEffect(() => {
223
- fetchCoupons();
224
- }, [limit, offset]);
225
+ fetchFlashSales();
226
+ }, [pagination.limit, pagination.offset]);
225
227
  return {
226
228
  data,
227
229
  isLoading,
228
230
  error,
229
- refetch: fetchCoupons
231
+ refetch: fetchFlashSales
230
232
  };
231
233
  };
232
- const columnHelper$1 = ui.createDataTableColumnHelper();
233
- const useCouponColumns = () => React.useMemo(
234
- () => [
235
- columnHelper$1.accessor("name", {
234
+ const FlashSalePage = () => {
235
+ const navigate = reactRouterDom.useNavigate();
236
+ const limit = 20;
237
+ const [pagination, setPagination] = React.useState({
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", {
236
247
  header: "Name",
237
248
  cell: (info) => info.getValue()
238
249
  }),
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",
250
+ columnHelper2.accessor("ends_at", {
249
251
  header: "Status",
250
252
  cell: (info) => {
251
- const endsAt = info.row.original.ends_at;
252
- if (!endsAt) {
253
+ const date = info.getValue();
254
+ if (!date) {
253
255
  return "";
254
256
  }
255
- const isActive = dayjs__default.default(endsAt).isAfter(dayjs__default.default());
257
+ const isActive = dayjs__default.default(date).isAfter(dayjs__default.default());
256
258
  return /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { color: isActive ? "green" : "grey", children: isActive ? "Active" : "Inactive" });
257
259
  }
258
260
  }),
259
- columnHelper$1.accessor("starts_at", {
261
+ columnHelper2.accessor("starts_at", {
260
262
  header: "Start Date",
261
- cell: (info) => info.getValue() ? dayjs__default.default(info.getValue()).format("YYYY-MM-DD HH:mm") : ""
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
+ }
262
270
  }),
263
- columnHelper$1.accessor("ends_at", {
271
+ columnHelper2.accessor("ends_at", {
264
272
  header: "End Date",
265
- cell: (info) => info.getValue() ? dayjs__default.default(info.getValue()).format("YYYY-MM-DD HH:mm") : ""
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
+ }
266
280
  })
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();
281
+ ];
282
+ const { data } = useFlashSales({ limit, offset });
280
283
  const table = ui.useDataTable({
281
- data: (data == null ? void 0 : data.campaigns) ?? [],
284
+ data: (data == null ? void 0 : data.campaigns) || [],
282
285
  columns: columns2,
283
- getRowId: (row) => row.id,
286
+ getRowId: (campaign) => campaign.id,
284
287
  pagination: {
285
288
  state: pagination,
286
289
  onPaginationChange: setPagination
287
290
  },
288
- rowCount: (data == null ? void 0 : data.count) ?? 0,
291
+ rowCount: (data == null ? void 0 : data.count) || 0,
289
292
  onRowClick: (_, row) => {
290
- navigate(`/coupons/${row.id}`);
293
+ navigate(`/flash-sales/${row.id}`);
291
294
  }
292
295
  });
293
296
  return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { children: [
294
297
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-center", children: [
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" })
298
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-xl font-semibold", children: "Campaigns" }),
299
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { onClick: () => navigate("/flash-sales/create"), children: "Create Campaign" })
297
300
  ] }),
298
301
  /* @__PURE__ */ jsxRuntime.jsxs(ui.DataTable, { instance: table, children: [
299
302
  /* @__PURE__ */ jsxRuntime.jsx(ui.DataTable.Table, {}),
@@ -301,109 +304,106 @@ const CouponPage = () => {
301
304
  ] })
302
305
  ] });
303
306
  };
304
- const Coupons = () => {
305
- return /* @__PURE__ */ jsxRuntime.jsx(CouponPage, {});
307
+ const FlashSale = () => {
308
+ return /* @__PURE__ */ jsxRuntime.jsx(FlashSalePage, {});
306
309
  };
307
310
  const config$3 = adminSdk.defineRouteConfig({
308
- label: "Coupons",
309
- icon: icons.Tag
311
+ label: "Flash Sale",
312
+ icon: icons.Sparkles
310
313
  });
311
- const useFlashSales = (pagination) => {
314
+ const useCoupons = ({ limit, offset }) => {
312
315
  const [data, setData] = React.useState(null);
313
316
  const [isLoading, setIsLoading] = React.useState(true);
314
317
  const [error, setError] = React.useState(null);
315
- const fetchFlashSales = async () => {
318
+ const fetchCoupons = async () => {
316
319
  setIsLoading(true);
317
320
  setError(null);
318
321
  try {
319
- const response = await axios__default.default.get("/admin/flash-sales", {
320
- params: pagination
322
+ const response = await axios__default.default.get("/admin/coupons", {
323
+ params: { limit, offset }
321
324
  });
322
325
  setData(response.data);
323
326
  } catch (err) {
324
- setError(
325
- err instanceof Error ? err : new Error("Failed to fetch flash sales")
326
- );
327
+ setError(err instanceof Error ? err : new Error("Failed to fetch coupons"));
327
328
  } finally {
328
329
  setIsLoading(false);
329
330
  }
330
331
  };
331
332
  React.useEffect(() => {
332
- fetchFlashSales();
333
- }, [pagination.limit, pagination.offset]);
333
+ fetchCoupons();
334
+ }, [limit, offset]);
334
335
  return {
335
336
  data,
336
337
  isLoading,
337
338
  error,
338
- refetch: fetchFlashSales
339
+ refetch: fetchCoupons
339
340
  };
340
341
  };
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", {
342
+ const columnHelper$1 = ui.createDataTableColumnHelper();
343
+ const useCouponColumns = () => React.useMemo(
344
+ () => [
345
+ columnHelper$1.accessor("name", {
354
346
  header: "Name",
355
347
  cell: (info) => info.getValue()
356
348
  }),
357
- columnHelper2.accessor("ends_at", {
349
+ columnHelper$1.display({
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",
358
359
  header: "Status",
359
360
  cell: (info) => {
360
- const date = info.getValue();
361
- if (!date) {
361
+ const endsAt = info.row.original.ends_at;
362
+ if (!endsAt) {
362
363
  return "";
363
364
  }
364
- const isActive = dayjs__default.default(date).isAfter(dayjs__default.default());
365
+ const isActive = dayjs__default.default(endsAt).isAfter(dayjs__default.default());
365
366
  return /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { color: isActive ? "green" : "grey", children: isActive ? "Active" : "Inactive" });
366
367
  }
367
368
  }),
368
- columnHelper2.accessor("starts_at", {
369
+ columnHelper$1.accessor("starts_at", {
369
370
  header: "Start Date",
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
- }
371
+ cell: (info) => info.getValue() ? dayjs__default.default(info.getValue()).format("YYYY-MM-DD HH:mm") : ""
377
372
  }),
378
- columnHelper2.accessor("ends_at", {
373
+ columnHelper$1.accessor("ends_at", {
379
374
  header: "End Date",
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
- }
375
+ cell: (info) => info.getValue() ? dayjs__default.default(info.getValue()).format("YYYY-MM-DD HH:mm") : ""
387
376
  })
388
- ];
389
- const { data } = useFlashSales({ limit, offset });
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();
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: (campaign) => campaign.id,
393
+ getRowId: (row) => row.id,
394
394
  pagination: {
395
395
  state: pagination,
396
396
  onPaginationChange: setPagination
397
397
  },
398
- rowCount: (data == null ? void 0 : data.count) || 0,
398
+ rowCount: (data == null ? void 0 : data.count) ?? 0,
399
399
  onRowClick: (_, row) => {
400
- navigate(`/flash-sales/${row.id}`);
400
+ navigate(`/coupons/${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: "Campaigns" }),
406
- /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { onClick: () => navigate("/flash-sales/create"), children: "Create Campaign" })
405
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-xl font-semibold", children: "Coupons" }),
406
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { onClick: () => navigate("/coupons/create"), children: "Create Coupon" })
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 FlashSalePage = () => {
411
411
  ] })
412
412
  ] });
413
413
  };
414
- const FlashSale = () => {
415
- return /* @__PURE__ */ jsxRuntime.jsx(FlashSalePage, {});
414
+ const Coupons = () => {
415
+ return /* @__PURE__ */ jsxRuntime.jsx(CouponPage, {});
416
416
  };
417
417
  const config$2 = adminSdk.defineRouteConfig({
418
- label: "Flash Sale",
419
- icon: icons.Sparkles
418
+ label: "Coupons",
419
+ icon: icons.Tag
420
420
  });
421
421
  var isCheckBoxInput = (element) => element.type === "checkbox";
422
422
  var isDateObject = (value) => value instanceof Date;
@@ -2299,64 +2299,134 @@ var n = function(e2, o2) {
2299
2299
  }
2300
2300
  };
2301
2301
  };
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
- });
2302
+ const sdk = new Medusa__default.default({
2303
+ baseUrl: "/",
2304
+ debug: false,
2305
+ auth: {
2306
+ type: "session"
2328
2307
  }
2329
2308
  });
2330
- const CouponForm = ({
2331
- initialData,
2332
- onSubmit,
2333
- onCancel,
2334
- disabled = false
2335
- }) => {
2336
- const {
2337
- register,
2338
- control,
2339
- handleSubmit,
2340
- watch,
2309
+ const columnHelper = ui.createDataTableColumnHelper();
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 = ({
2400
+ initialData,
2401
+ initialProducts,
2402
+ onSubmit,
2403
+ onCancel,
2404
+ disabled = false
2405
+ }) => {
2406
+ var _a;
2407
+ const {
2408
+ control,
2409
+ register,
2410
+ handleSubmit,
2411
+ watch,
2341
2412
  setValue,
2342
2413
  formState: { errors }
2343
2414
  } = useForm({
2344
- resolver: t(couponSchema),
2415
+ resolver: t(campaignDataSchema),
2345
2416
  defaultValues: {
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"
2417
+ name: (initialData == null ? void 0 : initialData.name) || "",
2418
+ description: (initialData == null ? void 0 : initialData.description) || "",
2419
+ type: "flash-sale",
2420
+ starts_at: (initialData == null ? void 0 : initialData.starts_at) || "",
2421
+ ends_at: (initialData == null ? void 0 : initialData.ends_at) || "",
2422
+ products: initialProducts ? Array.from(initialProducts.values()) : []
2357
2423
  }
2358
2424
  });
2359
- const discountType = watch("discount_type");
2425
+ const { fields, append, remove } = useFieldArray({
2426
+ control,
2427
+ name: "products"
2428
+ });
2429
+ const [openProductModal, setOpenProductModal] = React.useState(false);
2360
2430
  const startsAt = watch("starts_at");
2361
2431
  const endsAt = watch("ends_at");
2362
2432
  const handleDateTimeChange = (field, type, value) => {
@@ -2373,34 +2443,42 @@ const CouponForm = ({
2373
2443
  onSubmit(data);
2374
2444
  };
2375
2445
  const onFormError = () => {
2376
- const errorMessages = Object.values(errors).map((err) => err == null ? void 0 : err.message).filter(Boolean).join(", ");
2377
- ui.toast.error("Invalid coupon data", {
2446
+ const errorMessages = Object.entries(errors).map(([key, value]) => {
2447
+ var _a2;
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", {
2378
2456
  description: errorMessages
2379
2457
  });
2380
2458
  };
2381
2459
  return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { children: [
2382
2460
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
2383
- /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-xl font-semibold", children: "Coupon" }),
2461
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-xl font-semibold", children: "Flash sale campaign" }),
2384
2462
  /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "transparent", onClick: onCancel, children: "Cancel" })
2385
2463
  ] }),
2386
2464
  /* @__PURE__ */ jsxRuntime.jsxs(
2387
2465
  "form",
2388
2466
  {
2389
2467
  onSubmit: handleSubmit(onFormSubmit, onFormError),
2390
- className: "space-y-6 my-8",
2468
+ className: "space-y-4 my-8",
2391
2469
  children: [
2392
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
2470
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2393
2471
  /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { children: "Name" }),
2394
2472
  /* @__PURE__ */ jsxRuntime.jsx(ui.Input, { ...register("name"), disabled }),
2395
2473
  errors.name && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-red-500 text-sm mt-1", children: errors.name.message })
2396
2474
  ] }),
2397
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
2475
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2398
2476
  /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { children: "Description" }),
2399
2477
  /* @__PURE__ */ jsxRuntime.jsx(ui.Textarea, { ...register("description"), disabled }),
2400
2478
  errors.description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-red-500 text-sm mt-1", children: errors.description.message })
2401
2479
  ] }),
2402
2480
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
2403
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
2481
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2404
2482
  /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { children: "Start Date" }),
2405
2483
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
2406
2484
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -2420,13 +2498,13 @@ const CouponForm = ({
2420
2498
  value: startsAt ? dayjs__default.default(startsAt).format("HH:mm") : "",
2421
2499
  onChange: (e2) => handleDateTimeChange("starts_at", "time", e2.target.value),
2422
2500
  disabled,
2423
- className: "flex-1"
2501
+ className: "w-32"
2424
2502
  }
2425
2503
  )
2426
2504
  ] }),
2427
2505
  errors.starts_at && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-red-500 text-sm mt-1", children: errors.starts_at.message })
2428
2506
  ] }),
2429
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
2507
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2430
2508
  /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { children: "End Date" }),
2431
2509
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
2432
2510
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -2446,1493 +2524,1450 @@ const CouponForm = ({
2446
2524
  value: endsAt ? dayjs__default.default(endsAt).format("HH:mm") : "",
2447
2525
  onChange: (e2) => handleDateTimeChange("ends_at", "time", e2.target.value),
2448
2526
  disabled,
2449
- className: "flex-1"
2527
+ className: "w-32"
2450
2528
  }
2451
2529
  )
2452
2530
  ] }),
2453
2531
  errors.ends_at && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-red-500 text-sm mt-1", children: errors.ends_at.message })
2454
2532
  ] })
2455
2533
  ] }),
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(
2534
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-center", children: [
2535
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { children: "Products" }),
2536
+ /* @__PURE__ */ jsxRuntime.jsx(
2537
+ ui.Button,
2538
+ {
2539
+ type: "button",
2540
+ variant: "secondary",
2541
+ onClick: () => setOpenProductModal(true),
2542
+ disabled,
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(
2465
2560
  Controller,
2466
2561
  {
2467
- name: "discount_type",
2562
+ name: `products.${index}.discountType`,
2468
2563
  control,
2469
- render: ({ field }) => /* @__PURE__ */ jsxRuntime.jsxs(
2564
+ render: ({ field: field2 }) => /* @__PURE__ */ jsxRuntime.jsxs(
2470
2565
  ui.Select,
2471
2566
  {
2472
- value: field.value,
2473
- onValueChange: field.onChange,
2567
+ value: field2.value,
2568
+ onValueChange: field2.onChange,
2474
2569
  disabled,
2475
2570
  children: [
2476
2571
  /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder: "Select discount type" }) }),
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
- ] })
2572
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Content, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: "percentage", children: "Percentage" }) })
2481
2573
  ]
2482
2574
  }
2483
2575
  )
2484
2576
  }
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(
2577
+ ) }),
2578
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(
2492
2579
  Controller,
2493
2580
  {
2494
- name: "discount_value",
2581
+ name: `products.${index}.discountValue`,
2495
2582
  control,
2496
- render: ({ field }) => /* @__PURE__ */ jsxRuntime.jsx(
2583
+ render: ({ field: field2 }) => /* @__PURE__ */ jsxRuntime.jsx(
2497
2584
  ui.Input,
2498
2585
  {
2499
2586
  type: "number",
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)),
2587
+ value: field2.value,
2588
+ onChange: (e2) => field2.onChange(Number(e2.target.value)),
2504
2589
  disabled
2505
2590
  }
2506
2591
  )
2507
2592
  }
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(
2593
+ ) }),
2594
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(
2521
2595
  Controller,
2522
2596
  {
2523
- name: "allocation",
2597
+ name: `products.${index}.limit`,
2524
2598
  control,
2525
- render: ({ field }) => /* @__PURE__ */ jsxRuntime.jsxs(
2526
- ui.Select,
2599
+ render: ({ field: field2 }) => /* @__PURE__ */ jsxRuntime.jsx(
2600
+ ui.Input,
2527
2601
  {
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
- ]
2538
- }
2539
- )
2602
+ type: "number",
2603
+ value: field2.value,
2604
+ onChange: (e2) => field2.onChange(Number(e2.target.value)),
2605
+ disabled
2606
+ }
2607
+ )
2540
2608
  }
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(
2609
+ ) }),
2610
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(
2546
2611
  Controller,
2547
2612
  {
2548
- name: "target_type",
2613
+ name: `products.${index}.maxQty`,
2549
2614
  control,
2550
- render: ({ field }) => /* @__PURE__ */ jsxRuntime.jsxs(
2551
- ui.Select,
2615
+ render: ({ field: field2 }) => /* @__PURE__ */ jsxRuntime.jsx(
2616
+ ui.Input,
2552
2617
  {
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
- ]
2618
+ type: "number",
2619
+ value: field2.value,
2620
+ onChange: (e2) => field2.onChange(Number(e2.target.value)),
2621
+ disabled
2563
2622
  }
2564
2623
  )
2565
2624
  }
2566
- )
2567
- ] })
2625
+ ) }),
2626
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(
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)) })
2568
2637
  ] }),
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
- ] })
2638
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { type: "submit", disabled, children: "Save" })
2573
2639
  ]
2574
2640
  }
2575
- )
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
+ ] }) })
2576
2661
  ] });
2577
2662
  };
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;
2585
- }
2586
- setIsLoading(true);
2587
- setError(null);
2663
+ const FlashSaleCreate = () => {
2664
+ const navigate = reactRouterDom.useNavigate();
2665
+ async function handleSubmit(campaignData) {
2666
+ var _a;
2588
2667
  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);
2668
+ await axios__default.default.post("/admin/flash-sales", {
2669
+ ...campaignData,
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
+ });
2595
2688
  }
2596
- };
2597
- React.useEffect(() => {
2598
- fetchCoupon();
2599
- }, [id]);
2600
- return {
2601
- data,
2602
- isLoading,
2603
- error,
2604
- refetch: fetchCoupon
2605
- };
2689
+ }
2690
+ return /* @__PURE__ */ jsxRuntime.jsx(
2691
+ FlashSaleForm,
2692
+ {
2693
+ onSubmit: handleSubmit,
2694
+ onCancel: () => navigate("/flash-sales")
2695
+ }
2696
+ );
2606
2697
  };
2607
- const MarkdownEditor = ({
2608
- label,
2609
- value,
2610
- onChange,
2611
- placeholder,
2612
- helpText,
2613
- rows = 10,
2614
- showPreview = true
2698
+ const couponSchema = z__default.default.object({
2699
+ name: z__default.default.string().min(1, "Name is required"),
2700
+ description: z__default.default.string().min(1, "Description is required"),
2701
+ type: z__default.default.literal("coupon"),
2702
+ code: z__default.default.string().min(1, "Coupon code is required"),
2703
+ discount_type: z__default.default.enum(["percentage", "fixed"]),
2704
+ discount_value: z__default.default.number().positive("Discount must be positive"),
2705
+ currency_code: z__default.default.string().optional(),
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
2615
2731
  }) => {
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);
2732
+ const {
2733
+ register,
2734
+ control,
2735
+ handleSubmit,
2736
+ watch,
2737
+ setValue,
2738
+ formState: { errors }
2739
+ } = useForm({
2740
+ resolver: t(couponSchema),
2741
+ defaultValues: {
2742
+ name: (initialData == null ? void 0 : initialData.name) ?? "",
2743
+ description: (initialData == null ? void 0 : initialData.description) ?? "",
2744
+ type: "coupon",
2745
+ code: (initialData == null ? void 0 : initialData.code) ?? "",
2746
+ discount_type: (initialData == null ? void 0 : initialData.discount_type) ?? "percentage",
2747
+ discount_value: (initialData == null ? void 0 : initialData.discount_value) ?? 0,
2748
+ currency_code: (initialData == null ? void 0 : initialData.currency_code) ?? "",
2749
+ starts_at: (initialData == null ? void 0 : initialData.starts_at) ?? dayjs__default.default().startOf("day").format("YYYY-MM-DDTHH:mm"),
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
+ }
2634
2767
  };
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;
2768
+ const onFormSubmit = (data) => {
2769
+ onSubmit(data);
2669
2770
  };
2670
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
2771
+ const onFormError = () => {
2772
+ const errorMessages = Object.values(errors).map((err) => err == null ? void 0 : err.message).filter(Boolean).join(", ");
2773
+ ui.toast.error("Invalid coupon data", {
2774
+ description: errorMessages
2775
+ });
2776
+ };
2777
+ return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { children: [
2671
2778
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
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
- ] })
2677
- ] }),
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("![alt](", ")"),
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
- )
2779
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-xl font-semibold", children: "Coupon" }),
2780
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "transparent", onClick: onCancel, children: "Cancel" })
2867
2781
  ] }),
2868
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-ui-fg-subtle", children: "Supports Markdown formatting: **bold**, *italic*, `code`, ```code blocks```, [links](url), ![images](url), and lists" })
2869
- ] });
2870
- };
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);
2881
- const [error, setError] = React.useState(null);
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;
2893
- }
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;
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);
2912
- try {
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);
2782
+ /* @__PURE__ */ jsxRuntime.jsxs(
2783
+ "form",
2784
+ {
2785
+ onSubmit: handleSubmit(onFormSubmit, onFormError),
2786
+ className: "space-y-6 my-8",
2787
+ children: [
2788
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
2789
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { children: "Name" }),
2790
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Input, { ...register("name"), disabled }),
2791
+ errors.name && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-red-500 text-sm mt-1", children: errors.name.message })
2792
+ ] }),
2793
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
2794
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { children: "Description" }),
2795
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Textarea, { ...register("description"), disabled }),
2796
+ errors.description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-red-500 text-sm mt-1", children: errors.description.message })
2797
+ ] }),
2798
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
2799
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
2800
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { children: "Start Date" }),
2801
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
2802
+ /* @__PURE__ */ jsxRuntime.jsx(
2803
+ ui.Input,
2804
+ {
2805
+ type: "date",
2806
+ value: startsAt ? dayjs__default.default(startsAt).format("YYYY-MM-DD") : "",
2807
+ onChange: (e2) => handleDateTimeChange("starts_at", "date", e2.target.value),
2808
+ disabled,
2809
+ className: "flex-1"
2810
+ }
2811
+ ),
2812
+ /* @__PURE__ */ jsxRuntime.jsx(
2813
+ ui.Input,
2814
+ {
2815
+ type: "time",
2816
+ value: startsAt ? dayjs__default.default(startsAt).format("HH:mm") : "",
2817
+ onChange: (e2) => handleDateTimeChange("starts_at", "time", e2.target.value),
2818
+ disabled,
2819
+ className: "flex-1"
2820
+ }
2821
+ )
2822
+ ] }),
2823
+ errors.starts_at && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-red-500 text-sm mt-1", children: errors.starts_at.message })
2824
+ ] }),
2825
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
2826
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { children: "End Date" }),
2827
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
2828
+ /* @__PURE__ */ jsxRuntime.jsx(
2829
+ ui.Input,
2830
+ {
2831
+ type: "date",
2832
+ value: endsAt ? dayjs__default.default(endsAt).format("YYYY-MM-DD") : "",
2833
+ onChange: (e2) => handleDateTimeChange("ends_at", "date", e2.target.value),
2834
+ disabled,
2835
+ className: "flex-1"
2836
+ }
2837
+ ),
2838
+ /* @__PURE__ */ jsxRuntime.jsx(
2839
+ ui.Input,
2840
+ {
2841
+ type: "time",
2842
+ value: endsAt ? dayjs__default.default(endsAt).format("HH:mm") : "",
2843
+ onChange: (e2) => handleDateTimeChange("ends_at", "time", e2.target.value),
2844
+ disabled,
2845
+ className: "flex-1"
2846
+ }
2847
+ )
2848
+ ] }),
2849
+ errors.ends_at && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-red-500 text-sm mt-1", children: errors.ends_at.message })
2850
+ ] })
2851
+ ] }),
2852
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
2853
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
2854
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { children: "Coupon Code" }),
2855
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Input, { ...register("code"), disabled }),
2856
+ errors.code && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-red-500 text-sm mt-1", children: errors.code.message })
2857
+ ] }),
2858
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
2859
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { children: "Discount Type" }),
2860
+ /* @__PURE__ */ jsxRuntime.jsx(
2861
+ Controller,
2862
+ {
2863
+ name: "discount_type",
2864
+ control,
2865
+ render: ({ field }) => /* @__PURE__ */ jsxRuntime.jsxs(
2866
+ ui.Select,
2867
+ {
2868
+ value: field.value,
2869
+ onValueChange: field.onChange,
2870
+ disabled,
2871
+ children: [
2872
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder: "Select discount type" }) }),
2873
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Select.Content, { children: [
2874
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: "percentage", children: "Percentage" }),
2875
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: "fixed", children: "Fixed Amount" })
2876
+ ] })
2877
+ ]
2878
+ }
2879
+ )
2880
+ }
2881
+ )
2882
+ ] })
2883
+ ] }),
2884
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
2885
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
2886
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { children: "Discount Value" }),
2887
+ /* @__PURE__ */ jsxRuntime.jsx(
2888
+ Controller,
2889
+ {
2890
+ name: "discount_value",
2891
+ control,
2892
+ render: ({ field }) => /* @__PURE__ */ jsxRuntime.jsx(
2893
+ ui.Input,
2894
+ {
2895
+ type: "number",
2896
+ value: field.value,
2897
+ min: 0,
2898
+ step: field.value % 1 === 0 ? 1 : 0.01,
2899
+ onChange: (e2) => field.onChange(Number(e2.target.value)),
2900
+ disabled
2901
+ }
2902
+ )
2903
+ }
2904
+ ),
2905
+ errors.discount_value && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-red-500 text-sm mt-1", children: errors.discount_value.message })
2906
+ ] }),
2907
+ discountType === "fixed" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
2908
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { children: "Currency Code" }),
2909
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Input, { ...register("currency_code"), disabled }),
2910
+ errors.currency_code && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-red-500 text-sm mt-1", children: errors.currency_code.message })
2911
+ ] })
2912
+ ] }),
2913
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
2914
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
2915
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { children: "Allocation" }),
2916
+ /* @__PURE__ */ jsxRuntime.jsx(
2917
+ Controller,
2918
+ {
2919
+ name: "allocation",
2920
+ control,
2921
+ render: ({ field }) => /* @__PURE__ */ jsxRuntime.jsxs(
2922
+ ui.Select,
2923
+ {
2924
+ value: field.value,
2925
+ onValueChange: field.onChange,
2926
+ disabled,
2927
+ children: [
2928
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder: "Select allocation" }) }),
2929
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Select.Content, { children: [
2930
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: "total", children: "Order Total" }),
2931
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: "each", children: "Each Item" })
2932
+ ] })
2933
+ ]
2934
+ }
2935
+ )
2936
+ }
2937
+ )
2938
+ ] }),
2939
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
2940
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { children: "Target Type" }),
2941
+ /* @__PURE__ */ jsxRuntime.jsx(
2942
+ Controller,
2943
+ {
2944
+ name: "target_type",
2945
+ control,
2946
+ render: ({ field }) => /* @__PURE__ */ jsxRuntime.jsxs(
2947
+ ui.Select,
2948
+ {
2949
+ value: field.value,
2950
+ onValueChange: field.onChange,
2951
+ disabled,
2952
+ children: [
2953
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder: "Select target" }) }),
2954
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Select.Content, { children: [
2955
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: "order", children: "Order" }),
2956
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: "items", children: "Items" })
2957
+ ] })
2958
+ ]
2959
+ }
2960
+ )
2961
+ }
2962
+ )
2963
+ ] })
2964
+ ] }),
2965
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-end gap-2", children: [
2966
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "secondary", type: "button", onClick: onCancel, children: "Cancel" }),
2967
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { type: "submit", disabled, children: "Save Coupon" })
2968
+ ] })
2969
+ ]
2934
2970
  }
2935
- } catch (err) {
2936
- setError(`Error uploading ${imageType}`);
2937
- setPreviewUrl(null);
2938
- console.error(`Error uploading ${imageType}:`, err);
2939
- } finally {
2940
- setIsUploading(false);
2971
+ )
2972
+ ] });
2973
+ };
2974
+ const useCouponById = (id) => {
2975
+ const [data, setData] = React.useState(null);
2976
+ const [isLoading, setIsLoading] = React.useState(true);
2977
+ const [error, setError] = React.useState(null);
2978
+ const fetchCoupon = async () => {
2979
+ if (!id) {
2980
+ return;
2941
2981
  }
2942
- };
2943
- const handleDelete = async () => {
2944
- if (!confirm(`Are you sure you want to delete the ${imageType}?`)) return;
2945
- setIsUploading(true);
2982
+ setIsLoading(true);
2946
2983
  setError(null);
2947
2984
  try {
2948
- const response = await fetch(
2949
- `/admin/campaigns/${campaignId}/${imageType}`,
2950
- {
2951
- method: "DELETE",
2952
- credentials: "include"
2953
- }
2954
- );
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
- }
2985
+ const response = await axios__default.default.get(`/admin/coupons/${id}`);
2986
+ setData(response.data);
2966
2987
  } catch (err) {
2967
- setError(`Error deleting ${imageType}`);
2968
- console.error(`Error deleting ${imageType}:`, err);
2988
+ setError(err instanceof Error ? err : new Error("Failed to fetch coupon"));
2969
2989
  } finally {
2970
- setIsUploading(false);
2990
+ setIsLoading(false);
2971
2991
  }
2972
2992
  };
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" })
3006
- ] }),
3007
- /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "secondary", size: "small", onClick: onClose, children: /* @__PURE__ */ jsxRuntime.jsx(icons.X, {}) })
2993
+ React.useEffect(() => {
2994
+ fetchCoupon();
2995
+ }, [id]);
2996
+ return {
2997
+ data,
2998
+ isLoading,
2999
+ error,
3000
+ refetch: fetchCoupon
3001
+ };
3002
+ };
3003
+ const MarkdownEditor = ({
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
3028
+ );
3029
+ }, 0);
3030
+ };
3031
+ const renderMarkdownPreview = (markdown) => {
3032
+ let html = markdown;
3033
+ html = html.replace(/^### (.*$)/gim, "<h3>$1</h3>");
3034
+ html = html.replace(/^## (.*$)/gim, "<h2>$1</h2>");
3035
+ html = html.replace(/^# (.*$)/gim, "<h1>$1</h1>");
3036
+ html = html.replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>");
3037
+ html = html.replace(/__(.+?)__/g, "<strong>$1</strong>");
3038
+ html = html.replace(/\*(.+?)\*/g, "<em>$1</em>");
3039
+ html = html.replace(/_(.+?)_/g, "<em>$1</em>");
3040
+ html = html.replace(
3041
+ /```(\w+)?\n([\s\S]+?)```/g,
3042
+ '<pre class="bg-ui-bg-subtle p-4 rounded-lg overflow-x-auto"><code class="language-$1">$2</code></pre>'
3043
+ );
3044
+ html = html.replace(
3045
+ /`([^`]+)`/g,
3046
+ '<code class="bg-ui-bg-subtle px-1 py-0.5 rounded text-sm">$1</code>'
3047
+ );
3048
+ html = html.replace(
3049
+ /\[([^\]]+)\]\(([^)]+)\)/g,
3050
+ '<a href="$2" class="text-ui-fg-interactive underline">$1</a>'
3051
+ );
3052
+ html = html.replace(
3053
+ /!\[([^\]]*)\]\(([^)]+)\)/g,
3054
+ '<img src="$2" alt="$1" class="max-w-full h-auto rounded-lg my-2" />'
3055
+ );
3056
+ html = html.replace(/^\* (.+)$/gim, "<li>$1</li>");
3057
+ html = html.replace(/^\- (.+)$/gim, "<li>$1</li>");
3058
+ html = html.replace(
3059
+ /(<li>.*<\/li>)/s,
3060
+ "<ul class='list-disc ml-6'>$1</ul>"
3061
+ );
3062
+ html = html.replace(/\n\n/g, "</p><p>");
3063
+ html = `<p>${html}</p>`;
3064
+ return html;
3065
+ };
3066
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
3067
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
3068
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: `markdown-${label}`, className: "font-medium", children: label }),
3069
+ helpText && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1 text-xs text-ui-fg-subtle", children: [
3070
+ /* @__PURE__ */ jsxRuntime.jsx(icons.InformationCircle, { className: "h-4 w-4" }),
3071
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: helpText })
3072
+ ] })
3008
3073
  ] }),
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: [
3074
+ showPreview ? /* @__PURE__ */ jsxRuntime.jsxs(ui.Tabs, { value: activeTab, onValueChange: (v) => setActiveTab(v), children: [
3075
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Tabs.List, { children: [
3076
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Tabs.Trigger, { value: "write", children: "Write" }),
3077
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Tabs.Trigger, { value: "preview", children: "Preview" })
3078
+ ] }),
3079
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Tabs.Content, { value: "write", className: "mt-4", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
3080
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap gap-2 border-b pb-2", children: [
3013
3081
  /* @__PURE__ */ jsxRuntime.jsx(
3014
- "img",
3082
+ ui.Button,
3015
3083
  {
3016
- src: imageToDisplay,
3017
- alt: `Campaign ${imageType}`,
3018
- className: ui.clx(
3019
- "w-full object-contain",
3020
- isThumbnail ? "h-32" : "h-64"
3021
- )
3084
+ type: "button",
3085
+ variant: "secondary",
3086
+ size: "small",
3087
+ onClick: () => insertMarkdown("**", "**"),
3088
+ title: "Bold",
3089
+ children: /* @__PURE__ */ jsxRuntime.jsx("strong", { children: "B" })
3022
3090
  }
3023
3091
  ),
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(
3092
+ /* @__PURE__ */ jsxRuntime.jsx(
3028
3093
  ui.Button,
3029
3094
  {
3095
+ type: "button",
3030
3096
  variant: "secondary",
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
- ]
3097
+ size: "small",
3098
+ onClick: () => insertMarkdown("*", "*"),
3099
+ title: "Italic",
3100
+ children: /* @__PURE__ */ jsxRuntime.jsx("em", { children: "I" })
3040
3101
  }
3041
3102
  ),
3042
- /* @__PURE__ */ jsxRuntime.jsxs(
3103
+ /* @__PURE__ */ jsxRuntime.jsx(
3043
3104
  ui.Button,
3044
3105
  {
3045
- variant: "danger",
3046
- disabled: isUploading,
3047
- onClick: handleDelete,
3048
- children: [
3049
- /* @__PURE__ */ jsxRuntime.jsx(icons.Trash, { className: "mr-2" }),
3050
- "Delete"
3051
- ]
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" })
3052
3112
  }
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
3113
  ),
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 })
3204
- ] }),
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,
3318
- {
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
3326
- }
3327
- ) }),
3328
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-4", children: /* @__PURE__ */ jsxRuntime.jsx(
3329
- MarkdownEditor,
3330
- {
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
3338
- }
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
- ] })
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
+ ),
3147
+ /* @__PURE__ */ jsxRuntime.jsx(
3148
+ ui.Button,
3149
+ {
3150
+ type: "button",
3151
+ variant: "secondary",
3152
+ size: "small",
3153
+ onClick: () => insertMarkdown("![alt](", ")"),
3154
+ title: "Image",
3155
+ children: "Image"
3156
+ }
3157
+ ),
3158
+ /* @__PURE__ */ jsxRuntime.jsx(
3159
+ ui.Button,
3160
+ {
3161
+ type: "button",
3162
+ variant: "secondary",
3163
+ size: "small",
3164
+ onClick: () => insertMarkdown("```\n", "\n```"),
3165
+ title: "Code Block",
3166
+ children: "Code"
3167
+ }
3168
+ ),
3169
+ /* @__PURE__ */ jsxRuntime.jsx(
3170
+ ui.Button,
3171
+ {
3172
+ type: "button",
3173
+ variant: "secondary",
3174
+ size: "small",
3175
+ onClick: () => insertMarkdown("- "),
3176
+ title: "List",
3177
+ children: "List"
3178
+ }
3179
+ )
3180
+ ] }),
3181
+ /* @__PURE__ */ jsxRuntime.jsx(
3182
+ ui.Textarea,
3183
+ {
3184
+ id: `markdown-${label}`,
3185
+ value,
3186
+ onChange: (e2) => onChange(e2.target.value),
3187
+ placeholder,
3188
+ rows,
3189
+ className: "font-mono text-sm"
3190
+ }
3191
+ )
3192
+ ] }) }),
3193
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Tabs.Content, { value: "preview", className: "mt-4", children: /* @__PURE__ */ jsxRuntime.jsx(
3194
+ "div",
3195
+ {
3196
+ className: ui.clx(
3197
+ "min-h-[200px] rounded-lg border border-ui-border-base bg-ui-bg-subtle p-4",
3198
+ "prose prose-sm max-w-none"
3199
+ ),
3200
+ dangerouslySetInnerHTML: {
3201
+ __html: value ? renderMarkdownPreview(value) : '<p class="text-ui-fg-subtle">Nothing to preview</p>'
3202
+ }
3203
+ }
3204
+ ) })
3205
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
3206
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap gap-2 border-b pb-2", children: [
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
+ )
3434
3251
  ] }),
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" }) })
3252
+ /* @__PURE__ */ jsxRuntime.jsx(
3253
+ ui.Textarea,
3254
+ {
3255
+ id: `markdown-${label}`,
3256
+ value,
3257
+ onChange: (e2) => onChange(e2.target.value),
3258
+ placeholder,
3259
+ rows,
3260
+ className: "font-mono text-sm"
3261
+ }
3262
+ )
3436
3263
  ] }),
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
- )
3264
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-ui-fg-subtle", children: "Supports Markdown formatting: **bold**, *italic*, `code`, ```code blocks```, [links](url), ![images](url), and lists" })
3447
3265
  ] });
3448
3266
  };
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"
3267
+ const CampaignImageUploader = ({
3268
+ campaignId,
3269
+ imageType,
3270
+ currentImageUrl,
3271
+ onClose,
3272
+ onSuccess
3273
+ }) => {
3274
+ const [displayImage, setDisplayImage] = React.useState(currentImageUrl || null);
3275
+ const [isUploading, setIsUploading] = React.useState(false);
3276
+ const [isDragging, setIsDragging] = React.useState(false);
3277
+ const [error, setError] = React.useState(null);
3278
+ const [previewUrl, setPreviewUrl] = React.useState(null);
3279
+ const isThumbnail = imageType === "thumbnail";
3280
+ const maxSize = 5;
3281
+ const allowedTypes = ["image/jpeg", "image/png", "image/gif", "image/webp"];
3282
+ const formatFileTypes = () => {
3283
+ return "JPEG, PNG, GIF, or WebP";
3475
3284
  };
3476
- const handleSubmit = async (formData) => {
3477
- var _a2, _b2;
3478
- if (!id) {
3479
- return;
3285
+ const validateFile = (file) => {
3286
+ if (!allowedTypes.includes(file.type)) {
3287
+ setError(`Please upload a valid image file (${formatFileTypes()})`);
3288
+ return false;
3289
+ }
3290
+ if (file.size > maxSize * 1024 * 1024) {
3291
+ setError(`File size must be less than ${maxSize}MB`);
3292
+ return false;
3480
3293
  }
3294
+ return true;
3295
+ };
3296
+ const handleFileUpload = async (file) => {
3297
+ var _a, _b;
3298
+ if (!validateFile(file)) return;
3299
+ setError(null);
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);
3481
3308
  try {
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;
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);
3497
3330
  }
3498
- ui.toast.error("Failed to update coupon", {
3499
- description: message
3500
- });
3331
+ } catch (err) {
3332
+ setError(`Error uploading ${imageType}`);
3333
+ setPreviewUrl(null);
3334
+ console.error(`Error uploading ${imageType}:`, err);
3335
+ } finally {
3336
+ setIsUploading(false);
3337
+ }
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
+ try {
3344
+ const response = await fetch(
3345
+ `/admin/campaigns/${campaignId}/${imageType}`,
3346
+ {
3347
+ method: "DELETE",
3348
+ credentials: "include"
3349
+ }
3350
+ );
3351
+ if (response.ok) {
3352
+ setDisplayImage(null);
3353
+ setPreviewUrl(null);
3354
+ onSuccess == null ? void 0 : onSuccess();
3355
+ setTimeout(() => {
3356
+ onClose();
3357
+ }, 500);
3358
+ } else {
3359
+ const errorData = await response.json();
3360
+ setError(errorData.error || `Failed to delete ${imageType}`);
3361
+ }
3362
+ } catch (err) {
3363
+ setError(`Error deleting ${imageType}`);
3364
+ console.error(`Error deleting ${imageType}:`, err);
3365
+ } finally {
3366
+ setIsUploading(false);
3501
3367
  }
3502
3368
  };
3503
- return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { children: [
3369
+ const handleDragEnter = React.useCallback((e2) => {
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: [
3504
3395
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-6 flex items-center justify-between", children: [
3505
3396
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
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" })
3397
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Heading, { level: "h2", children: [
3398
+ "Upload Campaign ",
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" })
3508
3402
  ] }),
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
- ] })
3403
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "secondary", size: "small", onClick: onClose, children: /* @__PURE__ */ jsxRuntime.jsx(icons.X, {}) })
3513
3404
  ] }),
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,
3405
+ error && /* @__PURE__ */ jsxRuntime.jsx(ui.Alert, { variant: "error", dismissible: true, className: "mb-4", children: error }),
3406
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
3407
+ imageToDisplay ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
3408
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative overflow-hidden rounded-lg border border-ui-border-base bg-ui-bg-subtle", children: [
3409
+ /* @__PURE__ */ jsxRuntime.jsx(
3410
+ "img",
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",
3521
3453
  {
3522
- initialData: initialValues,
3523
- onSubmit: handleSubmit,
3524
- onCancel: () => navigate("/coupons"),
3525
- disabled: !isEditing
3454
+ className: ui.clx(
3455
+ "flex flex-col items-center justify-center rounded-lg border-2 border-dashed transition-colors",
3456
+ isThumbnail ? "h-48" : "h-64",
3457
+ isDragging ? "border-ui-border-interactive bg-ui-bg-highlight" : "border-ui-border-base bg-ui-bg-subtle",
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
+ ]
3526
3482
  }
3527
- ) }),
3528
- /* @__PURE__ */ jsxRuntime.jsx(ui.Tabs.Content, { value: "details", className: "mt-6", children: /* @__PURE__ */ jsxRuntime.jsx(
3529
- CampaignDetailForm,
3483
+ ),
3484
+ /* @__PURE__ */ jsxRuntime.jsx(
3485
+ "input",
3530
3486
  {
3531
- campaignId: campaign.id,
3532
- campaignName: campaign.name ?? ""
3533
- }
3534
- ) })
3535
- ] })
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
- ] });
3487
+ id: `${imageType}-file-input`,
3488
+ type: "file",
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"
3497
+ }
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" })
3503
+ ] })
3504
+ ] }) });
3645
3505
  };
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
3506
+ const CampaignDetailForm = ({
3507
+ campaignId,
3508
+ campaignName
3678
3509
  }) => {
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
- }
3697
- });
3698
- const { fields, append, remove } = useFieldArray({
3699
- control,
3700
- name: "products"
3510
+ const [campaignDetail, setCampaignDetail] = React.useState(
3511
+ null
3512
+ );
3513
+ const [isLoading, setIsLoading] = React.useState(true);
3514
+ const [isSaving, setIsSaving] = React.useState(false);
3515
+ const [showImageUploader, setShowImageUploader] = React.useState(null);
3516
+ const [formData, setFormData] = React.useState({
3517
+ detail_content: "",
3518
+ terms_and_conditions: "",
3519
+ meta_title: "",
3520
+ meta_description: "",
3521
+ meta_keywords: "",
3522
+ link_url: "",
3523
+ link_text: "",
3524
+ display_order: 0
3701
3525
  });
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}`);
3526
+ React.useEffect(() => {
3527
+ fetchCampaignDetail();
3528
+ }, [campaignId]);
3529
+ const fetchCampaignDetail = async () => {
3530
+ setIsLoading(true);
3531
+ try {
3532
+ const response = await fetch(`/admin/campaigns/${campaignId}/detail`, {
3533
+ credentials: "include"
3534
+ });
3535
+ if (response.ok) {
3536
+ const data = await response.json();
3537
+ if (data.campaign_detail) {
3538
+ console.log("Campaign detail loaded:", {
3539
+ image_url: data.campaign_detail.image_url,
3540
+ thumbnail_url: data.campaign_detail.thumbnail_url,
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);
3713
3563
  }
3714
3564
  };
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(", ");
3565
+ const handleSave = async () => {
3566
+ setIsSaving(true);
3567
+ try {
3568
+ const response = await fetch(`/admin/campaigns/${campaignId}/detail`, {
3569
+ method: "POST",
3570
+ headers: {
3571
+ "Content-Type": "application/json"
3572
+ },
3573
+ body: JSON.stringify(formData),
3574
+ credentials: "include"
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");
3725
3583
  }
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
- });
3584
+ } catch (error) {
3585
+ console.error("Error saving campaign detail:", error);
3586
+ ui.toast.error("Error saving campaign details");
3587
+ } finally {
3588
+ setIsSaving(false);
3589
+ }
3731
3590
  };
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
- }
3591
+ if (isLoading) {
3592
+ return /* @__PURE__ */ jsxRuntime.jsx(ui.Container, { children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "py-8 text-center", children: "Loading campaign details..." }) });
3593
+ }
3594
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3595
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "space-y-6", children: [
3596
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
3597
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
3598
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: "Campaign Details" }),
3599
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-ui-fg-subtle", children: campaignName })
3600
+ ] }),
3601
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { onClick: handleSave, isLoading: isSaving, children: "Save Details" })
3602
+ ] }),
3603
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
3604
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h3", children: "Images" }),
3605
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
3606
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
3607
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { children: "Main Campaign Image" }),
3608
+ /* @__PURE__ */ jsxRuntime.jsx(
3609
+ "div",
3610
+ {
3611
+ className: ui.clx(
3612
+ "relative flex h-64 items-center justify-center overflow-hidden rounded-lg border-2 border-dashed",
3613
+ (campaignDetail == null ? void 0 : campaignDetail.image_url) ? "border-ui-border-base" : "border-ui-border-base bg-ui-bg-subtle"
3766
3614
  ),
3767
- /* @__PURE__ */ jsxRuntime.jsx(
3768
- ui.Input,
3615
+ children: (campaignDetail == null ? void 0 : campaignDetail.image_url) ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3616
+ /* @__PURE__ */ jsxRuntime.jsx(
3617
+ "img",
3618
+ {
3619
+ src: campaignDetail.image_url,
3620
+ alt: "Campaign",
3621
+ className: "h-full w-full object-cover",
3622
+ onError: (e2) => {
3623
+ console.error(
3624
+ "Failed to load image:",
3625
+ campaignDetail.image_url
3626
+ );
3627
+ e2.currentTarget.style.display = "none";
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,
3769
3646
  {
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"
3647
+ variant: "secondary",
3648
+ onClick: () => setShowImageUploader("image"),
3649
+ children: [
3650
+ /* @__PURE__ */ jsxRuntime.jsx(icons.PhotoSolid, { className: "mr-2" }),
3651
+ "Upload Image"
3652
+ ]
3775
3653
  }
3776
3654
  )
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
- }
3655
+ }
3656
+ )
3657
+ ] }),
3658
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
3659
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { children: "Thumbnail" }),
3660
+ /* @__PURE__ */ jsxRuntime.jsx(
3661
+ "div",
3662
+ {
3663
+ className: ui.clx(
3664
+ "relative flex h-64 items-center justify-center overflow-hidden rounded-lg border-2 border-dashed",
3665
+ (campaignDetail == null ? void 0 : campaignDetail.thumbnail_url) ? "border-ui-border-base" : "border-ui-border-base bg-ui-bg-subtle"
3792
3666
  ),
3793
- /* @__PURE__ */ jsxRuntime.jsx(
3794
- ui.Input,
3667
+ children: (campaignDetail == null ? void 0 : campaignDetail.thumbnail_url) ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3668
+ /* @__PURE__ */ jsxRuntime.jsx(
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,
3795
3698
  {
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"
3699
+ variant: "secondary",
3700
+ onClick: () => setShowImageUploader("thumbnail"),
3701
+ children: [
3702
+ /* @__PURE__ */ jsxRuntime.jsx(icons.PhotoSolid, { className: "mr-2" }),
3703
+ "Upload Thumbnail"
3704
+ ]
3801
3705
  }
3802
3706
  )
3803
- ] }),
3804
- errors.ends_at && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-red-500 text-sm mt-1", children: errors.ends_at.message })
3805
- ] })
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
+ )
3806
3751
  ] }),
3807
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-center", children: [
3808
- /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { children: "Products" }),
3752
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
3753
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "link-text", children: "Link Text" }),
3809
3754
  /* @__PURE__ */ jsxRuntime.jsx(
3810
- ui.Button,
3755
+ ui.Input,
3811
3756
  {
3812
- type: "button",
3813
- variant: "secondary",
3814
- onClick: () => setOpenProductModal(true),
3815
- disabled,
3816
- children: "Add Product"
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"
3817
3778
  }
3818
3779
  )
3819
3780
  ] }),
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)) })
3781
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
3782
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "meta-description", children: "Meta Description" }),
3783
+ /* @__PURE__ */ jsxRuntime.jsx(
3784
+ ui.Input,
3785
+ {
3786
+ id: "meta-description",
3787
+ value: formData.meta_description,
3788
+ onChange: (e2) => setFormData({
3789
+ ...formData,
3790
+ meta_description: e2.target.value
3791
+ }),
3792
+ placeholder: "Campaign meta description for SEO"
3793
+ }
3794
+ )
3910
3795
  ] }),
3911
- /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { type: "submit", disabled, children: "Save" })
3912
- ]
3796
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
3797
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "meta-keywords", children: "Meta Keywords" }),
3798
+ /* @__PURE__ */ jsxRuntime.jsx(
3799
+ ui.Input,
3800
+ {
3801
+ id: "meta-keywords",
3802
+ value: formData.meta_keywords,
3803
+ onChange: (e2) => setFormData({
3804
+ ...formData,
3805
+ meta_keywords: e2.target.value
3806
+ }),
3807
+ placeholder: "keyword1, keyword2, keyword3"
3808
+ }
3809
+ ),
3810
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-ui-fg-subtle", children: "Comma-separated keywords for SEO" })
3811
+ ] }),
3812
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
3813
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "display-order", children: "Display Order" }),
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
3913
3841
  }
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,
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;
3893
+ }
3894
+ ui.toast.error("Failed to update coupon", {
3895
+ description: message
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,
3919
3917
  {
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
- }
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,
3926
+ {
3927
+ campaignId: campaign.id,
3928
+ campaignName: campaign.name ?? ""
3931
3929
  }
3932
3930
  ) })
3933
- ] }) })
3931
+ ] })
3934
3932
  ] });
3935
3933
  };
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
+ };
3936
3971
  const useFlashSaleById = (id) => {
3937
3972
  const [data, setData] = React.useState(null);
3938
3973
  const [isLoading, setIsLoading] = React.useState(true);
@@ -4051,41 +4086,6 @@ const config = adminSdk.defineRouteConfig({
4051
4086
  label: "Flash Sale Detail",
4052
4087
  icon: icons.Sparkles
4053
4088
  });
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,13 +4094,17 @@ const widgetModule = { widgets: [
4094
4094
  ] };
4095
4095
  const routeModule = {
4096
4096
  routes: [
4097
+ {
4098
+ Component: FlashSale,
4099
+ path: "/flash-sales"
4100
+ },
4097
4101
  {
4098
4102
  Component: Coupons,
4099
4103
  path: "/coupons"
4100
4104
  },
4101
4105
  {
4102
- Component: FlashSale,
4103
- path: "/flash-sales"
4106
+ Component: FlashSaleCreate,
4107
+ path: "/flash-sales/create"
4104
4108
  },
4105
4109
  {
4106
4110
  Component: CouponDetail,
@@ -4113,24 +4117,20 @@ const routeModule = {
4113
4117
  {
4114
4118
  Component: FlashSaleDetail,
4115
4119
  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$3.label,
4127
- icon: config$3.icon,
4126
+ label: config$2.label,
4127
+ icon: config$2.icon,
4128
4128
  path: "/coupons",
4129
4129
  nested: void 0
4130
4130
  },
4131
4131
  {
4132
- label: config$2.label,
4133
- icon: config$2.icon,
4132
+ label: config$3.label,
4133
+ icon: config$3.icon,
4134
4134
  path: "/flash-sales",
4135
4135
  nested: void 0
4136
4136
  },