@lodashventure/medusa-campaign 1.5.3 → 1.5.4

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.
@@ -1,7 +1,7 @@
1
1
  import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
2
  import { defineWidgetConfig, defineRouteConfig } from "@medusajs/admin-sdk";
3
- import { Container, Heading, Text, Alert, Badge, Button, createDataTableColumnHelper, useDataTable, DataTable, Label, Input, Textarea, Select, toast, Table, FocusModal, Tabs, clx } from "@medusajs/ui";
4
- import { Sparkles, PhotoSolid, PencilSquare, Eye, Tag, Trash, InformationCircle, CommandLine, X, CloudArrowUp } from "@medusajs/icons";
3
+ import { Container, Heading, Text, Alert, Badge, Button, createDataTableColumnHelper, useDataTable, DataTable, Label, Input, Textarea, Select, toast, Tabs, clx, Table, FocusModal } from "@medusajs/ui";
4
+ import { Sparkles, PhotoSolid, PencilSquare, Eye, Tag, InformationCircle, CommandLine, X, CloudArrowUp, Trash } from "@medusajs/icons";
5
5
  import { useState, useEffect, useMemo, useCallback } from "react";
6
6
  import { useNavigate, useParams } from "react-router-dom";
7
7
  import dayjs from "dayjs";
@@ -692,1274 +692,1396 @@ const CouponForm = ({
692
692
  )
693
693
  ] });
694
694
  };
695
- const CouponCreate = () => {
696
- const navigate = useNavigate();
697
- const handleSubmit = async (formData) => {
698
- var _a, _b;
695
+ const useCouponById = (id) => {
696
+ const [data, setData] = useState(null);
697
+ const [isLoading, setIsLoading] = useState(true);
698
+ const [error, setError] = useState(null);
699
+ const fetchCoupon = async () => {
700
+ if (!id) {
701
+ return;
702
+ }
703
+ setIsLoading(true);
704
+ setError(null);
699
705
  try {
700
- await axios.post("/admin/coupons", {
701
- ...formData,
702
- starts_at: new Date(formData.starts_at).toUTCString(),
703
- ends_at: new Date(formData.ends_at).toUTCString(),
704
- currency_code: formData.discount_type === "fixed" ? formData.currency_code : void 0
705
- });
706
- toast.success("Coupon created successfully");
707
- navigate("/coupons");
708
- } catch (error) {
709
- let message = "Failed to create coupon";
710
- if (axios.isAxiosError(error)) {
711
- message = ((_b = (_a = error.response) == null ? void 0 : _a.data) == null ? void 0 : _b.message) ?? message;
712
- } else if (error instanceof Error) {
713
- message = error.message;
714
- }
715
- toast.error("Failed to create coupon", {
716
- description: message
717
- });
706
+ const response = await axios.get(`/admin/coupons/${id}`);
707
+ setData(response.data);
708
+ } catch (err) {
709
+ setError(err instanceof Error ? err : new Error("Failed to fetch coupon"));
710
+ } finally {
711
+ setIsLoading(false);
718
712
  }
719
713
  };
720
- return /* @__PURE__ */ jsx(
721
- CouponForm,
722
- {
723
- onSubmit: handleSubmit,
724
- onCancel: () => navigate("/coupons")
725
- }
726
- );
727
- };
728
- const sdk = new Medusa({
729
- baseUrl: "/",
730
- debug: false,
731
- auth: {
732
- type: "session"
733
- }
734
- });
735
- const columnHelper = createDataTableColumnHelper();
736
- const columns = [
737
- columnHelper.accessor("title", {
738
- header: "Title",
739
- cell: (info) => /* @__PURE__ */ jsx(Text, { size: "large", className: "py-2", children: info.getValue() })
740
- }),
741
- columnHelper.accessor("description", {
742
- header: "Description",
743
- cell: (info) => /* @__PURE__ */ jsx(Text, { size: "large", className: "py-2", children: info.getValue() })
744
- })
745
- ];
746
- const ProductSelector = ({
747
- selectedProductIds,
748
- onSelectProduct
749
- }) => {
750
- const [search, setSearch] = useState("");
751
- const debouncedSearchHandler = debounce((value) => {
752
- setSearch(value);
753
- }, 500);
754
- const [products, setProducts] = useState(null);
755
- const [isLoading, setIsLoading] = useState(true);
756
714
  useEffect(() => {
757
- const fetchProducts = async () => {
758
- setIsLoading(true);
759
- try {
760
- const result = await sdk.admin.product.list({
761
- q: search,
762
- limit: 100
763
- });
764
- setProducts(result);
765
- } catch (error) {
766
- console.error("Failed to fetch products:", error);
767
- } finally {
768
- setIsLoading(false);
769
- }
770
- };
771
- fetchProducts();
772
- }, [selectedProductIds, search]);
773
- const selectableProducts = useMemo(() => {
774
- var _a;
775
- return (_a = products == null ? void 0 : products.products) == null ? void 0 : _a.filter(
776
- (product) => !selectedProductIds.includes(product.id)
777
- );
778
- }, [products == null ? void 0 : products.products, selectedProductIds]);
779
- const table = useDataTable({
780
- data: selectableProducts || [],
781
- columns,
782
- getRowId: (product) => product.id,
783
- onRowClick: (_, row) => {
784
- onSelectProduct == null ? void 0 : onSelectProduct(row.original);
785
- }
786
- });
787
- return /* @__PURE__ */ jsxs(Container, { children: [
788
- /* @__PURE__ */ jsx(
789
- Input,
790
- {
791
- className: "text-lg py-2",
792
- placeholder: "Search products...",
793
- onChange: (e) => debouncedSearchHandler(e.target.value)
794
- }
795
- ),
796
- /* @__PURE__ */ jsx(DataTable, { instance: table, children: /* @__PURE__ */ jsx(DataTable.Table, {}) })
797
- ] });
715
+ fetchCoupon();
716
+ }, [id]);
717
+ return {
718
+ data,
719
+ isLoading,
720
+ error,
721
+ refetch: fetchCoupon
722
+ };
798
723
  };
