@lodashventure/medusa-campaign 1.5.1 → 1.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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, Tabs, clx, Table, FocusModal } from "@medusajs/ui";
4
- import { Sparkles, PhotoSolid, PencilSquare, Eye, Tag, InformationCircle, CommandLine, X, CloudArrowUp, Trash } from "@medusajs/icons";
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";
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,1396 +692,1274 @@ const CouponForm = ({
692
692
  )
693
693
  ] });
694
694
  };
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);
695
+ const CouponCreate = () => {
696
+ const navigate = useNavigate();
697
+ const handleSubmit = async (formData) => {
698
+ var _a, _b;
705
699
  try {
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);
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
+ });
712
718
  }
713
719
  };
714
- useEffect(() => {
715
- fetchCoupon();
716
- }, [id]);
717
- return {
718
- data,
719
- isLoading,
720
- error,
721
- refetch: fetchCoupon
722
- };
720
+ return /* @__PURE__ */ jsx(
721
+ CouponForm,
722
+ {
723
+ onSubmit: handleSubmit,
724
+ onCancel: () => navigate("/coupons")
725
+ }
726
+ );
723
727
  };
724
- const MarkdownEditor = ({
725
- label,
726
- value,
727
- onChange,
728
- placeholder,
729
- helpText,
730
- rows = 10,
731
- showPreview = true
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
732
749
  }) => {
733
- const [activeTab, setActiveTab] = useState("write");
734
- const insertMarkdown = (before, after = "") => {
735
- const textarea = document.getElementById(
736
- `markdown-${label}`
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
+ 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)
737
777
  );
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);
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
+ ] });
798
+ };
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
831
+ }) => {
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
+ }
751
867
  };
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;
868
+ const onFormSubmit = (data) => {
869
+ onSubmit(data);
786
870
  };
787
- return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
788
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
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
- ] })
794
- ] }),
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",
916
- {
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>'
923
- }
924
- }
925
- ) })
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" })
986
- ] });
987
- };
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);
998
- const [error, setError] = useState(null);
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;
1010
- }
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;
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);
1029
- try {
1030
- const response = await fetch(
1031
- `/admin/campaigns/${campaignId}/${imageType}`,
1032
- {
1033
- method: "POST",
1034
- body: formData,
1035
- credentials: "include"
1036
- }
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);
1056
- } finally {
1057
- setIsUploading(false);
1058
- }
1059
- };
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
- }
1071
- );
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
- }
1089
- };
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" })
1123
- ] }),
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: [
1130
- /* @__PURE__ */ jsx(
1131
- "img",
1132
- {
1133
- src: imageToDisplay,
1134
- alt: `Campaign ${imageType}`,
1135
- className: clx(
1136
- "w-full object-contain",
1137
- isThumbnail ? "h-32" : "h-64"
1138
- )
1139
- }
1140
- ),
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(
1145
- Button,
1146
- {
1147
- variant: "secondary",
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
- ]
1157
- }
1158
- ),
1159
- /* @__PURE__ */ jsxs(
1160
- Button,
1161
- {
1162
- variant: "danger",
1163
- disabled: isUploading,
1164
- onClick: handleDelete,
1165
- children: [
1166
- /* @__PURE__ */ jsx(Trash, { className: "mr-2" }),
1167
- "Delete"
1168
- ]
1169
- }
1170
- )
1171
- ] })
1172
- ] }) : /* @__PURE__ */ jsxs(
1173
- "div",
1174
- {
1175
- className: clx(
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"
1180
- ),
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
- });
1276
- }
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");
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(", ");
1304
878
  }
