@plusscommunities/pluss-feature-builder-web-a 1.0.2-beta.1 → 1.0.2-beta.2

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.
package/dist/index.cjs.js CHANGED
@@ -2117,438 +2117,16 @@ const Text$8 = Components$4.Text;
2117
2117
 
2118
2118
  function ownKeys$8(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
2119
2119
  function _objectSpread$8(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys$8(Object(t), !0).forEach(function (r) { _defineProperty__default["default"](e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys$8(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
2120
- const SET_NAVIGATION_STATE$1 = "SET_NAVIGATION_STATE";
2121
- const UPDATE_STEP_VALIDATION$1 = "UPDATE_STEP_VALIDATION";
2122
- const MARK_STEP_COMPLETE$1 = "MARK_STEP_COMPLETE";
2123
- const setCurrentStep = function (step) {
2124
- let previousStep = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
2125
- return {
2126
- type: SET_NAVIGATION_STATE$1,
2127
- payload: {
2128
- currentStep: step,
2129
- previousStep,
2130
- canGoBack: step !== "welcome",
2131
- canGoForward: true
2132
- }
2133
- };
2134
- };
2135
- const goToStep = step => (dispatch, getState) => {
2136
- const state = getState()[require("../values.config").reducerKey];
2137
- const currentStep = state && state.wizard && state.wizard.navigation && state.wizard.navigation.currentStep;
2138
-
2139
- // Clear form submission state when changing steps
2140
- const {
2141
- clearFormSubmissionState
2142
- } = require("./formActions");
2143
- dispatch(clearFormSubmissionState());
2144
- dispatch(setCurrentStep(step, currentStep));
2145
- };
2146
- const updateStepValidation = function (step, isValid) {
2147
- let errors = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
2148
- return {
2149
- type: UPDATE_STEP_VALIDATION$1,
2150
- payload: {
2151
- step,
2152
- isValid,
2153
- errors
2154
- }
2155
- };
2156
- };
2157
- const validateAndUpdateStep = step => (dispatch, getState) => {
2158
- // Use existing selectors to get form data
2159
- const state = getState();
2160
- const title = selectFormTitle(state);
2161
- const icon = selectFormIcon(state);
2162
- const displayName = selectFormDisplayName(state);
2163
- const layout = selectFormLayout(state);
2164
- const fields = selectFormFields(state);
2165
- const form = {
2166
- title,
2167
- icon,
2168
- displayName,
2169
- layout,
2170
- fields
2171
- };
2172
- const {
2173
- isValid,
2174
- errors
2175
- } = getFormValidation(form, step);
2176
- dispatch(updateStepValidation(step, isValid, errors));
2177
-
2178
- // If valid, mark as complete
2179
- if (isValid) {
2180
- dispatch({
2181
- type: MARK_STEP_COMPLETE$1,
2182
- payload: step
2183
- });
2184
- }
2185
- return {
2186
- isValid,
2187
- errors
2188
- };
2189
- };
2190
- const getFormValidation = (form, step) => {
2191
- // Return validation results for undefined form (prevent crashes)
2192
- if (!form) {
2193
- switch (step) {
2194
- case "overview":
2195
- return {
2196
- isValid: false,
2197
- errors: {
2198
- title: "Title is required",
2199
- displayName: "Display name is required",
2200
- icon: "Icon is required"
2201
- }
2202
- };
2203
- case "fields":
2204
- return {
2205
- isValid: false,
2206
- errors: {
2207
- missingTitle: "Title field is required",
2208
- missingImage: "Feature image field is required",
2209
- fieldLabels: "Some fields are missing labels"
2210
- }
2211
- };
2212
- case "layout":
2213
- return {
2214
- isValid: false,
2215
- errors: {
2216
- layoutType: "Layout type is required"
2217
- }
2218
- };
2219
- default:
2220
- return {
2221
- isValid: false,
2222
- errors: {}
2223
- };
2224
- }
2225
- }
2226
- switch (step) {
2227
- case "overview":
2228
- {
2229
- const hasTitle = form.title && form.title.trim().length > 0;
2230
- const hasDisplayName = form.displayName && form.displayName.trim().length > 0;
2231
- const hasIcon = form.icon && form.icon.length > 0;
2232
- return {
2233
- isValid: hasTitle && hasDisplayName && hasIcon,
2234
- errors: {
2235
- title: !hasTitle ? "Title is required" : null,
2236
- displayName: !hasDisplayName ? "Display name is required" : null,
2237
- icon: !hasIcon ? "Icon is required" : null
2238
- }
2239
- };
2240
- }
2241
- case "fields":
2242
- {
2243
- const hasTitleField = form.fields && form.fields.some(field => field.id === "mandatory-title");
2244
- const hasImageField = form.fields && form.fields.some(field => field.id === "mandatory-feature-image");
2245
-
2246
- // Check each field for missing labels and create field-specific errors
2247
- const fieldErrors = {};
2248
- let allFieldsHaveLabels = true;
2249
- if (form.fields) {
2250
- form.fields.forEach(field => {
2251
- if ((field.type === "text" || field.type === "description" || field.type === "title" || field.type === "image" || field.type === "gallery" || field.type === "feature-image" || field.type === "file" || field.type === "cta") && field.values) {
2252
- if (!field.values.label || field.values.label.trim().length === 0) {
2253
- fieldErrors[field.id] = "Field label is required";
2254
- allFieldsHaveLabels = false;
2255
- }
2256
- }
2257
- });
2258
- }
2259
- return {
2260
- isValid: hasTitleField && hasImageField && allFieldsHaveLabels,
2261
- errors: _objectSpread$8({
2262
- missingTitle: !hasTitleField ? "Title field is required" : null,
2263
- missingImage: !hasImageField ? "Feature image field is required" : null
2264
- }, fieldErrors)
2265
- };
2266
- }
2267
- case "layout":
2268
- {
2269
- const hasLayoutType = form.layout && form.layout.type && form.layout.type.length > 0;
2270
- return {
2271
- isValid: hasLayoutType,
2272
- errors: {
2273
- layoutType: !hasLayoutType ? "Layout type is required" : null
2274
- }
2275
- };
2276
- }
2277
- default:
2278
- return {
2279
- isValid: true,
2280
- errors: {}
2281
- };
2282
- }
2283
- };
2284
- const setCurrentStepAndSave = function (step) {
2285
- let previousStep = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
2286
- return dispatch => {
2287
- dispatch(setCurrentStep(step, previousStep));
2288
- };
2289
- };
2290
-
2291
- var css$d = ".fullWidthContent {\n\tmax-width: 100%;\n\tmargin-left: auto;\n\tmargin-right: auto;\n\tpadding: 2rem 2rem 3rem 2rem; /* Add extra bottom padding */\n}\n\n/* Full-width container that allows scrollbar at edge */\n.fullWidthContainer {\n\tdisplay: flex;\n\tflex-direction: column;\n\tflex: 1;\n\tmin-height: 0; /* Allow content to determine height */\n\twidth: 100%; /* Take full width to ensure scrollbar is at edge */\n}\n\n/* Content container to keep content centered */\n.contentContainer {\n\tdisplay: flex;\n\tflex-direction: column;\n\tflex: 1;\n\tmin-height: 0;\n\tmax-width: 960px;\n\tmargin: 0 auto;\n\tpadding: 64px 32px;\n\twidth: 100%;\n\tbox-sizing: border-box;\n}\n\n/* Legacy container class for backward compatibility */\n.container{\n\tdisplay: flex;\n\tflex-direction: column;\n\tflex: 1;\n\tmin-height: 0; /* Allow content to determine height */\n max-width: 960px;\n margin: 0 auto;\n padding: 64px 32px;\n}\n\n/* Responsive adjustments for content container */\n@media (max-width: 768px) {\n\t.contentContainer {\n\t\tpadding: 1.5rem 1.5rem 2.5rem 1.5rem; /* Slightly reduced but still adequate */\n\t}\n\t.container {\n\t\tpadding: 1.5rem 1.5rem 2.5rem 1.5rem; /* Legacy container support */\n\t}\n}\n\n@media (max-width: 480px) {\n\t.contentContainer {\n\t\tpadding: 1rem 1rem 2rem 1rem; /* Maintain padding on small screens */\n\t}\n\t.container {\n\t\tpadding: 1rem 1rem 2rem 1rem; /* Legacy container support */\n\t}\n}\n\n/* Enhanced sidebar navigation for progress indication */\n:global(.hub-sideBar) {\n\t/* Make sidebar more prominent during creation mode */\n}\n\n:global(.sideNav-item.isCurrent) {\n\tbackground: var(--colour-purple, #6e79c5) !important;\n\tcolor: var(--colour-white, #ffffff) !important;\n\tfont-weight: 600 !important;\n\tborder-left: 4px solid var(--colour-branding-dark, #364196) !important;\n}\n\n:global(.sideNav-item.isCurrent) i {\n\tcolor: var(--colour-white, #ffffff) !important;\n}\n\n:global(.sideNav-item.isCompleted) {\n\tcolor: var(--colour-branding-dark, #364196) !important;\n\tposition: relative;\n}\n\n:global(.sideNav-item.isCompleted::after) {\n\tcontent: \"✓\";\n\tposition: absolute;\n\tright: 1rem;\n\ttop: 50%;\n\ttransform: translateY(-50%);\n\tcolor: var(--colour-branding-dark, #364196);\n\tfont-weight: bold;\n}\n\n:global(.sideNav-item.isDisabled) {\n\topacity: 0.4;\n\tcursor: not-allowed;\n}\n\n/* Enhanced progress section */\n:global(.hub-sideBar-section) {\n\tborder-bottom: 2px solid var(--colour-branding-main-fade, rgba(74, 87, 183, 0.15));\n\tpadding-bottom: 1rem;\n\tmargin-bottom: 1rem;\n}\n\n:global(.hub-sideBar-section:last-child) {\n\tborder-bottom: none;\n}\n\n/* Delete button styling - bold red for development */\n.deleteButton {\n\tcolor: #dc3545 !important;\n\tfont-weight: bold !important;\n}\n\n:global(.sideNav-item.deleteButton) {\n\tcolor: #dc3545 !important;\n\tfont-weight: bold !important;\n}\n\n:global(.sideNav-item.deleteButton:hover) {\n\tbackground-color: rgba(220, 53, 69, 0.1) !important;\n}\n\n:global(.sideNav-item.deleteButton i) {\n\tcolor: #dc3545 !important;\n}\n\n/* Development delete button */\n.devDeleteButton {\n\tposition: absolute;\n\tbottom: 20px;\n\tleft: 20px;\n\tbackground-color: #ff0000;\n\tcolor: #ffffff;\n\tborder: none;\n\tpadding: 8px 12px;\n\tborder-radius: 4px;\n\tcursor: pointer;\n\tfont-size: 12px;\n\tfont-weight: bold;\n\tz-index: 1000;\n\tbox-shadow: 0 2px 4px rgba(0,0,0,0.3);\n}\n\n.devDeleteButton:hover {\n\tbackground-color: #cc0000;\n}\n";
2292
- n(css$d,{});
2293
-
2294
- const {
2295
- Helper: Helper$2,
2296
- Session: Session$2
2297
- } = PlussCore__namespace;
2298
- const {
2299
- getUrl: getUrl$2
2300
- } = Helper$2;
2301
- const {
2302
- authedFunction: authedFunction$2
2303
- } = Session$2;
2304
-
2305
- /**
2306
- * Sidebar Layout component for feature builder wizard
2307
- * Provides navigation sidebar with step progression, completion tracking
2308
- * Includes delete functionality for feature definitions
2309
- * Manages step accessibility based on wizard mode and validation state
2310
- *
2311
- * @param {Object} props - Component props
2312
- * @param {React.ReactNode} props.children - Child components to render in main content area
2313
- * @param {Object} props.history - React Router history object for navigation
2314
- * @returns {React.ReactElement} Layout component with sidebar navigation
2315
- *
2316
- * @example
2317
- * <SidebarLayout history={historyObject}>
2318
- * <YourMainContent />
2319
- * </SidebarLayout>
2320
- */
2321
- const SideBarInner = props => {
2322
- const {
2323
- children,
2324
- history
2325
- } = props;
2326
- const dispatch = reactRedux.useDispatch();
2327
-
2328
- // Get wizard state
2329
- const mode = reactRedux.useSelector(selectWizardMode);
2330
- const isEditMode = reactRedux.useSelector(selectIsEditMode);
2331
- const isCreateMode = reactRedux.useSelector(selectIsCreateMode);
2332
- const currentStep = reactRedux.useSelector(selectCurrentStep);
2333
- reactRedux.useSelector(selectStepProgress);
2334
- const definition = reactRedux.useSelector(selectDefinition);
2335
- const definitionId = reactRedux.useSelector(selectDefinitionId);
2336
- const auth = reactRedux.useSelector(state => state.auth);
2337
-
2338
- // Delete functionality state
2339
- const [showDeleteConfirm, setShowDeleteConfirm] = React.useState(false);
2340
- const [isDeleting, setIsDeleting] = React.useState(false);
2341
- const goTo = url => history.push(url);
2342
- const isSelected = url => {
2343
- return history.location.pathname === url;
2344
- };
2345
-
2346
- // Define step configuration with dynamic URL based on mode
2347
- const getStepUrl = stepKey => {
2348
- // Always use /definition/ in the URL since that's how routes are registered
2349
- return "/feature-builder/definition/".concat(stepKey);
2350
- };
2351
- const steps = [{
2352
- key: "overview",
2353
- text: "Feature Overview",
2354
- icon: "info-circle",
2355
- url: getStepUrl("overview")
2356
- }, {
2357
- key: "fields",
2358
- text: "Configure Fields",
2359
- icon: "edit",
2360
- url: getStepUrl("fields")
2361
- }, {
2362
- key: "layout",
2363
- text: "Choose Layout",
2364
- icon: "columns",
2365
- url: getStepUrl("layout")
2366
- }];
2367
-
2368
- // Build sidebar items based on mode
2369
- const buildSidebarItems = () => {
2370
- return steps.map((step, index) => {
2371
- const isCompleted = selectIsStepComplete(step.key);
2372
- const isAccessible = selectIsStepAccessible(step.key);
2373
-
2374
- // Add step number to text for better clarity
2375
- const stepText = "".concat(index + 1, ". ").concat(step.text);
2376
- const itemProps = {
2377
- type: "navItem",
2378
- text: stepText,
2379
- icon: step.icon,
2380
- selected: isSelected(step.url),
2381
- onclick: isAccessible ? () => {
2382
- goTo(step.url);
2383
- dispatch(goToStep(step.key));
2384
- } : null,
2385
- isFontAwesome: true,
2386
- // Enhanced completion indicator
2387
- completed: isCompleted,
2388
- // Allow navigation to completed steps even in create mode
2389
- disabled: mode === "create" && !isAccessible
2390
- };
2391
- return itemProps;
2392
- });
2393
- };
2394
-
2395
- // Determine sidebar title - always use "Build Your Feature" now
2396
- const getSidebarTitle = () => {
2397
- return "Build Your Feature";
2398
- };
2399
-
2400
- // Simple help text
2401
- const getHelpText = () => {
2402
- return "Get help with feature builder";
2403
- };
2404
-
2405
- // Build sidebar sections - simplified without progress section
2406
- const sidebarSections = [{
2407
- title: getSidebarTitle(),
2408
- items: buildSidebarItems()
2409
- }];
2410
-
2411
- // Delete functionality handlers
2412
- const handleDeleteClick = () => {
2413
- setShowDeleteConfirm(true);
2414
- };
2415
- const handleConfirmDelete = async () => {
2416
- if (!definition) return;
2417
- setIsDeleting(true);
2418
- try {
2419
- await authedFunction$2({
2420
- method: "POST",
2421
- url: getUrl$2("feature-builder", "definition/update/delete"),
2422
- data: {
2423
- id: definitionId,
2424
- site: auth.site
2425
- }
2426
- });
2427
-
2428
- // Redirect to welcome screen after deletion
2429
- history.push("/feature-builder/definition/overview");
2430
- } finally {
2431
- setIsDeleting(false);
2432
- setShowDeleteConfirm(false);
2433
- }
2434
- };
2435
- const handleCancelDelete = () => {
2436
- setShowDeleteConfirm(false);
2437
- };
2438
-
2439
- // Add effect to manually attach click handlers since HubSidebar might not use onclick properly
2440
- React.useEffect(() => {
2441
- const attachClickHandlers = () => {
2442
- const stepsWithUrls = [{
2443
- key: "overview",
2444
- url: getStepUrl("overview")
2445
- }, {
2446
- key: "fields",
2447
- url: getStepUrl("fields")
2448
- }, {
2449
- key: "layout",
2450
- url: getStepUrl("layout")
2451
- }];
2452
- stepsWithUrls.forEach(step => {
2453
- const navItem = Array.from(document.querySelectorAll(".sideNav-item")).find(item => item.textContent && item.textContent.includes(step.key === "overview" ? "Feature Overview" : step.key === "fields" ? "Configure Fields" : "Choose Layout"));
2454
- if (navItem) {
2455
- // Remove any existing click listeners
2456
- navItem.onclick = null;
2457
-
2458
- // Check if this step is accessible
2459
- const isAccessible = selectIsStepAccessible(step.key)({
2460
- [values.reducerKey]: {
2461
- wizard: {
2462
- mode: mode,
2463
- navigation: {
2464
- currentStep: currentStep
2465
- }
2466
- }
2467
- }
2468
- });
2469
-
2470
- // Add our click handler only if accessible
2471
- if (isAccessible) {
2472
- navItem.onclick = event => {
2473
- event.preventDefault();
2474
- event.stopPropagation();
2475
- history.push(step.url);
2476
- dispatch(goToStep(step.key));
2477
- };
2478
- }
2479
-
2480
- // Make sure it's styled appropriately
2481
- navItem.style.cursor = isAccessible ? "pointer" : "not-allowed";
2482
- navItem.style.pointerEvents = "auto";
2483
- navItem.style.opacity = isAccessible ? "1" : "0.5";
2484
- }
2485
- });
2486
- };
2487
-
2488
- // Initial attachment
2489
- attachClickHandlers();
2490
-
2491
- // Re-attach after a short delay to ensure HubSidebar has rendered
2492
- const timeoutId = setTimeout(attachClickHandlers, 100);
2493
-
2494
- // Also try to re-attach when DOM changes (observe for mutations)
2495
- const observer = new MutationObserver(() => {
2496
- setTimeout(attachClickHandlers, 50);
2497
- });
2498
-
2499
- // Start observing the document body for changes
2500
- observer.observe(document.body, {
2501
- childList: true,
2502
- subtree: true
2503
- });
2504
- return () => {
2505
- clearTimeout(timeoutId);
2506
- observer.disconnect();
2507
- };
2508
- }, [history, dispatch, isEditMode, isCreateMode, currentStep]);
2509
- return /*#__PURE__*/React__default["default"].createElement("div", {
2510
- className: "hub-wrapperContainer"
2511
- }, /*#__PURE__*/React__default["default"].createElement(HubSidebar, {
2512
- sections: sidebarSections,
2513
- helpGuide: {
2514
- text: getHelpText(),
2515
- url: "https://www.plusscommunities.com/user-guide"
2516
- }
2517
- }), /*#__PURE__*/React__default["default"].createElement("button", {
2518
- onClick: handleDeleteClick,
2519
- className: css$d.devDeleteButton
2520
- }, "DELETE FEATURE DEF"), /*#__PURE__*/React__default["default"].createElement("div", {
2521
- className: "hub-contentWrapper"
2522
- }, /*#__PURE__*/React__default["default"].createElement("div", {
2523
- className: css$d.fullWidthContainer
2524
- }, /*#__PURE__*/React__default["default"].createElement("div", {
2525
- className: css$d.contentContainer
2526
- }, children))), /*#__PURE__*/React__default["default"].createElement(DeleteConfirmationPopup, {
2527
- isOpen: showDeleteConfirm,
2528
- listing: definition,
2529
- onConfirm: handleConfirmDelete,
2530
- onCancel: handleCancelDelete,
2531
- isDeleting: isDeleting,
2532
- deleteType: "featureDefinition"
2533
- }));
2534
- };
2535
- const SidebarLayout = reactRouter.withRouter(SideBarInner);
2536
-
2537
- var css$c = ".content {\n\tflex: 1 1 auto;\n\tdisplay: flex;\n\tflex-flow: column nowrap;\n\tpadding: 2rem 0 3rem 0; /* Add proper top/bottom padding */\n\tmin-height: 100%; /* Ensure full height for proper spacing */\n}\n\n/* New page wrapper for centered container approach */\n.pageWrapper {\n\tpadding: 2rem 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tjustify-content: flex-start;\n}\n\n@media (min-width: 768px) {\n\t.pageWrapper {\n\t\tpadding: 3rem 0;\n\t}\n}\n\n/* Form container styling when using CenteredContainer */\n.formContainer {\n\tbackground-color: var(--bg-white);\n}\n\n/* Section styling */\n.section {\n\tmargin-bottom: 2rem;\n\t\tpadding: 0 0 2em;\n}\n\n.section--no-border {\n\t\tborder-bottom: none;\n}\n\n.subtitle {\n\tcolor: var(--text-bluegrey, #6c7a90);\n}\n\n.sectionHeader {\n\t\tdisplay: flex;\n\t\tjustify-content: space-between;\n}\n\n/* Add Field Button styling */\n.addFieldButton {\n\tbackground: var(--colour-branding-action, #5c90df);\n\tcolor: white;\n\tborder: none;\n\tpadding: 0.75rem 1.5rem;\n\tborder-radius: 6px;\n\tfont-size: 1rem;\n\tcursor: pointer;\n\tmargin-bottom: 2rem;\n\ttransition: all 0.2s ease;\n}\n\n.addFieldButton:hover {\n\tbackground: var(--colour-branding-action-hover, #364196);\n\ttransform: translateY(-1px);\n\tbox-shadow: 0 2px 4px rgba(92, 144, 223, 0.3);\n}\n\n.addFieldButton:focus {\n\toutline: 2px solid var(--colour-branding-action, #5c90df);\n\toutline-offset: 2px;\n}\n\n.addFieldButton:active {\n\ttransform: translateY(0);\n}\n\n/* Refresh button styling */\n.refreshButton {\n\tmargin-left: 10px;\n\tpadding: 2px 8px;\n\tfont-size: 12px;\n\tbackground: var(--colour-dusk, #536280);\n\tcolor: white;\n\tborder: none;\n\tborder-radius: 4px;\n\tcursor: pointer;\n\ttransition: background-color 0.2s ease;\n}\n\n.refreshButton:hover {\n\tbackground: var(--colour-dusk-hover, #485968);\n}\n\n/* Error message container */\n.errorMessageContainer {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 0.5rem;\n}\n\n/* Add Field Container - matches field structure */\n.addFieldContainer {\n\tdisplay: flex;\n\tmargin-top: 32px;\n}\n\n.fieldNumberContainer {\n\tdisplay: flex;\n\twidth: 40px;\n}\n\n/* Add Field Section */\n.addFieldSection {\n\tdisplay: flex;\n\tgap: 1rem;\n\talign-items: center;\n\tjustify-content: flex-start; /* Align to start to match field content */\n\tmargin-top: 0; /* Remove top margin since it's in the container */\n}\n\n/* Desktop: horizontal layout for field selector and button */\n@media (min-width: 769px) {\n\t.addFieldSection {\n\t\tflex-direction: row;\n\t\talign-items: baseline;\n\t\tgap: 2rem;\n\t\talign-items: flex-start;\n\t\tjustify-content: flex-start; /* Align to start to match field content */\n\t}\n}\n\n/* Help Section for Popup */\n.helpSection {\n\tbackground-color: var(--colour-branding-action-superlight);\n\tborder: 1px solid var(--colour-branding-inactive);\n\tborder-radius: 8px;\n\tpadding: 1.5rem;\n\tmargin-bottom: 1.5rem;\n}\n\n.helpTitle {\n\tcolor: var(--text-dark);\n\tfont-weight: 600;\n\tmargin-bottom: 0.75rem;\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 0.5rem;\n\tfont-size: 1.25rem;\n}\n\n.helpText {\n\tcolor: var(--colour-branding-action);\n\tline-height: 1.6;\n\tfont-size: 1.25rem;\n}\n\n/* Field Type Cards for Popup */\n.fieldTypeCards {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 1rem; /* Spacing between vertical cards */\n\tpadding: 1rem 0;\n}\n\n/* Responsive layout: maintain vertical layout on all screen sizes */\n@media (min-width: 768px) {\n\t.fieldTypeCards {\n\t\tgap: 1.5rem; /* Slightly larger gap on desktop */\n\t}\n}\n\n.fieldTypeCard {\n\tdisplay: flex;\n\tflex-direction: row;\n\talign-items: flex-start;\n\tpadding: 1.75rem 1.25rem; /* More vertical padding */\n\tborder: 1px solid var(--border-line-grey);\n\tborder-radius: 8px;\n\tbackground-color: var(--bg-white);\n\tcursor: pointer;\n\ttransition: all 0.2s ease;\n\tbox-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);\n\tmin-height: 120px; /* Adjust height for horizontal layout */\n\tgap: 1rem; /* Space between icon and content */\n}\n\n.fieldTypeCard:hover {\n\tborder-color: var(--text-light);\n\tbackground-color: var(--bg-bluegrey);\n\tbox-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);\n}\n\n.fieldTypeCard:focus {\n\toutline: 2px solid var(--text-light);\n\toutline-offset: 2px;\n}\n\n.fieldTypeCard:active {\n\ttransform: scale(0.98);\n\ttransition: transform 0.1s ease;\n}\n\n.fieldTypeCardIcon {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\twidth: 48px;\n\theight: 48px;\n\tmargin-bottom: 0; /* Remove bottom margin for horizontal layout */\n\tfont-size: 20px;\n\tflex-shrink: 0;\n\tborder: 1px solid var(--border-line-grey); /* Subtle border */\n\tborder-radius: 8px;\n\tbackground-color: var(--bg-bluegrey);\n\tpadding: 8px;\n}\n\n\n.fieldTypeCardContent {\n\tflex: 1;\n\twidth: 100%;\n\tmin-width: 0; /* Prevent content from overflowing */\n}\n\n.fieldTypeCardTitle {\n\tmargin: 0 0 0.25rem 0;\n\tfont-weight: 600;\n\tfont-size: 1.4rem; /* Increased for better readability */\n}\n\n.fieldTypeCardDescription {\n\tmargin: 0 0 0.75rem 0;\n\tfont-size: 1.6rem; /* Increased for better readability */\n\tline-height: 1.5; /* Improved line height for better readability */\n}\n\n.fieldTypeCardUseCase {\n\tmargin: 0;\n\tfont-size: 1.4rem;\n\tfont-style: italic;\n\tcolor: var(--text-light);\n\tpadding-top: 0.5rem;\n\tborder-top: 1px solid var(--border-line-grey);\n}\n\n/* Field selector and button container - horizontal layout */\n.addFieldSection> div:first-child {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 0.5rem;\n\tflex: 1;\n}\n\n/* Container for add field button alignment */\n.addFieldButtonContainer {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: flex-end;\n}\n\n/* Empty State */\n.emptyState {\n\tpadding: 2rem;\n\ttext-align: center;\n\tbackground-color: var(--bg-bluegrey, #f4f7f9);\n\tborder-radius: 8px;\n\tborder: 1px dashed var(--border-line-grey, #dbddf1);\n\tmargin-bottom: 1rem;\n}\n\n.emptyStateText {\n\tcolor: var(--text-bluegrey, #6c7a90);\n\tfont-style: italic;\n}\n\n.fieldTypeSelector {\n\tdisplay: flex;\n\tflex-direction: column;\n\tmax-width: 500px; /* Increased from 300px to accommodate longer text */\n\twidth: 100%;\n}\n\n@media (max-width: 768px) {\n\t.addFieldSection {\n\t\talign-items: stretch;\n\t}\n\t\n\t.fieldTypeSelector {\n\t\tmax-width: none;\n\t}\n}\n\n.grid__four {\n max-width: 1200px;\n\tdisplay: grid;\n\tgrid-template-columns: repeat(2, minmax(250px, 1fr));\n margin: 2rem 0;\n\tgap: 2rem;\n\tjustify-items: center;\n}\n\n@media (max-width: 768px) {\n\t.grid__four {\n\t\tgrid-template-columns: 1fr;\n\t\tgap: 2rem;\n\t}\n}\n\n/* Navigation styles */\n.navigation {\n\tdisplay: flex;\n\tjustify-content: space-between;\n\tgap: 1rem;\n\tmargin-top: 3rem; /* Increase from 2rem */\n\tpadding-top: 2rem;\n\tpadding-bottom: 2rem; /* Add bottom padding */\n\tborder-top: 1px solid #e9ecef;\n}\n\n/* Single button alignment - align to right for overview step save button */\n.navigation> :only-child {\n\tmargin-left: auto;\n\tmargin-right: 0;\n}\n\n/* Two button alignment - one left, one right */\n.navigation> :first-child:not(:only-child) {\n\tmargin-right: auto;\n}\n\n.navigation> :last-child:not(:only-child) {\n\tmargin-left: auto;\n}\n\n/* Special case for overview step - align save button to right */\n.overviewStep .navigation> :only-child {\n\tmargin-left: auto;\n\tmargin-right: 0;\n}\n\n.previousButton {\n\tbackground: #6c757d;\n\tcolor: white;\n\tborder: none;\n\tpadding: 0.75rem 1.5rem;\n\tborder-radius: 6px;\n\tfont-size: 1rem;\n\tcursor: pointer;\n\ttransition: all 0.2s ease;\n}\n\n.previousButton:hover {\n\tbackground: #5a6268;\n}\n\n.saveButton {\n\tbackground: var(--colour-purple, #6e79c5);\n\tcolor: white;\n\tborder: none;\n\tpadding: 0.75rem 2rem;\n\tborder-radius: 6px;\n\tfont-size: 1rem;\n\tfont-weight: 500;\n\tcursor: pointer;\n\ttransition: all 0.2s ease;\n}\n\n.saveButton:hover:not(:disabled) {\n\tbackground: var(--colour-purple-hover, #5a66b3);\n\ttransform: translateY(-1px);\n\tbox-shadow: 0 2px 4px rgba(110, 121, 197, 0.3);\n}\n\n.saveButton:focus {\n\toutline: 2px solid var(--colour-purple, #6e79c5);\n\toutline-offset: 2px;\n}\n\n.saveButton:disabled {\n\tbackground: #6c757d;\n\tcursor: not-allowed;\n\topacity: 0.65;\n\ttransform: none;\n\tbox-shadow: none;\n}\n\n.nextButton {\n\tbackground: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n\tcolor: white;\n\tborder: none;\n\tpadding: 0.75rem 2rem;\n\tborder-radius: 6px;\n\tfont-size: 1rem;\n\tfont-weight: 500;\n\tcursor: pointer;\n\ttransition: all 0.2s ease;\n}\n\n.nextButton:hover:not(:disabled) {\n\ttransform: translateY(-2px);\n\tbox-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);\n}\n\n.nextButton:disabled {\n\tbackground: #6c757d;\n\tcursor: not-allowed;\n\topacity: 0.65;\n\ttransform: none;\n\tbox-shadow: none;\n}\n\n\n\n.errorMessage {\n\tcolor: var(--colour-red);\n\tmargin: 0.25rem 0;\n\tpadding: 0.25rem 0;\n\tfont-size: 0.875rem;\n\tline-height: 1.4;\n}\n\n/* Layout option styles */\n.layoutOption {\n\tdisplay: flex;\n\tflex-direction: column;\n\talign-items: center;\n\ttext-align: center;\n\tcursor: pointer;\n\ttransition: all 0.2s ease;\n\tbackground-color: transparent;\n}\n\n.layoutOptionImage {\n\twidth: 250px;\n\theight: 250px;\n\tborder-radius: 12px;\n\toverflow: hidden;\n\tmargin-bottom: 1rem;\n\tborder: 2px solid var(--border-line-grey, #dbddf1);\n\ttransition: all 0.2s ease;\n}\n\n.layoutOption.selected .layoutOptionImage {\n\tborder-color: var(--border-line-grey, #dbddf1);\n\tbox-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n}\n\n.layoutOptionImg {\n\twidth: 100%;\n\theight: 100%;\n\tobject-fit: cover;\n}\n\n.layoutOptionContent {\n\tmax-width: 250px;\n}\n\n.layoutOptionTitle {\n\tfont-size: 1.6rem;\n\tfont-weight: 600;\n\tmargin: 0 0 0.5rem 0;\n\tline-height: 1.3;\n\tcolor: #333;\n}\n\n.layoutOptionDescription {\n\tfont-size: 1.2rem;\n\tmargin: 0;\n\tline-height: 1.3;\n\tcolor: #6c757d;\n}\n\n.layoutOption.hasError .layoutOptionImage {\n\tborder-color: var(--colour-red, #dc3545);\n}\n\n/* Field error styles */\n.fieldError {\n\tcolor: #dc3545;\n\tfont-size: 1.6rem;\n\tfont-weight: 500;\n\tmargin-top: 0.5rem;\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 0.25rem;\n}\n\n.errorIcon {\n\tfont-size: 1rem;\n}\n\n/* Field wrapper styles are now handled in Fields.module.css */\n\n/* Field type indicator styles are now handled in Fields.module.css */\n\n@keyframes errorShake {\n\t0%, 100% { transform: translateX(0); }\n\t25% { transform: translateX(-3px); }\n\t75% { transform: translateX(3px); }\n}\n\n@keyframes errorPulse {\n\t0% { \n\t\tbox-shadow: 0 1px 3px rgba(192, 39, 67, 0.1), 0 1px 2px rgba(192, 39, 67, 0.08);\n\t}\n\t50% { \n\t\tbox-shadow: 0 2px 6px rgba(192, 39, 67, 0.2), 0 1px 3px rgba(192, 39, 67, 0.15);\n\t}\n\t100% { \n\t\tbox-shadow: 0 1px 3px rgba(192, 39, 67, 0.1), 0 1px 2px rgba(192, 39, 67, 0.08);\n\t}\n}\n\n.successMessage {\n\tbackground: var(--colour-branding-secondary-light);\n\tborder: 1px solid var(--colour-branding-secondary);\n\tborder-radius: 8px;\n\tpadding: 1rem;\n\tmargin-top: 1rem;\n\tcolor: var(--colour-green);\n\tfont-weight: 500;\n}\n\n/* Mode-aware styling */\n.createMode {\n\tcolor: var(--colour-purple, #6e79c5);\n}\n\n.editMode {\n\tcolor: var(--colour-branding-dark, #364196);\n}\n\n/* Responsive Design */\n@media (max-width: 768px) {\n\t.content {\n\t\tpadding: 1.5rem 0 2.5rem 0; /* Maintain proper padding on mobile */\n\t}\n\t\n\t.section {\n\t\tmargin-bottom: 1.5rem;\n\t}\n\t\n\t/* Mobile: vertical layout for field selector and button */\n\t.addFieldSection {\n\t\tflex-direction: column;\n\t\talign-items: stretch; /* Stretch to fill available space */\n\t}\n\t\n\t/* Button should not be full width on mobile */\n\t.addFieldSection Button {\n\t\twidth: auto; /* Let button use its natural width */\n\t\tpadding: 1rem 1.5rem; /* Keep proper padding but not full width */\n\t}\n\t\n\t.addFieldButtonContainer {\n\t\talign-items: stretch;\n\t\tjustify-content: stretch;\n\t\tmin-height: auto;\n\t}\n\t\n\t.navigation {\n\t\tflex-direction: column;\n\t\tgap: 0.75rem;\n\t\tjustify-content: flex-start; /* Align to start on mobile */\n\t\tmargin-top: 2rem; /* Slightly reduce on mobile */\n\t\tpadding-top: 1.5rem;\n\t\tpadding-bottom: 1.5rem; /* Maintain bottom padding */\n\t}\n\t\n\t/* Mobile button alignment - single button aligns right, two buttons stack */\n\t.navigation> :only-child {\n\t\tmargin-left: auto;\n\t\tmargin-right: 0;\n\t}\n\t\n\t.navigation> :first-child:not(:only-child),\n\t.navigation> :last-child:not(:only-child) {\n\t\tmargin-left: 0;\n\t\tmargin-right: 0;\n\t}\n\t\n\t.previousButton,\n\t.nextButton,\n\t.saveButton {\n\t\twidth: 100%;\n\t\tpadding: 1rem;\n\t}\n}\n\n@media (max-width: 480px) {\n\t.content {\n\t\tpadding: 1rem 0 2rem 0; /* Still maintain padding on small screens */\n\t}\n\t\n\t.section {\n\t\tmargin-bottom: 1rem;\n\t}\n\t\n\t.navigation {\n\t\tmargin-top: 1.5rem; /* Further reduce on very small screens */\n\t\tpadding-top: 1rem;\n\t\tpadding-bottom: 1.5rem;\n\t\tjustify-content: flex-start; /* Align to start on small screens */\n\t}\n\t\n\t/* Small screen button alignment */\n\t.navigation> :only-child {\n\t\tmargin-left: auto;\n\t\tmargin-right: 0;\n\t}\n\t\n\t.navigation> :first-child:not(:only-child),\n\t.navigation> :last-child:not(:only-child) {\n\t\tmargin-left: 0;\n\t\tmargin-right: 0;\n\t}\n\t\n\t.errorMessageContainer {\n\t\tflex-direction: column;\n\t\talign-items: flex-start;\n\t\tgap: 0.75rem;\n\t}\n\t\n\t.refreshButton {\n\t\talign-self: flex-end;\n\t}\n}\n\n/* Full width content when sidebar is hidden */\n:global(.hub-contentWrapper.fullWidthContent) {\n\tmax-width: 1000px;\n\tmargin: 0 auto;\n\tpadding: 2rem;\n}\n\n.hub-contentWrapper--col {\n flex-flow: column;\n}\n\n/* Validation error message - displayed at top of form */\n.validationErrorMessage {\n\tbackground-color: var(--colour-branding-secondary-light);\n\tborder: 1px solid var(--colour-branding-secondary);\n\tborder-radius: 6px;\n\tcolor: var(--colour-red);\n\tpadding: 12px 16px;\n\tfont-weight: 500;\n\tfont-size: 14px;\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\t\tmargin: 24px 32px;\n}\n\n.validationErrorMessage::before {\n\tcontent: \"⚠\";\n\tfont-size: 16px;\n\tcolor: var(--colour-red);\n}\n\n/* Loading overlay for forms - deprecated, use SkeletonLoader instead */\n/*\n.loadingOverlay {\n\tposition: absolute;\n\ttop: 0;\n\tleft: 0;\n\tright: 0;\n\tbottom: 0;\n\tbackground: rgba(255, 255, 255, 0.95);\n\tbackdrop-filter: blur(2px);\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\tz-index: 10;\n\tborder-radius: 8px;\n\tanimation: fadeIn 0.2s ease-in-out;\n}\n\n@keyframes fadeIn {\n\tfrom { opacity: 0; }\n\tto { opacity: 1; }\n}\n*/\n\n/* Form header with buttons */\n.formHeader {\n\tdisplay: flex;\n\tjustify-content: space-between;\n\talign-items: flex-start;\n\tmargin-bottom: 16px;\n}\n\n/* Loading container for fields */\n.fieldsLoadingContainer {\n\ttext-align: center;\n\tpadding: 60px 20px;\n}\n\n/* Help note styling */\n.helpNote {\n\tfont-size: 13px;\n\tcolor: #6c757d;\n}\n\n/* Loading container for overview */\n.overviewLoadingContainer {\n\ttext-align: center;\n\tpadding: 60px 20px;\n}\n\n/* Form layout header */\n.formLayoutHeader {\n\tdisplay: flex;\n\tjustify-content: space-between;\n\talign-items: flex-start;\n\tmargin-bottom: 16px;\n}\n\n/* Section margins */\n.gridIconSection {\n\tmargin-bottom: 3rem;\n}\n\n.layoutSection {\n\tmargin-bottom: 2rem;\n}\n\n/* Grid icon loading state */\n.gridIconLoading {\n\tgrid-column: 1 / -1;\n}\n\n/* Hide upload button in grid icon section */\n.gridIconSection :global(.iconLoader__buttonOverlay button:first-child) {\n\tdisplay: none !important;\n}\n";
2538
- n(css$c,{});
2539
-
2540
- function ownKeys$7(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
2541
- function _objectSpread$7(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys$7(Object(t), !0).forEach(function (r) { _defineProperty__default["default"](e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys$7(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
2542
2120
  const {
2543
- Helper: Helper$1,
2544
- Session: Session$1
2121
+ Helper: Helper$2,
2122
+ Session: Session$2
2545
2123
  } = PlussCore__namespace;
2546
2124
  const {
2547
- getUrl: getUrl$1
2548
- } = Helper$1;
2125
+ getUrl: getUrl$2
2126
+ } = Helper$2;
2549
2127
  const {
2550
- authedFunction: authedFunction$1
2551
- } = Session$1;
2128
+ authedFunction: authedFunction$2
2129
+ } = Session$2;
2552
2130
  const featureDefinitionActions = {
2553
2131
  /**
2554
2132
  * Get the single feature definition by ID
@@ -2559,9 +2137,9 @@ const featureDefinitionActions = {
2559
2137
  id,
2560
2138
  site
2561
2139
  };
2562
- return authedFunction$1({
2140
+ return authedFunction$2({
2563
2141
  method: "GET",
2564
- url: getUrl$1("feature-builder", "definition/get/single", query)
2142
+ url: getUrl$2("feature-builder", "definition/get/single", query)
2565
2143
  });
2566
2144
  },
2567
2145
  /**
@@ -2575,9 +2153,9 @@ const featureDefinitionActions = {
2575
2153
  *
2576
2154
  */
2577
2155
  create: async (id, site, featureDefinition) => {
2578
- return authedFunction$1({
2156
+ return authedFunction$2({
2579
2157
  method: "POST",
2580
- url: getUrl$1("feature-builder", "definition/update/create"),
2158
+ url: getUrl$2("feature-builder", "definition/update/create"),
2581
2159
  data: {
2582
2160
  id,
2583
2161
  site,
@@ -2610,12 +2188,12 @@ const featureDefinitionActions = {
2610
2188
  */
2611
2189
  edit: async (featureDefinitionData, site) => {
2612
2190
  // Ensure site is included in the request body
2613
- const dataWithSite = _objectSpread$7({
2191
+ const dataWithSite = _objectSpread$8({
2614
2192
  site: site
2615
2193
  }, featureDefinitionData);
2616
- return authedFunction$1({
2194
+ return authedFunction$2({
2617
2195
  method: "POST",
2618
- url: getUrl$1("feature-builder", "definition/update/edit"),
2196
+ url: getUrl$2("feature-builder", "definition/update/edit"),
2619
2197
  data: dataWithSite
2620
2198
  });
2621
2199
  },
@@ -2629,9 +2207,9 @@ const featureDefinitionActions = {
2629
2207
  *
2630
2208
  */
2631
2209
  delete: async (id, site) => {
2632
- return authedFunction$1({
2210
+ return authedFunction$2({
2633
2211
  method: "POST",
2634
- url: getUrl$1("feature-builder", "definition/update/delete"),
2212
+ url: getUrl$2("feature-builder", "definition/update/delete"),
2635
2213
  data: {
2636
2214
  id,
2637
2215
  site
@@ -2640,8 +2218,8 @@ const featureDefinitionActions = {
2640
2218
  }
2641
2219
  };
2642
2220
 
2643
- function ownKeys$6(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
2644
- function _objectSpread$6(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys$6(Object(t), !0).forEach(function (r) { _defineProperty__default["default"](e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys$6(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
2221
+ function ownKeys$7(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
2222
+ function _objectSpread$7(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys$7(Object(t), !0).forEach(function (r) { _defineProperty__default["default"](e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys$7(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
2645
2223
 
2646
2224
  // IMPORTANT: Using local UPDATE_STRINGS action type to make extension self-contained
2647
2225
  // The main app's StringsReducer will handle this action type the same way
@@ -2650,12 +2228,12 @@ const updateFeatureBuilderString = title => (dispatch, getState) => {
2650
2228
  var _getState$strings;
2651
2229
  const currentStrings = ((_getState$strings = getState().strings) === null || _getState$strings === void 0 ? void 0 : _getState$strings.config) || {};
2652
2230
  const titleCased = toTitleCase(title) || values.textMenuTitle;
2653
- const updatedStrings = _objectSpread$6(_objectSpread$6({}, currentStrings), {}, {
2654
- sideNav: _objectSpread$6(_objectSpread$6({}, currentStrings.sideNav), {}, {
2231
+ const updatedStrings = _objectSpread$7(_objectSpread$7({}, currentStrings), {}, {
2232
+ sideNav: _objectSpread$7(_objectSpread$7({}, currentStrings.sideNav), {}, {
2655
2233
  [values.featureKey]: titleCased,
2656
2234
  [values.menuKey]: "Manage ".concat(titleCased)
2657
2235
  }),
2658
- permission: _objectSpread$6(_objectSpread$6({}, currentStrings.permission), {}, {
2236
+ permission: _objectSpread$7(_objectSpread$7({}, currentStrings.permission), {}, {
2659
2237
  [values.permissionFeatureBuilderDefinition]: "Manage custom feature ".concat(titleCased),
2660
2238
  [values.permissionFeatureBuilderContent]: "Manage ".concat(titleCased, " content")
2661
2239
  })
@@ -2668,8 +2246,8 @@ const updateFeatureBuilderString = title => (dispatch, getState) => {
2668
2246
  const updateFeatureBuilderIcon = icon => (dispatch, getState) => {
2669
2247
  var _getState$strings2;
2670
2248
  const currentStrings = ((_getState$strings2 = getState().strings) === null || _getState$strings2 === void 0 ? void 0 : _getState$strings2.config) || {};
2671
- const updatedStrings = _objectSpread$6(_objectSpread$6({}, currentStrings), {}, {
2672
- sideNav: _objectSpread$6(_objectSpread$6({}, currentStrings.sideNav), {}, {
2249
+ const updatedStrings = _objectSpread$7(_objectSpread$7({}, currentStrings), {}, {
2250
+ sideNav: _objectSpread$7(_objectSpread$7({}, currentStrings.sideNav), {}, {
2673
2251
  [values.featureKey + "-icon"]: icon,
2674
2252
  [values.menuKey + "-icon"]: icon
2675
2253
  })
@@ -2825,167 +2403,586 @@ const addField = function () {
2825
2403
  // Generate a unique ID for the new field
2826
2404
  const fieldId = "custom-field-".concat(Date.now(), "-").concat(Math.random().toString(36).substr(2, 9));
2827
2405
  return {
2828
- type: actionsTypes.ADD_FIELD,
2406
+ type: actionsTypes.ADD_FIELD,
2407
+ payload: {
2408
+ id: fieldId,
2409
+ type: fieldType
2410
+ }
2411
+ };
2412
+ };
2413
+ const deleteField = id => {
2414
+ return {
2415
+ type: actionsTypes.DELETE_FIELD,
2416
+ payload: id
2417
+ };
2418
+ };
2419
+ const updateFieldById = (id, updatedField) => {
2420
+ return {
2421
+ type: actionsTypes.UPDATE_FIELD,
2422
+ payload: {
2423
+ id,
2424
+ updatedField
2425
+ }
2426
+ };
2427
+ };
2428
+
2429
+ /**
2430
+ * Action creator to set a description field as the summary field
2431
+ * Ensures only one description field can be marked as summary by
2432
+ * automatically unsetting all other description fields when a new one is selected
2433
+ *
2434
+ * @param {string} fieldId - ID of the description field to set as summary
2435
+ * @returns {Object} Redux action object for summary field selection
2436
+ *
2437
+ * @example
2438
+ * dispatch(setSummaryField('field-description-123'));
2439
+ */
2440
+ const setSummaryField = fieldId => {
2441
+ return {
2442
+ type: actionsTypes.SET_SUMMARY_FIELD,
2443
+ payload: fieldId
2444
+ };
2445
+ };
2446
+ const setLayoutType = layoutType => {
2447
+ return {
2448
+ type: actionsTypes.SET_LAYOUT_TYPE,
2449
+ payload: layoutType
2450
+ };
2451
+ };
2452
+ const submitFormRequest = () => {
2453
+ return {
2454
+ type: actionsTypes.SUBMIT_FORM_REQUEST
2455
+ };
2456
+ };
2457
+ const submitFormSuccess = () => {
2458
+ return {
2459
+ type: actionsTypes.SUBMIT_FORM_SUCCESS
2460
+ };
2461
+ };
2462
+ const submitFormFailure = error => {
2463
+ return {
2464
+ type: actionsTypes.SUBMIT_FORM_FAILURE,
2465
+ payload: error
2466
+ };
2467
+ };
2468
+ const clearFormSubmissionState = () => {
2469
+ return {
2470
+ type: actionsTypes.CLEAR_FORM_SUBMISSION_STATE
2471
+ };
2472
+ };
2473
+
2474
+ /**
2475
+ * Submits the complete feature form to the server
2476
+ * Handles form validation, API submission, and error handling
2477
+ *
2478
+ * @returns {Function} Async thunk function for Redux
2479
+ * @throws {Error} When form validation fails or API submission encounters error
2480
+ */
2481
+ function submitForm() {
2482
+ return async (dispatch, getState) => {
2483
+ const state = getState()[values.reducerKey];
2484
+ const form = state && state.form;
2485
+ if (!form) {
2486
+ dispatch(submitFormFailure(new Error("Form data is missing. Please refresh the page and try again.")));
2487
+ return;
2488
+ }
2489
+ dispatch(submitFormRequest());
2490
+ try {
2491
+ // Get site from auth store
2492
+ const site = getState().auth.site;
2493
+ if (!site) {
2494
+ throw new Error("Authentication error: Site context not found. Please refresh and login again.");
2495
+ }
2496
+
2497
+ // Use mode from fetch instead of checking definition ID
2498
+ const definitionState = state && state.definition;
2499
+ const mode = definitionState && definitionState.mode; // Use stored mode from fetch
2500
+ const definitionId = definitionState && definitionState.id;
2501
+ if (mode === "edit") {
2502
+ // Always update when in edit mode
2503
+ const updatedDefinition = {
2504
+ id: definitionId,
2505
+ site: site,
2506
+ // Include site from auth store
2507
+ featureDefinition: {
2508
+ // Wrap in expected structure for backend
2509
+ title: form.title,
2510
+ icon: form.icon,
2511
+ displayName: form.displayName,
2512
+ layout: form.layout,
2513
+ fields: form.fields
2514
+ }
2515
+ };
2516
+ await featureDefinitionActions.edit(updatedDefinition, site);
2517
+ } else {
2518
+ // Always create when in create mode (or mode is undefined/null)
2519
+ if (!values.featureId || !site) {
2520
+ throw new Error("Authentication error: Missing required context (featureId or site).");
2521
+ }
2522
+ await featureDefinitionActions.create(values.featureId, site,
2523
+ // Use actual site from auth store
2524
+ {
2525
+ title: form.title,
2526
+ icon: form.icon,
2527
+ displayName: form.displayName,
2528
+ layout: form.layout,
2529
+ fields: form.fields
2530
+ });
2531
+ }
2532
+ dispatch(submitFormSuccess());
2533
+ } catch (err) {
2534
+ // Handle different types of errors
2535
+ let errorToDisplay = err;
2536
+ if (err.response) {
2537
+ // API error (400, 401, 404, 500, etc.)
2538
+ const {
2539
+ status,
2540
+ data
2541
+ } = err.response;
2542
+ if (status === 400 && data && data.error) {
2543
+ errorToDisplay = new Error("Validation error: ".concat(data.error));
2544
+ } else if (status === 401) {
2545
+ errorToDisplay = new Error("You are not authorized to perform this action");
2546
+ } else if (status === 404) {
2547
+ errorToDisplay = new Error("Feature definition not found");
2548
+ } else if (status >= 500) {
2549
+ errorToDisplay = new Error("Server error. Please try again later.");
2550
+ } else {
2551
+ errorToDisplay = new Error(data && data.error || "Request failed with status ".concat(status));
2552
+ }
2553
+ } else if (err.request) {
2554
+ // Network error (no response received)
2555
+ errorToDisplay = new Error("Network error. Please check your connection and try again.");
2556
+ } else if (err.message) {
2557
+ // Other JavaScript errors
2558
+ errorToDisplay = err;
2559
+ } else {
2560
+ // Unknown error
2561
+ errorToDisplay = new Error("An unexpected error occurred while saving");
2562
+ }
2563
+ dispatch(submitFormFailure(errorToDisplay));
2564
+ }
2565
+ };
2566
+ }
2567
+
2568
+ function ownKeys$6(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
2569
+ function _objectSpread$6(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys$6(Object(t), !0).forEach(function (r) { _defineProperty__default["default"](e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys$6(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
2570
+ const SET_NAVIGATION_STATE$1 = "SET_NAVIGATION_STATE";
2571
+ const UPDATE_STEP_VALIDATION$1 = "UPDATE_STEP_VALIDATION";
2572
+ const MARK_STEP_COMPLETE$1 = "MARK_STEP_COMPLETE";
2573
+ const setCurrentStep = function (step) {
2574
+ let previousStep = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
2575
+ return {
2576
+ type: SET_NAVIGATION_STATE$1,
2829
2577
  payload: {
2830
- id: fieldId,
2831
- type: fieldType
2578
+ currentStep: step,
2579
+ previousStep,
2580
+ canGoBack: step !== "welcome",
2581
+ canGoForward: true
2832
2582
  }
2833
2583
  };
2834
2584
  };
2835
- const deleteField = id => {
2585
+ const goToStep = step => (dispatch, getState) => {
2586
+ const state = getState()[values.reducerKey];
2587
+ const currentStep = state && state.wizard && state.wizard.navigation && state.wizard.navigation.currentStep;
2588
+
2589
+ // Clear form submission state when changing steps
2590
+ dispatch(clearFormSubmissionState());
2591
+ dispatch(setCurrentStep(step, currentStep));
2592
+ };
2593
+ const updateStepValidation = function (step, isValid) {
2594
+ let errors = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
2836
2595
  return {
2837
- type: actionsTypes.DELETE_FIELD,
2838
- payload: id
2596
+ type: UPDATE_STEP_VALIDATION$1,
2597
+ payload: {
2598
+ step,
2599
+ isValid,
2600
+ errors
2601
+ }
2839
2602
  };
2840
2603
  };
2841
- const updateFieldById = (id, updatedField) => {
2604
+ const validateAndUpdateStep = step => (dispatch, getState) => {
2605
+ // Use existing selectors to get form data
2606
+ const state = getState();
2607
+ const title = selectFormTitle(state);
2608
+ const icon = selectFormIcon(state);
2609
+ const displayName = selectFormDisplayName(state);
2610
+ const layout = selectFormLayout(state);
2611
+ const fields = selectFormFields(state);
2612
+ const form = {
2613
+ title,
2614
+ icon,
2615
+ displayName,
2616
+ layout,
2617
+ fields
2618
+ };
2619
+ const {
2620
+ isValid,
2621
+ errors
2622
+ } = getFormValidation(form, step);
2623
+ dispatch(updateStepValidation(step, isValid, errors));
2624
+
2625
+ // If valid, mark as complete
2626
+ if (isValid) {
2627
+ dispatch({
2628
+ type: MARK_STEP_COMPLETE$1,
2629
+ payload: step
2630
+ });
2631
+ }
2842
2632
  return {
2843
- type: actionsTypes.UPDATE_FIELD,
2844
- payload: {
2845
- id,
2846
- updatedField
2633
+ isValid,
2634
+ errors
2635
+ };
2636
+ };
2637
+ const getFormValidation = (form, step) => {
2638
+ // Return validation results for undefined form (prevent crashes)
2639
+ if (!form) {
2640
+ switch (step) {
2641
+ case "overview":
2642
+ return {
2643
+ isValid: false,
2644
+ errors: {
2645
+ title: "Title is required",
2646
+ displayName: "Display name is required",
2647
+ icon: "Icon is required"
2648
+ }
2649
+ };
2650
+ case "fields":
2651
+ return {
2652
+ isValid: false,
2653
+ errors: {
2654
+ missingTitle: "Title field is required",
2655
+ missingImage: "Feature image field is required",
2656
+ fieldLabels: "Some fields are missing labels"
2657
+ }
2658
+ };
2659
+ case "layout":
2660
+ return {
2661
+ isValid: false,
2662
+ errors: {
2663
+ layoutType: "Layout type is required"
2664
+ }
2665
+ };
2666
+ default:
2667
+ return {
2668
+ isValid: false,
2669
+ errors: {}
2670
+ };
2847
2671
  }
2672
+ }
2673
+ switch (step) {
2674
+ case "overview":
2675
+ {
2676
+ const hasTitle = form.title && form.title.trim().length > 0;
2677
+ const hasDisplayName = form.displayName && form.displayName.trim().length > 0;
2678
+ const hasIcon = form.icon && form.icon.length > 0;
2679
+ return {
2680
+ isValid: hasTitle && hasDisplayName && hasIcon,
2681
+ errors: {
2682
+ title: !hasTitle ? "Title is required" : null,
2683
+ displayName: !hasDisplayName ? "Display name is required" : null,
2684
+ icon: !hasIcon ? "Icon is required" : null
2685
+ }
2686
+ };
2687
+ }
2688
+ case "fields":
2689
+ {
2690
+ const hasTitleField = form.fields && form.fields.some(field => field.id === "mandatory-title");
2691
+ const hasImageField = form.fields && form.fields.some(field => field.id === "mandatory-feature-image");
2692
+
2693
+ // Check each field for missing labels and create field-specific errors
2694
+ const fieldErrors = {};
2695
+ let allFieldsHaveLabels = true;
2696
+ if (form.fields) {
2697
+ form.fields.forEach(field => {
2698
+ if ((field.type === "text" || field.type === "description" || field.type === "title" || field.type === "image" || field.type === "gallery" || field.type === "feature-image" || field.type === "file" || field.type === "cta") && field.values) {
2699
+ if (!field.values.label || field.values.label.trim().length === 0) {
2700
+ fieldErrors[field.id] = "Field label is required";
2701
+ allFieldsHaveLabels = false;
2702
+ }
2703
+ }
2704
+ });
2705
+ }
2706
+ return {
2707
+ isValid: hasTitleField && hasImageField && allFieldsHaveLabels,
2708
+ errors: _objectSpread$6({
2709
+ missingTitle: !hasTitleField ? "Title field is required" : null,
2710
+ missingImage: !hasImageField ? "Feature image field is required" : null
2711
+ }, fieldErrors)
2712
+ };
2713
+ }
2714
+ case "layout":
2715
+ {
2716
+ const hasLayoutType = form.layout && form.layout.type && form.layout.type.length > 0;
2717
+ return {
2718
+ isValid: hasLayoutType,
2719
+ errors: {
2720
+ layoutType: !hasLayoutType ? "Layout type is required" : null
2721
+ }
2722
+ };
2723
+ }
2724
+ default:
2725
+ return {
2726
+ isValid: true,
2727
+ errors: {}
2728
+ };
2729
+ }
2730
+ };
2731
+ const setCurrentStepAndSave = function (step) {
2732
+ let previousStep = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
2733
+ return dispatch => {
2734
+ dispatch(setCurrentStep(step, previousStep));
2848
2735
  };
2849
2736
  };
2850
2737
 
2738
+ var css$d = ".fullWidthContent {\n\tmax-width: 100%;\n\tmargin-left: auto;\n\tmargin-right: auto;\n\tpadding: 2rem 2rem 3rem 2rem; /* Add extra bottom padding */\n}\n\n/* Full-width container that allows scrollbar at edge */\n.fullWidthContainer {\n\tdisplay: flex;\n\tflex-direction: column;\n\tflex: 1;\n\tmin-height: 0; /* Allow content to determine height */\n\twidth: 100%; /* Take full width to ensure scrollbar is at edge */\n}\n\n/* Content container to keep content centered */\n.contentContainer {\n\tdisplay: flex;\n\tflex-direction: column;\n\tflex: 1;\n\tmin-height: 0;\n\tmax-width: 960px;\n\tmargin: 0 auto;\n\tpadding: 64px 32px;\n\twidth: 100%;\n\tbox-sizing: border-box;\n}\n\n/* Legacy container class for backward compatibility */\n.container{\n\tdisplay: flex;\n\tflex-direction: column;\n\tflex: 1;\n\tmin-height: 0; /* Allow content to determine height */\n max-width: 960px;\n margin: 0 auto;\n padding: 64px 32px;\n}\n\n/* Responsive adjustments for content container */\n@media (max-width: 768px) {\n\t.contentContainer {\n\t\tpadding: 1.5rem 1.5rem 2.5rem 1.5rem; /* Slightly reduced but still adequate */\n\t}\n\t.container {\n\t\tpadding: 1.5rem 1.5rem 2.5rem 1.5rem; /* Legacy container support */\n\t}\n}\n\n@media (max-width: 480px) {\n\t.contentContainer {\n\t\tpadding: 1rem 1rem 2rem 1rem; /* Maintain padding on small screens */\n\t}\n\t.container {\n\t\tpadding: 1rem 1rem 2rem 1rem; /* Legacy container support */\n\t}\n}\n\n/* Enhanced sidebar navigation for progress indication */\n:global(.hub-sideBar) {\n\t/* Make sidebar more prominent during creation mode */\n}\n\n:global(.sideNav-item.isCurrent) {\n\tbackground: var(--colour-purple, #6e79c5) !important;\n\tcolor: var(--colour-white, #ffffff) !important;\n\tfont-weight: 600 !important;\n\tborder-left: 4px solid var(--colour-branding-dark, #364196) !important;\n}\n\n:global(.sideNav-item.isCurrent) i {\n\tcolor: var(--colour-white, #ffffff) !important;\n}\n\n:global(.sideNav-item.isCompleted) {\n\tcolor: var(--colour-branding-dark, #364196) !important;\n\tposition: relative;\n}\n\n:global(.sideNav-item.isCompleted::after) {\n\tcontent: \"✓\";\n\tposition: absolute;\n\tright: 1rem;\n\ttop: 50%;\n\ttransform: translateY(-50%);\n\tcolor: var(--colour-branding-dark, #364196);\n\tfont-weight: bold;\n}\n\n:global(.sideNav-item.isDisabled) {\n\topacity: 0.4;\n\tcursor: not-allowed;\n}\n\n/* Enhanced progress section */\n:global(.hub-sideBar-section) {\n\tborder-bottom: 2px solid var(--colour-branding-main-fade, rgba(74, 87, 183, 0.15));\n\tpadding-bottom: 1rem;\n\tmargin-bottom: 1rem;\n}\n\n:global(.hub-sideBar-section:last-child) {\n\tborder-bottom: none;\n}\n\n/* Delete button styling - bold red for development */\n.deleteButton {\n\tcolor: #dc3545 !important;\n\tfont-weight: bold !important;\n}\n\n:global(.sideNav-item.deleteButton) {\n\tcolor: #dc3545 !important;\n\tfont-weight: bold !important;\n}\n\n:global(.sideNav-item.deleteButton:hover) {\n\tbackground-color: rgba(220, 53, 69, 0.1) !important;\n}\n\n:global(.sideNav-item.deleteButton i) {\n\tcolor: #dc3545 !important;\n}\n\n/* Development delete button */\n.devDeleteButton {\n\tposition: absolute;\n\tbottom: 20px;\n\tleft: 20px;\n\tbackground-color: #ff0000;\n\tcolor: #ffffff;\n\tborder: none;\n\tpadding: 8px 12px;\n\tborder-radius: 4px;\n\tcursor: pointer;\n\tfont-size: 12px;\n\tfont-weight: bold;\n\tz-index: 1000;\n\tbox-shadow: 0 2px 4px rgba(0,0,0,0.3);\n}\n\n.devDeleteButton:hover {\n\tbackground-color: #cc0000;\n}\n";
2739
+ n(css$d,{});
2740
+
2741
+ const {
2742
+ Helper: Helper$1,
2743
+ Session: Session$1
2744
+ } = PlussCore__namespace;
2745
+ const {
2746
+ getUrl: getUrl$1
2747
+ } = Helper$1;
2748
+ const {
2749
+ authedFunction: authedFunction$1
2750
+ } = Session$1;
2751
+
2851
2752
  /**
2852
- * Action creator to set a description field as the summary field
2853
- * Ensures only one description field can be marked as summary by
2854
- * automatically unsetting all other description fields when a new one is selected
2753
+ * Sidebar Layout component for feature builder wizard
2754
+ * Provides navigation sidebar with step progression, completion tracking
2755
+ * Includes delete functionality for feature definitions
2756
+ * Manages step accessibility based on wizard mode and validation state
2855
2757
  *
2856
- * @param {string} fieldId - ID of the description field to set as summary
2857
- * @returns {Object} Redux action object for summary field selection
2758
+ * @param {Object} props - Component props
2759
+ * @param {React.ReactNode} props.children - Child components to render in main content area
2760
+ * @param {Object} props.history - React Router history object for navigation
2761
+ * @returns {React.ReactElement} Layout component with sidebar navigation
2858
2762
  *
2859
2763
  * @example
2860
- * dispatch(setSummaryField('field-description-123'));
2764
+ * <SidebarLayout history={historyObject}>
2765
+ * <YourMainContent />
2766
+ * </SidebarLayout>
2861
2767
  */
2862
- const setSummaryField = fieldId => {
2863
- return {
2864
- type: actionsTypes.SET_SUMMARY_FIELD,
2865
- payload: fieldId
2768
+ const SideBarInner = props => {
2769
+ const {
2770
+ children,
2771
+ history
2772
+ } = props;
2773
+ const dispatch = reactRedux.useDispatch();
2774
+
2775
+ // Get wizard state
2776
+ const mode = reactRedux.useSelector(selectWizardMode);
2777
+ const isEditMode = reactRedux.useSelector(selectIsEditMode);
2778
+ const isCreateMode = reactRedux.useSelector(selectIsCreateMode);
2779
+ const currentStep = reactRedux.useSelector(selectCurrentStep);
2780
+ reactRedux.useSelector(selectStepProgress);
2781
+ const definition = reactRedux.useSelector(selectDefinition);
2782
+ const definitionId = reactRedux.useSelector(selectDefinitionId);
2783
+ const auth = reactRedux.useSelector(state => state.auth);
2784
+
2785
+ // Delete functionality state
2786
+ const [showDeleteConfirm, setShowDeleteConfirm] = React.useState(false);
2787
+ const [isDeleting, setIsDeleting] = React.useState(false);
2788
+ const goTo = url => history.push(url);
2789
+ const isSelected = url => {
2790
+ return history.location.pathname === url;
2791
+ };
2792
+
2793
+ // Define step configuration with dynamic URL based on mode
2794
+ const getStepUrl = stepKey => {
2795
+ // Always use /definition/ in the URL since that's how routes are registered
2796
+ return "/feature-builder/definition/".concat(stepKey);
2797
+ };
2798
+ const steps = [{
2799
+ key: "overview",
2800
+ text: "Feature Overview",
2801
+ icon: "info-circle",
2802
+ url: getStepUrl("overview")
2803
+ }, {
2804
+ key: "fields",
2805
+ text: "Configure Fields",
2806
+ icon: "edit",
2807
+ url: getStepUrl("fields")
2808
+ }, {
2809
+ key: "layout",
2810
+ text: "Choose Layout",
2811
+ icon: "columns",
2812
+ url: getStepUrl("layout")
2813
+ }];
2814
+
2815
+ // Build sidebar items based on mode
2816
+ const buildSidebarItems = () => {
2817
+ return steps.map((step, index) => {
2818
+ const isCompleted = selectIsStepComplete(step.key);
2819
+ const isAccessible = selectIsStepAccessible(step.key);
2820
+
2821
+ // Add step number to text for better clarity
2822
+ const stepText = "".concat(index + 1, ". ").concat(step.text);
2823
+ const itemProps = {
2824
+ type: "navItem",
2825
+ text: stepText,
2826
+ icon: step.icon,
2827
+ selected: isSelected(step.url),
2828
+ onclick: isAccessible ? () => {
2829
+ goTo(step.url);
2830
+ dispatch(goToStep(step.key));
2831
+ } : null,
2832
+ isFontAwesome: true,
2833
+ // Enhanced completion indicator
2834
+ completed: isCompleted,
2835
+ // Allow navigation to completed steps even in create mode
2836
+ disabled: mode === "create" && !isAccessible
2837
+ };
2838
+ return itemProps;
2839
+ });
2866
2840
  };
2867
- };
2868
- const setLayoutType = layoutType => {
2869
- return {
2870
- type: actionsTypes.SET_LAYOUT_TYPE,
2871
- payload: layoutType
2841
+
2842
+ // Determine sidebar title - always use "Build Your Feature" now
2843
+ const getSidebarTitle = () => {
2844
+ return "Build Your Feature";
2872
2845
  };
2873
- };
2874
- const submitFormRequest = () => {
2875
- return {
2876
- type: actionsTypes.SUBMIT_FORM_REQUEST
2846
+
2847
+ // Simple help text
2848
+ const getHelpText = () => {
2849
+ return "Get help with feature builder";
2877
2850
  };
2878
- };
2879
- const submitFormSuccess = () => {
2880
- return {
2881
- type: actionsTypes.SUBMIT_FORM_SUCCESS
2851
+
2852
+ // Build sidebar sections - simplified without progress section
2853
+ const sidebarSections = [{
2854
+ title: getSidebarTitle(),
2855
+ items: buildSidebarItems()
2856
+ }];
2857
+
2858
+ // Delete functionality handlers
2859
+ const handleDeleteClick = () => {
2860
+ setShowDeleteConfirm(true);
2882
2861
  };
2883
- };
2884
- const submitFormFailure = error => {
2885
- return {
2886
- type: actionsTypes.SUBMIT_FORM_FAILURE,
2887
- payload: error
2862
+ const handleConfirmDelete = async () => {
2863
+ if (!definition) return;
2864
+ setIsDeleting(true);
2865
+ try {
2866
+ await authedFunction$1({
2867
+ method: "POST",
2868
+ url: getUrl$1("feature-builder", "definition/update/delete"),
2869
+ data: {
2870
+ id: definitionId,
2871
+ site: auth.site
2872
+ }
2873
+ });
2874
+
2875
+ // Redirect to welcome screen after deletion
2876
+ history.push("/feature-builder/definition/overview");
2877
+ } finally {
2878
+ setIsDeleting(false);
2879
+ setShowDeleteConfirm(false);
2880
+ }
2888
2881
  };
2889
- };
2890
- const clearFormSubmissionState = () => {
2891
- return {
2892
- type: actionsTypes.CLEAR_FORM_SUBMISSION_STATE
2882
+ const handleCancelDelete = () => {
2883
+ setShowDeleteConfirm(false);
2893
2884
  };
2894
- };
2895
2885
 
2896
- /**
2897
- * Submits the complete feature form to the server
2898
- * Handles form validation, API submission, and error handling
2899
- *
2900
- * @returns {Function} Async thunk function for Redux
2901
- * @throws {Error} When form validation fails or API submission encounters error
2902
- */
2903
- function submitForm() {
2904
- return async (dispatch, getState) => {
2905
- const state = getState()[values.reducerKey];
2906
- const form = state && state.form;
2907
- if (!form) {
2908
- dispatch(submitFormFailure(new Error("Form data is missing. Please refresh the page and try again.")));
2909
- return;
2910
- }
2911
- dispatch(submitFormRequest());
2912
- try {
2913
- // Get site from auth store
2914
- const site = getState().auth.site;
2915
- if (!site) {
2916
- throw new Error("Authentication error: Site context not found. Please refresh and login again.");
2917
- }
2886
+ // Add effect to manually attach click handlers since HubSidebar might not use onclick properly
2887
+ React.useEffect(() => {
2888
+ const attachClickHandlers = () => {
2889
+ const stepsWithUrls = [{
2890
+ key: "overview",
2891
+ url: getStepUrl("overview")
2892
+ }, {
2893
+ key: "fields",
2894
+ url: getStepUrl("fields")
2895
+ }, {
2896
+ key: "layout",
2897
+ url: getStepUrl("layout")
2898
+ }];
2899
+ stepsWithUrls.forEach(step => {
2900
+ const navItem = Array.from(document.querySelectorAll(".sideNav-item")).find(item => item.textContent && item.textContent.includes(step.key === "overview" ? "Feature Overview" : step.key === "fields" ? "Configure Fields" : "Choose Layout"));
2901
+ if (navItem) {
2902
+ // Remove any existing click listeners
2903
+ navItem.onclick = null;
2918
2904
 
2919
- // Use mode from fetch instead of checking definition ID
2920
- const definitionState = state && state.definition;
2921
- const mode = definitionState && definitionState.mode; // Use stored mode from fetch
2922
- const definitionId = definitionState && definitionState.id;
2923
- if (mode === "edit") {
2924
- // Always update when in edit mode
2925
- const updatedDefinition = {
2926
- id: definitionId,
2927
- site: site,
2928
- // Include site from auth store
2929
- featureDefinition: {
2930
- // Wrap in expected structure for backend
2931
- title: form.title,
2932
- icon: form.icon,
2933
- displayName: form.displayName,
2934
- layout: form.layout,
2935
- fields: form.fields
2905
+ // Check if this step is accessible
2906
+ const isAccessible = selectIsStepAccessible(step.key)({
2907
+ [values.reducerKey]: {
2908
+ wizard: {
2909
+ mode: mode,
2910
+ navigation: {
2911
+ currentStep: currentStep
2912
+ }
2913
+ }
2914
+ }
2915
+ });
2916
+
2917
+ // Add our click handler only if accessible
2918
+ if (isAccessible) {
2919
+ navItem.onclick = event => {
2920
+ event.preventDefault();
2921
+ event.stopPropagation();
2922
+ history.push(step.url);
2923
+ dispatch(goToStep(step.key));
2924
+ };
2936
2925
  }
2937
- };
2938
- await featureDefinitionActions.edit(updatedDefinition, site);
2939
- } else {
2940
- // Always create when in create mode (or mode is undefined/null)
2941
- if (!values.featureId || !site) {
2942
- throw new Error("Authentication error: Missing required context (featureId or site).");
2943
- }
2944
- await featureDefinitionActions.create(values.featureId, site,
2945
- // Use actual site from auth store
2946
- {
2947
- title: form.title,
2948
- icon: form.icon,
2949
- displayName: form.displayName,
2950
- layout: form.layout,
2951
- fields: form.fields
2952
- });
2953
- }
2954
- dispatch(submitFormSuccess());
2955
- } catch (err) {
2956
- // Handle different types of errors
2957
- let errorToDisplay = err;
2958
- if (err.response) {
2959
- // API error (400, 401, 404, 500, etc.)
2960
- const {
2961
- status,
2962
- data
2963
- } = err.response;
2964
- if (status === 400 && data && data.error) {
2965
- errorToDisplay = new Error("Validation error: ".concat(data.error));
2966
- } else if (status === 401) {
2967
- errorToDisplay = new Error("You are not authorized to perform this action");
2968
- } else if (status === 404) {
2969
- errorToDisplay = new Error("Feature definition not found");
2970
- } else if (status >= 500) {
2971
- errorToDisplay = new Error("Server error. Please try again later.");
2972
- } else {
2973
- errorToDisplay = new Error(data && data.error || "Request failed with status ".concat(status));
2926
+
2927
+ // Make sure it's styled appropriately
2928
+ navItem.style.cursor = isAccessible ? "pointer" : "not-allowed";
2929
+ navItem.style.pointerEvents = "auto";
2930
+ navItem.style.opacity = isAccessible ? "1" : "0.5";
2974
2931
  }
2975
- } else if (err.request) {
2976
- // Network error (no response received)
2977
- errorToDisplay = new Error("Network error. Please check your connection and try again.");
2978
- } else if (err.message) {
2979
- // Other JavaScript errors
2980
- errorToDisplay = err;
2981
- } else {
2982
- // Unknown error
2983
- errorToDisplay = new Error("An unexpected error occurred while saving");
2984
- }
2985
- dispatch(submitFormFailure(errorToDisplay));
2932
+ });
2933
+ };
2934
+
2935
+ // Initial attachment
2936
+ attachClickHandlers();
2937
+
2938
+ // Re-attach after a short delay to ensure HubSidebar has rendered
2939
+ const timeoutId = setTimeout(attachClickHandlers, 100);
2940
+
2941
+ // Also try to re-attach when DOM changes (observe for mutations)
2942
+ const observer = new MutationObserver(() => {
2943
+ setTimeout(attachClickHandlers, 50);
2944
+ });
2945
+
2946
+ // Start observing the document body for changes
2947
+ observer.observe(document.body, {
2948
+ childList: true,
2949
+ subtree: true
2950
+ });
2951
+ return () => {
2952
+ clearTimeout(timeoutId);
2953
+ observer.disconnect();
2954
+ };
2955
+ }, [history, dispatch, isEditMode, isCreateMode, currentStep]);
2956
+ return /*#__PURE__*/React__default["default"].createElement("div", {
2957
+ className: "hub-wrapperContainer"
2958
+ }, /*#__PURE__*/React__default["default"].createElement(HubSidebar, {
2959
+ sections: sidebarSections,
2960
+ helpGuide: {
2961
+ text: getHelpText(),
2962
+ url: "https://www.plusscommunities.com/user-guide"
2986
2963
  }
2987
- };
2988
- }
2964
+ }), /*#__PURE__*/React__default["default"].createElement("button", {
2965
+ onClick: handleDeleteClick,
2966
+ className: css$d.devDeleteButton
2967
+ }, "DELETE FEATURE DEF"), /*#__PURE__*/React__default["default"].createElement("div", {
2968
+ className: "hub-contentWrapper"
2969
+ }, /*#__PURE__*/React__default["default"].createElement("div", {
2970
+ className: css$d.fullWidthContainer
2971
+ }, /*#__PURE__*/React__default["default"].createElement("div", {
2972
+ className: css$d.contentContainer
2973
+ }, children))), /*#__PURE__*/React__default["default"].createElement(DeleteConfirmationPopup, {
2974
+ isOpen: showDeleteConfirm,
2975
+ listing: definition,
2976
+ onConfirm: handleConfirmDelete,
2977
+ onCancel: handleCancelDelete,
2978
+ isDeleting: isDeleting,
2979
+ deleteType: "featureDefinition"
2980
+ }));
2981
+ };
2982
+ const SidebarLayout = reactRouter.withRouter(SideBarInner);
2983
+
2984
+ var css$c = ".content {\n\tflex: 1 1 auto;\n\tdisplay: flex;\n\tflex-flow: column nowrap;\n\tpadding: 2rem 0 3rem 0; /* Add proper top/bottom padding */\n\tmin-height: 100%; /* Ensure full height for proper spacing */\n}\n\n/* New page wrapper for centered container approach */\n.pageWrapper {\n\tpadding: 2rem 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tjustify-content: flex-start;\n}\n\n@media (min-width: 768px) {\n\t.pageWrapper {\n\t\tpadding: 3rem 0;\n\t}\n}\n\n/* Form container styling when using CenteredContainer */\n.formContainer {\n\tbackground-color: var(--bg-white);\n}\n\n/* Section styling */\n.section {\n\tmargin-bottom: 2rem;\n\t\tpadding: 0 0 2em;\n}\n\n.section--no-border {\n\t\tborder-bottom: none;\n}\n\n.subtitle {\n\tcolor: var(--text-bluegrey, #6c7a90);\n}\n\n.sectionHeader {\n\t\tdisplay: flex;\n\t\tjustify-content: space-between;\n}\n\n/* Add Field Button styling */\n.addFieldButton {\n\tbackground: var(--colour-branding-action, #5c90df);\n\tcolor: white;\n\tborder: none;\n\tpadding: 0.75rem 1.5rem;\n\tborder-radius: 6px;\n\tfont-size: 1rem;\n\tcursor: pointer;\n\tmargin-bottom: 2rem;\n\ttransition: all 0.2s ease;\n}\n\n.addFieldButton:hover {\n\tbackground: var(--colour-branding-action-hover, #364196);\n\ttransform: translateY(-1px);\n\tbox-shadow: 0 2px 4px rgba(92, 144, 223, 0.3);\n}\n\n.addFieldButton:focus {\n\toutline: 2px solid var(--colour-branding-action, #5c90df);\n\toutline-offset: 2px;\n}\n\n.addFieldButton:active {\n\ttransform: translateY(0);\n}\n\n/* Refresh button styling */\n.refreshButton {\n\tmargin-left: 10px;\n\tpadding: 2px 8px;\n\tfont-size: 12px;\n\tbackground: var(--colour-dusk, #536280);\n\tcolor: white;\n\tborder: none;\n\tborder-radius: 4px;\n\tcursor: pointer;\n\ttransition: background-color 0.2s ease;\n}\n\n.refreshButton:hover {\n\tbackground: var(--colour-dusk-hover, #485968);\n}\n\n/* Error message container */\n.errorMessageContainer {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 0.5rem;\n}\n\n/* Add Field Container - matches field structure */\n.addFieldContainer {\n\tdisplay: flex;\n\tmargin-top: 32px;\n}\n\n.fieldNumberContainer {\n\tdisplay: flex;\n\twidth: 40px;\n}\n\n/* Add Field Section */\n.addFieldSection {\n\tdisplay: flex;\n\tgap: 1rem;\n\talign-items: center;\n\tjustify-content: flex-start; /* Align to start to match field content */\n\tmargin-top: 0; /* Remove top margin since it's in the container */\n}\n\n/* Desktop: horizontal layout for field selector and button */\n@media (min-width: 769px) {\n\t.addFieldSection {\n\t\tflex-direction: row;\n\t\talign-items: baseline;\n\t\tgap: 2rem;\n\t\talign-items: flex-start;\n\t\tjustify-content: flex-start; /* Align to start to match field content */\n\t}\n}\n\n/* Help Section for Popup */\n.helpSection {\n\tbackground-color: var(--colour-branding-action-superlight);\n\tborder: 1px solid var(--colour-branding-inactive);\n\tborder-radius: 8px;\n\tpadding: 1.5rem;\n\tmargin-bottom: 1.5rem;\n}\n\n.helpTitle {\n\tcolor: var(--text-dark);\n\tfont-weight: 600;\n\tmargin-bottom: 0.75rem;\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 0.5rem;\n\tfont-size: 1.25rem;\n}\n\n.helpText {\n\tcolor: var(--colour-branding-action);\n\tline-height: 1.6;\n\tfont-size: 1.25rem;\n}\n\n/* Field Type Cards for Popup */\n.fieldTypeCards {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 1rem; /* Spacing between vertical cards */\n\tpadding: 1rem 0;\n}\n\n/* Responsive layout: maintain vertical layout on all screen sizes */\n@media (min-width: 768px) {\n\t.fieldTypeCards {\n\t\tgap: 1.5rem; /* Slightly larger gap on desktop */\n\t}\n}\n\n.fieldTypeCard {\n\tdisplay: flex;\n\tflex-direction: row;\n\talign-items: flex-start;\n\tpadding: 1.75rem 1.25rem; /* More vertical padding */\n\tborder: 1px solid var(--border-line-grey);\n\tborder-radius: 8px;\n\tbackground-color: var(--bg-white);\n\tcursor: pointer;\n\ttransition: all 0.2s ease;\n\tbox-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);\n\tmin-height: 120px; /* Adjust height for horizontal layout */\n\tgap: 1rem; /* Space between icon and content */\n}\n\n.fieldTypeCard:hover {\n\tborder-color: var(--text-light);\n\tbackground-color: var(--bg-bluegrey);\n\tbox-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);\n}\n\n.fieldTypeCard:focus {\n\toutline: 2px solid var(--text-light);\n\toutline-offset: 2px;\n}\n\n.fieldTypeCard:active {\n\ttransform: scale(0.98);\n\ttransition: transform 0.1s ease;\n}\n\n.fieldTypeCardIcon {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\twidth: 48px;\n\theight: 48px;\n\tmargin-bottom: 0; /* Remove bottom margin for horizontal layout */\n\tfont-size: 20px;\n\tflex-shrink: 0;\n\tborder: 1px solid var(--border-line-grey); /* Subtle border */\n\tborder-radius: 8px;\n\tbackground-color: var(--bg-bluegrey);\n\tpadding: 8px;\n}\n\n\n.fieldTypeCardContent {\n\tflex: 1;\n\twidth: 100%;\n\tmin-width: 0; /* Prevent content from overflowing */\n}\n\n.fieldTypeCardTitle {\n\tmargin: 0 0 0.25rem 0;\n\tfont-weight: 600;\n\tfont-size: 1.4rem; /* Increased for better readability */\n}\n\n.fieldTypeCardDescription {\n\tmargin: 0 0 0.75rem 0;\n\tfont-size: 1.6rem; /* Increased for better readability */\n\tline-height: 1.5; /* Improved line height for better readability */\n}\n\n.fieldTypeCardUseCase {\n\tmargin: 0;\n\tfont-size: 1.4rem;\n\tfont-style: italic;\n\tcolor: var(--text-light);\n\tpadding-top: 0.5rem;\n\tborder-top: 1px solid var(--border-line-grey);\n}\n\n/* Field selector and button container - horizontal layout */\n.addFieldSection> div:first-child {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 0.5rem;\n\tflex: 1;\n}\n\n/* Container for add field button alignment */\n.addFieldButtonContainer {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: flex-end;\n}\n\n/* Empty State */\n.emptyState {\n\tpadding: 2rem;\n\ttext-align: center;\n\tbackground-color: var(--bg-bluegrey, #f4f7f9);\n\tborder-radius: 8px;\n\tborder: 1px dashed var(--border-line-grey, #dbddf1);\n\tmargin-bottom: 1rem;\n}\n\n.emptyStateText {\n\tcolor: var(--text-bluegrey, #6c7a90);\n\tfont-style: italic;\n}\n\n.fieldTypeSelector {\n\tdisplay: flex;\n\tflex-direction: column;\n\tmax-width: 500px; /* Increased from 300px to accommodate longer text */\n\twidth: 100%;\n}\n\n@media (max-width: 768px) {\n\t.addFieldSection {\n\t\talign-items: stretch;\n\t}\n\t\n\t.fieldTypeSelector {\n\t\tmax-width: none;\n\t}\n}\n\n.grid__four {\n max-width: 1200px;\n\tdisplay: grid;\n\tgrid-template-columns: repeat(2, minmax(250px, 1fr));\n margin: 2rem 0;\n\tgap: 2rem;\n\tjustify-items: center;\n}\n\n@media (max-width: 768px) {\n\t.grid__four {\n\t\tgrid-template-columns: 1fr;\n\t\tgap: 2rem;\n\t}\n}\n\n/* Navigation styles */\n.navigation {\n\tdisplay: flex;\n\tjustify-content: space-between;\n\tgap: 1rem;\n\tmargin-top: 3rem; /* Increase from 2rem */\n\tpadding-top: 2rem;\n\tpadding-bottom: 2rem; /* Add bottom padding */\n\tborder-top: 1px solid #e9ecef;\n}\n\n/* Single button alignment - align to right for overview step save button */\n.navigation> :only-child {\n\tmargin-left: auto;\n\tmargin-right: 0;\n}\n\n/* Two button alignment - one left, one right */\n.navigation> :first-child:not(:only-child) {\n\tmargin-right: auto;\n}\n\n.navigation> :last-child:not(:only-child) {\n\tmargin-left: auto;\n}\n\n/* Special case for overview step - align save button to right */\n.overviewStep .navigation> :only-child {\n\tmargin-left: auto;\n\tmargin-right: 0;\n}\n\n.previousButton {\n\tbackground: #6c757d;\n\tcolor: white;\n\tborder: none;\n\tpadding: 0.75rem 1.5rem;\n\tborder-radius: 6px;\n\tfont-size: 1rem;\n\tcursor: pointer;\n\ttransition: all 0.2s ease;\n}\n\n.previousButton:hover {\n\tbackground: #5a6268;\n}\n\n.saveButton {\n\tbackground: var(--colour-purple, #6e79c5);\n\tcolor: white;\n\tborder: none;\n\tpadding: 0.75rem 2rem;\n\tborder-radius: 6px;\n\tfont-size: 1rem;\n\tfont-weight: 500;\n\tcursor: pointer;\n\ttransition: all 0.2s ease;\n}\n\n.saveButton:hover:not(:disabled) {\n\tbackground: var(--colour-purple-hover, #5a66b3);\n\ttransform: translateY(-1px);\n\tbox-shadow: 0 2px 4px rgba(110, 121, 197, 0.3);\n}\n\n.saveButton:focus {\n\toutline: 2px solid var(--colour-purple, #6e79c5);\n\toutline-offset: 2px;\n}\n\n.saveButton:disabled {\n\tbackground: #6c757d;\n\tcursor: not-allowed;\n\topacity: 0.65;\n\ttransform: none;\n\tbox-shadow: none;\n}\n\n.nextButton {\n\tbackground: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n\tcolor: white;\n\tborder: none;\n\tpadding: 0.75rem 2rem;\n\tborder-radius: 6px;\n\tfont-size: 1rem;\n\tfont-weight: 500;\n\tcursor: pointer;\n\ttransition: all 0.2s ease;\n}\n\n.nextButton:hover:not(:disabled) {\n\ttransform: translateY(-2px);\n\tbox-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);\n}\n\n.nextButton:disabled {\n\tbackground: #6c757d;\n\tcursor: not-allowed;\n\topacity: 0.65;\n\ttransform: none;\n\tbox-shadow: none;\n}\n\n\n\n.errorMessage {\n\tcolor: var(--colour-red);\n\tmargin: 0.25rem 0;\n\tpadding: 0.25rem 0;\n\tfont-size: 0.875rem;\n\tline-height: 1.4;\n}\n\n/* Layout option styles */\n.layoutOption {\n\tdisplay: flex;\n\tflex-direction: column;\n\talign-items: center;\n\ttext-align: center;\n\tcursor: pointer;\n\ttransition: all 0.2s ease;\n\tbackground-color: transparent;\n}\n\n.layoutOptionImage {\n\twidth: 250px;\n\theight: 250px;\n\tborder-radius: 12px;\n\toverflow: hidden;\n\tmargin-bottom: 1rem;\n\tborder: 2px solid var(--border-line-grey, #dbddf1);\n\ttransition: all 0.2s ease;\n}\n\n.layoutOption.selected .layoutOptionImage {\n\tborder-color: var(--border-line-grey, #dbddf1);\n\tbox-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n}\n\n.layoutOptionImg {\n\twidth: 100%;\n\theight: 100%;\n\tobject-fit: cover;\n}\n\n.layoutOptionContent {\n\tmax-width: 250px;\n}\n\n.layoutOptionTitle {\n\tfont-size: 1.6rem;\n\tfont-weight: 600;\n\tmargin: 0 0 0.5rem 0;\n\tline-height: 1.3;\n\tcolor: #333;\n}\n\n.layoutOptionDescription {\n\tfont-size: 1.2rem;\n\tmargin: 0;\n\tline-height: 1.3;\n\tcolor: #6c757d;\n}\n\n.layoutOption.hasError .layoutOptionImage {\n\tborder-color: var(--colour-red, #dc3545);\n}\n\n/* Field error styles */\n.fieldError {\n\tcolor: #dc3545;\n\tfont-size: 1.6rem;\n\tfont-weight: 500;\n\tmargin-top: 0.5rem;\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 0.25rem;\n}\n\n.errorIcon {\n\tfont-size: 1rem;\n}\n\n/* Field wrapper styles are now handled in Fields.module.css */\n\n/* Field type indicator styles are now handled in Fields.module.css */\n\n@keyframes errorShake {\n\t0%, 100% { transform: translateX(0); }\n\t25% { transform: translateX(-3px); }\n\t75% { transform: translateX(3px); }\n}\n\n@keyframes errorPulse {\n\t0% { \n\t\tbox-shadow: 0 1px 3px rgba(192, 39, 67, 0.1), 0 1px 2px rgba(192, 39, 67, 0.08);\n\t}\n\t50% { \n\t\tbox-shadow: 0 2px 6px rgba(192, 39, 67, 0.2), 0 1px 3px rgba(192, 39, 67, 0.15);\n\t}\n\t100% { \n\t\tbox-shadow: 0 1px 3px rgba(192, 39, 67, 0.1), 0 1px 2px rgba(192, 39, 67, 0.08);\n\t}\n}\n\n.successMessage {\n\tbackground: var(--colour-branding-secondary-light);\n\tborder: 1px solid var(--colour-branding-secondary);\n\tborder-radius: 8px;\n\tpadding: 1rem;\n\tmargin-top: 1rem;\n\tcolor: var(--colour-green);\n\tfont-weight: 500;\n}\n\n/* Mode-aware styling */\n.createMode {\n\tcolor: var(--colour-purple, #6e79c5);\n}\n\n.editMode {\n\tcolor: var(--colour-branding-dark, #364196);\n}\n\n/* Responsive Design */\n@media (max-width: 768px) {\n\t.content {\n\t\tpadding: 1.5rem 0 2.5rem 0; /* Maintain proper padding on mobile */\n\t}\n\t\n\t.section {\n\t\tmargin-bottom: 1.5rem;\n\t}\n\t\n\t/* Mobile: vertical layout for field selector and button */\n\t.addFieldSection {\n\t\tflex-direction: column;\n\t\talign-items: stretch; /* Stretch to fill available space */\n\t}\n\t\n\t/* Button should not be full width on mobile */\n\t.addFieldSection Button {\n\t\twidth: auto; /* Let button use its natural width */\n\t\tpadding: 1rem 1.5rem; /* Keep proper padding but not full width */\n\t}\n\t\n\t.addFieldButtonContainer {\n\t\talign-items: stretch;\n\t\tjustify-content: stretch;\n\t\tmin-height: auto;\n\t}\n\t\n\t.navigation {\n\t\tflex-direction: column;\n\t\tgap: 0.75rem;\n\t\tjustify-content: flex-start; /* Align to start on mobile */\n\t\tmargin-top: 2rem; /* Slightly reduce on mobile */\n\t\tpadding-top: 1.5rem;\n\t\tpadding-bottom: 1.5rem; /* Maintain bottom padding */\n\t}\n\t\n\t/* Mobile button alignment - single button aligns right, two buttons stack */\n\t.navigation> :only-child {\n\t\tmargin-left: auto;\n\t\tmargin-right: 0;\n\t}\n\t\n\t.navigation> :first-child:not(:only-child),\n\t.navigation> :last-child:not(:only-child) {\n\t\tmargin-left: 0;\n\t\tmargin-right: 0;\n\t}\n\t\n\t.previousButton,\n\t.nextButton,\n\t.saveButton {\n\t\twidth: 100%;\n\t\tpadding: 1rem;\n\t}\n}\n\n@media (max-width: 480px) {\n\t.content {\n\t\tpadding: 1rem 0 2rem 0; /* Still maintain padding on small screens */\n\t}\n\t\n\t.section {\n\t\tmargin-bottom: 1rem;\n\t}\n\t\n\t.navigation {\n\t\tmargin-top: 1.5rem; /* Further reduce on very small screens */\n\t\tpadding-top: 1rem;\n\t\tpadding-bottom: 1.5rem;\n\t\tjustify-content: flex-start; /* Align to start on small screens */\n\t}\n\t\n\t/* Small screen button alignment */\n\t.navigation> :only-child {\n\t\tmargin-left: auto;\n\t\tmargin-right: 0;\n\t}\n\t\n\t.navigation> :first-child:not(:only-child),\n\t.navigation> :last-child:not(:only-child) {\n\t\tmargin-left: 0;\n\t\tmargin-right: 0;\n\t}\n\t\n\t.errorMessageContainer {\n\t\tflex-direction: column;\n\t\talign-items: flex-start;\n\t\tgap: 0.75rem;\n\t}\n\t\n\t.refreshButton {\n\t\talign-self: flex-end;\n\t}\n}\n\n/* Full width content when sidebar is hidden */\n:global(.hub-contentWrapper.fullWidthContent) {\n\tmax-width: 1000px;\n\tmargin: 0 auto;\n\tpadding: 2rem;\n}\n\n.hub-contentWrapper--col {\n flex-flow: column;\n}\n\n/* Validation error message - displayed at top of form */\n.validationErrorMessage {\n\tbackground-color: var(--colour-branding-secondary-light);\n\tborder: 1px solid var(--colour-branding-secondary);\n\tborder-radius: 6px;\n\tcolor: var(--colour-red);\n\tpadding: 12px 16px;\n\tfont-weight: 500;\n\tfont-size: 14px;\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\t\tmargin: 24px 32px;\n}\n\n.validationErrorMessage::before {\n\tcontent: \"⚠\";\n\tfont-size: 16px;\n\tcolor: var(--colour-red);\n}\n\n/* Loading overlay for forms - deprecated, use SkeletonLoader instead */\n/*\n.loadingOverlay {\n\tposition: absolute;\n\ttop: 0;\n\tleft: 0;\n\tright: 0;\n\tbottom: 0;\n\tbackground: rgba(255, 255, 255, 0.95);\n\tbackdrop-filter: blur(2px);\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\tz-index: 10;\n\tborder-radius: 8px;\n\tanimation: fadeIn 0.2s ease-in-out;\n}\n\n@keyframes fadeIn {\n\tfrom { opacity: 0; }\n\tto { opacity: 1; }\n}\n*/\n\n/* Form header with buttons */\n.formHeader {\n\tdisplay: flex;\n\tjustify-content: space-between;\n\talign-items: flex-start;\n\tmargin-bottom: 16px;\n}\n\n/* Loading container for fields */\n.fieldsLoadingContainer {\n\ttext-align: center;\n\tpadding: 60px 20px;\n}\n\n/* Help note styling */\n.helpNote {\n\tfont-size: 13px;\n\tcolor: #6c757d;\n}\n\n/* Loading container for overview */\n.overviewLoadingContainer {\n\ttext-align: center;\n\tpadding: 60px 20px;\n}\n\n/* Form layout header */\n.formLayoutHeader {\n\tdisplay: flex;\n\tjustify-content: space-between;\n\talign-items: flex-start;\n\tmargin-bottom: 16px;\n}\n\n/* Section margins */\n.gridIconSection {\n\tmargin-bottom: 3rem;\n}\n\n.layoutSection {\n\tmargin-bottom: 2rem;\n}\n\n/* Grid icon loading state */\n.gridIconLoading {\n\tgrid-column: 1 / -1;\n}\n\n/* Hide upload button in grid icon section */\n.gridIconSection :global(.iconLoader__buttonOverlay button:first-child) {\n\tdisplay: none !important;\n}\n";
2985
+ n(css$c,{});
2989
2986
 
2990
2987
  /*
2991
2988
  * Icon categories and definitions for the feature builder
@@ -3306,9 +3303,6 @@ const FormOverviewStepInner = props => {
3306
3303
 
3307
3304
  // Clear submission state after showing toast (only for non-edit cases)
3308
3305
  if (!isEditMode) {
3309
- const {
3310
- clearFormSubmissionState
3311
- } = require("../actions/formActions");
3312
3306
  dispatch(clearFormSubmissionState());
3313
3307
  }
3314
3308
  }