799
- const customCampaignSchema = z.object({
800
- name: z.string().min(1, "Name is required"),
801
- description: z.string().min(1, "Description is required"),
802
- type: z.literal("flash-sale"),
803
- starts_at: z.string().min(1, "Start date is required"),
804
- ends_at: z.string().min(1, "End date is required")
805
- });
806
- const campaignProductSchema = z.object({
807
- product: z.object({
808
- id: z.string(),
809
- title: z.string()
810
- }),
811
- discountType: z.enum([
812
- "percentage"
813
- // , "fixed"
814
- ]),
815
- discountValue: z.number().min(1),
816
- limit: z.number().min(1),
817
- maxQty: z.number().min(1)
818
- });
819
- const campaignDataSchema = customCampaignSchema.extend({
820
- products: z.array(campaignProductSchema).min(1, "At least one product is required")
821
- }).refine(
822
- (data) => new Date(data.starts_at) < new Date(data.ends_at),
823
- "End date must be after start date"
824
- );
825
- const FlashSaleForm = ({
826
- initialData,
827
- initialProducts,
828
- onSubmit,
829
- onCancel,
830
- disabled = false
724
+ const MarkdownEditor = ({
725
+ label,
726
+ value,
727
+ onChange,
728
+ placeholder,
729
+ helpText,
730
+ rows = 10,
731
+ showPreview = true
831
732
  }) => {
832
- var _a;
833
- const {
834
- control,
835
- register,
836
- handleSubmit,
837
- watch,
838
- setValue,
839
- formState: { errors }
840
- } = useForm({
841
- resolver: zodResolver(campaignDataSchema),
842
- defaultValues: {
843
- name: (initialData == null ? void 0 : initialData.name) || "",
844
- description: (initialData == null ? void 0 : initialData.description) || "",
845
- type: "flash-sale",
846
- starts_at: (initialData == null ? void 0 : initialData.starts_at) || "",
847
- ends_at: (initialData == null ? void 0 : initialData.ends_at) || "",
848
- products: initialProducts ? Array.from(initialProducts.values()) : []
849
- }
850
- });
851
- const { fields, append, remove, update } = useFieldArray({
852
- control,
853
- name: "products"
854
- });
855
- const [openProductModal, setOpenProductModal] = useState(false);
856
- const startsAt = watch("starts_at");
857
- const endsAt = watch("ends_at");
858
- const handleDateTimeChange = (field, type, value) => {
859
- const currentValue = watch(field);
860
- if (type === "date") {
861
- const time = currentValue ? dayjs(currentValue).format("HH:mm") : field === "starts_at" ? "00:00" : "23:59";
862
- setValue(field, `${value}T${time}`);
863
- } else {
864
- const date = currentValue ? dayjs(currentValue).format("YYYY-MM-DD") : dayjs().format("YYYY-MM-DD");
865
- setValue(field, `${date}T${value}`);
866
- }
733
+ const [activeTab, setActiveTab] = useState("write");
734
+ const insertMarkdown = (before, after = "") => {
735
+ const textarea = document.getElementById(
736
+ `markdown-${label}`
737
+ );
738
+ if (!textarea) return;
739
+ const start = textarea.selectionStart;
740
+ const end = textarea.selectionEnd;
741
+ const selectedText = value.substring(start, end);
742
+ const newText = value.substring(0, start) + before + selectedText + after + value.substring(end);
743
+ onChange(newText);
744
+ setTimeout(() => {
745
+ textarea.focus();
746
+ textarea.setSelectionRange(
747
+ start + before.length,
748
+ start + before.length + selectedText.length
749
+ );
750
+ }, 0);
867
751
  };
868
- const onFormSubmit = (data) => {
869
- onSubmit(data);
752
+ const renderMarkdownPreview = (markdown) => {
753
+ let html = markdown;
754
+ html = html.replace(/^### (.*$)/gim, "<h3>$1</h3>");
755
+ html = html.replace(/^## (.*$)/gim, "<h2>$1</h2>");
756
+ html = html.replace(/^# (.*$)/gim, "<h1>$1</h1>");
757
+ html = html.replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>");
758
+ html = html.replace(/__(.+?)__/g, "<strong>$1</strong>");
759
+ html = html.replace(/\*(.+?)\*/g, "<em>$1</em>");
760
+ html = html.replace(/_(.+?)_/g, "<em>$1</em>");
761
+ html = html.replace(
762
+ /```(\w+)?\n([\s\S]+?)```/g,
763
+ '<pre class="bg-ui-bg-subtle p-4 rounded-lg overflow-x-auto"><code class="language-$1">$2</code></pre>'
764
+ );
765
+ html = html.replace(
766
+ /`([^`]+)`/g,
767
+ '<code class="bg-ui-bg-subtle px-1 py-0.5 rounded text-sm">$1</code>'
768
+ );
769
+ html = html.replace(
770
+ /\[([^\]]+)\]\(([^)]+)\)/g,
771
+ '<a href="$2" class="text-ui-fg-interactive underline">$1</a>'
772
+ );
773
+ html = html.replace(
774
+ /!\[([^\]]*)\]\(([^)]+)\)/g,
775
+ '<img src="$2" alt="$1" class="max-w-full h-auto rounded-lg my-2" />'
776
+ );
777
+ html = html.replace(/^\* (.+)$/gim, "<li>$1</li>");
778
+ html = html.replace(/^\- (.+)$/gim, "<li>$1</li>");
779
+ html = html.replace(
780
+ /(<li>.*<\/li>)/s,
781
+ "<ul class='list-disc ml-6'>$1</ul>"
782
+ );
783
+ html = html.replace(/\n\n/g, "</p><p>");
784
+ html = `<p>${html}</p>`;
785
+ return html;
870
786
  };
871
- const onFormError = () => {
872
- const errorMessages = Object.entries(errors).map(([key, value]) => {
873
- var _a2;
874
- if (key === "products" && Array.isArray(value)) {
875
- return value.map(
876
- (item, index) => item ? `Product ${index + 1}: ${Object.values(item).join(", ")}` : ""
877
- ).filter(Boolean).join(", ");
878
- }
879
- return (value == null ? void 0 : value.message) || ((_a2 = value == null ? void 0 : value.root) == null ? void 0 : _a2.message);
880
- }).filter(Boolean).join(", ");
881
- toast.error("Invalid data", {
882
- description: errorMessages
883
- });
884
- };
885
- return /* @__PURE__ */ jsxs(Container, { children: [
787
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
886
788
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
887
- /* @__PURE__ */ jsx("h1", { className: "text-xl font-semibold", children: "Flash sale campaign" }),
888
- /* @__PURE__ */ jsx(Button, { variant: "transparent", onClick: onCancel, children: "Cancel" })
789
+ /* @__PURE__ */ jsx(Label, { htmlFor: `markdown-${label}`, className: "font-medium", children: label }),
790
+ helpText && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 text-xs text-ui-fg-subtle", children: [
791
+ /* @__PURE__ */ jsx(InformationCircle, { className: "h-4 w-4" }),
792
+ /* @__PURE__ */ jsx("span", { children: helpText })
793
+ ] })
889
794
  ] }),
890
- /* @__PURE__ */ jsxs(
891
- "form",
892
- {
893
- onSubmit: handleSubmit(onFormSubmit, onFormError),
894
- className: "space-y-4 my-8",
895
- children: [
896
- /* @__PURE__ */ jsxs("div", { children: [
897
- /* @__PURE__ */ jsx(Label, { children: "Name" }),
898
- /* @__PURE__ */ jsx(Input, { ...register("name"), disabled }),
899
- errors.name && /* @__PURE__ */ jsx("p", { className: "text-red-500 text-sm mt-1", children: errors.name.message })
900
- ] }),
901
- /* @__PURE__ */ jsxs("div", { children: [
902
- /* @__PURE__ */ jsx(Label, { children: "Description" }),
903
- /* @__PURE__ */ jsx(Textarea, { ...register("description"), disabled }),
904
- errors.description && /* @__PURE__ */ jsx("p", { className: "text-red-500 text-sm mt-1", children: errors.description.message })
905
- ] }),
906
- /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
907
- /* @__PURE__ */ jsxs("div", { children: [
908
- /* @__PURE__ */ jsx(Label, { children: "Start Date" }),
909
- /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
910
- /* @__PURE__ */ jsx(
911
- Input,
912
- {
913
- type: "date",
914
- value: startsAt ? dayjs(startsAt).format("YYYY-MM-DD") : "",
915
- onChange: (e) => handleDateTimeChange("starts_at", "date", e.target.value),
916
- disabled,
917
- className: "flex-1"
918
- }
919
- ),
920
- /* @__PURE__ */ jsx(
921
- Input,
922
- {
923
- type: "time",
924
- value: startsAt ? dayjs(startsAt).format("HH:mm") : "",
925
- onChange: (e) => handleDateTimeChange("starts_at", "time", e.target.value),
926
- disabled,
927
- className: "w-32"
928
- }
929
- )
930
- ] }),
931
- errors.starts_at && /* @__PURE__ */ jsx("p", { className: "text-red-500 text-sm mt-1", children: errors.starts_at.message })
932
- ] }),
933
- /* @__PURE__ */ jsxs("div", { children: [
934
- /* @__PURE__ */ jsx(Label, { children: "End Date" }),
935
- /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
936
- /* @__PURE__ */ jsx(
937
- Input,
938
- {
939
- type: "date",
940
- value: endsAt ? dayjs(endsAt).format("YYYY-MM-DD") : "",
941
- onChange: (e) => handleDateTimeChange("ends_at", "date", e.target.value),
942
- disabled,
943
- className: "flex-1"
944
- }
945
- ),
946
- /* @__PURE__ */ jsx(
947
- Input,
948
- {
949
- type: "time",
950
- value: endsAt ? dayjs(endsAt).format("HH:mm") : "",
951
- onChange: (e) => handleDateTimeChange("ends_at", "time", e.target.value),
952
- disabled,
953
- className: "w-32"
954
- }
955
- )
956
- ] }),
957
- errors.ends_at && /* @__PURE__ */ jsx("p", { className: "text-red-500 text-sm mt-1", children: errors.ends_at.message })
958
- ] })
959
- ] }),
960
- /* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center", children: [
961
- /* @__PURE__ */ jsx(Label, { children: "Products" }),
962
- /* @__PURE__ */ jsx(
963
- Button,
964
- {
965
- type: "button",
966
- variant: "secondary",
967
- onClick: () => setOpenProductModal(true),
968
- disabled,
969
- children: "Add Product"
970
- }
971
- )
972
- ] }),
973
- ((_a = errors.products) == null ? void 0 : _a.root) && /* @__PURE__ */ jsx("p", { className: "text-red-500 text-sm", children: errors.products.root.message }),
974
- /* @__PURE__ */ jsxs(Table, { children: [
975
- /* @__PURE__ */ jsx(Table.Header, { children: /* @__PURE__ */ jsxs(Table.Row, { children: [
976
- /* @__PURE__ */ jsx(Table.HeaderCell, { children: "Product" }),
977
- /* @__PURE__ */ jsx(Table.HeaderCell, { children: "Discount Type" }),
978
- /* @__PURE__ */ jsx(Table.HeaderCell, { children: "Discount Value" }),
979
- /* @__PURE__ */ jsx(Table.HeaderCell, { children: "Limit" }),
980
- /* @__PURE__ */ jsx(Table.HeaderCell, { children: "Max Qty per Order" }),
981
- /* @__PURE__ */ jsx(Table.HeaderCell, { children: "Actions" })
982
- ] }) }),
983
- /* @__PURE__ */ jsx(Table.Body, { children: fields.map((field, index) => /* @__PURE__ */ jsxs(Table.Row, { children: [
984
- /* @__PURE__ */ jsx(Table.Cell, { children: field.product.title }),
985
- /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(
986
- Controller,
987
- {
988
- name: `products.${index}.discountType`,
989
- control,
990
- render: ({ field: field2 }) => /* @__PURE__ */ jsxs(
991
- Select,
992
- {
993
- value: field2.value,
994
- onValueChange: field2.onChange,
995
- disabled,
996
- children: [
997
- /* @__PURE__ */ jsx(Select.Trigger, { children: /* @__PURE__ */ jsx(Select.Value, { placeholder: "Select discount type" }) }),
998
- /* @__PURE__ */ jsx(Select.Content, { children: /* @__PURE__ */ jsx(Select.Item, { value: "percentage", children: "Percentage" }) })
999
- ]
1000
- }
1001
- )
1002
- }
1003
- ) }),
1004
- /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(
1005
- Controller,
1006
- {
1007
- name: `products.${index}.discountValue`,
1008
- control,
1009
- render: ({ field: field2 }) => /* @__PURE__ */ jsx(
1010
- Input,
1011
- {
1012
- type: "number",
1013
- value: field2.value,
1014
- onChange: (e) => field2.onChange(Number(e.target.value)),
1015
- disabled
1016
- }
1017
- )
1018
- }
1019
- ) }),
1020
- /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(
1021
- Controller,
1022
- {
1023
- name: `products.${index}.limit`,
1024
- control,
1025
- render: ({ field: field2 }) => /* @__PURE__ */ jsx(
1026
- Input,
1027
- {
1028
- type: "number",
1029
- value: field2.value,
1030
- onChange: (e) => field2.onChange(Number(e.target.value)),
1031
- disabled
1032
- }
1033
- )
1034
- }
1035
- ) }),
1036
- /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(
1037
- Controller,
1038
- {
1039
- name: `products.${index}.maxQty`,
1040
- control,
1041
- render: ({ field: field2 }) => /* @__PURE__ */ jsx(
1042
- Input,
1043
- {
1044
- type: "number",
1045
- value: field2.value,
1046
- onChange: (e) => field2.onChange(Number(e.target.value)),
1047
- disabled
1048
- }
1049
- )
1050
- }
1051
- ) }),
1052
- /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(
1053
- Button,
1054
- {
1055
- type: "button",
1056
- variant: "danger",
1057
- onClick: () => remove(index),
1058
- disabled,
1059
- children: /* @__PURE__ */ jsx(Trash, {})
1060
- }
1061
- ) })
1062
- ] }, field.id)) })
1063
- ] }),
1064
- /* @__PURE__ */ jsx(Button, { type: "submit", disabled, children: "Save" })
1065
- ]
1066
- }
1067
- ),
1068
- /* @__PURE__ */ jsx(FocusModal, { open: openProductModal, onOpenChange: setOpenProductModal, children: /* @__PURE__ */ jsxs(FocusModal.Content, { children: [
1069
- /* @__PURE__ */ jsx(FocusModal.Header, { title: "Add Product" }),
1070
- /* @__PURE__ */ jsx(FocusModal.Body, { children: /* @__PURE__ */ jsx(
1071
- ProductSelector,
795
+ showPreview ? /* @__PURE__ */ jsxs(Tabs, { value: activeTab, onValueChange: (v) => setActiveTab(v), children: [
796
+ /* @__PURE__ */ jsxs(Tabs.List, { children: [
797
+ /* @__PURE__ */ jsx(Tabs.Trigger, { value: "write", children: "Write" }),
798
+ /* @__PURE__ */ jsx(Tabs.Trigger, { value: "preview", children: "Preview" })
799
+ ] }),
800
+ /* @__PURE__ */ jsx(Tabs.Content, { value: "write", className: "mt-4", children: /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
801
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-2 border-b pb-2", children: [
802
+ /* @__PURE__ */ jsx(
803
+ Button,
804
+ {
805
+ type: "button",
806
+ variant: "secondary",
807
+ size: "small",
808
+ onClick: () => insertMarkdown("**", "**"),
809
+ title: "Bold",
810
+ children: /* @__PURE__ */ jsx("strong", { children: "B" })
811
+ }
812
+ ),
813
+ /* @__PURE__ */ jsx(
814
+ Button,
815
+ {
816
+ type: "button",
817
+ variant: "secondary",
818
+ size: "small",
819
+ onClick: () => insertMarkdown("*", "*"),
820
+ title: "Italic",
821
+ children: /* @__PURE__ */ jsx("em", { children: "I" })
822
+ }
823
+ ),
824
+ /* @__PURE__ */ jsx(
825
+ Button,
826
+ {
827
+ type: "button",
828
+ variant: "secondary",
829
+ size: "small",
830
+ onClick: () => insertMarkdown("`", "`"),
831
+ title: "Inline Code",
832
+ children: /* @__PURE__ */ jsx(CommandLine, { className: "h-4 w-4" })
833
+ }
834
+ ),
835
+ /* @__PURE__ */ jsx(
836
+ Button,
837
+ {
838
+ type: "button",
839
+ variant: "secondary",
840
+ size: "small",
841
+ onClick: () => insertMarkdown("## "),
842
+ title: "Heading",
843
+ children: "H2"
844
+ }
845
+ ),
846
+ /* @__PURE__ */ jsx(
847
+ Button,
848
+ {
849
+ type: "button",
850
+ variant: "secondary",
851
+ size: "small",
852
+ onClick: () => insertMarkdown("### "),
853
+ title: "Heading",
854
+ children: "H3"
855
+ }
856
+ ),
857
+ /* @__PURE__ */ jsx(
858
+ Button,
859
+ {
860
+ type: "button",
861
+ variant: "secondary",
862
+ size: "small",
863
+ onClick: () => insertMarkdown("[", "](url)"),
864
+ title: "Link",
865
+ children: "Link"
866
+ }
867
+ ),
868
+ /* @__PURE__ */ jsx(
869
+ Button,
870
+ {
871
+ type: "button",
872
+ variant: "secondary",
873
+ size: "small",
874
+ onClick: () => insertMarkdown("![alt](", ")"),
875
+ title: "Image",
876
+ children: "Image"
877
+ }
878
+ ),
879
+ /* @__PURE__ */ jsx(
880
+ Button,
881
+ {
882
+ type: "button",
883
+ variant: "secondary",
884
+ size: "small",
885
+ onClick: () => insertMarkdown("```\n", "\n```"),
886
+ title: "Code Block",
887
+ children: "Code"
888
+ }
889
+ ),
890
+ /* @__PURE__ */ jsx(
891
+ Button,
892
+ {
893
+ type: "button",
894
+ variant: "secondary",
895
+ size: "small",
896
+ onClick: () => insertMarkdown("- "),
897
+ title: "List",
898
+ children: "List"
899
+ }
900
+ )
901
+ ] }),
902
+ /* @__PURE__ */ jsx(
903
+ Textarea,
904
+ {
905
+ id: `markdown-${label}`,
906
+ value,
907
+ onChange: (e) => onChange(e.target.value),
908
+ placeholder,
909
+ rows,
910
+ className: "font-mono text-sm"
911
+ }
912
+ )
913
+ ] }) }),
914
+ /* @__PURE__ */ jsx(Tabs.Content, { value: "preview", className: "mt-4", children: /* @__PURE__ */ jsx(
915
+ "div",
1072
916
  {
1073
- selectedProductIds: fields.map((f) => f.product.id),
1074
- onSelectProduct: (product) => {
1075
- append({
1076
- product,
1077
- discountType: "percentage",
1078
- discountValue: 10,
1079
- limit: 10,
1080
- maxQty: 1
1081
- });
1082
- setOpenProductModal(false);
917
+ className: clx(
918
+ "min-h-[200px] rounded-lg border border-ui-border-base bg-ui-bg-subtle p-4",
919
+ "prose prose-sm max-w-none"
920
+ ),
921
+ dangerouslySetInnerHTML: {
922
+ __html: value ? renderMarkdownPreview(value) : '<p class="text-ui-fg-subtle">Nothing to preview</p>'
1083
923
  }
1084
924
  }
1085
925
  ) })
1086
- ] }) })
926
+ ] }) : /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
927
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-2 border-b pb-2", children: [
928
+ /* @__PURE__ */ jsx(
929
+ Button,
930
+ {
931
+ type: "button",
932
+ variant: "secondary",
933
+ size: "small",
934
+ onClick: () => insertMarkdown("**", "**"),
935
+ title: "Bold",
936
+ children: /* @__PURE__ */ jsx("strong", { children: "B" })
937
+ }
938
+ ),
939
+ /* @__PURE__ */ jsx(
940
+ Button,
941
+ {
942
+ type: "button",
943
+ variant: "secondary",
944
+ size: "small",
945
+ onClick: () => insertMarkdown("*", "*"),
946
+ title: "Italic",
947
+ children: /* @__PURE__ */ jsx("em", { children: "I" })
948
+ }
949
+ ),
950
+ /* @__PURE__ */ jsx(
951
+ Button,
952
+ {
953
+ type: "button",
954
+ variant: "secondary",
955
+ size: "small",
956
+ onClick: () => insertMarkdown("`", "`"),
957
+ title: "Inline Code",
958
+ children: /* @__PURE__ */ jsx(CommandLine, { className: "h-4 w-4" })
959
+ }
960
+ ),
961
+ /* @__PURE__ */ jsx(
962
+ Button,
963
+ {
964
+ type: "button",
965
+ variant: "secondary",
966
+ size: "small",
967
+ onClick: () => insertMarkdown("```\n", "\n```"),
968
+ title: "Code Block",
969
+ children: "Code"
970
+ }
971
+ )
972
+ ] }),
973
+ /* @__PURE__ */ jsx(
974
+ Textarea,
975
+ {
976
+ id: `markdown-${label}`,
977
+ value,
978
+ onChange: (e) => onChange(e.target.value),
979
+ placeholder,
980
+ rows,
981
+ className: "font-mono text-sm"
982
+ }
983
+ )
984
+ ] }),
985
+ /* @__PURE__ */ jsx("div", { className: "text-xs text-ui-fg-subtle", children: "Supports Markdown formatting: **bold**, *italic*, `code`, ```code blocks```, [links](url), ![images](url), and lists" })
1087
986
  ] });
1088
987
  };
1089
- const useFlashSaleById = (id) => {
1090
- const [data, setData] = useState(null);
1091
- const [isLoading, setIsLoading] = useState(true);
988
+ const CampaignImageUploader = ({
989
+ campaignId,
990
+ imageType,
991
+ currentImageUrl,
992
+ onClose,
993
+ onSuccess
994
+ }) => {
995
+ const [displayImage, setDisplayImage] = useState(currentImageUrl || null);
996
+ const [isUploading, setIsUploading] = useState(false);
997
+ const [isDragging, setIsDragging] = useState(false);
1092
998
  const [error, setError] = useState(null);
1093
- const fetchFlashSale = async () => {
1094
- if (!id) {
1095
- setIsLoading(false);
1096
- return;
999
+ const [previewUrl, setPreviewUrl] = useState(null);
1000
+ const isThumbnail = imageType === "thumbnail";
1001
+ const maxSize = 5;
1002
+ const allowedTypes = ["image/jpeg", "image/png", "image/gif", "image/webp"];
1003
+ const formatFileTypes = () => {
1004
+ return "JPEG, PNG, GIF, or WebP";
1005
+ };
1006
+ const validateFile = (file) => {
1007
+ if (!allowedTypes.includes(file.type)) {
1008
+ setError(`Please upload a valid image file (${formatFileTypes()})`);
1009
+ return false;
1097
1010
  }
1098
- setIsLoading(true);
1011
+ if (file.size > maxSize * 1024 * 1024) {
1012
+ setError(`File size must be less than ${maxSize}MB`);
1013
+ return false;
1014
+ }
1015
+ return true;
1016
+ };
1017
+ const handleFileUpload = async (file) => {
1018
+ var _a, _b;
1019
+ if (!validateFile(file)) return;
1099
1020
  setError(null);
1021
+ const reader = new FileReader();
1022
+ reader.onloadend = () => {
1023
+ setPreviewUrl(reader.result);
1024
+ };
1025
+ reader.readAsDataURL(file);
1026
+ setIsUploading(true);
1027
+ const formData = new FormData();
1028
+ formData.append("file", file);
1100
1029
  try {
1101
- const response = await axios.get(`/admin/flash-sales/${id}`);
1102
- setData(response.data);
1103
- } catch (err) {
1104
- setError(
1105
- err instanceof Error ? err : new Error("Failed to fetch flash sale")
1030
+ const response = await fetch(
1031
+ `/admin/campaigns/${campaignId}/${imageType}`,
1032
+ {
1033
+ method: "POST",
1034
+ body: formData,
1035
+ credentials: "include"
1036
+ }
1106
1037
  );
1038
+ if (response.ok) {
1039
+ const result = await response.json();
1040
+ const newImageUrl = imageType === "image" ? (_a = result.campaign_detail) == null ? void 0 : _a.image_url : (_b = result.campaign_detail) == null ? void 0 : _b.thumbnail_url;
1041
+ setDisplayImage(newImageUrl);
1042
+ setPreviewUrl(null);
1043
+ onSuccess == null ? void 0 : onSuccess();
1044
+ setTimeout(() => {
1045
+ onClose();
1046
+ }, 1e3);
1047
+ } else {
1048
+ const errorData = await response.json();
1049
+ setError(errorData.error || `Failed to upload ${imageType}`);
1050
+ setPreviewUrl(null);
1051
+ }
1052
+ } catch (err) {
1053
+ setError(`Error uploading ${imageType}`);
1054
+ setPreviewUrl(null);
1055
+ console.error(`Error uploading ${imageType}:`, err);
1107
1056
  } finally {
1108
- setIsLoading(false);
1057
+ setIsUploading(false);
1109
1058
  }
1110
1059
  };
1111
- useEffect(() => {
1112
- fetchFlashSale();
1113
- }, [id]);
1114
- return {
1115
- data,
1116
- isLoading,
1117
- error,
1118
- refetch: fetchFlashSale
1119
- };
1120
- };
1121
- const MarkdownEditor = ({
1122
- label,
1123
- value,
1124
- onChange,
1125
- placeholder,
1126
- helpText,
1127
- rows = 10,
1128
- showPreview = true
1129
- }) => {
1130
- const [activeTab, setActiveTab] = useState("write");
1131
- const insertMarkdown = (before, after = "") => {
1132
- const textarea = document.getElementById(
1133
- `markdown-${label}`
1134
- );
1135
- if (!textarea) return;
1136
- const start = textarea.selectionStart;
1137
- const end = textarea.selectionEnd;
1138
- const selectedText = value.substring(start, end);
1139
- const newText = value.substring(0, start) + before + selectedText + after + value.substring(end);
1140
- onChange(newText);
1141
- setTimeout(() => {
1142
- textarea.focus();
1143
- textarea.setSelectionRange(
1144
- start + before.length,
1145
- start + before.length + selectedText.length
1060
+ const handleDelete = async () => {
1061
+ if (!confirm(`Are you sure you want to delete the ${imageType}?`)) return;
1062
+ setIsUploading(true);
1063
+ setError(null);
1064
+ try {
1065
+ const response = await fetch(
1066
+ `/admin/campaigns/${campaignId}/${imageType}`,
1067
+ {
1068
+ method: "DELETE",
1069
+ credentials: "include"
1070
+ }
1146
1071
  );
1147
- }, 0);
1148
- };
1149
- const renderMarkdownPreview = (markdown) => {
1150
- let html = markdown;
1151
- html = html.replace(/^### (.*$)/gim, "<h3>$1</h3>");
1152
- html = html.replace(/^## (.*$)/gim, "<h2>$1</h2>");
1153
- html = html.replace(/^# (.*$)/gim, "<h1>$1</h1>");
1154
- html = html.replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>");
1155
- html = html.replace(/__(.+?)__/g, "<strong>$1</strong>");
1156
- html = html.replace(/\*(.+?)\*/g, "<em>$1</em>");
1157
- html = html.replace(/_(.+?)_/g, "<em>$1</em>");
1158
- html = html.replace(
1159
- /```(\w+)?\n([\s\S]+?)```/g,
1160
- '<pre class="bg-ui-bg-subtle p-4 rounded-lg overflow-x-auto"><code class="language-$1">$2</code></pre>'
1161
- );
1162
- html = html.replace(
1163
- /`([^`]+)`/g,
1164
- '<code class="bg-ui-bg-subtle px-1 py-0.5 rounded text-sm">$1</code>'
1165
- );
1166
- html = html.replace(
1167
- /\[([^\]]+)\]\(([^)]+)\)/g,
1168
- '<a href="$2" class="text-ui-fg-interactive underline">$1</a>'
1169
- );
1170
- html = html.replace(
1171
- /!\[([^\]]*)\]\(([^)]+)\)/g,
1172
- '<img src="$2" alt="$1" class="max-w-full h-auto rounded-lg my-2" />'
1173
- );
1174
- html = html.replace(/^\* (.+)$/gim, "<li>$1</li>");
1175
- html = html.replace(/^\- (.+)$/gim, "<li>$1</li>");
1176
- html = html.replace(
1177
- /(<li>.*<\/li>)/s,
1178
- "<ul class='list-disc ml-6'>$1</ul>"
1179
- );
1180
- html = html.replace(/\n\n/g, "</p><p>");
1181
- html = `<p>${html}</p>`;
1182
- return html;
1072
+ if (response.ok) {
1073
+ setDisplayImage(null);
1074
+ setPreviewUrl(null);
1075
+ onSuccess == null ? void 0 : onSuccess();
1076
+ setTimeout(() => {
1077
+ onClose();
1078
+ }, 500);
1079
+ } else {
1080
+ const errorData = await response.json();
1081
+ setError(errorData.error || `Failed to delete ${imageType}`);
1082
+ }
1083
+ } catch (err) {
1084
+ setError(`Error deleting ${imageType}`);
1085
+ console.error(`Error deleting ${imageType}:`, err);
1086
+ } finally {
1087
+ setIsUploading(false);
1088
+ }
1183
1089
  };
1184
- return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1185
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
1186
- /* @__PURE__ */ jsx(Label, { htmlFor: `markdown-${label}`, className: "font-medium", children: label }),
1187
- helpText && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 text-xs text-ui-fg-subtle", children: [
1188
- /* @__PURE__ */ jsx(InformationCircle, { className: "h-4 w-4" }),
1189
- /* @__PURE__ */ jsx("span", { children: helpText })
1190
- ] })
1191
- ] }),
1192
- showPreview ? /* @__PURE__ */ jsxs(Tabs, { value: activeTab, onValueChange: (v) => setActiveTab(v), children: [
1193
- /* @__PURE__ */ jsxs(Tabs.List, { children: [
1194
- /* @__PURE__ */ jsx(Tabs.Trigger, { value: "write", children: "Write" }),
1195
- /* @__PURE__ */ jsx(Tabs.Trigger, { value: "preview", children: "Preview" })
1090
+ const handleDragEnter = useCallback((e) => {
1091
+ e.preventDefault();
1092
+ e.stopPropagation();
1093
+ setIsDragging(true);
1094
+ }, []);
1095
+ const handleDragLeave = useCallback((e) => {
1096
+ e.preventDefault();
1097
+ e.stopPropagation();
1098
+ setIsDragging(false);
1099
+ }, []);
1100
+ const handleDragOver = useCallback((e) => {
1101
+ e.preventDefault();
1102
+ e.stopPropagation();
1103
+ }, []);
1104
+ const handleDrop = useCallback((e) => {
1105
+ var _a;
1106
+ e.preventDefault();
1107
+ e.stopPropagation();
1108
+ setIsDragging(false);
1109
+ const file = (_a = e.dataTransfer.files) == null ? void 0 : _a[0];
1110
+ if (file) {
1111
+ handleFileUpload(file);
1112
+ }
1113
+ }, []);
1114
+ const imageToDisplay = previewUrl || displayImage;
1115
+ return /* @__PURE__ */ jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/50", children: /* @__PURE__ */ jsxs("div", { className: "w-full max-w-2xl rounded-lg bg-ui-bg-base p-6", children: [
1116
+ /* @__PURE__ */ jsxs("div", { className: "mb-6 flex items-center justify-between", children: [
1117
+ /* @__PURE__ */ jsxs("div", { children: [
1118
+ /* @__PURE__ */ jsxs(Heading, { level: "h2", children: [
1119
+ "Upload Campaign ",
1120
+ isThumbnail ? "Thumbnail" : "Image"
1121
+ ] }),
1122
+ /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-subtle", children: isThumbnail ? "Thumbnail for campaign listing" : "Main campaign image" })
1196
1123
  ] }),
1197
- /* @__PURE__ */ jsx(Tabs.Content, { value: "write", className: "mt-4", children: /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1198
- /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-2 border-b pb-2", children: [
1199
- /* @__PURE__ */ jsx(
1200
- Button,
1201
- {
1202
- type: "button",
1203
- variant: "secondary",
1204
- size: "small",
1205
- onClick: () => insertMarkdown("**", "**"),
1206
- title: "Bold",
1207
- children: /* @__PURE__ */ jsx("strong", { children: "B" })
1208
- }
1209
- ),
1210
- /* @__PURE__ */ jsx(
1211
- Button,
1212
- {
1213
- type: "button",
1214
- variant: "secondary",
1215
- size: "small",
1216
- onClick: () => insertMarkdown("*", "*"),
1217
- title: "Italic",
1218
- children: /* @__PURE__ */ jsx("em", { children: "I" })
1219
- }
1220
- ),
1221
- /* @__PURE__ */ jsx(
1222
- Button,
1223
- {
1224
- type: "button",
1225
- variant: "secondary",
1226
- size: "small",
1227
- onClick: () => insertMarkdown("`", "`"),
1228
- title: "Inline Code",
1229
- children: /* @__PURE__ */ jsx(CommandLine, { className: "h-4 w-4" })
1230
- }
1231
- ),
1232
- /* @__PURE__ */ jsx(
1233
- Button,
1234
- {
1235
- type: "button",
1236
- variant: "secondary",
1237
- size: "small",
1238
- onClick: () => insertMarkdown("## "),
1239
- title: "Heading",
1240
- children: "H2"
1241
- }
1242
- ),
1243
- /* @__PURE__ */ jsx(
1244
- Button,
1245
- {
1246
- type: "button",
1247
- variant: "secondary",
1248
- size: "small",
1249
- onClick: () => insertMarkdown("### "),
1250
- title: "Heading",
1251
- children: "H3"
1252
- }
1253
- ),
1254
- /* @__PURE__ */ jsx(
1255
- Button,
1256
- {
1257
- type: "button",
1258
- variant: "secondary",
1259
- size: "small",
1260
- onClick: () => insertMarkdown("[", "](url)"),
1261
- title: "Link",
1262
- children: "Link"
1263
- }
1264
- ),
1124
+ /* @__PURE__ */ jsx(Button, { variant: "secondary", size: "small", onClick: onClose, children: /* @__PURE__ */ jsx(X, {}) })
1125
+ ] }),
1126
+ error && /* @__PURE__ */ jsx(Alert, { variant: "error", dismissible: true, className: "mb-4", children: error }),
1127
+ /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
1128
+ imageToDisplay ? /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
1129
+ /* @__PURE__ */ jsxs("div", { className: "relative overflow-hidden rounded-lg border border-ui-border-base bg-ui-bg-subtle", children: [
1265
1130
  /* @__PURE__ */ jsx(
1266
- Button,
1131
+ "img",
1267
1132
  {
1268
- type: "button",
1269
- variant: "secondary",
1270
- size: "small",
1271
- onClick: () => insertMarkdown("![alt](", ")"),
1272
- title: "Image",
1273
- children: "Image"
1133
+ src: imageToDisplay,
1134
+ alt: `Campaign ${imageType}`,
1135
+ className: clx(
1136
+ "w-full object-contain",
1137
+ isThumbnail ? "h-32" : "h-64"
1138
+ )
1274
1139
  }
1275
1140
  ),
1276
- /* @__PURE__ */ jsx(
1141
+ isUploading && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center justify-center bg-black/50", children: /* @__PURE__ */ jsx("div", { className: "text-white", children: "Uploading..." }) })
1142
+ ] }),
1143
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
1144
+ /* @__PURE__ */ jsxs(
1277
1145
  Button,
1278
1146
  {
1279
- type: "button",
1280
1147
  variant: "secondary",
1281
- size: "small",
1282
- onClick: () => insertMarkdown("```\n", "\n```"),
1283
- title: "Code Block",
1284
- children: "Code"
1148
+ disabled: isUploading,
1149
+ onClick: () => {
1150
+ var _a;
1151
+ return (_a = document.getElementById(`${imageType}-file-input`)) == null ? void 0 : _a.click();
1152
+ },
1153
+ children: [
1154
+ /* @__PURE__ */ jsx(CloudArrowUp, { className: "mr-2" }),
1155
+ "Replace"
1156
+ ]
1285
1157
  }
1286
1158
  ),
1287
- /* @__PURE__ */ jsx(
1159
+ /* @__PURE__ */ jsxs(
1288
1160
  Button,
1289
1161
  {
1290
- type: "button",
1291
- variant: "secondary",
1292
- size: "small",
1293
- onClick: () => insertMarkdown("- "),
1294
- title: "List",
1295
- children: "List"
1162
+ variant: "danger",
1163
+ disabled: isUploading,
1164
+ onClick: handleDelete,
1165
+ children: [
1166
+ /* @__PURE__ */ jsx(Trash, { className: "mr-2" }),
1167
+ "Delete"
1168
+ ]
1296
1169
  }
1297
1170
  )
1298
- ] }),
1299
- /* @__PURE__ */ jsx(
1300
- Textarea,
1301
- {
1302
- id: `markdown-${label}`,
1303
- value,
1304
- onChange: (e) => onChange(e.target.value),
1305
- placeholder,
1306
- rows,
1307
- className: "font-mono text-sm"
1308
- }
1309
- )
1310
- ] }) }),
1311
- /* @__PURE__ */ jsx(Tabs.Content, { value: "preview", className: "mt-4", children: /* @__PURE__ */ jsx(
1171
+ ] })
1172
+ ] }) : /* @__PURE__ */ jsxs(
1312
1173
  "div",
1313
1174
  {
1314
1175
  className: clx(
1315
- "min-h-[200px] rounded-lg border border-ui-border-base bg-ui-bg-subtle p-4",
1316
- "prose prose-sm max-w-none"
1176
+ "flex flex-col items-center justify-center rounded-lg border-2 border-dashed transition-colors",
1177
+ isThumbnail ? "h-48" : "h-64",
1178
+ isDragging ? "border-ui-border-interactive bg-ui-bg-highlight" : "border-ui-border-base bg-ui-bg-subtle",
1179
+ !isUploading && "cursor-pointer"
1317
1180
  ),
1318
- dangerouslySetInnerHTML: {
1319
- __html: value ? renderMarkdownPreview(value) : '<p class="text-ui-fg-subtle">Nothing to preview</p>'
1320
- }
1181
+ onDragEnter: handleDragEnter,
1182
+ onDragLeave: handleDragLeave,
1183
+ onDragOver: handleDragOver,
1184
+ onDrop: handleDrop,
1185
+ onClick: () => {
1186
+ var _a;
1187
+ return !isUploading && ((_a = document.getElementById(`${imageType}-file-input`)) == null ? void 0 : _a.click());
1188
+ },
1189
+ children: [
1190
+ /* @__PURE__ */ jsx(PhotoSolid, { className: "mb-4 h-12 w-12 text-ui-fg-subtle" }),
1191
+ /* @__PURE__ */ jsx(Text, { className: "mb-2 text-lg font-medium", children: isDragging ? `Drop ${imageType} here` : `Upload ${isThumbnail ? "Thumbnail" : "Image"}` }),
1192
+ /* @__PURE__ */ jsx(Text, { className: "mb-4 text-center text-ui-fg-subtle", children: "Drag and drop an image here, or click to select" }),
1193
+ /* @__PURE__ */ jsxs(Text, { className: "text-sm text-ui-fg-subtle", children: [
1194
+ formatFileTypes(),
1195
+ " • Max ",
1196
+ maxSize,
1197
+ "MB"
1198
+ ] }),
1199
+ isThumbnail && /* @__PURE__ */ jsx(Text, { className: "mt-2 text-xs text-ui-fg-subtle", children: "Recommended: 16:9 aspect ratio, minimum 400x225px" }),
1200
+ !isThumbnail && /* @__PURE__ */ jsx(Text, { className: "mt-2 text-xs text-ui-fg-subtle", children: "Recommended: High resolution, minimum 1200x600px" }),
1201
+ isUploading && /* @__PURE__ */ jsx("div", { className: "mt-4", children: /* @__PURE__ */ jsx(Text, { children: "Uploading..." }) })
1202
+ ]
1203
+ }
1204
+ ),
1205
+ /* @__PURE__ */ jsx(
1206
+ "input",
1207
+ {
1208
+ id: `${imageType}-file-input`,
1209
+ type: "file",
1210
+ accept: allowedTypes.join(","),
1211
+ onChange: (e) => {
1212
+ var _a;
1213
+ const file = (_a = e.target.files) == null ? void 0 : _a[0];
1214
+ if (file) handleFileUpload(file);
1215
+ e.target.value = "";
1216
+ },
1217
+ className: "hidden"
1218
+ }
1219
+ )
1220
+ ] }),
1221
+ /* @__PURE__ */ jsxs("div", { className: "mt-6 flex items-center justify-between border-t pt-4", children: [
1222
+ /* @__PURE__ */ jsx(Text, { className: "text-sm text-ui-fg-subtle", children: isThumbnail ? "Thumbnail will be displayed in campaign listings" : "Main image for the campaign detail page" }),
1223
+ /* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: onClose, children: "Close" })
1224
+ ] })
1225
+ ] }) });
1226
+ };
1227
+ const CampaignDetailForm = ({
1228
+ campaignId,
1229
+ campaignName
1230
+ }) => {
1231
+ const [campaignDetail, setCampaignDetail] = useState(
1232
+ null
1233
+ );
1234
+ const [isLoading, setIsLoading] = useState(true);
1235
+ const [isSaving, setIsSaving] = useState(false);
1236
+ const [showImageUploader, setShowImageUploader] = useState(null);
1237
+ const [formData, setFormData] = useState({
1238
+ detail_content: "",
1239
+ terms_and_conditions: "",
1240
+ meta_title: "",
1241
+ meta_description: "",
1242
+ meta_keywords: "",
1243
+ link_url: "",
1244
+ link_text: "",
1245
+ display_order: 0
1246
+ });
1247
+ useEffect(() => {
1248
+ fetchCampaignDetail();
1249
+ }, [campaignId]);
1250
+ const fetchCampaignDetail = async () => {
1251
+ setIsLoading(true);
1252
+ try {
1253
+ const response = await fetch(`/admin/campaigns/${campaignId}/detail`, {
1254
+ credentials: "include"
1255
+ });
1256
+ if (response.ok) {
1257
+ const data = await response.json();
1258
+ if (data.campaign_detail) {
1259
+ console.log("Campaign detail loaded:", {
1260
+ image_url: data.campaign_detail.image_url,
1261
+ thumbnail_url: data.campaign_detail.thumbnail_url,
1262
+ decoded_image: data.campaign_detail.image_url ? decodeURIComponent(data.campaign_detail.image_url) : null,
1263
+ decoded_thumbnail: data.campaign_detail.thumbnail_url ? decodeURIComponent(data.campaign_detail.thumbnail_url) : null
1264
+ });
1265
+ setCampaignDetail(data.campaign_detail);
1266
+ setFormData({
1267
+ detail_content: data.campaign_detail.detail_content || "",
1268
+ terms_and_conditions: data.campaign_detail.terms_and_conditions || "",
1269
+ meta_title: data.campaign_detail.meta_title || "",
1270
+ meta_description: data.campaign_detail.meta_description || "",
1271
+ meta_keywords: data.campaign_detail.meta_keywords || "",
1272
+ link_url: data.campaign_detail.link_url || "",
1273
+ link_text: data.campaign_detail.link_text || "",
1274
+ display_order: data.campaign_detail.display_order || 0
1275
+ });
1321
1276
  }
1322
- ) })
1323
- ] }) : /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1324
- /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-2 border-b pb-2", children: [
1325
- /* @__PURE__ */ jsx(
1326
- Button,
1327
- {
1328
- type: "button",
1329
- variant: "secondary",
1330
- size: "small",
1331
- onClick: () => insertMarkdown("**", "**"),
1332
- title: "Bold",
1333
- children: /* @__PURE__ */ jsx("strong", { children: "B" })
1334
- }
1335
- ),
1336
- /* @__PURE__ */ jsx(
1337
- Button,
1338
- {
1339
- type: "button",
1340
- variant: "secondary",
1341
- size: "small",
1342
- onClick: () => insertMarkdown("*", "*"),
1343
- title: "Italic",
1344
- children: /* @__PURE__ */ jsx("em", { children: "I" })
1345
- }
1346
- ),
1347
- /* @__PURE__ */ jsx(
1348
- Button,
1349
- {
1350
- type: "button",
1351
- variant: "secondary",
1352
- size: "small",
1353
- onClick: () => insertMarkdown("`", "`"),
1354
- title: "Inline Code",
1355
- children: /* @__PURE__ */ jsx(CommandLine, { className: "h-4 w-4" })
1356
- }
1357
- ),
1358
- /* @__PURE__ */ jsx(
1359
- Button,
1360
- {
1361
- type: "button",
1362
- variant: "secondary",
1363
- size: "small",
1364
- onClick: () => insertMarkdown("```\n", "\n```"),
1365
- title: "Code Block",
1366
- children: "Code"
1367
- }
1368
- )
1277
+ } else if (response.status !== 404) {
1278
+ console.error("Failed to fetch campaign detail");
1279
+ }
1280
+ } catch (error) {
1281
+ console.error("Error fetching campaign detail:", error);
1282
+ } finally {
1283
+ setIsLoading(false);
1284
+ }
1285
+ };
1286
+ const handleSave = async () => {
1287
+ setIsSaving(true);
1288
+ try {
1289
+ const response = await fetch(`/admin/campaigns/${campaignId}/detail`, {
1290
+ method: "POST",
1291
+ headers: {
1292
+ "Content-Type": "application/json"
1293
+ },
1294
+ body: JSON.stringify(formData),
1295
+ credentials: "include"
1296
+ });
1297
+ if (response.ok) {
1298
+ const data = await response.json();
1299
+ setCampaignDetail(data.campaign_detail);
1300
+ toast.success("Campaign details saved successfully");
1301
+ } else {
1302
+ const errorData = await response.json();
1303
+ toast.error(errorData.error || "Failed to save campaign details");
1304
+ }
1305
+ } catch (error) {
1306
+ console.error("Error saving campaign detail:", error);
1307
+ toast.error("Error saving campaign details");
1308
+ } finally {
1309
+ setIsSaving(false);
1310
+ }
1311
+ };
1312
+ if (isLoading) {
1313
+ return /* @__PURE__ */ jsx(Container, { children: /* @__PURE__ */ jsx("div", { className: "py-8 text-center", children: "Loading campaign details..." }) });
1314
+ }
1315
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1316
+ /* @__PURE__ */ jsxs(Container, { className: "space-y-6", children: [
1317
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
1318
+ /* @__PURE__ */ jsxs("div", { children: [
1319
+ /* @__PURE__ */ jsx(Heading, { level: "h2", children: "Campaign Details" }),
1320
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-ui-fg-subtle", children: campaignName })
1321
+ ] }),
1322
+ /* @__PURE__ */ jsx(Button, { onClick: handleSave, isLoading: isSaving, children: "Save Details" })
1369
1323
  ] }),