1305
- } catch (error) {
1306
- console.error("Error saving campaign detail:", error);
1307
- toast.error("Error saving campaign details");
1308
- } finally {
1309
- setIsSaving(false);
1310
- }
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
+ });
1311
884
  };
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" })
1323
- ] }),
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"
885
+ return /* @__PURE__ */ jsxs(Container, { children: [
886
+ /* @__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" })
889
+ ] }),
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
+ }
1335
919
  ),
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,
920
+ /* @__PURE__ */ jsx(
921
+ Input,
1367
922
  {
1368
- variant: "secondary",
1369
- onClick: () => setShowImageUploader("image"),
1370
- children: [
1371
- /* @__PURE__ */ jsx(PhotoSolid, { className: "mr-2" }),
1372
- "Upload Image"
1373
- ]
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"
1374
928
  }
1375
929
  )
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"
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
+ }
1387
945
  ),
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,
946
+ /* @__PURE__ */ jsx(
947
+ Input,
1419
948
  {
1420
- variant: "secondary",
1421
- onClick: () => setShowImageUploader("thumbnail"),
1422
- children: [
1423
- /* @__PURE__ */ jsx(PhotoSolid, { className: "mr-2" }),
1424
- "Upload Thumbnail"
1425
- ]
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"
1426
954
  }
1427
955
  )
1428
- }
1429
- )
1430
- ] })
1431
- ] })
1432
- ] }),
1433
- /* @__PURE__ */ jsx("div", { className: "space-y-4", children: /* @__PURE__ */ jsx(
1434
- MarkdownEditor,
1435
- {
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
1443
- }
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
- )
956
+ ] }),
957
+ errors.ends_at && /* @__PURE__ */ jsx("p", { className: "text-red-500 text-sm mt-1", children: errors.ends_at.message })
958
+ ] })
1501
959
  ] }),
1502
- /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1503
- /* @__PURE__ */ jsx(Label, { htmlFor: "meta-description", children: "Meta Description" }),
960
+ /* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center", children: [
961
+ /* @__PURE__ */ jsx(Label, { children: "Products" }),
1504
962
  /* @__PURE__ */ jsx(
1505
- Input,
963
+ Button,
1506
964
  {
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"
965
+ type: "button",
966
+ variant: "secondary",
967
+ onClick: () => setOpenProductModal(true),
968
+ disabled,
969
+ children: "Add Product"
1514
970
  }
1515
971
  )
1516
972
  ] }),
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" })
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)) })
1532
1063
  ] }),
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
- ] })
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,
1072
+ {
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);
1083
+ }
1084
+ }
1085
+ ) })
1086
+ ] }) })
1087
+ ] });
1088
+ };
1089
+ const useFlashSaleById = (id) => {
1090
+ const [data, setData] = useState(null);
1091
+ const [isLoading, setIsLoading] = useState(true);
1092
+ const [error, setError] = useState(null);
1093
+ const fetchFlashSale = async () => {
1094
+ if (!id) {
1095
+ setIsLoading(false);
1096
+ return;
1097
+ }
1098
+ setIsLoading(true);
1099
+ setError(null);
1100
+ 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")
1106
+ );
1107
+ } finally {
1108
+ setIsLoading(false);
1109
+ }
1110
+ };
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
1146
+ );
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;
1183
+ };
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" })
1196
+ ] }),
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
+ ),
1265
+ /* @__PURE__ */ jsx(
1266
+ Button,
1267
+ {
1268
+ type: "button",
1269
+ variant: "secondary",
1270
+ size: "small",
1271
+ onClick: () => insertMarkdown("![alt](", ")"),
1272
+ title: "Image",
1273
+ children: "Image"
1274
+ }
1275
+ ),
1276
+ /* @__PURE__ */ jsx(
1277
+ Button,
1278
+ {
1279
+ type: "button",
1280
+ variant: "secondary",
1281
+ size: "small",
1282
+ onClick: () => insertMarkdown("```\n", "\n```"),
1283
+ title: "Code Block",
1284
+ children: "Code"
1285
+ }
1286
+ ),
1287
+ /* @__PURE__ */ jsx(
1288
+ Button,
1289
+ {
1290
+ type: "button",
1291
+ variant: "secondary",
1292
+ size: "small",
1293
+ onClick: () => insertMarkdown("- "),
1294
+ title: "List",
1295
+ children: "List"
1296
+ }
1297
+ )
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(
1312
+ "div",
1313
+ {
1314
+ 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"
1317
+ ),
1318
+ dangerouslySetInnerHTML: {
1319
+ __html: value ? renderMarkdownPreview(value) : '<p class="text-ui-fg-subtle">Nothing to preview</p>'
1320
+ }
1321
+ }
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
+ )
1551
1369
  ] }),
1552
- /* @__PURE__ */ jsx("div", { className: "flex justify-end border-t pt-4", children: /* @__PURE__ */ jsx(Button, { onClick: handleSave, isLoading: isSaving, children: "Save Campaign Details" }) })
1370
+ /* @__PURE__ */ jsx(
1371
+ Textarea,
1372
+ {
1373
+ id: `markdown-${label}`,
1374
+ value,
1375
+ onChange: (e) => onChange(e.target.value),
1376
+ placeholder,
1377
+ rows,
1378
+ className: "font-mono text-sm"
1379
+ }
1380
+ )
1553
1381
  ] }),
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
- )
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" })
1564
1383
  ] });
