@lodashventure/medusa-campaign 1.4.3 → 1.4.6

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