1370
- /* @__PURE__ */ jsx(
1371
- Textarea,
1324
+ /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
1325
+ /* @__PURE__ */ jsx(Heading, { level: "h3", children: "Images" }),
1326
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
1327
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1328
+ /* @__PURE__ */ jsx(Label, { children: "Main Campaign Image" }),
1329
+ /* @__PURE__ */ jsx(
1330
+ "div",
1331
+ {
1332
+ className: clx(
1333
+ "relative flex h-64 items-center justify-center overflow-hidden rounded-lg border-2 border-dashed",
1334
+ (campaignDetail == null ? void 0 : campaignDetail.image_url) ? "border-ui-border-base" : "border-ui-border-base bg-ui-bg-subtle"
1335
+ ),
1336
+ children: (campaignDetail == null ? void 0 : campaignDetail.image_url) ? /* @__PURE__ */ jsxs(Fragment, { children: [
1337
+ /* @__PURE__ */ jsx(
1338
+ "img",
1339
+ {
1340
+ src: campaignDetail.image_url,
1341
+ alt: "Campaign",
1342
+ className: "h-full w-full object-cover",
1343
+ onError: (e) => {
1344
+ console.error(
1345
+ "Failed to load image:",
1346
+ campaignDetail.image_url
1347
+ );
1348
+ e.currentTarget.style.display = "none";
1349
+ }
1350
+ }
1351
+ ),
1352
+ /* @__PURE__ */ jsxs(
1353
+ Button,
1354
+ {
1355
+ variant: "secondary",
1356
+ size: "small",
1357
+ className: "absolute bottom-2 right-2",
1358
+ onClick: () => setShowImageUploader("image"),
1359
+ children: [
1360
+ /* @__PURE__ */ jsx(PencilSquare, { className: "mr-1" }),
1361
+ "Change"
1362
+ ]
1363
+ }
1364
+ )
1365
+ ] }) : /* @__PURE__ */ jsxs(
1366
+ Button,
1367
+ {
1368
+ variant: "secondary",
1369
+ onClick: () => setShowImageUploader("image"),
1370
+ children: [
1371
+ /* @__PURE__ */ jsx(PhotoSolid, { className: "mr-2" }),
1372
+ "Upload Image"
1373
+ ]
1374
+ }
1375
+ )
1376
+ }
1377
+ )
1378
+ ] }),
1379
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1380
+ /* @__PURE__ */ jsx(Label, { children: "Thumbnail" }),
1381
+ /* @__PURE__ */ jsx(
1382
+ "div",
1383
+ {
1384
+ className: clx(
1385
+ "relative flex h-64 items-center justify-center overflow-hidden rounded-lg border-2 border-dashed",
1386
+ (campaignDetail == null ? void 0 : campaignDetail.thumbnail_url) ? "border-ui-border-base" : "border-ui-border-base bg-ui-bg-subtle"
1387
+ ),
1388
+ children: (campaignDetail == null ? void 0 : campaignDetail.thumbnail_url) ? /* @__PURE__ */ jsxs(Fragment, { children: [
1389
+ /* @__PURE__ */ jsx(
1390
+ "img",
1391
+ {
1392
+ src: campaignDetail.thumbnail_url,
1393
+ alt: "Thumbnail",
1394
+ className: "h-full w-full object-cover",
1395
+ onError: (e) => {
1396
+ console.error(
1397
+ "Failed to load thumbnail:",
1398
+ campaignDetail.thumbnail_url
1399
+ );
1400
+ e.currentTarget.style.display = "none";
1401
+ }
1402
+ }
1403
+ ),
1404
+ /* @__PURE__ */ jsxs(
1405
+ Button,
1406
+ {
1407
+ variant: "secondary",
1408
+ size: "small",
1409
+ className: "absolute bottom-2 right-2",
1410
+ onClick: () => setShowImageUploader("thumbnail"),
1411
+ children: [
1412
+ /* @__PURE__ */ jsx(PencilSquare, { className: "mr-1" }),
1413
+ "Change"
1414
+ ]
1415
+ }
1416
+ )
1417
+ ] }) : /* @__PURE__ */ jsxs(
1418
+ Button,
1419
+ {
1420
+ variant: "secondary",
1421
+ onClick: () => setShowImageUploader("thumbnail"),
1422
+ children: [
1423
+ /* @__PURE__ */ jsx(PhotoSolid, { className: "mr-2" }),
1424
+ "Upload Thumbnail"
1425
+ ]
1426
+ }
1427
+ )
1428
+ }
1429
+ )
1430
+ ] })
1431
+ ] })
1432
+ ] }),
1433
+ /* @__PURE__ */ jsx("div", { className: "space-y-4", children: /* @__PURE__ */ jsx(
1434
+ MarkdownEditor,
1372
1435
  {
1373
- id: `markdown-${label}`,
1374
- value,
1375
- onChange: (e) => onChange(e.target.value),
1376
- placeholder,
1377
- rows,
1378
- className: "font-mono text-sm"
1436
+ label: "Campaign Detail Content",
1437
+ value: formData.detail_content,
1438
+ onChange: (value) => setFormData({ ...formData, detail_content: value }),
1439
+ placeholder: "Enter campaign details in Markdown format. You can include images, links, code blocks, and more...",
1440
+ helpText: "Supports Markdown and code syntax",
1441
+ rows: 12,
1442
+ showPreview: true
1379
1443
  }
1380
- )
1444
+ ) }),
1445
+ /* @__PURE__ */ jsx("div", { className: "space-y-4", children: /* @__PURE__ */ jsx(
1446
+ MarkdownEditor,
1447
+ {
1448
+ label: "Terms and Conditions",
1449
+ value: formData.terms_and_conditions,
1450
+ onChange: (value) => setFormData({ ...formData, terms_and_conditions: value }),
1451
+ placeholder: "Enter terms and conditions for this campaign...",
1452
+ helpText: "Optional - Campaign specific terms",
1453
+ rows: 8,
1454
+ showPreview: true
1455
+ }
1456
+ ) }),
1457
+ /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
1458
+ /* @__PURE__ */ jsx(Heading, { level: "h3", children: "Call to Action Link" }),
1459
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
1460
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1461
+ /* @__PURE__ */ jsx(Label, { htmlFor: "link-url", children: "Link URL" }),
1462
+ /* @__PURE__ */ jsx(
1463
+ Input,
1464
+ {
1465
+ id: "link-url",
1466
+ type: "url",
1467
+ value: formData.link_url,
1468
+ onChange: (e) => setFormData({ ...formData, link_url: e.target.value }),
1469
+ placeholder: "https://example.com/campaign"
1470
+ }
1471
+ )
1472
+ ] }),
1473
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1474
+ /* @__PURE__ */ jsx(Label, { htmlFor: "link-text", children: "Link Text" }),
1475
+ /* @__PURE__ */ jsx(
1476
+ Input,
1477
+ {
1478
+ id: "link-text",
1479
+ value: formData.link_text,
1480
+ onChange: (e) => setFormData({ ...formData, link_text: e.target.value }),
1481
+ placeholder: "Shop Now"
1482
+ }
1483
+ )
1484
+ ] })
1485
+ ] })
1486
+ ] }),
1487
+ /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
1488
+ /* @__PURE__ */ jsx(Heading, { level: "h3", children: "SEO & Metadata" }),
1489
+ /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
1490
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1491
+ /* @__PURE__ */ jsx(Label, { htmlFor: "meta-title", children: "Meta Title" }),
1492
+ /* @__PURE__ */ jsx(
1493
+ Input,
1494
+ {
1495
+ id: "meta-title",
1496
+ value: formData.meta_title,
1497
+ onChange: (e) => setFormData({ ...formData, meta_title: e.target.value }),
1498
+ placeholder: "Campaign meta title for SEO"
1499
+ }
1500
+ )
1501
+ ] }),
1502
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1503
+ /* @__PURE__ */ jsx(Label, { htmlFor: "meta-description", children: "Meta Description" }),
1504
+ /* @__PURE__ */ jsx(
1505
+ Input,
1506
+ {
1507
+ id: "meta-description",
1508
+ value: formData.meta_description,
1509
+ onChange: (e) => setFormData({
1510
+ ...formData,
1511
+ meta_description: e.target.value
1512
+ }),
1513
+ placeholder: "Campaign meta description for SEO"
1514
+ }
1515
+ )
1516
+ ] }),
1517
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1518
+ /* @__PURE__ */ jsx(Label, { htmlFor: "meta-keywords", children: "Meta Keywords" }),
1519
+ /* @__PURE__ */ jsx(
1520
+ Input,
1521
+ {
1522
+ id: "meta-keywords",
1523
+ value: formData.meta_keywords,
1524
+ onChange: (e) => setFormData({
1525
+ ...formData,
1526
+ meta_keywords: e.target.value
1527
+ }),
1528
+ placeholder: "keyword1, keyword2, keyword3"
1529
+ }
1530
+ ),
1531
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-ui-fg-subtle", children: "Comma-separated keywords for SEO" })
1532
+ ] }),
1533
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1534
+ /* @__PURE__ */ jsx(Label, { htmlFor: "display-order", children: "Display Order" }),
1535
+ /* @__PURE__ */ jsx(
1536
+ Input,
1537
+ {
1538
+ id: "display-order",
1539
+ type: "number",
1540
+ value: formData.display_order,
1541
+ onChange: (e) => setFormData({
1542
+ ...formData,
1543
+ display_order: parseInt(e.target.value) || 0
1544
+ }),
1545
+ placeholder: "0"
1546
+ }
1547
+ ),
1548
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-ui-fg-subtle", children: "Lower numbers appear first in listings" })
1549
+ ] })
1550
+ ] })
1551
+ ] }),
1552
+ /* @__PURE__ */ jsx("div", { className: "flex justify-end border-t pt-4", children: /* @__PURE__ */ jsx(Button, { onClick: handleSave, isLoading: isSaving, children: "Save Campaign Details" }) })
1381
1553
  ] }),
