@mohasinac/appkit 2.7.50 → 2.7.52

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.
Files changed (143) hide show
  1. package/dist/_internal/server/jobs/core/__tests__/couponExpiry.test.d.ts +1 -0
  2. package/dist/_internal/server/jobs/core/__tests__/couponExpiry.test.js +65 -0
  3. package/dist/_internal/server/jobs/core/__tests__/listingProcessor.test.d.ts +1 -0
  4. package/dist/_internal/server/jobs/core/__tests__/listingProcessor.test.js +163 -0
  5. package/dist/_internal/server/jobs/core/__tests__/mediaTmpCleanup.test.d.ts +1 -0
  6. package/dist/_internal/server/jobs/core/__tests__/mediaTmpCleanup.test.js +115 -0
  7. package/dist/_internal/server/jobs/core/__tests__/pendingOrderTimeout.test.d.ts +1 -0
  8. package/dist/_internal/server/jobs/core/__tests__/pendingOrderTimeout.test.js +96 -0
  9. package/dist/client.d.ts +29 -6
  10. package/dist/client.js +16 -4
  11. package/dist/constants/api-endpoints.d.ts +42 -0
  12. package/dist/constants/api-endpoints.js +14 -0
  13. package/dist/features/_guide-cls.d.ts +13 -0
  14. package/dist/features/_guide-cls.js +14 -0
  15. package/dist/features/about/components/HelpPageView.js +29 -26
  16. package/dist/features/account/components/BuyerAccountGuideView.d.ts +1 -0
  17. package/dist/features/account/components/BuyerAccountGuideView.js +39 -0
  18. package/dist/features/account/components/BuyerAuctionsGuideView.d.ts +1 -0
  19. package/dist/features/account/components/BuyerAuctionsGuideView.js +38 -0
  20. package/dist/features/account/components/BuyerOrdersGuideView.d.ts +1 -0
  21. package/dist/features/account/components/BuyerOrdersGuideView.js +48 -0
  22. package/dist/features/account/components/BuyerShoppingGuideView.d.ts +1 -0
  23. package/dist/features/account/components/BuyerShoppingGuideView.js +38 -0
  24. package/dist/features/admin/components/AdminAnalyticsGuideView.d.ts +1 -0
  25. package/dist/features/admin/components/AdminAnalyticsGuideView.js +24 -0
  26. package/dist/features/admin/components/AdminBlogEditorView.js +100 -77
  27. package/dist/features/admin/components/AdminBundleEditorView.js +20 -14
  28. package/dist/features/admin/components/AdminBundlesView.d.ts +1 -3
  29. package/dist/features/admin/components/AdminBundlesView.js +173 -52
  30. package/dist/features/admin/components/AdminCatalogGuideView.d.ts +1 -0
  31. package/dist/features/admin/components/AdminCatalogGuideView.js +28 -0
  32. package/dist/features/admin/components/AdminContentGuideView.d.ts +1 -0
  33. package/dist/features/admin/components/AdminContentGuideView.js +36 -0
  34. package/dist/features/admin/components/AdminCouponEditorView.js +1 -1
  35. package/dist/features/admin/components/AdminGuideHubView.d.ts +5 -0
  36. package/dist/features/admin/components/AdminGuideHubView.js +79 -0
  37. package/dist/features/admin/components/AdminOrdersGuideView.d.ts +1 -0
  38. package/dist/features/admin/components/AdminOrdersGuideView.js +44 -0
  39. package/dist/features/admin/components/AdminProductsView.js +8 -3
  40. package/dist/features/admin/components/AdminSiteConfigGuideView.d.ts +1 -0
  41. package/dist/features/admin/components/AdminSiteConfigGuideView.js +21 -0
  42. package/dist/features/admin/components/AdminStoresGuideView.d.ts +1 -0
  43. package/dist/features/admin/components/AdminStoresGuideView.js +32 -0
  44. package/dist/features/admin/components/AdminTeamGuideView.d.ts +1 -0
  45. package/dist/features/admin/components/AdminTeamGuideView.js +33 -0
  46. package/dist/features/admin/components/AdminTrustGuideView.d.ts +1 -0
  47. package/dist/features/admin/components/AdminTrustGuideView.js +40 -0
  48. package/dist/features/admin/components/AdminUsersGuideView.d.ts +1 -0
  49. package/dist/features/admin/components/AdminUsersGuideView.js +38 -0
  50. package/dist/features/categories/components/CategoryBundlesListing.js +3 -33
  51. package/dist/features/categories/components/index.d.ts +0 -2
  52. package/dist/features/categories/components/index.js +0 -1
  53. package/dist/features/events/components/AdminEventEditorView.js +223 -269
  54. package/dist/features/grouped/repository/grouped-listings.repository.d.ts +9 -0
  55. package/dist/features/grouped/repository/grouped-listings.repository.js +12 -0
  56. package/dist/features/media/upload/MediaUploadField.js +1 -1
  57. package/dist/features/pre-orders/components/PreOrdersIndexListing.js +3 -1
  58. package/dist/features/products/components/AuctionsIndexListing.js +3 -1
  59. package/dist/features/products/components/MarketplaceBundleCard.d.ts +21 -0
  60. package/dist/features/products/components/MarketplaceBundleCard.js +56 -0
  61. package/dist/features/products/components/ProductsIndexListing.js +3 -1
  62. package/dist/features/products/components/index.d.ts +2 -4
  63. package/dist/features/products/components/index.js +1 -2
  64. package/dist/features/promotions/schemas/firestore.js +1 -1
  65. package/dist/features/reviews/components/index.d.ts +0 -2
  66. package/dist/features/reviews/components/index.js +0 -1
  67. package/dist/features/seller/components/BrandInlineSelect.js +23 -3
  68. package/dist/features/seller/components/CategoryInlineSelect.js +20 -3
  69. package/dist/features/seller/components/SellerAnalyticsAlertsView.d.ts +6 -0
  70. package/dist/features/seller/components/SellerAnalyticsAlertsView.js +124 -0
  71. package/dist/features/seller/components/SellerAnalyticsView.d.ts +2 -0
  72. package/dist/features/seller/components/SellerAnalyticsView.js +17 -9
  73. package/dist/features/seller/components/SellerBundlesView.d.ts +5 -0
  74. package/dist/features/seller/components/SellerBundlesView.js +104 -0
  75. package/dist/features/seller/components/SellerClassifiedView.d.ts +5 -0
  76. package/dist/features/seller/components/SellerClassifiedView.js +113 -0
  77. package/dist/features/seller/components/SellerDashboardView.js +19 -7
  78. package/dist/features/seller/components/SellerDigitalCodesView.d.ts +5 -0
  79. package/dist/features/seller/components/SellerDigitalCodesView.js +115 -0
  80. package/dist/features/seller/components/SellerGoogleReviewsView.d.ts +14 -0
  81. package/dist/features/seller/components/SellerGoogleReviewsView.js +95 -0
  82. package/dist/features/seller/components/SellerGroupedListingsView.d.ts +6 -0
  83. package/dist/features/seller/components/SellerGroupedListingsView.js +112 -0
  84. package/dist/features/seller/components/SellerLiveView.d.ts +5 -0
  85. package/dist/features/seller/components/SellerLiveView.js +113 -0
  86. package/dist/features/seller/components/SellerOrdersView.js +18 -2
  87. package/dist/features/seller/components/SellerPayoutMethodsView.d.ts +7 -0
  88. package/dist/features/seller/components/SellerPayoutMethodsView.js +120 -0
  89. package/dist/features/seller/components/SellerPayoutSettingsView.js +50 -12
  90. package/dist/features/seller/components/SellerProductShell.d.ts +1 -1
  91. package/dist/features/seller/components/SellerProductShell.js +9 -7
  92. package/dist/features/seller/components/SellerProductsFilterDrawer.js +3 -1
  93. package/dist/features/seller/components/SellerProductsView.js +48 -54
  94. package/dist/features/seller/components/SellerShippingConfigsView.d.ts +7 -0
  95. package/dist/features/seller/components/SellerShippingConfigsView.js +149 -0
  96. package/dist/features/seller/components/SellerShippingView.js +37 -10
  97. package/dist/features/seller/components/SellerStoreCategoriesView.d.ts +9 -0
  98. package/dist/features/seller/components/SellerStoreCategoriesView.js +122 -0
  99. package/dist/features/seller/components/SellerStorefrontView.d.ts +0 -7
  100. package/dist/features/seller/components/SellerStorefrontView.js +26 -29
  101. package/dist/features/seller/components/SellerTemplatesView.d.ts +10 -0
  102. package/dist/features/seller/components/SellerTemplatesView.js +265 -0
  103. package/dist/features/seller/components/index.d.ts +26 -0
  104. package/dist/features/seller/components/index.js +13 -0
  105. package/dist/features/stores/api/[storeSlug]/reviews/route.js +49 -7
  106. package/dist/features/stores/components/StoreCapabilitiesGuideView.d.ts +3 -0
  107. package/dist/features/stores/components/StoreCapabilitiesGuideView.js +101 -0
  108. package/dist/features/stores/components/StoreFinanceGuideView.d.ts +3 -0
  109. package/dist/features/stores/components/StoreFinanceGuideView.js +79 -0
  110. package/dist/features/stores/components/StoreGuideHubView.d.ts +5 -0
  111. package/dist/features/stores/components/StoreGuideHubView.js +89 -0
  112. package/dist/features/stores/components/StoreListingsGuideView.d.ts +3 -0
  113. package/dist/features/stores/components/StoreListingsGuideView.js +151 -0
  114. package/dist/features/stores/components/StoreOrdersGuideView.d.ts +3 -0
  115. package/dist/features/stores/components/StoreOrdersGuideView.js +122 -0
  116. package/dist/features/stores/components/StoreReviewsListing.js +100 -23
  117. package/dist/features/stores/components/StoreSettingsGuideView.d.ts +3 -0
  118. package/dist/features/stores/components/StoreSettingsGuideView.js +56 -0
  119. package/dist/features/stores/components/index.d.ts +12 -1
  120. package/dist/features/stores/components/index.js +6 -1
  121. package/dist/features/stores/hooks/useStores.d.ts +5 -0
  122. package/dist/features/stores/hooks/useStores.js +10 -0
  123. package/dist/features/stores/manifest.js +1 -1
  124. package/dist/features/whatsapp-bot/components/SellerWhatsAppSettingsView.js +13 -2
  125. package/dist/index.d.ts +54 -9
  126. package/dist/index.js +58 -15
  127. package/dist/next/routing/route-map.d.ts +55 -0
  128. package/dist/next/routing/route-map.js +25 -0
  129. package/dist/providers/db-firebase/__tests__/filter-aliases.test.d.ts +1 -0
  130. package/dist/providers/db-firebase/__tests__/filter-aliases.test.js +93 -0
  131. package/dist/providers/db-firebase/sieve.d.ts +49 -0
  132. package/dist/providers/db-firebase/sieve.js +61 -8
  133. package/dist/repositories/index.d.ts +1 -0
  134. package/dist/repositories/index.js +2 -0
  135. package/dist/tailwind-utilities.css +1 -1
  136. package/dist/ui/components/ListingToolbar.d.ts +8 -1
  137. package/dist/ui/components/ListingToolbar.js +4 -2
  138. package/dist/ui/components/__tests__/BulkActionBar.test.d.ts +1 -0
  139. package/dist/ui/components/__tests__/BulkActionBar.test.js +96 -0
  140. package/dist/ui/components/__tests__/ListingToolbar.test.d.ts +1 -0
  141. package/dist/ui/components/__tests__/ListingToolbar.test.js +125 -0
  142. package/dist/ui/index.d.ts +1 -1
  143. package/package.json +8 -2