1565
1384
  };
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"
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";
1592
1402
  };
1593
- const handleSubmit = async (formData) => {
1594
- var _a2, _b2;
1595
- if (!id) {
1596
- return;
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;
1597
1411
  }
1412
+ return true;
1413
+ };
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);
1598
1426
  try {
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;
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);
1614
1448
  }
1615
- toast.error("Failed to update coupon", {
1616
- description: message
1617
- });
1449
+ } catch (err) {
1450
+ setError(`Error uploading ${imageType}`);
1451
+ setPreviewUrl(null);
1452
+ console.error(`Error uploading ${imageType}:`, err);
1453
+ } finally {
1454
+ setIsUploading(false);
1455
+ }
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
+ 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}`);
1479
+ }
1480
+ } catch (err) {
1481
+ setError(`Error deleting ${imageType}`);
1482
+ console.error(`Error deleting ${imageType}:`, err);
1483
+ } finally {
1484
+ setIsUploading(false);
1618
1485
  }
1619
1486
  };
1620
- return /* @__PURE__ */ jsxs(Container, { children: [
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: [
1621
1513
  /* @__PURE__ */ jsxs("div", { className: "mb-6 flex items-center justify-between", children: [
1622
1514
  /* @__PURE__ */ jsxs("div", { children: [
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" })
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" })
1625
1520
  ] }),
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
- ] })
1521
+ /* @__PURE__ */ jsx(Button, { variant: "secondary", size: "small", onClick: onClose, children: /* @__PURE__ */ jsx(X, {}) })
1630
1522
  ] }),
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,
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",
1638
1571
  {
1639
- initialData: initialValues,
1640
- onSubmit: handleSubmit,
1641
- onCancel: () => navigate("/coupons"),
1642
- disabled: !isEditing
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
+ ]
1643
1600
  }
1644
- ) }),
1645
- /* @__PURE__ */ jsx(Tabs.Content, { value: "details", className: "mt-6", children: /* @__PURE__ */ jsx(
1646
- CampaignDetailForm,
1601
+ ),
1602
+ /* @__PURE__ */ jsx(
1603
+ "input",
1647
1604
  {
1648
- campaignId: campaign.id,
1649
- campaignName: campaign.name ?? ""
1650
- }
1651
- ) })
1652
- ] })
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
- ] });
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"
1615
+ }
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" })
1621
+ ] })
1622
+ ] }) });
1762
1623
  };
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
1624
+ const CampaignDetailForm = ({
1625
+ campaignId,
1626
+ campaignName
1795
1627
  }) => {
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
- }
1814
- });
1815
- const { fields, append, remove, update } = useFieldArray({
1816
- control,
1817
- name: "products"
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
1818
1643
  });
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}`);
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);
1830
1681
  }
1831
1682
  };
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(", ");
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");
1842
1701
  }
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
- });
1702
+ } catch (error) {
1703
+ console.error("Error saving campaign detail:", error);
1704
+ toast.error("Error saving campaign details");
1705
+ } finally {
1706
+ setIsSaving(false);
1707
+ }
1848
1708
  };
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,
1886
- {
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"
1892
- }
1893
- )
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
- }
1909
- ),
1910
- /* @__PURE__ */ jsx(
1911
- Input,
1912
- {
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"
1918
- }
1919
- )
1920
- ] }),
1921
- errors.ends_at && /* @__PURE__ */ jsx("p", { className: "text-red-500 text-sm mt-1", children: errors.ends_at.message })
1922
- ] })
1923
- ] }),
1924
- /* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center", children: [
1925
- /* @__PURE__ */ jsx(Label, { children: "Products" }),
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" }),
1926
1726
  /* @__PURE__ */ jsx(
1927
- Button,
1727
+ "div",
1928
1728
  {
1929
- type: "button",
1930
- variant: "secondary",
1931
- onClick: () => setOpenProductModal(true),
1932
- disabled,
1933
- children: "Add Product"
1934
- }
1935
- )
1936
- ] }),
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,
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",
1956
1736
  {
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
- ]
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
+ }
1964
1747
  }
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,
1748
+ ),
1749
+ /* @__PURE__ */ jsxs(
1750
+ Button,
1975
1751
  {
1976
- type: "number",
1977
- value: field2.value,
1978
- onChange: (e) => field2.onChange(Number(e.target.value)),
1979
- disabled
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
+ ]
1980
1760
  }