1382
- /* @__PURE__ */ jsx("div", { className: "text-xs text-ui-fg-subtle", children: "Supports Markdown formatting: **bold**, *italic*, `code`, ```code blocks```, [links](url), ![images](url), and lists" })
1554
+ showImageUploader && /* @__PURE__ */ jsx(
1555
+ CampaignImageUploader,
1556
+ {
1557
+ campaignId,
1558
+ imageType: showImageUploader,
1559
+ currentImageUrl: showImageUploader === "image" ? campaignDetail == null ? void 0 : campaignDetail.image_url : campaignDetail == null ? void 0 : campaignDetail.thumbnail_url,
1560
+ onClose: () => setShowImageUploader(null),
1561
+ onSuccess: fetchCampaignDetail
1562
+ }
1563
+ )
1383
1564
  ] });
1384
1565
  };
1385
- const CampaignImageUploader = ({
1386
- campaignId,
1387
- imageType,
1388
- currentImageUrl,
1389
- onClose,
1390
- onSuccess
1391
- }) => {
1392
- const [displayImage, setDisplayImage] = useState(currentImageUrl || null);
1393
- const [isUploading, setIsUploading] = useState(false);
1394
- const [isDragging, setIsDragging] = useState(false);
1395
- const [error, setError] = useState(null);
1396
- const [previewUrl, setPreviewUrl] = useState(null);
1397
- const isThumbnail = imageType === "thumbnail";
1398
- const maxSize = 5;
1399
- const allowedTypes = ["image/jpeg", "image/png", "image/gif", "image/webp"];
1400
- const formatFileTypes = () => {
1401
- return "JPEG, PNG, GIF, or WebP";
1402
- };
1403
- const validateFile = (file) => {
1404
- if (!allowedTypes.includes(file.type)) {
1405
- setError(`Please upload a valid image file (${formatFileTypes()})`);
1406
- return false;
1407
- }
1408
- if (file.size > maxSize * 1024 * 1024) {
1409
- setError(`File size must be less than ${maxSize}MB`);
1410
- return false;
1411
- }
1412
- return true;
1566
+ const CouponDetail = () => {
1567
+ var _a, _b, _c, _d, _e, _f, _g;
1568
+ const { id } = useParams();
1569
+ const navigate = useNavigate();
1570
+ const [isEditing, setIsEditing] = useState(false);
1571
+ const { data, isLoading, refetch } = useCouponById(id ?? "");
1572
+ if (isLoading) {
1573
+ return /* @__PURE__ */ jsx(Container, { className: "flex items-center justify-center h-screen", children: /* @__PURE__ */ jsx("div", { className: "animate-spin h-10 w-10 border-4 border-primary rounded-full border-t-transparent" }) });
1574
+ }
1575
+ if (!(data == null ? void 0 : data.campaign)) {
1576
+ return /* @__PURE__ */ jsx(Container, { children: /* @__PURE__ */ jsx("p", { children: "Coupon not found" }) });
1577
+ }
1578
+ const campaign = data.campaign;
1579
+ const promotion = (_a = campaign.promotions) == null ? void 0 : _a[0];
1580
+ const initialValues = {
1581
+ name: campaign.name ?? "",
1582
+ description: campaign.description ?? "",
1583
+ type: "coupon",
1584
+ code: (promotion == null ? void 0 : promotion.code) ?? "",
1585
+ discount_type: ((_b = promotion == null ? void 0 : promotion.application_method) == null ? void 0 : _b.type) ?? "percentage",
1586
+ discount_value: Number(((_c = promotion == null ? void 0 : promotion.application_method) == null ? void 0 : _c.value) ?? 0),
1587
+ currency_code: ((_d = promotion == null ? void 0 : promotion.application_method) == null ? void 0 : _d.currency_code) ?? void 0,
1588
+ starts_at: campaign.starts_at ? dayjs(campaign.starts_at).format("YYYY-MM-DDTHH:mm") : void 0,
1589
+ ends_at: campaign.ends_at ? dayjs(campaign.ends_at).format("YYYY-MM-DDTHH:mm") : void 0,
1590
+ allocation: ((_e = promotion == null ? void 0 : promotion.application_method) == null ? void 0 : _e.allocation) === "total" ? "across" : ((_f = promotion == null ? void 0 : promotion.application_method) == null ? void 0 : _f.allocation) ?? "across",
1591
+ target_type: ((_g = promotion == null ? void 0 : promotion.application_method) == null ? void 0 : _g.target_type) ?? "order"
1413
1592
  };
1414
- const handleFileUpload = async (file) => {
1415
- var _a, _b;
1416
- if (!validateFile(file)) return;
1417
- setError(null);
1418
- const reader = new FileReader();
1419
- reader.onloadend = () => {
1420
- setPreviewUrl(reader.result);
1421
- };
1422
- reader.readAsDataURL(file);
1423
- setIsUploading(true);
1424
- const formData = new FormData();
1425
- formData.append("file", file);
1426
- try {
1427
- const response = await fetch(
1428
- `/admin/campaigns/${campaignId}/${imageType}`,
1429
- {
1430
- method: "POST",
1431
- body: formData,
1432
- credentials: "include"
1433
- }
1434
- );
1435
- if (response.ok) {
1436
- const result = await response.json();
1437
- const newImageUrl = imageType === "image" ? (_a = result.campaign_detail) == null ? void 0 : _a.image_url : (_b = result.campaign_detail) == null ? void 0 : _b.thumbnail_url;
1438
- setDisplayImage(newImageUrl);
1439
- setPreviewUrl(null);
1440
- onSuccess == null ? void 0 : onSuccess();
1441
- setTimeout(() => {
1442
- onClose();
1443
- }, 1e3);
1444
- } else {
1445
- const errorData = await response.json();
1446
- setError(errorData.error || `Failed to upload ${imageType}`);
1447
- setPreviewUrl(null);
1448
- }
1449
- } catch (err) {
1450
- setError(`Error uploading ${imageType}`);
1451
- setPreviewUrl(null);
1452
- console.error(`Error uploading ${imageType}:`, err);
1453
- } finally {
1454
- setIsUploading(false);
1593
+ const handleSubmit = async (formData) => {
1594
+ var _a2, _b2;
1595
+ if (!id) {
1596
+ return;
1455
1597
  }
1456
- };
1457
- const handleDelete = async () => {
1458
- if (!confirm(`Are you sure you want to delete the ${imageType}?`)) return;
1459
- setIsUploading(true);
1460
- setError(null);
1461
1598
  try {
1462
- const response = await fetch(
1463
- `/admin/campaigns/${campaignId}/${imageType}`,
1464
- {
1465
- method: "DELETE",
1466
- credentials: "include"
1467
- }
1468
- );
1469
- if (response.ok) {
1470
- setDisplayImage(null);
1471
- setPreviewUrl(null);
1472
- onSuccess == null ? void 0 : onSuccess();
1473
- setTimeout(() => {
1474
- onClose();
1475
- }, 500);
1476
- } else {
1477
- const errorData = await response.json();
1478
- setError(errorData.error || `Failed to delete ${imageType}`);
1599
+ await axios.put(`/admin/coupons/${id}`, {
1600
+ ...formData,
1601
+ starts_at: new Date(formData.starts_at).toUTCString(),
1602
+ ends_at: new Date(formData.ends_at).toUTCString(),
1603
+ currency_code: formData.discount_type === "fixed" ? formData.currency_code : void 0
1604
+ });
1605
+ toast.success("Coupon updated successfully");
1606
+ setIsEditing(false);
1607
+ refetch();
1608
+ } catch (error) {
1609
+ let message = "Failed to update coupon";
1610
+ if (axios.isAxiosError(error)) {
1611
+ message = ((_b2 = (_a2 = error.response) == null ? void 0 : _a2.data) == null ? void 0 : _b2.message) ?? message;
1612
+ } else if (error instanceof Error) {
1613
+ message = error.message;
1479
1614
  }
1480
- } catch (err) {
1481
- setError(`Error deleting ${imageType}`);
1482
- console.error(`Error deleting ${imageType}:`, err);
1483
- } finally {
1484
- setIsUploading(false);
1615
+ toast.error("Failed to update coupon", {
1616
+ description: message
1617
+ });
1485
1618
  }
1486
1619
  };
1487
- const handleDragEnter = useCallback((e) => {
1488
- e.preventDefault();
1489
- e.stopPropagation();
1490
- setIsDragging(true);
1491
- }, []);
1492
- const handleDragLeave = useCallback((e) => {
1493
- e.preventDefault();
1494
- e.stopPropagation();
1495
- setIsDragging(false);
1496
- }, []);
1497
- const handleDragOver = useCallback((e) => {
1498
- e.preventDefault();
1499
- e.stopPropagation();
1500
- }, []);
1501
- const handleDrop = useCallback((e) => {
1502
- var _a;
1503
- e.preventDefault();
1504
- e.stopPropagation();
1505
- setIsDragging(false);
1506
- const file = (_a = e.dataTransfer.files) == null ? void 0 : _a[0];
1507
- if (file) {
1508
- handleFileUpload(file);
1509
- }
1510
- }, []);
1511
- const imageToDisplay = previewUrl || displayImage;
1512
- return /* @__PURE__ */ jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/50", children: /* @__PURE__ */ jsxs("div", { className: "w-full max-w-2xl rounded-lg bg-ui-bg-base p-6", children: [
1620
+ return /* @__PURE__ */ jsxs(Container, { children: [
1513
1621
  /* @__PURE__ */ jsxs("div", { className: "mb-6 flex items-center justify-between", children: [
1514
1622
  /* @__PURE__ */ jsxs("div", { children: [
1515
- /* @__PURE__ */ jsxs(Heading, { level: "h2", children: [
1516
- "Upload Campaign ",
1517
- isThumbnail ? "Thumbnail" : "Image"
1518
- ] }),
1519
- /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-subtle", children: isThumbnail ? "Thumbnail for campaign listing" : "Main campaign image" })
1623
+ /* @__PURE__ */ jsx("h1", { className: "text-2xl font-semibold mb-1", children: campaign.name }),
1624
+ /* @__PURE__ */ jsx("p", { className: "text-ui-fg-subtle", children: "Manage coupon configuration and content" })
1520
1625
  ] }),
1521
- /* @__PURE__ */ jsx(Button, { variant: "secondary", size: "small", onClick: onClose, children: /* @__PURE__ */ jsx(X, {}) })
1626
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
1627
+ /* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: () => navigate("/coupons"), children: "Back to Coupons" }),
1628
+ /* @__PURE__ */ jsx(Button, { onClick: () => setIsEditing((prev) => !prev), children: isEditing ? "View Mode" : "Edit Mode" })
1629
+ ] })
1522
1630
  ] }),
1523
- error && /* @__PURE__ */ jsx(Alert, { variant: "error", dismissible: true, className: "mb-4", children: error }),
1524
- /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
1525
- imageToDisplay ? /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
1526
- /* @__PURE__ */ jsxs("div", { className: "relative overflow-hidden rounded-lg border border-ui-border-base bg-ui-bg-subtle", children: [
1527
- /* @__PURE__ */ jsx(
1528
- "img",
1529
- {
1530
- src: imageToDisplay,
1531
- alt: `Campaign ${imageType}`,
1532
- className: clx(
1533
- "w-full object-contain",
1534
- isThumbnail ? "h-32" : "h-64"
1535
- )
1536
- }
1537
- ),
1538
- isUploading && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center justify-center bg-black/50", children: /* @__PURE__ */ jsx("div", { className: "text-white", children: "Uploading..." }) })
1539
- ] }),
1540
- /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
1541
- /* @__PURE__ */ jsxs(
1542
- Button,
1543
- {
1544
- variant: "secondary",
1545
- disabled: isUploading,
1546
- onClick: () => {
1547
- var _a;
1548
- return (_a = document.getElementById(`${imageType}-file-input`)) == null ? void 0 : _a.click();
1549
- },
1550
- children: [
1551
- /* @__PURE__ */ jsx(CloudArrowUp, { className: "mr-2" }),
1552
- "Replace"
1553
- ]
1554
- }
1555
- ),
1556
- /* @__PURE__ */ jsxs(
1557
- Button,
1558
- {
1559
- variant: "danger",
1560
- disabled: isUploading,
1561
- onClick: handleDelete,
1562
- children: [
1563
- /* @__PURE__ */ jsx(Trash, { className: "mr-2" }),
1564
- "Delete"
1565
- ]
1566
- }
1567
- )
1568
- ] })
1569
- ] }) : /* @__PURE__ */ jsxs(
1570
- "div",
1631
+ /* @__PURE__ */ jsxs(Tabs, { defaultValue: "settings", children: [
1632
+ /* @__PURE__ */ jsxs(Tabs.List, { children: [
1633
+ /* @__PURE__ */ jsx(Tabs.Trigger, { value: "settings", children: "Coupon Settings" }),
1634
+ /* @__PURE__ */ jsx(Tabs.Trigger, { value: "details", children: "Coupon Details" })
1635
+ ] }),
1636
+ /* @__PURE__ */ jsx(Tabs.Content, { value: "settings", className: "mt-6", children: /* @__PURE__ */ jsx(
1637
+ CouponForm,
1571
1638
  {
1572
- className: clx(
1573
- "flex flex-col items-center justify-center rounded-lg border-2 border-dashed transition-colors",
1574
- isThumbnail ? "h-48" : "h-64",
1575
- isDragging ? "border-ui-border-interactive bg-ui-bg-highlight" : "border-ui-border-base bg-ui-bg-subtle",
1576
- !isUploading && "cursor-pointer"
1577
- ),
1578
- onDragEnter: handleDragEnter,
1579
- onDragLeave: handleDragLeave,
1580
- onDragOver: handleDragOver,
1581
- onDrop: handleDrop,
1582
- onClick: () => {
1583
- var _a;
1584
- return !isUploading && ((_a = document.getElementById(`${imageType}-file-input`)) == null ? void 0 : _a.click());
1585
- },
1586
- children: [
1587
- /* @__PURE__ */ jsx(PhotoSolid, { className: "mb-4 h-12 w-12 text-ui-fg-subtle" }),
1588
- /* @__PURE__ */ jsx(Text, { className: "mb-2 text-lg font-medium", children: isDragging ? `Drop ${imageType} here` : `Upload ${isThumbnail ? "Thumbnail" : "Image"}` }),
1589
- /* @__PURE__ */ jsx(Text, { className: "mb-4 text-center text-ui-fg-subtle", children: "Drag and drop an image here, or click to select" }),
1590
- /* @__PURE__ */ jsxs(Text, { className: "text-sm text-ui-fg-subtle", children: [
1591
- formatFileTypes(),
1592
- " • Max ",
1593
- maxSize,
1594
- "MB"
1595
- ] }),
1596
- isThumbnail && /* @__PURE__ */ jsx(Text, { className: "mt-2 text-xs text-ui-fg-subtle", children: "Recommended: 16:9 aspect ratio, minimum 400x225px" }),
1597
- !isThumbnail && /* @__PURE__ */ jsx(Text, { className: "mt-2 text-xs text-ui-fg-subtle", children: "Recommended: High resolution, minimum 1200x600px" }),
1598
- isUploading && /* @__PURE__ */ jsx("div", { className: "mt-4", children: /* @__PURE__ */ jsx(Text, { children: "Uploading..." }) })
1599
- ]
1639
+ initialData: initialValues,
1640
+ onSubmit: handleSubmit,
1641
+ onCancel: () => navigate("/coupons"),
1642
+ disabled: !isEditing
1600
1643
  }
1601
- ),
1602
- /* @__PURE__ */ jsx(
1603
- "input",
1644
+ ) }),
1645
+ /* @__PURE__ */ jsx(Tabs.Content, { value: "details", className: "mt-6", children: /* @__PURE__ */ jsx(
1646
+ CampaignDetailForm,
1604
1647
  {
1605
- id: `${imageType}-file-input`,
1606
- type: "file",
1607
- accept: allowedTypes.join(","),
1608
- onChange: (e) => {
1609
- var _a;
1610
- const file = (_a = e.target.files) == null ? void 0 : _a[0];
1611
- if (file) handleFileUpload(file);
1612
- e.target.value = "";
1613
- },
1614
- className: "hidden"
1648
+ campaignId: campaign.id,
1649
+ campaignName: campaign.name ?? ""
1615
1650
  }
1616
- )
1617
- ] }),
1618
- /* @__PURE__ */ jsxs("div", { className: "mt-6 flex items-center justify-between border-t pt-4", children: [
1619
- /* @__PURE__ */ jsx(Text, { className: "text-sm text-ui-fg-subtle", children: isThumbnail ? "Thumbnail will be displayed in campaign listings" : "Main image for the campaign detail page" }),
1620
- /* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: onClose, children: "Close" })
1651
+ ) })
1621
1652
  ] })
1622
- ] }) });
1653
+ ] });
1654
+ };
1655
+ const config$1 = defineRouteConfig({
1656
+ label: "Coupon Detail",
1657
+ icon: Tag
1658
+ });
1659
+ const CouponCreate = () => {
1660
+ const navigate = useNavigate();
1661
+ const handleSubmit = async (formData) => {
1662
+ var _a, _b;
1663
+ try {
1664
+ await axios.post("/admin/coupons", {
1665
+ ...formData,
1666
+ starts_at: new Date(formData.starts_at).toUTCString(),
1667
+ ends_at: new Date(formData.ends_at).toUTCString(),
1668
+ currency_code: formData.discount_type === "fixed" ? formData.currency_code : void 0
1669
+ });
1670
+ toast.success("Coupon created successfully");
1671
+ navigate("/coupons");
1672
+ } catch (error) {
1673
+ let message = "Failed to create coupon";
1674
+ if (axios.isAxiosError(error)) {
1675
+ message = ((_b = (_a = error.response) == null ? void 0 : _a.data) == null ? void 0 : _b.message) ?? message;
1676
+ } else if (error instanceof Error) {
1677
+ message = error.message;
1678
+ }
1679
+ toast.error("Failed to create coupon", {
1680
+ description: message
1681
+ });
1682
+ }
1683
+ };
1684
+ return /* @__PURE__ */ jsx(
1685
+ CouponForm,
1686
+ {
1687
+ onSubmit: handleSubmit,
1688
+ onCancel: () => navigate("/coupons")
1689
+ }
1690
+ );
1691
+ };
1692
+ const sdk = new Medusa({
1693
+ baseUrl: "/",
1694
+ debug: false,
1695
+ auth: {
1696
+ type: "session"
1697
+ }
1698
+ });
1699
+ const columnHelper = createDataTableColumnHelper();
1700
+ const columns = [
1701
+ columnHelper.accessor("title", {
1702
+ header: "Title",
1703
+ cell: (info) => /* @__PURE__ */ jsx(Text, { size: "large", className: "py-2", children: info.getValue() })
1704
+ }),
1705
+ columnHelper.accessor("description", {
1706
+ header: "Description",
1707
+ cell: (info) => /* @__PURE__ */ jsx(Text, { size: "large", className: "py-2", children: info.getValue() })
1708
+ })
1709
+ ];
1710
+ const ProductSelector = ({
1711
+ selectedProductIds,
1712
+ onSelectProduct
1713
+ }) => {
1714
+ const [search, setSearch] = useState("");
1715
+ const debouncedSearchHandler = debounce((value) => {
1716
+ setSearch(value);
1717
+ }, 500);
1718
+ const [products, setProducts] = useState(null);
1719
+ const [isLoading, setIsLoading] = useState(true);
1720
+ useEffect(() => {
1721
+ const fetchProducts = async () => {
1722
+ setIsLoading(true);
1723
+ try {
1724
+ const result = await sdk.admin.product.list({
1725
+ q: search,
1726
+ limit: 100
1727
+ });
1728
+ setProducts(result);
1729
+ } catch (error) {
1730
+ console.error("Failed to fetch products:", error);
1731
+ } finally {
1732
+ setIsLoading(false);
1733
+ }
1734
+ };
1735
+ fetchProducts();
1736
+ }, [selectedProductIds, search]);
1737
+ const selectableProducts = useMemo(() => {
1738
+ var _a;
1739
+ return (_a = products == null ? void 0 : products.products) == null ? void 0 : _a.filter(
1740
+ (product) => !selectedProductIds.includes(product.id)
1741
+ );
1742
+ }, [products == null ? void 0 : products.products, selectedProductIds]);
1743
+ const table = useDataTable({
1744
+ data: selectableProducts || [],
1745
+ columns,
1746
+ getRowId: (product) => product.id,
1747
+ onRowClick: (_, row) => {
1748
+ onSelectProduct == null ? void 0 : onSelectProduct(row.original);
1749
+ }
1750
+ });
1751
+ return /* @__PURE__ */ jsxs(Container, { children: [
1752
+ /* @__PURE__ */ jsx(
1753
+ Input,
1754
+ {
1755
+ className: "text-lg py-2",
1756
+ placeholder: "Search products...",
1757
+ onChange: (e) => debouncedSearchHandler(e.target.value)
1758
+ }
1759
+ ),
1760
+ /* @__PURE__ */ jsx(DataTable, { instance: table, children: /* @__PURE__ */ jsx(DataTable.Table, {}) })
1761
+ ] });
1623
1762
  };
1624
- const CampaignDetailForm = ({
1625
- campaignId,
1626
- campaignName
1763
+ const customCampaignSchema = z.object({
1764
+ name: z.string().min(1, "Name is required"),
1765
+ description: z.string().min(1, "Description is required"),
1766
+ type: z.literal("flash-sale"),
1767
+ starts_at: z.string().min(1, "Start date is required"),
1768
+ ends_at: z.string().min(1, "End date is required")
1769
+ });
1770
+ const campaignProductSchema = z.object({
1771
+ product: z.object({
1772
+ id: z.string(),
1773
+ title: z.string()
1774
+ }),
1775
+ discountType: z.enum([
1776
+ "percentage"
1777
+ // , "fixed"
1778
+ ]),
1779
+ discountValue: z.number().min(1),
1780
+ limit: z.number().min(1),
1781
+ maxQty: z.number().min(1)
1782
+ });
1783
+ const campaignDataSchema = customCampaignSchema.extend({
1784
+ products: z.array(campaignProductSchema).min(1, "At least one product is required")
1785
+ }).refine(
1786
+ (data) => new Date(data.starts_at) < new Date(data.ends_at),
1787
+ "End date must be after start date"
1788
+ );
1789
+ const FlashSaleForm = ({
1790
+ initialData,
1791
+ initialProducts,
1792
+ onSubmit,
1793
+ onCancel,
1794
+ disabled = false
1627
1795
  }) => {
1628
- const [campaignDetail, setCampaignDetail] = useState(
1629
- null
1630
- );
1631
- const [isLoading, setIsLoading] = useState(true);
1632
- const [isSaving, setIsSaving] = useState(false);
1633
- const [showImageUploader, setShowImageUploader] = useState(null);
1634
- const [formData, setFormData] = useState({
1635
- detail_content: "",
1636
- terms_and_conditions: "",
1637
- meta_title: "",
1638
- meta_description: "",
1639
- meta_keywords: "",
1640
- link_url: "",
1641
- link_text: "",
1642
- display_order: 0
1796
+ var _a;
1797
+ const {
1798
+ control,
1799
+ register,
1800
+ handleSubmit,
1801
+ watch,
1802
+ setValue,
1803
+ formState: { errors }
1804
+ } = useForm({
1805
+ resolver: zodResolver(campaignDataSchema),
1806
+ defaultValues: {
1807
+ name: (initialData == null ? void 0 : initialData.name) || "",
1808
+ description: (initialData == null ? void 0 : initialData.description) || "",
1809
+ type: "flash-sale",
1810
+ starts_at: (initialData == null ? void 0 : initialData.starts_at) || "",
1811
+ ends_at: (initialData == null ? void 0 : initialData.ends_at) || "",
1812
+ products: initialProducts ? Array.from(initialProducts.values()) : []
1813
+ }
1643
1814
  });
1644
- useEffect(() => {
1645
- fetchCampaignDetail();
1646
- }, [campaignId]);
1647
- const fetchCampaignDetail = async () => {
1648
- setIsLoading(true);
1649
- try {
1650
- const response = await fetch(`/admin/campaigns/${campaignId}/detail`, {
1651
- credentials: "include"
1652
- });
1653
- if (response.ok) {
1654
- const data = await response.json();
1655
- if (data.campaign_detail) {
1656
- console.log("Campaign detail loaded:", {
1657
- image_url: data.campaign_detail.image_url,
1658
- thumbnail_url: data.campaign_detail.thumbnail_url,
1659
- decoded_image: data.campaign_detail.image_url ? decodeURIComponent(data.campaign_detail.image_url) : null,
1660
- decoded_thumbnail: data.campaign_detail.thumbnail_url ? decodeURIComponent(data.campaign_detail.thumbnail_url) : null
1661
- });
1662
- setCampaignDetail(data.campaign_detail);
1663
- setFormData({
1664
- detail_content: data.campaign_detail.detail_content || "",
1665
- terms_and_conditions: data.campaign_detail.terms_and_conditions || "",
1666
- meta_title: data.campaign_detail.meta_title || "",
1667
- meta_description: data.campaign_detail.meta_description || "",
1668
- meta_keywords: data.campaign_detail.meta_keywords || "",
1669
- link_url: data.campaign_detail.link_url || "",
1670
- link_text: data.campaign_detail.link_text || "",
1671
- display_order: data.campaign_detail.display_order || 0
1672
- });
1673
- }
1674
- } else if (response.status !== 404) {
1675
- console.error("Failed to fetch campaign detail");
1676
- }
1677
- } catch (error) {
1678
- console.error("Error fetching campaign detail:", error);
1679
- } finally {
1680
- setIsLoading(false);
1815
+ const { fields, append, remove, update } = useFieldArray({
1816
+ control,
1817
+ name: "products"
1818
+ });
1819
+ const [openProductModal, setOpenProductModal] = useState(false);
1820
+ const startsAt = watch("starts_at");
1821
+ const endsAt = watch("ends_at");
1822
+ const handleDateTimeChange = (field, type, value) => {
1823
+ const currentValue = watch(field);
1824
+ if (type === "date") {
1825
+ const time = currentValue ? dayjs(currentValue).format("HH:mm") : field === "starts_at" ? "00:00" : "23:59";
1826
+ setValue(field, `${value}T${time}`);
1827
+ } else {
1828
+ const date = currentValue ? dayjs(currentValue).format("YYYY-MM-DD") : dayjs().format("YYYY-MM-DD");
1829
+ setValue(field, `${date}T${value}`);
1681
1830
  }
1682
1831
  };
1683
- const handleSave = async () => {
1684
- setIsSaving(true);
1685
- try {
1686
- const response = await fetch(`/admin/campaigns/${campaignId}/detail`, {
1687
- method: "POST",
1688
- headers: {
1689
- "Content-Type": "application/json"
1690
- },
1691
- body: JSON.stringify(formData),
1692
- credentials: "include"
1693
- });
1694
- if (response.ok) {
1695
- const data = await response.json();
1696
- setCampaignDetail(data.campaign_detail);
1697
- toast.success("Campaign details saved successfully");
1698
- } else {
1699
- const errorData = await response.json();
1700
- toast.error(errorData.error || "Failed to save campaign details");
1832
+ const onFormSubmit = (data) => {
1833
+ onSubmit(data);
1834
+ };
1835
+ const onFormError = () => {
1836
+ const errorMessages = Object.entries(errors).map(([key, value]) => {
1837
+ var _a2;
1838
+ if (key === "products" && Array.isArray(value)) {
1839
+ return value.map(
1840
+ (item, index) => item ? `Product ${index + 1}: ${Object.values(item).join(", ")}` : ""
1841
+ ).filter(Boolean).join(", ");
1701
1842
  }
1702
- } catch (error) {
1703
- console.error("Error saving campaign detail:", error);
1704
- toast.error("Error saving campaign details");
1705
- } finally {
1706
- setIsSaving(false);
1707
- }
1843
+ return (value == null ? void 0 : value.message) || ((_a2 = value == null ? void 0 : value.root) == null ? void 0 : _a2.message);
1844
+ }).filter(Boolean).join(", ");
1845
+ toast.error("Invalid data", {
1846
+ description: errorMessages
1847
+ });
1708
1848
  };
1709
- if (isLoading) {
1710
- return /* @__PURE__ */ jsx(Container, { children: /* @__PURE__ */ jsx("div", { className: "py-8 text-center", children: "Loading campaign details..." }) });
1711
- }
1712
- return /* @__PURE__ */ jsxs(Fragment, { children: [
1713
- /* @__PURE__ */ jsxs(Container, { className: "space-y-6", children: [
1714
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
1715
- /* @__PURE__ */ jsxs("div", { children: [
1716
- /* @__PURE__ */ jsx(Heading, { level: "h2", children: "Campaign Details" }),
1717
- /* @__PURE__ */ jsx("p", { className: "text-sm text-ui-fg-subtle", children: campaignName })
1718
- ] }),
1719
- /* @__PURE__ */ jsx(Button, { onClick: handleSave, isLoading: isSaving, children: "Save Details" })
1720
- ] }),
1721
- /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
1722
- /* @__PURE__ */ jsx(Heading, { level: "h3", children: "Images" }),
1723
- /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
1724
- /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1725
- /* @__PURE__ */ jsx(Label, { children: "Main Campaign Image" }),
1726
- /* @__PURE__ */ jsx(
1727
- "div",
1728
- {
1729
- className: clx(
1730
- "relative flex h-64 items-center justify-center overflow-hidden rounded-lg border-2 border-dashed",
1731
- (campaignDetail == null ? void 0 : campaignDetail.image_url) ? "border-ui-border-base" : "border-ui-border-base bg-ui-bg-subtle"
1732
- ),
1733
- children: (campaignDetail == null ? void 0 : campaignDetail.image_url) ? /* @__PURE__ */ jsxs(Fragment, { children: [
1734
- /* @__PURE__ */ jsx(
1735
- "img",
1736
- {
1737
- src: campaignDetail.image_url,
1738
- alt: "Campaign",
1739
- className: "h-full w-full object-cover",
1740
- onError: (e) => {
1741
- console.error(
1742
- "Failed to load image:",
1743
- campaignDetail.image_url
1744
- );
1745
- e.currentTarget.style.display = "none";
1746
- }
1747
- }
1748
- ),
1749
- /* @__PURE__ */ jsxs(
1750
- Button,
1751
- {
1752
- variant: "secondary",
1753
- size: "small",
1754
- className: "absolute bottom-2 right-2",
1755
- onClick: () => setShowImageUploader("image"),
1756
- children: [
1757
- /* @__PURE__ */ jsx(PencilSquare, { className: "mr-1" }),
1758
- "Change"
1759
- ]
1760
- }
1761
- )
1762
- ] }) : /* @__PURE__ */ jsxs(
1763
- Button,
1849
+ return /* @__PURE__ */ jsxs(Container, { children: [
1850
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
1851
+ /* @__PURE__ */ jsx("h1", { className: "text-xl font-semibold", children: "Flash sale campaign" }),
1852
+ /* @__PURE__ */ jsx(Button, { variant: "transparent", onClick: onCancel, children: "Cancel" })
1853
+ ] }),
1854
+ /* @__PURE__ */ jsxs(
1855
+ "form",
1856
+ {
1857
+ onSubmit: handleSubmit(onFormSubmit, onFormError),
1858
+ className: "space-y-4 my-8",
1859
+ children: [
1860
+ /* @__PURE__ */ jsxs("div", { children: [
1861
+ /* @__PURE__ */ jsx(Label, { children: "Name" }),
1862
+ /* @__PURE__ */ jsx(Input, { ...register("name"), disabled }),
1863
+ errors.name && /* @__PURE__ */ jsx("p", { className: "text-red-500 text-sm mt-1", children: errors.name.message })
1864
+ ] }),
1865
+ /* @__PURE__ */ jsxs("div", { children: [
1866
+ /* @__PURE__ */ jsx(Label, { children: "Description" }),
1867
+ /* @__PURE__ */ jsx(Textarea, { ...register("description"), disabled }),
1868
+ errors.description && /* @__PURE__ */ jsx("p", { className: "text-red-500 text-sm mt-1", children: errors.description.message })
1869
+ ] }),
1870
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
1871
+ /* @__PURE__ */ jsxs("div", { children: [
1872
+ /* @__PURE__ */ jsx(Label, { children: "Start Date" }),
1873
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
1874
+ /* @__PURE__ */ jsx(
1875
+ Input,
1876
+ {
1877
+ type: "date",
1878
+ value: startsAt ? dayjs(startsAt).format("YYYY-MM-DD") : "",
1879
+ onChange: (e) => handleDateTimeChange("starts_at", "date", e.target.value),
1880
+ disabled,
1881
+ className: "flex-1"
1882
+ }
1883
+ ),
1884
+ /* @__PURE__ */ jsx(
1885
+ Input,
1764
1886
  {
1765
- variant: "secondary",
1766
- onClick: () => setShowImageUploader("image"),
1767
- children: [
1768
- /* @__PURE__ */ jsx(PhotoSolid, { className: "mr-2" }),
1769
- "Upload Image"
1770
- ]
1887
+ type: "time",
1888
+ value: startsAt ? dayjs(startsAt).format("HH:mm") : "",
1889
+ onChange: (e) => handleDateTimeChange("starts_at", "time", e.target.value),
1890
+ disabled,
1891
+ className: "w-32"
1771
1892
  }
1772
1893
  )
1773
- }
1774
- )
1775
- ] }),
1776
- /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1777
- /* @__PURE__ */ jsx(Label, { children: "Thumbnail" }),
1778
- /* @__PURE__ */ jsx(
1779
- "div",
1780
- {
1781
- className: clx(
1782
- "relative flex h-64 items-center justify-center overflow-hidden rounded-lg border-2 border-dashed",
1783
- (campaignDetail == null ? void 0 : campaignDetail.thumbnail_url) ? "border-ui-border-base" : "border-ui-border-base bg-ui-bg-subtle"
1894
+ ] }),
1895
+ errors.starts_at && /* @__PURE__ */ jsx("p", { className: "text-red-500 text-sm mt-1", children: errors.starts_at.message })
1896
+ ] }),
1897
+ /* @__PURE__ */ jsxs("div", { children: [
1898
+ /* @__PURE__ */ jsx(Label, { children: "End Date" }),
1899
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
1900
+ /* @__PURE__ */ jsx(
1901
+ Input,
1902
+ {
1903
+ type: "date",
1904
+ value: endsAt ? dayjs(endsAt).format("YYYY-MM-DD") : "",
1905
+ onChange: (e) => handleDateTimeChange("ends_at", "date", e.target.value),
1906
+ disabled,
1907
+ className: "flex-1"
1908
+ }
1784
1909
  ),
1785
- children: (campaignDetail == null ? void 0 : campaignDetail.thumbnail_url) ? /* @__PURE__ */ jsxs(Fragment, { children: [
1786
- /* @__PURE__ */ jsx(
1787
- "img",
1788
- {
1789
- src: campaignDetail.thumbnail_url,
1790
- alt: "Thumbnail",
1791
- className: "h-full w-full object-cover",
1792
- onError: (e) => {
1793
- console.error(
1794
- "Failed to load thumbnail:",
1795
- campaignDetail.thumbnail_url
1796
- );
1797
- e.currentTarget.style.display = "none";
1798
- }
1799
- }
1800
- ),
1801
- /* @__PURE__ */ jsxs(
1802
- Button,
1803
- {
1804
- variant: "secondary",
1805
- size: "small",
1806
- className: "absolute bottom-2 right-2",
1807
- onClick: () => setShowImageUploader("thumbnail"),
1808
- children: [
1809
- /* @__PURE__ */ jsx(PencilSquare, { className: "mr-1" }),
1810
- "Change"
1811
- ]
1812
- }
1813
- )
1814
- ] }) : /* @__PURE__ */ jsxs(
1815
- Button,
1910
+ /* @__PURE__ */ jsx(
1911
+ Input,
1816
1912
  {
1817
- variant: "secondary",
1818
- onClick: () => setShowImageUploader("thumbnail"),
1819
- children: [
1820
- /* @__PURE__ */ jsx(PhotoSolid, { className: "mr-2" }),
1821
- "Upload Thumbnail"
1822
- ]
1913
+ type: "time",
1914
+ value: endsAt ? dayjs(endsAt).format("HH:mm") : "",
1915
+ onChange: (e) => handleDateTimeChange("ends_at", "time", e.target.value),
1916
+ disabled,
1917
+ className: "w-32"
1823
1918
  }
1824
1919
  )
1825
- }
1826
- )
1827
- ] })
1828
- ] })
1829
- ] }),
1830
- /* @__PURE__ */ jsx("div", { className: "space-y-4", children: /* @__PURE__ */ jsx(
1831
- MarkdownEditor,
1832
- {
1833
- label: "Campaign Detail Content",
1834
- value: formData.detail_content,
1835
- onChange: (value) => setFormData({ ...formData, detail_content: value }),
1836
- placeholder: "Enter campaign details in Markdown format. You can include images, links, code blocks, and more...",
1837
- helpText: "Supports Markdown and code syntax",
1838
- rows: 12,
1839
- showPreview: true
1840
- }
1841
- ) }),
1842
- /* @__PURE__ */ jsx("div", { className: "space-y-4", children: /* @__PURE__ */ jsx(
1843
- MarkdownEditor,
1844
- {
1845
- label: "Terms and Conditions",
1846
- value: formData.terms_and_conditions,
1847
- onChange: (value) => setFormData({ ...formData, terms_and_conditions: value }),
1848
- placeholder: "Enter terms and conditions for this campaign...",
1849
- helpText: "Optional - Campaign specific terms",
1850
- rows: 8,
1851
- showPreview: true
1852
- }
1853
- ) }),
1854
- /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
1855
- /* @__PURE__ */ jsx(Heading, { level: "h3", children: "Call to Action Link" }),
1856
- /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
1857
- /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1858
- /* @__PURE__ */ jsx(Label, { htmlFor: "link-url", children: "Link URL" }),
1859
- /* @__PURE__ */ jsx(
1860
- Input,
1861
- {
1862
- id: "link-url",
1863
- type: "url",
1864
- value: formData.link_url,
1865
- onChange: (e) => setFormData({ ...formData, link_url: e.target.value }),
1866
- placeholder: "https://example.com/campaign"
1867
- }
1868
- )
1869
- ] }),
1870
- /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1871
- /* @__PURE__ */ jsx(Label, { htmlFor: "link-text", children: "Link Text" }),
1872
- /* @__PURE__ */ jsx(
1873
- Input,
1874
- {
1875
- id: "link-text",
1876
- value: formData.link_text,
1877
- onChange: (e) => setFormData({ ...formData, link_text: e.target.value }),
1878
- placeholder: "Shop Now"
1879
- }
1880
- )
1881
- ] })
1882
- ] })
1883
- ] }),
1884
- /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
1885
- /* @__PURE__ */ jsx(Heading, { level: "h3", children: "SEO & Metadata" }),
1886
- /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
1887
- /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1888
- /* @__PURE__ */ jsx(Label, { htmlFor: "meta-title", children: "Meta Title" }),
1889
- /* @__PURE__ */ jsx(
1890
- Input,
1891
- {
1892
- id: "meta-title",
1893
- value: formData.meta_title,
1894
- onChange: (e) => setFormData({ ...formData, meta_title: e.target.value }),
1895
- placeholder: "Campaign meta title for SEO"
1896
- }
1897
- )
1920
+ ] }),
1921
+ errors.ends_at && /* @__PURE__ */ jsx("p", { className: "text-red-500 text-sm mt-1", children: errors.ends_at.message })
1922
+ ] })
1898
1923
  ] }),