@@ -2,34 +2,13 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import React from "react";
4
4
  import { useMutation, useQuery } from "@tanstack/react-query";
5
- import { Alert, Button, Form, FormActions, Input, RichTextEditor, Select, StackedViewShell, Text, Toggle, } from "../../../ui";
5
+ import { Alert, Button, Div, Heading, Input, RichTextEditor, Select, StackedViewShell, TagInput, Text, Toggle, } from "../../../ui";
6
6
  import { apiClient } from "../../../http";
7
7
  import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
8
+ import { StepForm } from "../../shell";
9
+ // --- Constants ---------------------------------------------------------------
8
10
  const CLS_PANEL_SM = "rounded-lg border border-zinc-200 dark:border-zinc-700 bg-zinc-50 dark:bg-zinc-800 p-3 space-y-2";
9
11
  const CLS_PANEL_LG = "rounded-xl border border-zinc-200 dark:border-slate-700 p-4 space-y-4";
10
- function toLocalDatetime(iso) {
11
- if (!iso)
12
- return "";
13
- try {
14
- const d = new Date(iso);
15
- const offset = d.getTimezoneOffset();
16
- const local = new Date(d.getTime() - offset * 60000);
17
- return local.toISOString().slice(0, 16);
18
- }
19
- catch {
20
- return "";
21
- }
22
- }
23
- function toISOString(local) {
24
- if (!local)
25
- return "";
26
- try {
27
- return new Date(local).toISOString();
28
- }
29
- catch {
30
- return "";
31
- }
32
- }
33
12
  const EVENT_TYPE_OPTIONS = [
34
13
  { label: "Sale", value: "sale" },
35
14
  { label: "Offer / Coupon", value: "offer" },
@@ -66,54 +45,105 @@ const FORM_FIELD_TYPE_OPTIONS = [
66
45
  { label: "File upload", value: "file" },
67
46
  ];
68
47
  const FIELD_TYPES_WITH_OPTIONS = ["select", "multiselect", "radio", "checkbox"];
48
+ function FormFieldBuilder({ fields, setFields }) {
49
+ const addField = () => setFields([...fields, { id: `field-${Date.now()}`, type: "text", label: "", required: false, order: fields.length }]);
50
+ const removeField = (id) => setFields(fields.filter((f) => f.id !== id));
51
+ const updateField = (id, patch) => setFields(fields.map((f) => (f.id === id ? { ...f, ...patch } : f)));
52
+ return (_jsxs("div", { className: "space-y-3", children: [_jsx(Text, { className: "text-xs font-medium text-zinc-600 dark:text-zinc-400 uppercase tracking-wide", children: "Form fields" }), fields.length === 0 && (_jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "No fields yet. Add fields to collect responses from participants." })), fields.map((field, idx) => {
53
+ const hasOptions = FIELD_TYPES_WITH_OPTIONS.includes(field.type);
54
+ const isNumeric = field.type === "number" || field.type === "rating";
55
+ const isText = field.type === "text" || field.type === "textarea" || field.type === "email";
56
+ return (_jsxs("div", { className: "rounded-lg border border-zinc-200 dark:border-zinc-600 p-3 space-y-2 bg-white dark:bg-zinc-900", children: [_jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsxs(Text, { className: "text-xs font-medium text-zinc-500 dark:text-zinc-400", children: ["Field ", idx + 1] }), _jsx("button", { type: "button", onClick: () => removeField(field.id), className: "text-zinc-400 hover:text-red-500 transition-colors text-lg leading-none px-1", "aria-label": "Remove field", children: "\u00D7" })] }), _jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-2", children: [_jsx(Select, { label: "Type", value: field.type, options: FORM_FIELD_TYPE_OPTIONS, onChange: (e) => updateField(field.id, { type: e.target.value, options: undefined }) }), _jsx(Input, { label: "Label", value: field.label, onChange: (e) => updateField(field.id, { label: e.target.value }), placeholder: "e.g. Describe your entry", required: true })] }), !hasOptions && field.type !== "date" && field.type !== "file" && field.type !== "rating" && (_jsx(Input, { label: "Placeholder (optional)", value: field.placeholder ?? "", onChange: (e) => updateField(field.id, { placeholder: e.target.value || undefined }), placeholder: "Hint shown inside the field" })), hasOptions && (_jsxs("div", { className: "space-y-1", children: [_jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "Options \u2014 one per line" }), _jsx("textarea", { className: "w-full rounded-lg border border-zinc-200 dark:border-zinc-600 bg-white dark:bg-zinc-800 text-sm text-zinc-800 dark:text-zinc-200 px-3 py-2 resize-y min-h-[72px] focus:outline-none focus:ring-2 focus:ring-primary/50", value: (field.options ?? []).join("\n"), onChange: (e) => updateField(field.id, { options: e.target.value.split("\n").map((s) => s.trim()).filter(Boolean) }), placeholder: "Option A\nOption B\nOption C" })] })), (isText || isNumeric) && (_jsxs("div", { className: "rounded bg-zinc-50 dark:bg-zinc-800 p-2 space-y-2", children: [_jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "Validation (optional)" }), _jsxs("div", { className: "grid grid-cols-2 gap-2", children: [isText && (_jsxs(_Fragment, { children: [_jsx(Input, { label: "Min length", type: "number", value: String(field.validation?.minLength ?? ""), onChange: (e) => updateField(field.id, { validation: { ...field.validation, minLength: e.target.value ? Number(e.target.value) : undefined } }), placeholder: "0" }), _jsx(Input, { label: "Max length", type: "number", value: String(field.validation?.maxLength ?? ""), onChange: (e) => updateField(field.id, { validation: { ...field.validation, maxLength: e.target.value ? Number(e.target.value) : undefined } }), placeholder: "500" })] })), isNumeric && (_jsxs(_Fragment, { children: [_jsx(Input, { label: "Min value", type: "number", value: String(field.validation?.min ?? ""), onChange: (e) => updateField(field.id, { validation: { ...field.validation, min: e.target.value ? Number(e.target.value) : undefined } }), placeholder: "0" }), _jsx(Input, { label: "Max value", type: "number", value: String(field.validation?.max ?? ""), onChange: (e) => updateField(field.id, { validation: { ...field.validation, max: e.target.value ? Number(e.target.value) : undefined } }), placeholder: "100" })] }))] }), field.type === "text" && (_jsx(Input, { label: "Pattern (regex, optional)", value: field.validation?.pattern ?? "", onChange: (e) => updateField(field.id, { validation: { ...field.validation, pattern: e.target.value || undefined } }), placeholder: "^[A-Z].*" }))] })), _jsx(Toggle, { checked: field.required, onChange: (v) => updateField(field.id, { required: v }), label: "Required" })] }, field.id));
57
+ }), _jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: addField, children: "+ Add field" })] }));
58
+ }
69
59
  const POLL_VISIBILITY_OPTIONS = [
70
60
  { label: "Always visible", value: "always" },
71
61
  { label: "After voting", value: "after_vote" },
72
62
  { label: "After event ends", value: "after_end" },
73
63
  ];