1981
1761
  )
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,
1762
+ ] }) : /* @__PURE__ */ jsxs(
1763
+ Button,
1764
+ {
1765
+ variant: "secondary",
1766
+ onClick: () => setShowImageUploader("image"),
1767
+ children: [
1768
+ /* @__PURE__ */ jsx(PhotoSolid, { className: "mr-2" }),
1769
+ "Upload Image"
1770
+ ]
1771
+ }
1772
+ )
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"
1784
+ ),
1785
+ children: (campaignDetail == null ? void 0 : campaignDetail.thumbnail_url) ? /* @__PURE__ */ jsxs(Fragment, { children: [
1786
+ /* @__PURE__ */ jsx(
1787
+ "img",
1991
1788
  {
1992
- type: "number",
1993
- value: field2.value,
1994
- onChange: (e) => field2.onChange(Number(e.target.value)),
1995
- disabled
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
+ }
1996
1799
  }
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,
1800
+ ),
1801
+ /* @__PURE__ */ jsxs(
1802
+ Button,
2007
1803
  {
2008
- type: "number",
2009
- value: field2.value,
2010
- onChange: (e) => field2.onChange(Number(e.target.value)),
2011
- disabled
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
+ ]
2012
1812
  }
2013
1813
  )
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)) })
1814
+ ] }) : /* @__PURE__ */ jsxs(
1815
+ Button,
1816
+ {
1817
+ variant: "secondary",
1818
+ onClick: () => setShowImageUploader("thumbnail"),
1819
+ children: [
1820
+ /* @__PURE__ */ jsx(PhotoSolid, { className: "mr-2" }),
1821
+ "Upload Thumbnail"
1822
+ ]
1823
+ }
1824
+ )
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
+ )
2027
1898
  ] }),
2028
- /* @__PURE__ */ jsx(Button, { type: "submit", disabled, children: "Save" })
2029
- ]
1899
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1900
+ /* @__PURE__ */ jsx(Label, { htmlFor: "meta-description", children: "Meta Description" }),
1901
+ /* @__PURE__ */ jsx(
1902
+ Input,
1903
+ {
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"
1911
+ }
1912
+ )
1913
+ ] }),
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" })
1929
+ ] }),
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
2030
1959
  }
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
- ] }) })
1960
+ )
2051
1961
  ] });
2052
1962
  };
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
- };
2085
1963
  const FlashSaleDetail = () => {
2086
1964
  const { id } = useParams();
2087
1965
  const navigate = useNavigate();
@@ -2164,7 +2042,7 @@ const FlashSaleDetail = () => {
2164
2042
  ] })
2165
2043
  ] });
2166
2044
  };
2167
- const config = defineRouteConfig({
2045
+ const config$1 = defineRouteConfig({
2168
2046
  label: "Flash Sale Detail",
2169
2047
  icon: Sparkles
2170
2048
  });
@@ -2203,6 +2081,128 @@ const FlashSaleCreate = () => {
2203
2081
  }
2204
2082
  );
2205
2083
  };
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,10 +2219,6 @@ const routeModule = {
2219
2219
  Component: FlashSale,
2220
2220
  path: "/flash-sales"
2221
2221
  },
2222
- {
2223
- Component: CouponDetail,
2224
- path: "/coupons/:id"
2225
- },
2226
2222
  {
2227
2223
  Component: CouponCreate,
2228
2224
  path: "/coupons/create"
@@ -2234,6 +2230,10 @@ const routeModule = {
2234
2230
  {
2235
2231
  Component: FlashSaleCreate,
2236
2232
  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$1.label,
2256
- icon: config$1.icon,
2255
+ label: config.label,
2256
+ icon: config.icon,
2257
2257
  path: "/coupons/:id",
2258
2258
  nested: void 0
2259
2259
  },
2260
2260
  {
2261
- label: config.label,
2262
- icon: config.icon,
2261
+ label: config$1.label,
2262
+ icon: config$1.icon,
2263
2263
  path: "/flash-sales/:id",
2264
2264
  nested: void 0
2265
2265
  }