1899
- /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1900
- /* @__PURE__ */ jsx(Label, { htmlFor: "meta-description", children: "Meta Description" }),
1924
+ /* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center", children: [
1925
+ /* @__PURE__ */ jsx(Label, { children: "Products" }),
1901
1926
  /* @__PURE__ */ jsx(
1902
- Input,
1927
+ Button,
1903
1928
  {
1904
- id: "meta-description",
1905
- value: formData.meta_description,
1906
- onChange: (e) => setFormData({
1907
- ...formData,
1908
- meta_description: e.target.value
1909
- }),
1910
- placeholder: "Campaign meta description for SEO"
1929
+ type: "button",
1930
+ variant: "secondary",
1931
+ onClick: () => setOpenProductModal(true),
1932
+ disabled,
1933
+ children: "Add Product"
1911
1934
  }
1912
1935
  )
1913
1936
  ] }),
1914
- /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1915
- /* @__PURE__ */ jsx(Label, { htmlFor: "meta-keywords", children: "Meta Keywords" }),
1916
- /* @__PURE__ */ jsx(
1917
- Input,
1918
- {
1919
- id: "meta-keywords",
1920
- value: formData.meta_keywords,
1921
- onChange: (e) => setFormData({
1922
- ...formData,
1923
- meta_keywords: e.target.value
1924
- }),
1925
- placeholder: "keyword1, keyword2, keyword3"
1926
- }
1927
- ),
1928
- /* @__PURE__ */ jsx("p", { className: "text-xs text-ui-fg-subtle", children: "Comma-separated keywords for SEO" })
1937
+ ((_a = errors.products) == null ? void 0 : _a.root) && /* @__PURE__ */ jsx("p", { className: "text-red-500 text-sm", children: errors.products.root.message }),
1938
+ /* @__PURE__ */ jsxs(Table, { children: [
1939
+ /* @__PURE__ */ jsx(Table.Header, { children: /* @__PURE__ */ jsxs(Table.Row, { children: [
1940
+ /* @__PURE__ */ jsx(Table.HeaderCell, { children: "Product" }),
1941
+ /* @__PURE__ */ jsx(Table.HeaderCell, { children: "Discount Type" }),
1942
+ /* @__PURE__ */ jsx(Table.HeaderCell, { children: "Discount Value" }),
1943
+ /* @__PURE__ */ jsx(Table.HeaderCell, { children: "Limit" }),
1944
+ /* @__PURE__ */ jsx(Table.HeaderCell, { children: "Max Qty per Order" }),
1945
+ /* @__PURE__ */ jsx(Table.HeaderCell, { children: "Actions" })
1946
+ ] }) }),
1947
+ /* @__PURE__ */ jsx(Table.Body, { children: fields.map((field, index) => /* @__PURE__ */ jsxs(Table.Row, { children: [
1948
+ /* @__PURE__ */ jsx(Table.Cell, { children: field.product.title }),
1949
+ /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(
1950
+ Controller,
1951
+ {
1952
+ name: `products.${index}.discountType`,
1953
+ control,
1954
+ render: ({ field: field2 }) => /* @__PURE__ */ jsxs(
1955
+ Select,
1956
+ {
1957
+ value: field2.value,
1958
+ onValueChange: field2.onChange,
1959
+ disabled,
1960
+ children: [
1961
+ /* @__PURE__ */ jsx(Select.Trigger, { children: /* @__PURE__ */ jsx(Select.Value, { placeholder: "Select discount type" }) }),
1962
+ /* @__PURE__ */ jsx(Select.Content, { children: /* @__PURE__ */ jsx(Select.Item, { value: "percentage", children: "Percentage" }) })
1963
+ ]
1964
+ }
1965
+ )
1966
+ }
1967
+ ) }),
1968
+ /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(
1969
+ Controller,
1970
+ {
1971
+ name: `products.${index}.discountValue`,
1972
+ control,
1973
+ render: ({ field: field2 }) => /* @__PURE__ */ jsx(
1974
+ Input,
1975
+ {
1976
+ type: "number",
1977
+ value: field2.value,
1978
+ onChange: (e) => field2.onChange(Number(e.target.value)),
1979
+ disabled
1980
+ }
1981
+ )
1982
+ }
1983
+ ) }),
1984
+ /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(
1985
+ Controller,
1986
+ {
1987
+ name: `products.${index}.limit`,
1988
+ control,
1989
+ render: ({ field: field2 }) => /* @__PURE__ */ jsx(
1990
+ Input,
1991
+ {
1992
+ type: "number",
1993
+ value: field2.value,
1994
+ onChange: (e) => field2.onChange(Number(e.target.value)),
1995
+ disabled
1996
+ }
1997
+ )
1998
+ }
1999
+ ) }),
2000
+ /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(
2001
+ Controller,
2002
+ {
2003
+ name: `products.${index}.maxQty`,
2004
+ control,
2005
+ render: ({ field: field2 }) => /* @__PURE__ */ jsx(
2006
+ Input,
2007
+ {
2008
+ type: "number",
2009
+ value: field2.value,
2010
+ onChange: (e) => field2.onChange(Number(e.target.value)),
2011
+ disabled
2012
+ }
2013
+ )
2014
+ }
2015
+ ) }),
2016
+ /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(
2017
+ Button,
2018
+ {
2019
+ type: "button",
2020
+ variant: "danger",
2021
+ onClick: () => remove(index),
2022
+ disabled,
2023
+ children: /* @__PURE__ */ jsx(Trash, {})
2024
+ }
2025
+ ) })
2026
+ ] }, field.id)) })
1929
2027
  ] }),