64
+ // --- Helpers -----------------------------------------------------------------
65
+ function toEventSlug(str) {
66
+ const base = str.toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
67
+ return base.startsWith("event-") ? base : `event-${base}`;
68
+ }
69
+ function toLocalDatetime(iso) {
70
+ if (!iso)
71
+ return "";
72
+ try {
73
+ const d = new Date(iso);
74
+ const offset = d.getTimezoneOffset();
75
+ const local = new Date(d.getTime() - offset * 60000);
76
+ return local.toISOString().slice(0, 16);
77
+ }
78
+ catch {
79
+ return "";
80
+ }
81
+ }
82
+ function toISOString(local) {
83
+ if (!local)
84
+ return "";
85
+ try {
86
+ return new Date(local).toISOString();
87
+ }
88
+ catch {
89
+ return "";
90
+ }
91
+ }
92
+ const DEFAULT_DRAFT = {
93
+ type: "sale",
94
+ title: "",
95
+ slug: "",
96
+ description: "",
97
+ startsAt: "",
98
+ endsAt: "",
99
+ coverImageUrl: "",
100
+ tags: [],
101
+ status: "draft",
102
+ createdBy: "",
103
+ discountPercent: "10",
104
+ saleBannerText: "",
105
+ couponId: "",
106
+ displayCode: "",
107
+ offerBannerText: "",
108
+ pollOptions: [{ id: "opt1", label: "" }, { id: "opt2", label: "" }],
109
+ allowMultiSelect: false,
110
+ allowComment: false,
111
+ resultsVisibility: "always",
112
+ requireLogin: true,
113
+ maxEntriesPerUser: "1",
114
+ hasLeaderboard: false,
115
+ hasPointSystem: false,
116
+ pointsLabel: "Points",
117
+ entryReviewRequired: false,
118
+ surveyFields: [],
119
+ anonymous: false,
120
+ feedbackFields: [],
121
+ hasRaffle: false,
122
+ raffleType: "open_raffle",
123
+ raffleTopN: "10",
124
+ rafflePrize: "",
125
+ rafflePrizeCouponId: "",
126
+ spinPrizes: [],
127
+ spinMaxPerUser: "1",
128
+ spinWindowStart: "",
129
+ spinWindowEnd: "",
130
+ };
131
+ // --- Component ---------------------------------------------------------------
74
132
  export function AdminEventEditorView({ eventId, onSaved, embedded, ...rest }) {
75
- const [type, setType] = React.useState("sale");
76
- const [title, setTitle] = React.useState("");
77
- const [description, setDescription] = React.useState("");
78
- const [startsAt, setStartsAt] = React.useState("");
79
- const [endsAt, setEndsAt] = React.useState("");
80
- const [coverImageUrl, setCoverImageUrl] = React.useState("");
81
- const [status, setStatus] = React.useState("draft");
82
- const [discountPercent, setDiscountPercent] = React.useState("10");
83
- const [saleBannerText, setSaleBannerText] = React.useState("");
84
- const [couponId, setCouponId] = React.useState("");
85
- const [displayCode, setDisplayCode] = React.useState("");
86
- const [offerBannerText, setOfferBannerText] = React.useState("");
87
- const [pollOptions, setPollOptions] = React.useState([
88
- { id: "opt1", label: "" },
89
- { id: "opt2", label: "" },
90
- ]);
91
- const [allowMultiSelect, setAllowMultiSelect] = React.useState(false);
92
- const [allowComment, setAllowComment] = React.useState(false);
93
- const [resultsVisibility, setResultsVisibility] = React.useState("always");
94
- const [requireLogin, setRequireLogin] = React.useState(true);
95
- const [maxEntriesPerUser, setMaxEntriesPerUser] = React.useState("1");
96
- const [hasLeaderboard, setHasLeaderboard] = React.useState(false);
97
- const [hasPointSystem, setHasPointSystem] = React.useState(false);
98
- const [pointsLabel, setPointsLabel] = React.useState("Points");
99
- const [entryReviewRequired, setEntryReviewRequired] = React.useState(false);
100
- const [surveyFields, setSurveyFields] = React.useState([]);
101
- const [anonymous, setAnonymous] = React.useState(false);
102
- const [feedbackFields, setFeedbackFields] = React.useState([]);
103
- const [hasRaffle, setHasRaffle] = React.useState(false);
104
- const [raffleType, setRaffleType] = React.useState("open_raffle");
105
- const [raffleTopN, setRaffleTopN] = React.useState("10");
106
- const [rafflePrize, setRafflePrize] = React.useState("");
107
- const [rafflePrizeCouponId, setRafflePrizeCouponId] = React.useState("");
108
- const [spinPrizes, setSpinPrizes] = React.useState([]);
109
- const [spinMaxPerUser, setSpinMaxPerUser] = React.useState("1");
110
- const [spinWindowStart, setSpinWindowStart] = React.useState("");
111
- const [spinWindowEnd, setSpinWindowEnd] = React.useState("");
133
+ const isEdit = Boolean(eventId);
134
+ const [draft, setDraft] = React.useState(DEFAULT_DRAFT);
135
+ const [slugManual, setSlugManual] = React.useState(false);
136
+ const [currentStep, setCurrentStep] = React.useState(0);
137
+ const [saveMessage, setSaveMessage] = React.useState(null);
138
+ const [triggerMessage, setTriggerMessage] = React.useState(null);
112
139
  const [raffleWinnerName, setRaffleWinnerName] = React.useState("");
113
140
  const [raffleEntryCount, setRaffleEntryCount] = React.useState(null);
114
141
  const [raffleGithubUrl, setRaffleGithubUrl] = React.useState("");
115
- const [saveMessage, setSaveMessage] = React.useState(null);
116
- const [triggerMessage, setTriggerMessage] = React.useState(null);
142
+ const update = React.useCallback((partial) => {
143
+ setDraft((prev) => ({ ...prev, ...partial }));
144
+ setSaveMessage(null);
145
+ }, []);
146
+ // --- load existing event ---
117
147
  const eventQuery = useQuery({
118
148
  queryKey: ["admin-event-by-id", eventId],
119
149
  queryFn: () => apiClient.get(`${ADMIN_ENDPOINTS.EVENTS}/${eventId}`),
@@ -124,134 +154,103 @@ export function AdminEventEditorView({ eventId, onSaved, embedded, ...rest }) {
124
154
  const event = eventQuery.data?.data ?? eventQuery.data;
125
155
  if (!event?.id)
126
156
  return;
127
- setType(event.type || "sale");
128
- setTitle(event.title || "");
129
- setDescription(event.description || "");
130
- setStartsAt(toLocalDatetime(event.startsAt));
131
- setEndsAt(toLocalDatetime(event.endsAt));
132
- setCoverImageUrl(event.coverImageUrl || event.coverImage?.url || "");
133
- setStatus(event.status || "draft");
134
- if (event.saleConfig) {
135
- setDiscountPercent(String(event.saleConfig.discountPercent ?? 10));
136
- setSaleBannerText(event.saleConfig.bannerText || "");
137
- }
138
- if (event.offerConfig) {
139
- setCouponId(event.offerConfig.couponId || "");
140
- setDisplayCode(event.offerConfig.displayCode || "");
141
- setOfferBannerText(event.offerConfig.bannerText || "");
142
- }
143
- if (event.pollConfig) {
144
- if (event.pollConfig.options?.length)
145
- setPollOptions(event.pollConfig.options);
146
- setAllowMultiSelect(Boolean(event.pollConfig.allowMultiSelect));
147
- setAllowComment(Boolean(event.pollConfig.allowComment));
148
- setResultsVisibility(event.pollConfig.resultsVisibility || "always");
149
- }
150
- if (event.surveyConfig) {
151
- setRequireLogin(event.surveyConfig.requireLogin !== false);
152
- setMaxEntriesPerUser(String(event.surveyConfig.maxEntriesPerUser ?? 1));
153
- setHasLeaderboard(Boolean(event.surveyConfig.hasLeaderboard));
154
- setHasPointSystem(Boolean(event.surveyConfig.hasPointSystem));
155
- setPointsLabel(event.surveyConfig.pointsLabel ?? "Points");
156
- setEntryReviewRequired(Boolean(event.surveyConfig.entryReviewRequired));
157
- setSurveyFields(Array.isArray(event.surveyConfig.formFields) ? event.surveyConfig.formFields : []);
158
- }
159
- if (event.feedbackConfig) {
160
- setAnonymous(Boolean(event.feedbackConfig.anonymous));
161
- setFeedbackFields(Array.isArray(event.feedbackConfig.formFields) ? event.feedbackConfig.formFields : []);
162
- }
163
- setHasRaffle(Boolean(event.hasRaffle));
164
- setRaffleType(event.raffleType || "open_raffle");
165
- setRaffleTopN(String(event.raffleTopN ?? 10));
166
- setRafflePrize(event.rafflePrize || "");
167
- setRafflePrizeCouponId(event.rafflePrizeCouponId || "");
168
- setSpinPrizes(Array.isArray(event.spinPrizes) ? event.spinPrizes : []);
169
- setSpinMaxPerUser(String(event.spinMaxPerUser ?? 1));
170
- setSpinWindowStart(toLocalDatetime(event.spinWindowStart));
171
- setSpinWindowEnd(toLocalDatetime(event.spinWindowEnd));
157
+ setDraft({
158
+ type: event.type || "sale",
159
+ title: event.title || "",
160
+ slug: event.slug || "",
161
+ description: event.description || "",
162
+ startsAt: toLocalDatetime(event.startsAt),
163
+ endsAt: toLocalDatetime(event.endsAt),
164
+ coverImageUrl: event.coverImageUrl || event.coverImage?.url || "",
165
+ tags: Array.isArray(event.tags) ? event.tags : [],
166
+ status: event.status || "draft",
167
+ createdBy: event.createdBy || "",
168
+ discountPercent: String(event.saleConfig?.discountPercent ?? 10),
169
+ saleBannerText: event.saleConfig?.bannerText || "",
170
+ couponId: event.offerConfig?.couponId || "",
171
+ displayCode: event.offerConfig?.displayCode || "",
172
+ offerBannerText: event.offerConfig?.bannerText || "",
173
+ pollOptions: event.pollConfig?.options?.length
174
+ ? event.pollConfig.options
175
+ : [{ id: "opt1", label: "" }, { id: "opt2", label: "" }],
176
+ allowMultiSelect: Boolean(event.pollConfig?.allowMultiSelect),
177
+ allowComment: Boolean(event.pollConfig?.allowComment),
178
+ resultsVisibility: event.pollConfig?.resultsVisibility || "always",
179
+ requireLogin: event.surveyConfig?.requireLogin !== false,
180
+ maxEntriesPerUser: String(event.surveyConfig?.maxEntriesPerUser ?? 1),
181
+ hasLeaderboard: Boolean(event.surveyConfig?.hasLeaderboard),
182
+ hasPointSystem: Boolean(event.surveyConfig?.hasPointSystem),
183
+ pointsLabel: event.surveyConfig?.pointsLabel ?? "Points",
184
+ entryReviewRequired: Boolean(event.surveyConfig?.entryReviewRequired),
185
+ surveyFields: Array.isArray(event.surveyConfig?.formFields) ? event.surveyConfig.formFields : [],
186
+ anonymous: Boolean(event.feedbackConfig?.anonymous),
187
+ feedbackFields: Array.isArray(event.feedbackConfig?.formFields) ? event.feedbackConfig.formFields : [],
188
+ hasRaffle: Boolean(event.hasRaffle),
189
+ raffleType: event.raffleType || "open_raffle",
190
+ raffleTopN: String(event.raffleTopN ?? 10),
191
+ rafflePrize: event.rafflePrize || "",
192
+ rafflePrizeCouponId: event.rafflePrizeCouponId || "",
193
+ spinPrizes: Array.isArray(event.spinPrizes) ? event.spinPrizes : [],
194
+ spinMaxPerUser: String(event.spinMaxPerUser ?? 1),
195
+ spinWindowStart: toLocalDatetime(event.spinWindowStart),
196
+ spinWindowEnd: toLocalDatetime(event.spinWindowEnd),
197
+ });
198
+ setSlugManual(true);
172
199
  setRaffleWinnerName(event.raffleWinnerDisplayName || "");
173
200
  setRaffleEntryCount(typeof event.raffleEntryCount === "number" ? event.raffleEntryCount : null);
174
201
  setRaffleGithubUrl(event.raffleGithubFunctionUrl || "");
175
202
  }, [eventQuery.data]);
203
+ // --- save ---
176
204
  const saveMutation = useMutation({
177
205
  mutationFn: async () => {
178
206
  const buildTypeConfig = () => {
179
- if (type === "sale")
180
- return {
181
- saleConfig: {
182
- discountPercent: Number(discountPercent) || 10,
183
- bannerText: saleBannerText || undefined,
184
- },
185
- };
186
- if (type === "offer")
187
- return {
188
- offerConfig: { couponId, displayCode, bannerText: offerBannerText || undefined },
189
- };
190
- if (type === "poll")
191
- return {
192
- pollConfig: {
193
- options: pollOptions.filter((o) => o.label.trim()),
194
- allowMultiSelect,
195
- allowComment,
196
- resultsVisibility,
197
- },
198
- };
199
- if (type === "survey")
200
- return {
201
- surveyConfig: {
202
- requireLogin,
203
- maxEntriesPerUser: Number(maxEntriesPerUser) || 1,
204
- hasLeaderboard,
205
- hasPointSystem,
206
- pointsLabel: hasPointSystem ? (pointsLabel.trim() || "Points") : undefined,
207
- entryReviewRequired,
208
- formFields: surveyFields.map((f, i) => ({ ...f, order: i })),
209
- },
210
- };
211
- if (type === "feedback")
212
- return {
213
- feedbackConfig: {
214
- anonymous,
215
- formFields: feedbackFields.map((f, i) => ({ ...f, order: i })),
216
- },
217
- };
207
+ if (draft.type === "sale")
208
+ return { saleConfig: { discountPercent: Number(draft.discountPercent) || 10, bannerText: draft.saleBannerText || undefined } };
209
+ if (draft.type === "offer")
210
+ return { offerConfig: { couponId: draft.couponId, displayCode: draft.displayCode, bannerText: draft.offerBannerText || undefined } };
211
+ if (draft.type === "poll")
212
+ return { pollConfig: { options: draft.pollOptions.filter((o) => o.label.trim()), allowMultiSelect: draft.allowMultiSelect, allowComment: draft.allowComment, resultsVisibility: draft.resultsVisibility } };
213
+ if (draft.type === "survey")
214
+ return { surveyConfig: { requireLogin: draft.requireLogin, maxEntriesPerUser: Number(draft.maxEntriesPerUser) || 1, hasLeaderboard: draft.hasLeaderboard, hasPointSystem: draft.hasPointSystem, pointsLabel: draft.hasPointSystem ? (draft.pointsLabel.trim() || "Points") : undefined, entryReviewRequired: draft.entryReviewRequired, formFields: draft.surveyFields.map((f, i) => ({ ...f, order: i })) } };
215
+ if (draft.type === "feedback")
216
+ return { feedbackConfig: { anonymous: draft.anonymous, formFields: draft.feedbackFields.map((f, i) => ({ ...f, order: i })) } };
218
217
  return {};
219
218
  };
220
- const isRaffleType = type === "raffle" || type === "spin_wheel";
219
+ const isRaffleType = draft.type === "raffle" || draft.type === "spin_wheel";
221
220
  const raffleFields = {};
222
- if (hasRaffle || isRaffleType) {
221
+ if (draft.hasRaffle || isRaffleType) {
223
222
  raffleFields.hasRaffle = true;
224
- raffleFields.raffleType = isRaffleType && type === "spin_wheel"
225
- ? "spin_wheel"
226
- : raffleType;
227
- if (Number(raffleTopN) > 0)
228
- raffleFields.raffleTopN = Number(raffleTopN);
229
- if (rafflePrize)
230
- raffleFields.rafflePrize = rafflePrize;
231
- if (rafflePrizeCouponId)
232
- raffleFields.rafflePrizeCouponId = rafflePrizeCouponId;
233
- if (raffleType === "spin_wheel" || type === "spin_wheel") {
234
- raffleFields.spinPrizes = spinPrizes;
235
- if (Number(spinMaxPerUser) > 0)
236
- raffleFields.spinMaxPerUser = Number(spinMaxPerUser);
237
- if (spinWindowStart)
238
- raffleFields.spinWindowStart = toISOString(spinWindowStart);
239
- if (spinWindowEnd)
240
- raffleFields.spinWindowEnd = toISOString(spinWindowEnd);
223
+ raffleFields.raffleType = isRaffleType && draft.type === "spin_wheel" ? "spin_wheel" : draft.raffleType;
224
+ if (Number(draft.raffleTopN) > 0)
225
+ raffleFields.raffleTopN = Number(draft.raffleTopN);
226
+ if (draft.rafflePrize)
227
+ raffleFields.rafflePrize = draft.rafflePrize;
228
+ if (draft.rafflePrizeCouponId)
229
+ raffleFields.rafflePrizeCouponId = draft.rafflePrizeCouponId;
230
+ if (draft.raffleType === "spin_wheel" || draft.type === "spin_wheel") {
231
+ raffleFields.spinPrizes = draft.spinPrizes;
232
+ if (Number(draft.spinMaxPerUser) > 0)
233
+ raffleFields.spinMaxPerUser = Number(draft.spinMaxPerUser);
234
+ if (draft.spinWindowStart)
235
+ raffleFields.spinWindowStart = toISOString(draft.spinWindowStart);
236
+ if (draft.spinWindowEnd)
237
+ raffleFields.spinWindowEnd = toISOString(draft.spinWindowEnd);
241
238
  }
242
239
  }
243
240
  const payload = {
244
- type,
245
- title,
246
- description,
247
- startsAt: toISOString(startsAt),
248
- endsAt: toISOString(endsAt),
249
- coverImageUrl: coverImageUrl || undefined,
241
+ type: draft.type,
242
+ title: draft.title,
243
+ slug: draft.slug || toEventSlug(draft.title),
244
+ description: draft.description,
245
+ tags: draft.tags,
246
+ startsAt: toISOString(draft.startsAt),
247
+ endsAt: toISOString(draft.endsAt),
248
+ coverImageUrl: draft.coverImageUrl || undefined,
250
249
  ...buildTypeConfig(),
251
250
  ...raffleFields,
252
251
  };
253
252
  if (eventId) {
254
- payload.status = status;
253
+ payload.status = draft.status;
255
254
  await apiClient.patch(`${ADMIN_ENDPOINTS.EVENTS}/${eventId}`, payload);
256
255
  return eventId;
257
256
  }
@@ -267,68 +266,7 @@ export function AdminEventEditorView({ eventId, onSaved, embedded, ...rest }) {
267
266
  setSaveMessage(error instanceof Error ? error.message : "Save failed");
268
267
  },
269
268
  });
270
- const makeAddField = (setter) => () => setter((prev) => [
271
- ...prev,
272
- { id: `field-${Date.now()}`, type: "text", label: "", required: false, order: prev.length },
273
- ]);
274
- const makeRemoveField = (setter) => (id) => setter((prev) => prev.filter((f) => f.id !== id));
275
- const makeUpdateField = (setter) => (id, patch) => setter((prev) => prev.map((f) => (f.id === id ? { ...f, ...patch } : f)));
276
- const renderFormFieldBuilder = (fields, setter) => {
277
- const addField = makeAddField(setter);
278
- const removeField = makeRemoveField(setter);
279
- const updateField = makeUpdateField(setter);
280
- return (_jsxs("div", { className: "space-y-3", children: [_jsx(Text, { className: "text-xs font-medium text-zinc-600 dark:text-zinc-400 uppercase tracking-wide", children: "Form fields" }), fields.length === 0 && (_jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "No fields yet. Add fields to collect responses from participants." })), fields.map((field, idx) => {
281
- const hasOptions = FIELD_TYPES_WITH_OPTIONS.includes(field.type);
282
- const isNumeric = field.type === "number" || field.type === "rating";
283
- const isText = field.type === "text" || field.type === "textarea" || field.type === "email";
284
- return (_jsxs("div", { className: "rounded-lg border border-zinc-200 dark:border-zinc-600 p-3 space-y-2 bg-white dark:bg-zinc-900", children: [_jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsxs(Text, { className: "text-xs font-medium text-zinc-500 dark:text-zinc-400", children: ["Field ", idx + 1] }), _jsx("button", { type: "button", onClick: () => removeField(field.id), className: "text-zinc-400 hover:text-red-500 transition-colors text-lg leading-none px-1", "aria-label": "Remove field", children: "\u00D7" })] }), _jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-2", children: [_jsx(Select, { label: "Type", value: field.type, options: FORM_FIELD_TYPE_OPTIONS, onChange: (e) => updateField(field.id, { type: e.target.value, options: undefined }) }), _jsx(Input, { label: "Label", value: field.label, onChange: (e) => updateField(field.id, { label: e.target.value }), placeholder: "e.g. Describe your entry", required: true })] }), !hasOptions && field.type !== "date" && field.type !== "file" && field.type !== "rating" && (_jsx(Input, { label: "Placeholder (optional)", value: field.placeholder ?? "", onChange: (e) => updateField(field.id, { placeholder: e.target.value || undefined }), placeholder: "Hint shown inside the field" })), hasOptions && (_jsxs("div", { className: "space-y-1", children: [_jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "Options \u2014 one per line" }), _jsx("textarea", { className: "w-full rounded-lg border border-zinc-200 dark:border-zinc-600 bg-white dark:bg-zinc-800 text-sm text-zinc-800 dark:text-zinc-200 px-3 py-2 resize-y min-h-[72px] focus:outline-none focus:ring-2 focus:ring-primary/50", value: (field.options ?? []).join("\n"), onChange: (e) => updateField(field.id, {
285
- options: e.target.value.split("\n").map((s) => s.trim()).filter(Boolean),
286
- }), placeholder: "Option A\nOption B\nOption C" })] })), (isText || isNumeric) && (_jsxs("div", { className: "rounded bg-zinc-50 dark:bg-zinc-800 p-2 space-y-2", children: [_jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "Validation (optional)" }), _jsxs("div", { className: "grid grid-cols-2 gap-2", children: [isText && (_jsxs(_Fragment, { children: [_jsx(Input, { label: "Min length", type: "number", value: String(field.validation?.minLength ?? ""), onChange: (e) => updateField(field.id, {
287
- validation: {
288
- ...field.validation,
289
- minLength: e.target.value ? Number(e.target.value) : undefined,
290
- },
291
- }), placeholder: "0" }), _jsx(Input, { label: "Max length", type: "number", value: String(field.validation?.maxLength ?? ""), onChange: (e) => updateField(field.id, {
292
- validation: {
293
- ...field.validation,
294
- maxLength: e.target.value ? Number(e.target.value) : undefined,
295
- },
296
- }), placeholder: "500" })] })), isNumeric && (_jsxs(_Fragment, { children: [_jsx(Input, { label: "Min value", type: "number", value: String(field.validation?.min ?? ""), onChange: (e) => updateField(field.id, {
297
- validation: {
298
- ...field.validation,
299
- min: e.target.value ? Number(e.target.value) : undefined,
300
- },
301
- }), placeholder: "0" }), _jsx(Input, { label: "Max value", type: "number", value: String(field.validation?.max ?? ""), onChange: (e) => updateField(field.id, {
302
- validation: {
303
- ...field.validation,
304
- max: e.target.value ? Number(e.target.value) : undefined,
305
- },
306
- }), placeholder: "100" })] }))] }), field.type === "text" && (_jsx(Input, { label: "Pattern (regex, optional)", value: field.validation?.pattern ?? "", onChange: (e) => updateField(field.id, {
307
- validation: {
308
- ...field.validation,
309
- pattern: e.target.value || undefined,
310
- },
311
- }), placeholder: "^[A-Z].*" }))] })), _jsx(Toggle, { checked: field.required, onChange: (v) => updateField(field.id, { required: v }), label: "Required" })] }, field.id));
312
- }), _jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: addField, children: "+ Add field" })] }));
313
- };
314
- const addSpinPrize = () => {
315
- setSpinPrizes((prev) => [
316
- ...prev,
317
- {
318
- id: `prize-${Date.now()}`,
319
- label: "",
320
- couponId: undefined,
321
- weight: 1,
322
- isActive: true,
323
- },
324
- ]);
325
- };
326
- const removeSpinPrize = (id) => {
327
- setSpinPrizes((prev) => prev.filter((p) => p.id !== id));
328
- };
329
- const updateSpinPrize = (id, patch) => {
330
- setSpinPrizes((prev) => prev.map((p) => (p.id === id ? { ...p, ...patch } : p)));
331
- };
269
+ // --- raffle trigger ---
332
270
  const triggerRaffleMutation = useMutation({
333
271
  mutationFn: async () => {
334
272
  if (!eventId)
@@ -337,9 +275,7 @@ export function AdminEventEditorView({ eventId, onSaved, embedded, ...rest }) {
337
275
  },
338
276
  onSuccess: (res) => {
339
277
  const data = res.data ?? res;
340
- setTriggerMessage(data?.raffleWinnerDisplayName
341
- ? `Winner: ${data.raffleWinnerDisplayName}`
342
- : "Raffle triggered.");
278
+ setTriggerMessage(data?.raffleWinnerDisplayName ? `Winner: ${data.raffleWinnerDisplayName}` : "Raffle triggered.");
343
279
  if (data?.raffleWinnerDisplayName)
344
280
  setRaffleWinnerName(data.raffleWinnerDisplayName);
345
281
  if (typeof data?.raffleEntryCount === "number")
@@ -349,38 +285,56 @@ export function AdminEventEditorView({ eventId, onSaved, embedded, ...rest }) {
349
285
  setTriggerMessage(err instanceof Error ? err.message : "Trigger failed");
350
286
  },
351
287
  });
352
- const addPollOption = () => {
353
- setPollOptions((prev) => [...prev, { id: `opt${Date.now()}`, label: "" }]);
354
- };
355
- const removePollOption = (id) => {
356
- setPollOptions((prev) => prev.filter((o) => o.id !== id));
357
- };
358
- const updatePollOption = (id, label) => {
359
- setPollOptions((prev) => prev.map((o) => (o.id === id ? { ...o, label } : o)));
360
- };
361
- const isValid = !!title.trim() &&
362
- !!startsAt &&
363
- !!endsAt &&
364
- (type !== "poll" || pollOptions.filter((o) => o.label.trim()).length >= 2) &&
365
- (type !== "offer" || (!!couponId.trim() && !!displayCode.trim()));
288
+ const isLoading = saveMutation.isPending || eventQuery.isLoading;
289
+ // --- steps ---
290
+ const steps = [
291
+ {
292
+ label: "Details",
293
+ validate: (values) => {
294
+ if (!values.title.trim())
295
+ return "Title is required";
296
+ if (!values.startsAt)
297
+ return "Start date is required";
298
+ if (!values.endsAt)
299
+ return "End date is required";
300
+ return null;
301
+ },
302
+ render: ({ values, onChange }) => (_jsxs("div", { className: "space-y-5", children: [_jsx(Heading, { level: 3, className: "mb-2", children: "Event Details" }), !isEdit && (_jsx(Select, { label: "Event type", value: values.type, options: EVENT_TYPE_OPTIONS, onChange: (e) => onChange({ type: e.target.value }) })), _jsx(Input, { label: "Title", value: values.title, onChange: (e) => {
303
+ const v = e.target.value;
304
+ onChange({ title: v, ...(!slugManual && { slug: toEventSlug(v) }) });
305
+ }, placeholder: "Charizard Flash Sale 2026", required: true }), _jsx(Input, { label: "Slug", value: values.slug, onChange: (e) => { setSlugManual(true); onChange({ slug: e.target.value }); }, placeholder: "event-charizard-flash-sale-2026", helperText: "Auto-generated from title. Must start with 'event-'." }), _jsxs(Div, { children: [_jsx("label", { className: "block text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-1", children: "Description" }), _jsx(RichTextEditor, { value: values.description, onChange: (v) => onChange({ description: v }), minHeightClassName: "min-h-[120px]", placeholder: "Describe this event\u2026" })] }), _jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-3", children: [_jsx(Input, { label: "Starts at", type: "datetime-local", value: values.startsAt, onChange: (e) => onChange({ startsAt: e.target.value }), required: true }), _jsx(Input, { label: "Ends at", type: "datetime-local", value: values.endsAt, onChange: (e) => onChange({ endsAt: e.target.value }), required: true })] }), _jsx(TagInput, { label: "Tags", value: values.tags, onChange: (t) => onChange({ tags: t }), placeholder: "e.g. pokemon, sale, 2026" })] })),
306
+ },
307
+ {
308
+ label: "Media",
309
+ render: ({ values, onChange }) => (_jsxs("div", { className: "space-y-5", children: [_jsx(Heading, { level: 3, className: "mb-2", children: "Media" }), _jsx(Input, { label: "Cover Image URL (optional)", value: values.coverImageUrl, onChange: (e) => onChange({ coverImageUrl: e.target.value }), placeholder: "https://\u2026", helperText: "Direct link to the event cover image." })] })),
310
+ },
311
+ {
312
+ label: "Settings",
313
+ validate: (values) => {
314
+ if (values.type === "offer" && !values.couponId.trim())
315
+ return "Coupon ID is required for offer events";
316
+ if (values.type === "offer" && !values.displayCode.trim())
317
+ return "Display code is required for offer events";
318
+ if (values.type === "poll" && values.pollOptions.filter((o) => o.label.trim()).length < 2)
319
+ return "Poll events require at least 2 options";
320
+ return null;
321
+ },
322
+ render: ({ values, onChange }) => (_jsxs("div", { className: "space-y-5", children: [_jsx(Heading, { level: 3, className: "mb-2", children: "Event Settings" }), isEdit && (_jsx(Select, { label: "Status", value: values.status, options: EVENT_STATUS_OPTIONS, onChange: (e) => onChange({ status: e.target.value }) })), values.createdBy && (_jsxs(Div, { children: [_jsx(Text, { className: "text-sm font-medium mb-1", children: "Created by" }), _jsx(Text, { className: "text-sm text-[var(--appkit-color-text-muted)]", children: values.createdBy })] })), values.type === "sale" && (_jsxs("div", { className: CLS_PANEL_SM, children: [_jsx(Text, { className: "text-sm font-semibold text-zinc-700 dark:text-zinc-300", children: "Sale configuration" }), _jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-3", children: [_jsx(Input, { label: "Discount %", type: "number", value: values.discountPercent, onChange: (e) => onChange({ discountPercent: e.target.value }), placeholder: "10" }), _jsx(Input, { label: "Banner text (optional)", value: values.saleBannerText, onChange: (e) => onChange({ saleBannerText: e.target.value }), placeholder: "Limited time \u2014 ends Sunday!" })] })] })), values.type === "offer" && (_jsxs("div", { className: CLS_PANEL_SM, children: [_jsx(Text, { className: "text-sm font-semibold text-zinc-700 dark:text-zinc-300", children: "Offer configuration" }), _jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-3", children: [_jsx(Input, { label: "Coupon ID", value: values.couponId, onChange: (e) => onChange({ couponId: e.target.value }), placeholder: "Firestore coupon document ID", required: true }), _jsx(Input, { label: "Display code", value: values.displayCode, onChange: (e) => onChange({ displayCode: e.target.value }), placeholder: "CHARIZARD25", required: true })] }), _jsx(Input, { label: "Banner text (optional)", value: values.offerBannerText, onChange: (e) => onChange({ offerBannerText: e.target.value }), placeholder: "Use CHARIZARD25 at checkout" })] })), values.type === "poll" && (_jsxs("div", { className: CLS_PANEL_LG, children: [_jsx(Text, { className: "text-sm font-semibold text-zinc-700 dark:text-zinc-300", children: "Poll configuration" }), _jsxs("div", { className: "space-y-2", children: [_jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "Options (minimum 2)" }), values.pollOptions.map((opt, idx) => (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex-1", children: _jsx(Input, { value: opt.label, onChange: (e) => onChange({ pollOptions: values.pollOptions.map((o) => o.id === opt.id ? { ...o, label: e.target.value } : o) }), placeholder: `Option ${idx + 1}` }) }), values.pollOptions.length > 2 && (_jsx("button", { type: "button", onClick: () => onChange({ pollOptions: values.pollOptions.filter((o) => o.id !== opt.id) }), className: "text-zinc-400 hover:text-red-500 transition-colors px-2 py-1 text-lg leading-none", "aria-label": "Remove option", children: "\u00D7" }))] }, opt.id))), _jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: () => onChange({ pollOptions: [...values.pollOptions, { id: `opt${Date.now()}`, label: "" }] }), children: "+ Add option" })] }), _jsx(Select, { label: "Results visibility", value: values.resultsVisibility, options: POLL_VISIBILITY_OPTIONS, onChange: (e) => onChange({ resultsVisibility: e.target.value }) }), _jsxs("div", { className: "space-y-3", children: [_jsx(Toggle, { checked: values.allowMultiSelect, onChange: (v) => onChange({ allowMultiSelect: v }), label: "Allow multi-select" }), _jsx(Toggle, { checked: values.allowComment, onChange: (v) => onChange({ allowComment: v }), label: "Allow comment with vote" })] })] })), values.type === "survey" && (_jsxs("div", { className: CLS_PANEL_LG, children: [_jsx(Text, { className: "text-sm font-semibold text-zinc-700 dark:text-zinc-300", children: "Survey configuration" }), _jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-3", children: [_jsx(Input, { label: "Max entries per user", type: "number", value: values.maxEntriesPerUser, onChange: (e) => onChange({ maxEntriesPerUser: e.target.value }), placeholder: "1" }), values.hasPointSystem && (_jsx(Input, { label: "Points label", value: values.pointsLabel, onChange: (e) => onChange({ pointsLabel: e.target.value }), placeholder: "Points" }))] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Toggle, { checked: values.requireLogin, onChange: (v) => onChange({ requireLogin: v }), label: "Require login to participate" }), _jsx(Toggle, { checked: values.hasLeaderboard, onChange: (v) => onChange({ hasLeaderboard: v }), label: "Show leaderboard" }), _jsx(Toggle, { checked: values.hasPointSystem, onChange: (v) => onChange({ hasPointSystem: v }), label: "Enable point system" }), _jsx(Toggle, { checked: values.entryReviewRequired, onChange: (v) => onChange({ entryReviewRequired: v }), label: "Require employee review before entry is approved" })] }), _jsx(FormFieldBuilder, { fields: values.surveyFields, setFields: (f) => onChange({ surveyFields: f }) })] })), values.type === "feedback" && (_jsxs("div", { className: CLS_PANEL_LG, children: [_jsx(Text, { className: "text-sm font-semibold text-zinc-700 dark:text-zinc-300", children: "Feedback configuration" }), _jsx(Toggle, { checked: values.anonymous, onChange: (v) => onChange({ anonymous: v }), label: "Allow anonymous submissions" }), _jsx(FormFieldBuilder, { fields: values.feedbackFields, setFields: (f) => onChange({ feedbackFields: f }) })] }))] })),
323
+ },
324
+ {
325
+ label: "Raffle / Spin",
326
+ render: ({ values, onChange }) => {
327
+ const isRaffleType = values.type === "raffle" || values.type === "spin_wheel";
328
+ const showRaffleConfig = isRaffleType || values.hasRaffle;
329
+ const isSpinMode = values.type === "spin_wheel" || values.raffleType === "spin_wheel";
330
+ return (_jsxs("div", { className: "space-y-5", children: [_jsx(Heading, { level: 3, className: "mb-2", children: "Raffle & Spin Wheel" }), !isRaffleType && (_jsx(Toggle, { checked: values.hasRaffle, onChange: (v) => onChange({ hasRaffle: v }), label: "Attach a raffle to this event" })), !isRaffleType && !values.hasRaffle && (_jsx(Text, { className: "text-sm text-[var(--appkit-color-text-muted)]", children: "Enable the toggle above to attach a raffle draw to this event. Not applicable for this event type by default." })), showRaffleConfig && (_jsxs("div", { className: CLS_PANEL_LG, children: [_jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-3", children: [_jsx(Select, { label: "Raffle type", value: values.type === "spin_wheel" ? "spin_wheel" : values.raffleType, options: RAFFLE_TYPE_OPTIONS, onChange: (e) => onChange({ raffleType: e.target.value }), disabled: values.type === "spin_wheel" }), _jsx(Input, { label: "Top N (for top-N raffle types)", type: "number", value: values.raffleTopN, onChange: (e) => onChange({ raffleTopN: e.target.value }), placeholder: "10" })] }), _jsx(Input, { label: "Prize description", value: values.rafflePrize, onChange: (e) => onChange({ rafflePrize: e.target.value }), placeholder: "\u20B92,000 store credit + exclusive Pikachu sticker" }), _jsx(Input, { label: "Coupon ID for winner (optional)", value: values.rafflePrizeCouponId, onChange: (e) => onChange({ rafflePrizeCouponId: e.target.value }), placeholder: "coupon-vip-winner-2026" }), isSpinMode && (_jsxs("div", { className: "rounded-lg border border-zinc-200 dark:border-slate-700 p-3 space-y-3", children: [_jsx(Text, { className: "text-xs font-medium text-zinc-600 dark:text-zinc-300", children: "Spin prizes (weighted random)" }), values.spinPrizes.length === 0 && (_jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "No spin prizes yet. Add at least one to enable spinning." })), values.spinPrizes.map((p) => (_jsxs("div", { className: "grid grid-cols-12 gap-2 items-end", children: [_jsx("div", { className: "col-span-5", children: _jsx(Input, { label: "Label", value: p.label, onChange: (e) => onChange({ spinPrizes: values.spinPrizes.map((sp) => sp.id === p.id ? { ...sp, label: e.target.value } : sp) }), placeholder: "\u20B9100 off" }) }), _jsx("div", { className: "col-span-3", children: _jsx(Input, { label: "Coupon ID", value: p.couponId ?? "", onChange: (e) => onChange({ spinPrizes: values.spinPrizes.map((sp) => sp.id === p.id ? { ...sp, couponId: e.target.value || undefined } : sp) }) }) }), _jsx("div", { className: "col-span-2", children: _jsx(Input, { label: "Weight", type: "number", value: String(p.weight), onChange: (e) => onChange({ spinPrizes: values.spinPrizes.map((sp) => sp.id === p.id ? { ...sp, weight: Number(e.target.value) || 0 } : sp) }) }) }), _jsx("div", { className: "col-span-1 flex items-center justify-center pb-2", children: _jsx(Toggle, { checked: p.isActive, onChange: (v) => onChange({ spinPrizes: values.spinPrizes.map((sp) => sp.id === p.id ? { ...sp, isActive: v } : sp) }), label: "" }) }), _jsx("div", { className: "col-span-1 flex items-center justify-center pb-2", children: _jsx("button", { type: "button", onClick: () => onChange({ spinPrizes: values.spinPrizes.filter((sp) => sp.id !== p.id) }), className: "text-zinc-400 hover:text-red-500 text-lg leading-none px-2", "aria-label": "Remove prize", children: "\u00D7" }) })] }, p.id))), _jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: () => onChange({ spinPrizes: [...values.spinPrizes, { id: `prize-${Date.now()}`, label: "", couponId: undefined, weight: 1, isActive: true }] }), children: "+ Add spin prize" }), _jsx(Input, { label: "Max spins per user", type: "number", value: values.spinMaxPerUser, onChange: (e) => onChange({ spinMaxPerUser: e.target.value }), placeholder: "1" }), _jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-3", children: [_jsx(Input, { label: "Spin window start", type: "datetime-local", value: values.spinWindowStart, onChange: (e) => onChange({ spinWindowStart: e.target.value }) }), _jsx(Input, { label: "Spin window end", type: "datetime-local", value: values.spinWindowEnd, onChange: (e) => onChange({ spinWindowEnd: e.target.value }) })] })] })), isEdit && (_jsxs("div", { className: "rounded-lg border border-amber-200 bg-amber-50 dark:border-amber-700 dark:bg-amber-900/20 p-3 space-y-2", children: [_jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsx(Text, { className: "text-sm font-medium text-amber-900 dark:text-amber-100", children: raffleWinnerName ? `Winner: ${raffleWinnerName}` : "Raffle not yet triggered" }), _jsx(Button, { type: "button", variant: "primary", size: "sm", onClick: () => triggerRaffleMutation.mutate(), disabled: triggerRaffleMutation.isPending || Boolean(raffleWinnerName), children: triggerRaffleMutation.isPending ? "Triggering…" : raffleWinnerName ? "Already triggered" : "Trigger Raffle Now" })] }), raffleEntryCount !== null && _jsxs(Text, { className: "text-xs text-amber-800 dark:text-amber-200", children: ["Pool size: ", raffleEntryCount] }), raffleGithubUrl && _jsxs(Text, { className: "text-xs text-amber-800 dark:text-amber-200 break-all", children: ["Fairness proof: ", raffleGithubUrl] }), triggerMessage && _jsx(Text, { className: "text-xs text-amber-900 dark:text-amber-100", children: triggerMessage })] }))] }))] }));
331
+ },
332
+ },
333
+ ];
366
334
  const alertSection = eventQuery.error ? (_jsx(Alert, { variant: "error", title: "Could not load event", children: eventQuery.error instanceof Error ? eventQuery.error.message : "Unknown error" })) : null;