1930
- /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1931
- /* @__PURE__ */ jsx(Label, { htmlFor: "display-order", children: "Display Order" }),
1932
- /* @__PURE__ */ jsx(
1933
- Input,
1934
- {
1935
- id: "display-order",
1936
- type: "number",
1937
- value: formData.display_order,
1938
- onChange: (e) => setFormData({
1939
- ...formData,
1940
- display_order: parseInt(e.target.value) || 0
1941
- }),
1942
- placeholder: "0"
1943
- }
1944
- ),
1945
- /* @__PURE__ */ jsx("p", { className: "text-xs text-ui-fg-subtle", children: "Lower numbers appear first in listings" })
1946
- ] })
1947
- ] })
1948
- ] }),
1949
- /* @__PURE__ */ jsx("div", { className: "flex justify-end border-t pt-4", children: /* @__PURE__ */ jsx(Button, { onClick: handleSave, isLoading: isSaving, children: "Save Campaign Details" }) })
1950
- ] }),
1951
- showImageUploader && /* @__PURE__ */ jsx(
1952
- CampaignImageUploader,
1953
- {
1954
- campaignId,
1955
- imageType: showImageUploader,
1956
- currentImageUrl: showImageUploader === "image" ? campaignDetail == null ? void 0 : campaignDetail.image_url : campaignDetail == null ? void 0 : campaignDetail.thumbnail_url,
1957
- onClose: () => setShowImageUploader(null),
1958
- onSuccess: fetchCampaignDetail
2028
+ /* @__PURE__ */ jsx(Button, { type: "submit", disabled, children: "Save" })
2029
+ ]
1959
2030
  }
1960
- )
2031
+ ),
2032
+ /* @__PURE__ */ jsx(FocusModal, { open: openProductModal, onOpenChange: setOpenProductModal, children: /* @__PURE__ */ jsxs(FocusModal.Content, { children: [
2033
+ /* @__PURE__ */ jsx(FocusModal.Header, { title: "Add Product" }),
2034
+ /* @__PURE__ */ jsx(FocusModal.Body, { children: /* @__PURE__ */ jsx(
2035
+ ProductSelector,
2036
+ {
2037
+ selectedProductIds: fields.map((f) => f.product.id),
2038
+ onSelectProduct: (product) => {
2039
+ append({
2040
+ product,
2041
+ discountType: "percentage",
2042
+ discountValue: 10,
2043
+ limit: 10,
2044
+ maxQty: 1
2045
+ });
2046
+ setOpenProductModal(false);
2047
+ }
2048
+ }
2049
+ ) })
2050
+ ] }) })
1961
2051
  ] });
1962
2052
  };
2053
+ const useFlashSaleById = (id) => {
2054
+ const [data, setData] = useState(null);
2055
+ const [isLoading, setIsLoading] = useState(true);
2056
+ const [error, setError] = useState(null);
2057
+ const fetchFlashSale = async () => {
2058
+ if (!id) {
2059
+ setIsLoading(false);
2060
+ return;
2061
+ }
2062
+ setIsLoading(true);
2063
+ setError(null);
2064
+ try {
2065
+ const response = await axios.get(`/admin/flash-sales/${id}`);
2066
+ setData(response.data);
2067
+ } catch (err) {
2068
+ setError(
2069
+ err instanceof Error ? err : new Error("Failed to fetch flash sale")
2070
+ );
2071
+ } finally {
2072
+ setIsLoading(false);
2073
+ }
2074
+ };
2075
+ useEffect(() => {
2076
+ fetchFlashSale();
2077
+ }, [id]);
2078
+ return {
2079
+ data,
2080
+ isLoading,
2081
+ error,
2082
+ refetch: fetchFlashSale
2083
+ };
2084
+ };
1963
2085
  const FlashSaleDetail = () => {
1964
2086
  const { id } = useParams();
1965
2087
  const navigate = useNavigate();
@@ -2042,7 +2164,7 @@ const FlashSaleDetail = () => {
2042
2164
  ] })