367
- const formSection = (_jsxs(Form, { onSubmit: (e) => {
368
- e.preventDefault();
369
- saveMutation.mutate();
370
- }, className: "space-y-6", children: [!eventId && (_jsx(Select, { label: "Event type", value: type, options: EVENT_TYPE_OPTIONS, onChange: (e) => setType(e.target.value) })), _jsx(Input, { label: "Title", value: title, onChange: (e) => setTitle(e.target.value), placeholder: "Charizard Flash Sale 2026", required: true }), _jsxs(_Fragment, { children: [_jsx("label", { className: "block text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-1", children: "Description" }), _jsx(RichTextEditor, { value: description, onChange: setDescription, minHeightClassName: "min-h-[120px]", placeholder: "Describe this event\u2026" })] }), _jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-3", children: [_jsx(Input, { label: "Starts at", type: "datetime-local", value: startsAt, onChange: (e) => setStartsAt(e.target.value), required: true }), _jsx(Input, { label: "Ends at", type: "datetime-local", value: endsAt, onChange: (e) => setEndsAt(e.target.value), required: true })] }), _jsx(Input, { label: "Cover image URL (optional)", value: coverImageUrl, onChange: (e) => setCoverImageUrl(e.target.value), placeholder: "https://\u2026" }), eventId && (_jsx(Select, { label: "Status", value: status, options: EVENT_STATUS_OPTIONS, onChange: (e) => setStatus(e.target.value) })), type === "sale" && (_jsxs("div", { className: CLS_PANEL_SM, children: [_jsx(Text, { className: "text-sm font-semibold text-zinc-700 dark:text-zinc-300", children: "Sale configuration" }), _jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-3", children: [_jsx(Input, { label: "Discount %", type: "number", value: discountPercent, onChange: (e) => setDiscountPercent(e.target.value), placeholder: "10" }), _jsx(Input, { label: "Banner text (optional)", value: saleBannerText, onChange: (e) => setSaleBannerText(e.target.value), placeholder: "Limited time \u2014 ends Sunday!" })] })] })), type === "offer" && (_jsxs("div", { className: CLS_PANEL_SM, children: [_jsx(Text, { className: "text-sm font-semibold text-zinc-700 dark:text-zinc-300", children: "Offer configuration" }), _jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-3", children: [_jsx(Input, { label: "Coupon ID", value: couponId, onChange: (e) => setCouponId(e.target.value), placeholder: "Firestore coupon document ID", required: true }), _jsx(Input, { label: "Display code", value: displayCode, onChange: (e) => setDisplayCode(e.target.value), placeholder: "CHARIZARD25", required: true })] }), _jsx(Input, { label: "Banner text (optional)", value: offerBannerText, onChange: (e) => setOfferBannerText(e.target.value), placeholder: "Use CHARIZARD25 at checkout" })] })), type === "poll" && (_jsxs("div", { className: CLS_PANEL_LG, children: [_jsx(Text, { className: "text-sm font-semibold text-zinc-700 dark:text-zinc-300", children: "Poll configuration" }), _jsxs("div", { className: "space-y-2", children: [_jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "Options (minimum 2)" }), pollOptions.map((opt, idx) => (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex-1", children: _jsx(Input, { value: opt.label, onChange: (e) => updatePollOption(opt.id, e.target.value), placeholder: `Option ${idx + 1}` }) }), pollOptions.length > 2 && (_jsx("button", { type: "button", onClick: () => removePollOption(opt.id), className: "text-zinc-400 hover:text-red-500 transition-colors px-2 py-1 text-lg leading-none", "aria-label": "Remove option", children: "\u00D7" }))] }, opt.id))), _jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: addPollOption, children: "+ Add option" })] }), _jsx(Select, { label: "Results visibility", value: resultsVisibility, options: POLL_VISIBILITY_OPTIONS, onChange: (e) => setResultsVisibility(e.target.value) }), _jsxs("div", { className: "space-y-3", children: [_jsx(Toggle, { checked: allowMultiSelect, onChange: setAllowMultiSelect, label: "Allow multi-select" }), _jsx(Toggle, { checked: allowComment, onChange: setAllowComment, label: "Allow comment with vote" })] })] })), type === "survey" && (_jsxs("div", { className: CLS_PANEL_LG, children: [_jsx(Text, { className: "text-sm font-semibold text-zinc-700 dark:text-zinc-300", children: "Survey configuration" }), _jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-3", children: [_jsx(Input, { label: "Max entries per user", type: "number", value: maxEntriesPerUser, onChange: (e) => setMaxEntriesPerUser(e.target.value), placeholder: "1" }), hasPointSystem && (_jsx(Input, { label: "Points label", value: pointsLabel, onChange: (e) => setPointsLabel(e.target.value), placeholder: "Points" }))] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Toggle, { checked: requireLogin, onChange: setRequireLogin, label: "Require login to participate" }), _jsx(Toggle, { checked: hasLeaderboard, onChange: setHasLeaderboard, label: "Show leaderboard" }), _jsx(Toggle, { checked: hasPointSystem, onChange: setHasPointSystem, label: "Enable point system (employees assign points per entry)" }), _jsx(Toggle, { checked: entryReviewRequired, onChange: setEntryReviewRequired, label: "Require employee review before entry is approved" })] }), renderFormFieldBuilder(surveyFields, setSurveyFields)] })), type === "feedback" && (_jsxs("div", { className: CLS_PANEL_LG, children: [_jsx(Text, { className: "text-sm font-semibold text-zinc-700 dark:text-zinc-300", children: "Feedback configuration" }), _jsx(Toggle, { checked: anonymous, onChange: setAnonymous, label: "Allow anonymous submissions" }), renderFormFieldBuilder(feedbackFields, setFeedbackFields)] })), _jsxs("div", { className: CLS_PANEL_LG, children: [_jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsx(Text, { className: "text-sm font-semibold text-zinc-700 dark:text-zinc-300", children: "Raffle & spin wheel" }), type !== "raffle" && type !== "spin_wheel" && (_jsx(Toggle, { checked: hasRaffle, onChange: setHasRaffle, label: "Attach raffle" }))] }), (hasRaffle || type === "raffle" || type === "spin_wheel") && (_jsxs(_Fragment, { children: [_jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-3", children: [_jsx(Select, { label: "Raffle type", value: type === "spin_wheel" ? "spin_wheel" : raffleType, options: RAFFLE_TYPE_OPTIONS, onChange: (e) => setRaffleType(e.target.value), disabled: type === "spin_wheel" }), _jsx(Input, { label: "Top N (for top-N raffle types)", type: "number", value: raffleTopN, onChange: (e) => setRaffleTopN(e.target.value), placeholder: "10" })] }), _jsx(Input, { label: "Prize description", value: rafflePrize, onChange: (e) => setRafflePrize(e.target.value), placeholder: "\u20B92,000 store credit + exclusive Pikachu sticker" }), _jsx(Input, { label: "Coupon ID for winner (optional)", value: rafflePrizeCouponId, onChange: (e) => setRafflePrizeCouponId(e.target.value), placeholder: "coupon-vip-winner-2026" }), (raffleType === "spin_wheel" || type === "spin_wheel") && (_jsxs("div", { className: "rounded-lg border border-zinc-200 dark:border-slate-700 p-3 space-y-3", children: [_jsx(Text, { className: "text-xs font-medium text-zinc-600 dark:text-zinc-300", children: "Spin prizes (weighted random)" }), spinPrizes.length === 0 && (_jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "No spin prizes yet. Add at least one to enable spinning." })), spinPrizes.map((p) => (_jsxs("div", { className: "grid grid-cols-12 gap-2 items-end", children: [_jsx("div", { className: "col-span-5", children: _jsx(Input, { label: "Label", value: p.label, onChange: (e) => updateSpinPrize(p.id, { label: e.target.value }), placeholder: "\u20B9100 off" }) }), _jsx("div", { className: "col-span-3", children: _jsx(Input, { label: "Coupon ID", value: p.couponId ?? "", onChange: (e) => updateSpinPrize(p.id, {
371
- couponId: e.target.value || undefined,
372
- }) }) }), _jsx("div", { className: "col-span-2", children: _jsx(Input, { label: "Weight", type: "number", value: String(p.weight), onChange: (e) => updateSpinPrize(p.id, {
373
- weight: Number(e.target.value) || 0,
374
- }) }) }), _jsx("div", { className: "col-span-1 flex items-center justify-center pb-2", children: _jsx(Toggle, { checked: p.isActive, onChange: (v) => updateSpinPrize(p.id, { isActive: v }), label: "" }) }), _jsx("div", { className: "col-span-1 flex items-center justify-center pb-2", children: _jsx("button", { type: "button", onClick: () => removeSpinPrize(p.id), className: "text-zinc-400 hover:text-red-500 text-lg leading-none px-2", "aria-label": "Remove prize", children: "\u00D7" }) })] }, p.id))), _jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: addSpinPrize, children: "+ Add spin prize" }), _jsx(Input, { label: "Max spins per user", type: "number", value: spinMaxPerUser, onChange: (e) => setSpinMaxPerUser(e.target.value), placeholder: "1" }), _jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-3", children: [_jsx(Input, { label: "Spin window start", type: "datetime-local", value: spinWindowStart, onChange: (e) => setSpinWindowStart(e.target.value) }), _jsx(Input, { label: "Spin window end", type: "datetime-local", value: spinWindowEnd, onChange: (e) => setSpinWindowEnd(e.target.value) })] })] })), eventId && (_jsxs("div", { className: "rounded-lg border border-amber-200 bg-amber-50 dark:border-amber-700 dark:bg-amber-900/20 p-3 space-y-2", children: [_jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsx(Text, { className: "text-sm font-medium text-amber-900 dark:text-amber-100", children: raffleWinnerName
375
- ? `Winner: ${raffleWinnerName}`
376
- : "Raffle not yet triggered" }), _jsx(Button, { type: "button", variant: "primary", size: "sm", onClick: () => triggerRaffleMutation.mutate(), disabled: triggerRaffleMutation.isPending ||
377
- Boolean(raffleWinnerName), children: triggerRaffleMutation.isPending
378
- ? "Triggering…"
379
- : raffleWinnerName
380
- ? "Already triggered"
381
- : "Trigger Raffle Now" })] }), raffleEntryCount !== null && (_jsxs(Text, { className: "text-xs text-amber-800 dark:text-amber-200", children: ["Pool size: ", raffleEntryCount] })), raffleGithubUrl && (_jsxs(Text, { className: "text-xs text-amber-800 dark:text-amber-200 break-all", children: ["Fairness proof: ", raffleGithubUrl] })), triggerMessage && (_jsx(Text, { className: "text-xs text-amber-900 dark:text-amber-100", children: triggerMessage }))] }))] }))] }), _jsxs(FormActions, { align: "right", children: [_jsx(Button, { type: "button", variant: "outline", onClick: () => { window.location.href = "/admin/events"; }, children: "Cancel" }), _jsx(Button, { type: "submit", disabled: saveMutation.isPending || !isValid, children: saveMutation.isPending ? "Saving…" : eventId ? "Save changes" : "Create event" })] }), saveMessage && (_jsx(Alert, { variant: saveMessage.toLowerCase().includes("fail") ? "error" : "success", title: "Save status", children: saveMessage }))] }));
335
+ const formContent = (_jsxs("div", { className: "space-y-4", children: [alertSection, _jsx(StepForm, { steps: steps, values: draft, onChange: update, onComplete: () => { saveMutation.mutate(); }, formId: "admin-event", currentStep: currentStep, onStepChange: setCurrentStep, completeLabel: isEdit ? "Save Changes" : "Create Event", isLoading: isLoading }), saveMessage && (_jsx(Alert, { variant: saveMessage.toLowerCase().includes("fail") || saveMessage.toLowerCase().includes("error") ? "error" : "success", title: "Save status", children: saveMessage }))] }));
382
336
  if (embedded) {
383
- return (_jsxs("div", { className: "overflow-y-auto p-4 space-y-4", children: [alertSection, formSection] }));
337
+ return _jsx("div", { className: "overflow-y-auto p-4", children: formContent });
384
338
  }
385
- return (_jsx(StackedViewShell, { portal: "admin", ...rest, title: eventId ? "Edit Event" : "Create Event", sections: [alertSection, formSection] }));
339
+ return (_jsx(StackedViewShell, { portal: "admin", ...rest, title: isEdit ? "Edit Event" : "Create Event", sections: [formContent] }));
386
340
  }