2043
2165
  ] });
2044
2166
  };
2045
- const config$1 = defineRouteConfig({
2167
+ const config = defineRouteConfig({
2046
2168
  label: "Flash Sale Detail",
2047
2169
  icon: Sparkles
2048
2170
  });
@@ -2081,128 +2203,6 @@ const FlashSaleCreate = () => {
2081
2203
  }
2082
2204
  );
2083
2205
  };
2084
- const useCouponById = (id) => {
2085
- const [data, setData] = useState(null);
2086
- const [isLoading, setIsLoading] = useState(true);
2087
- const [error, setError] = useState(null);
2088
- const fetchCoupon = async () => {
2089
- if (!id) {
2090
- return;
2091
- }
2092
- setIsLoading(true);
2093
- setError(null);
2094
- try {
2095
- const response = await axios.get(`/admin/coupons/${id}`);
2096
- setData(response.data);
2097
- } catch (err) {
2098
- setError(err instanceof Error ? err : new Error("Failed to fetch coupon"));
2099
- } finally {
2100
- setIsLoading(false);
2101
- }
2102
- };
2103
- useEffect(() => {
2104
- fetchCoupon();
2105
- }, [id]);
2106
- return {
2107
- data,
2108
- isLoading,
2109
- error,
2110
- refetch: fetchCoupon
2111
- };
2112
- };
2113
- const CouponDetail = () => {
2114
- var _a, _b, _c, _d, _e, _f, _g;
2115
- const { id } = useParams();
2116
- const navigate = useNavigate();
2117
- const [isEditing, setIsEditing] = useState(false);
2118
- const { data, isLoading, refetch } = useCouponById(id ?? "");
2119
- if (isLoading) {
2120
- return /* @__PURE__ */ jsx(Container, { className: "flex items-center justify-center h-screen", children: /* @__PURE__ */ jsx("div", { className: "animate-spin h-10 w-10 border-4 border-primary rounded-full border-t-transparent" }) });
2121
- }
2122
- if (!(data == null ? void 0 : data.campaign)) {
2123
- return /* @__PURE__ */ jsx(Container, { children: /* @__PURE__ */ jsx("p", { children: "Coupon not found" }) });
2124
- }
2125
- const campaign = data.campaign;
2126
- const promotion = (_a = campaign.promotions) == null ? void 0 : _a[0];
2127
- const initialValues = {
2128
- name: campaign.name ?? "",
2129
- description: campaign.description ?? "",
2130
- type: "coupon",
2131
- code: (promotion == null ? void 0 : promotion.code) ?? "",
2132
- discount_type: ((_b = promotion == null ? void 0 : promotion.application_method) == null ? void 0 : _b.type) ?? "percentage",
2133
- discount_value: Number(((_c = promotion == null ? void 0 : promotion.application_method) == null ? void 0 : _c.value) ?? 0),
2134
- currency_code: ((_d = promotion == null ? void 0 : promotion.application_method) == null ? void 0 : _d.currency_code) ?? void 0,
2135
- starts_at: campaign.starts_at ? dayjs(campaign.starts_at).format("YYYY-MM-DDTHH:mm") : void 0,
2136
- ends_at: campaign.ends_at ? dayjs(campaign.ends_at).format("YYYY-MM-DDTHH:mm") : void 0,
2137
- allocation: ((_e = promotion == null ? void 0 : promotion.application_method) == null ? void 0 : _e.allocation) === "total" ? "across" : ((_f = promotion == null ? void 0 : promotion.application_method) == null ? void 0 : _f.allocation) ?? "across",
2138
- target_type: ((_g = promotion == null ? void 0 : promotion.application_method) == null ? void 0 : _g.target_type) ?? "order"
2139
- };
2140
- const handleSubmit = async (formData) => {
2141
- var _a2, _b2;
2142
- if (!id) {
2143
- return;
2144
- }
2145
- try {
2146
- await axios.put(`/admin/coupons/${id}`, {
2147
- ...formData,
2148
- starts_at: new Date(formData.starts_at).toUTCString(),
2149
- ends_at: new Date(formData.ends_at).toUTCString(),
2150
- currency_code: formData.discount_type === "fixed" ? formData.currency_code : void 0
2151
- });
2152
- toast.success("Coupon updated successfully");
2153
- setIsEditing(false);
2154
- refetch();
2155
- } catch (error) {
2156
- let message = "Failed to update coupon";
2157
- if (axios.isAxiosError(error)) {
2158
- message = ((_b2 = (_a2 = error.response) == null ? void 0 : _a2.data) == null ? void 0 : _b2.message) ?? message;
2159
- } else if (error instanceof Error) {
2160
- message = error.message;
2161
- }
2162
- toast.error("Failed to update coupon", {
2163
- description: message
2164
- });
2165
- }
2166
- };
2167
- return /* @__PURE__ */ jsxs(Container, { children: [
2168
- /* @__PURE__ */ jsxs("div", { className: "mb-6 flex items-center justify-between", children: [
2169
- /* @__PURE__ */ jsxs("div", { children: [
2170
- /* @__PURE__ */ jsx("h1", { className: "text-2xl font-semibold mb-1", children: campaign.name }),
2171
- /* @__PURE__ */ jsx("p", { className: "text-ui-fg-subtle", children: "Manage coupon configuration and content" })
2172
- ] }),
2173
- /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
2174
- /* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: () => navigate("/coupons"), children: "Back to Coupons" }),
2175
- /* @__PURE__ */ jsx(Button, { onClick: () => setIsEditing((prev) => !prev), children: isEditing ? "View Mode" : "Edit Mode" })
2176
- ] })
2177
- ] }),
2178
- /* @__PURE__ */ jsxs(Tabs, { defaultValue: "settings", children: [
2179
- /* @__PURE__ */ jsxs(Tabs.List, { children: [
2180
- /* @__PURE__ */ jsx(Tabs.Trigger, { value: "settings", children: "Coupon Settings" }),
2181
- /* @__PURE__ */ jsx(Tabs.Trigger, { value: "details", children: "Coupon Details" })
2182
- ] }),
2183
- /* @__PURE__ */ jsx(Tabs.Content, { value: "settings", className: "mt-6", children: /* @__PURE__ */ jsx(
2184
- CouponForm,
2185
- {
2186
- initialData: initialValues,
2187
- onSubmit: handleSubmit,
2188
- onCancel: () => navigate("/coupons"),
2189
- disabled: !isEditing
2190
- }
2191
- ) }),
2192
- /* @__PURE__ */ jsx(Tabs.Content, { value: "details", className: "mt-6", children: /* @__PURE__ */ jsx(
2193
- CampaignDetailForm,
2194
- {
2195
- campaignId: campaign.id,
2196
- campaignName: campaign.name ?? ""
2197
- }
2198
- ) })
2199
- ] })
2200
- ] });
2201
- };
2202
- const config = defineRouteConfig({
2203
- label: "Coupon Detail",
2204
- icon: Tag
2205
- });
2206
2206
  const widgetModule = { widgets: [
2207
2207
  {
2208
2208
  Component: CampaignDetailWidget,
@@ -2219,6 +2219,10 @@ const routeModule = {
2219
2219
  Component: FlashSale,
2220
2220
  path: "/flash-sales"
2221
2221
  },
2222
+ {
2223
+ Component: CouponDetail,
2224
+ path: "/coupons/:id"
2225
+ },
2222
2226
  {
2223
2227
  Component: CouponCreate,
2224
2228
  path: "/coupons/create"
@@ -2230,10 +2234,6 @@ const routeModule = {
2230
2234
  {
2231
2235
  Component: FlashSaleCreate,
2232
2236
  path: "/flash-sales/create"
2233
- },
2234
- {
2235
- Component: CouponDetail,
2236
- path: "/coupons/:id"
2237
2237
  }
2238
2238
  ]
2239
2239
  };
@@ -2252,14 +2252,14 @@ const menuItemModule = {
2252
2252
  nested: void 0
2253
2253
  },
2254
2254
  {
2255
- label: config.label,
2256
- icon: config.icon,
2255
+ label: config$1.label,
2256
+ icon: config$1.icon,
2257
2257
  path: "/coupons/:id",
2258
2258
  nested: void 0
2259
2259
  },
2260
2260
  {
2261
- label: config$1.label,
2262
- icon: config$1.icon,
2261
+ label: config.label,
2262
+ icon: config.icon,
2263
2263
  path: "/flash-sales/:id",
2264
2264
  nested: void 0
2265
2265
  }