@plusscommunities/pluss-feature-builder-web-a 1.0.2-beta.4 → 1.0.2-beta.5

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
@@ -199,6 +199,10 @@ const selectDefinitionError = state => {
199
199
  const definitionState = selectDefinitionState(state);
200
200
  return definitionState && definitionState.error;
201
201
  };
202
+ const selectDefinitionMode = state => {
203
+ const definitionState = selectDefinitionState(state);
204
+ return definitionState && definitionState.mode;
205
+ };
202
206
 
203
207
  // Check if we have a definition loaded
204
208
  const selectHasDefinition = state => !!selectDefinition(state);
@@ -2116,816 +2120,846 @@ const Text$8 = Components$4.Text;
2116
2120
 
2117
2121
  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; }
2118
2122
  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; }
2119
- const {
2120
- Helper: Helper$1,
2121
- Session: Session$1
2122
- } = PlussCore__namespace;
2123
- const {
2124
- getUrl: getUrl$1
2125
- } = Helper$1;
2126
- const {
2127
- authedFunction: authedFunction$1
2128
- } = Session$1;
2129
- const featureDefinitionActions = {
2130
- /**
2131
- * Get the single feature definition by ID
2132
- * Path: {id}
2133
- */
2134
- getSingle: async (id, site) => {
2135
- const query = {
2136
- id,
2137
- site
2138
- };
2139
- return authedFunction$1({
2140
- method: "GET",
2141
- url: getUrl$1("feature-builder", "definition/get/single", query)
2142
- });
2143
- },
2144
- /**
2145
- * Creates a new feature definition with the provided configuration
2146
- *
2147
- * @param {string} id - The unique identifier for the new feature definition
2148
- * @param {string} site - The site ID where the feature definition will be created
2149
- * @param {FeatureDefinition} featureDefinition - The feature definition data to create
2150
- * @returns {Promise<ApiResponse>} Promise resolving to API response with created feature definition
2151
- * @throws {Error} When creation fails due to validation or API errors
2152
- *
2153
- */
2154
- create: async (id, site, featureDefinition) => {
2155
- return authedFunction$1({
2156
- method: "POST",
2157
- url: getUrl$1("feature-builder", "definition/update/create"),
2158
- data: {
2159
- id,
2160
- site,
2161
- featureDefinition
2162
- }
2163
- });
2164
- },
2165
- /**
2166
- * Updates an existing feature definition with new configuration
2167
- *
2168
- * @param {FeatureDefinition} featureDefinitionData - The updated feature definition data
2169
- * @param {string} featureDefinitionData.id - The unique identifier of the feature definition to update
2170
- * @param {string} [featureDefinitionData.displayName] - Updated display name
2171
- * @param {Field[]} [featureDefinitionData.fields] - Updated field definitions
2172
- * @param {Object} [featureDefinitionData.layout] - Updated layout configuration
2173
- * @param {string} site - The site ID where the feature definition exists
2174
- * @returns {Promise<ApiResponse>} Promise resolving to API response with updated feature definition
2175
- * @throws {Error} When update fails due to validation or API errors
2176
- *
2177
- * @example
2178
- * try {
2179
- * const response = await featureDefinitionActions.edit(
2180
- * {
2181
- * id: 'feature-123',
2182
- * displayName: 'Updated Form',
2183
- * fields: [...]
2184
- * },
2185
- * 'site-123'
2186
- * );
2187
- */
2188
- edit: async (featureDefinitionData, site) => {
2189
- // Ensure site is included in the request body
2190
- const dataWithSite = _objectSpread$8({
2191
- site: site
2192
- }, featureDefinitionData);
2193
- return authedFunction$1({
2194
- method: "POST",
2195
- url: getUrl$1("feature-builder", "definition/update/edit"),
2196
- data: dataWithSite
2197
- });
2198
- },
2199
- /**
2200
- * Soft deletes a feature definition (marks as deleted but doesn't permanently remove)
2201
- *
2202
- * @param {string} id - The unique identifier of the feature definition to delete
2203
- * @param {string} site - The site ID where the feature definition exists
2204
- * @returns {Promise<ApiResponse>} Promise resolving to API response confirming deletion
2205
- * @throws {Error} When deletion fails or feature definition is not found
2206
- *
2207
- */
2208
- delete: async (id, site) => {
2209
- return authedFunction$1({
2210
- method: "POST",
2211
- url: getUrl$1("feature-builder", "definition/update/delete"),
2212
- data: {
2213
- id,
2214
- site
2215
- }
2216
- });
2217
- }
2218
- };
2219
-
2220
- 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; }
2221
- 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; }
2222
2123
 
2223
- // IMPORTANT: Using local UPDATE_STRINGS action type to make extension self-contained
2224
- // The main app's StringsReducer will handle this action type the same way
2225
- const UPDATE_STRINGS = "UPDATE_STRINGS";
2226
- const updateFeatureBuilderString = title => (dispatch, getState) => {
2227
- var _getState$strings;
2228
- const currentStrings = ((_getState$strings = getState().strings) === null || _getState$strings === void 0 ? void 0 : _getState$strings.config) || {};
2229
- const titleCased = toTitleCase(title) || values.textMenuTitle;
2230
- const updatedStrings = _objectSpread$7(_objectSpread$7({}, currentStrings), {}, {
2231
- sideNav: _objectSpread$7(_objectSpread$7({}, currentStrings.sideNav), {}, {
2232
- [values.featureKey]: titleCased,
2233
- [values.menuKey]: "Manage ".concat(titleCased)
2234
- }),
2235
- permission: _objectSpread$7(_objectSpread$7({}, currentStrings.permission), {}, {
2236
- [values.permissionFeatureBuilderDefinition]: "Manage custom feature ".concat(titleCased),
2237
- [values.permissionFeatureBuilderContent]: "Manage ".concat(titleCased, " content")
2238
- })
2239
- });
2240
- dispatch({
2241
- type: UPDATE_STRINGS,
2242
- payload: updatedStrings
2243
- });
2244
- };
2245
- const updateFeatureBuilderIcon = icon => (dispatch, getState) => {
2246
- var _getState$strings2;
2247
- const currentStrings = ((_getState$strings2 = getState().strings) === null || _getState$strings2 === void 0 ? void 0 : _getState$strings2.config) || {};
2248
- const updatedStrings = _objectSpread$7(_objectSpread$7({}, currentStrings), {}, {
2249
- sideNav: _objectSpread$7(_objectSpread$7({}, currentStrings.sideNav), {}, {
2250
- [values.featureKey + "-icon"]: icon,
2251
- [values.menuKey + "-icon"]: icon
2252
- })
2253
- });
2254
- dispatch({
2255
- type: UPDATE_STRINGS,
2256
- payload: updatedStrings
2257
- });
2124
+ // Wizard action types
2125
+ const REDUCER_PREFIX$2 = values.reducerKey.toUpperCase();
2126
+ const SET_NAVIGATION_STATE$1 = "".concat(REDUCER_PREFIX$2, "_SET_NAVIGATION_STATE");
2127
+ const UPDATE_STEP_VALIDATION$1 = "".concat(REDUCER_PREFIX$2, "_UPDATE_STEP_VALIDATION");
2128
+ const MARK_STEP_COMPLETE$1 = "".concat(REDUCER_PREFIX$2, "_MARK_STEP_COMPLETE");
2129
+ const setCurrentStep = function (step) {
2130
+ let previousStep = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
2131
+ return {
2132
+ type: SET_NAVIGATION_STATE$1,
2133
+ payload: {
2134
+ currentStep: step,
2135
+ previousStep,
2136
+ canGoBack: step !== "welcome",
2137
+ canGoForward: true
2138
+ }
2139
+ };
2258
2140
  };
2141
+ const goToStep = step => (dispatch, getState) => {
2142
+ const state = getState()[require("../values.config").reducerKey];
2143
+ const currentStep = state && state.wizard && state.wizard.navigation && state.wizard.navigation.currentStep;
2259
2144
 
2260
- /**
2261
- * Form Actions for Feature Builder
2262
- * Manages form state including title, icon, fields, layout, and submission
2263
- * Handles CRUD operations for form fields and layout configuration
2264
- * Coordinates with external menu updates and feature definition actions
2265
- *
2266
- * @namespace formActions
2267
- */
2268
-
2269
- /**
2270
- * @typedef {Object} FieldValues
2271
- * @property {string} [label] - Field label text
2272
- * @property {string} [placeholder] - Placeholder text for input
2273
- * @property {boolean} [isRequired] - Whether field is required
2274
- * @property {string} [helpText] - Help text for field guidance
2275
- * @property {boolean} [allowCaption] - Whether field allows captions
2276
- * @property {boolean} [useAsSummary] - Whether field is used as summary
2277
- */
2278
-
2279
- /**
2280
- * @typedef {Object} FormField
2281
- * @property {string} id - Unique field identifier
2282
- * @property {string} type - Field type (text, title, description, image, file, cta, feature-image)
2283
- * @property {boolean} isMandatory - Whether field is mandatory system field
2284
- * @property {FieldValues} values - Field configuration values
2285
- */
2286
-
2287
- /**
2288
- * @typedef {Object} LayoutConfig
2289
- * @property {string} type - Layout type (round, square, etc.)
2290
- * @property {string} [gridIcon] - Background image for grid layout
2291
- */
2292
-
2293
- const actionsTypes = {
2294
- SET_INITIAL_VALUES: "SET_INITIAL_VALUES",
2295
- SET_TITLE: "SET_TITLE",
2296
- SET_ICON: "SET_ICON",
2297
- SET_DISPLAY_NAME: "SET_DISPLAY_NAME",
2298
- ADD_FIELD: "ADD_FIELD",
2299
- DELETE_FIELD: "DELETE_FIELD",
2300
- UPDATE_FIELD: "UPDATE_FIELD",
2301
- SET_LAYOUT_GRID_ICON: "SET_LAYOUT_GRID_ICON",
2302
- SET_LAYOUT_TYPE: "SET_LAYOUT_TYPE",
2303
- SUBMIT_FORM_REQUEST: "SUBMIT_FORM_REQUEST",
2304
- SUBMIT_FORM_SUCCESS: "SUBMIT_FORM_SUCCESS",
2305
- SUBMIT_FORM_FAILURE: "SUBMIT_FORM_FAILURE",
2306
- CLEAR_FORM_SUBMISSION_STATE: "CLEAR_FORM_SUBMISSION_STATE",
2307
- SET_SUMMARY_FIELD: "SET_SUMMARY_FIELD"
2145
+ // Clear form submission state when changing steps
2146
+ const {
2147
+ clearFormSubmissionState
2148
+ } = require("./formActions");
2149
+ dispatch(clearFormSubmissionState());
2150
+ dispatch(setCurrentStep(step, currentStep));
2308
2151
  };
2309
-
2310
- /**
2311
- * Action creator to set initial form values
2312
- * Initializes form state with existing feature definition data
2313
- *
2314
- * @param {Object} initialValues - Initial form values object
2315
- * @param {string} initialValues.title - Feature title
2316
- * @param {string} initialValues.icon - Feature icon
2317
- * @param {string} initialValues.displayName - Feature display name
2318
- * @param {LayoutConfig} initialValues.layout - Layout configuration
2319
- * @param {FormField[]} initialValues.fields - Form fields array
2320
- * @returns {Object} Redux action object with type and payload
2321
- *
2322
- * @example
2323
- * dispatch(setInitialValues({
2324
- * title: 'Contact Form',
2325
- * icon: 'envelope',
2326
- * fields: []
2327
- * }));
2328
- */
2329
- const setInitialValues = initialValues => {
2152
+ const updateStepValidation = function (step, isValid) {
2153
+ let errors = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
2330
2154
  return {
2331
- type: actionsTypes.SET_INITIAL_VALUES,
2332
- payload: initialValues
2155
+ type: UPDATE_STEP_VALIDATION$1,
2156
+ payload: {
2157
+ step,
2158
+ isValid,
2159
+ errors
2160
+ }
2333
2161
  };
2334
2162
  };
2335
- const setTitle = title => {
2163
+ const validateAndUpdateStep = step => (dispatch, getState) => {
2164
+ // Use existing selectors to get form data
2165
+ const state = getState();
2166
+ const title = selectFormTitle(state);
2167
+ const icon = selectFormIcon(state);
2168
+ const displayName = selectFormDisplayName(state);
2169
+ const layout = selectFormLayout(state);
2170
+ const fields = selectFormFields(state);
2171
+ const form = {
2172
+ title,
2173
+ icon,
2174
+ displayName,
2175
+ layout,
2176
+ fields
2177
+ };
2178
+ const {
2179
+ isValid,
2180
+ errors
2181
+ } = getFormValidation(form, step);
2182
+ dispatch(updateStepValidation(step, isValid, errors));
2183
+
2184
+ // If valid, mark as complete
2185
+ if (isValid) {
2186
+ dispatch({
2187
+ type: MARK_STEP_COMPLETE$1,
2188
+ payload: step
2189
+ });
2190
+ }
2336
2191
  return {
2337
- type: actionsTypes.SET_TITLE,
2338
- payload: title
2192
+ isValid,
2193
+ errors
2339
2194
  };
2340
2195
  };
2196
+ const getFormValidation = (form, step) => {
2197
+ // Return validation results for undefined form (prevent crashes)
2198
+ if (!form) {
2199
+ switch (step) {
2200
+ case "overview":
2201
+ return {
2202
+ isValid: false,
2203
+ errors: {
2204
+ title: "Title is required",
2205
+ displayName: "Display name is required",
2206
+ icon: "Icon is required"
2207
+ }
2208
+ };
2209
+ case "fields":
2210
+ return {
2211
+ isValid: false,
2212
+ errors: {
2213
+ missingTitle: "Title field is required",
2214
+ missingImage: "Feature image field is required",
2215
+ fieldLabels: "Some fields are missing labels"
2216
+ }
2217
+ };
2218
+ case "layout":
2219
+ return {
2220
+ isValid: false,
2221
+ errors: {
2222
+ layoutType: "Layout type is required"
2223
+ }
2224
+ };
2225
+ default:
2226
+ return {
2227
+ isValid: false,
2228
+ errors: {}
2229
+ };
2230
+ }
2231
+ }
2232
+ switch (step) {
2233
+ case "overview":
2234
+ {
2235
+ const hasTitle = form.title && form.title.trim().length > 0;
2236
+ const hasDisplayName = form.displayName && form.displayName.trim().length > 0;
2237
+ const hasIcon = form.icon && form.icon.length > 0;
2238
+ return {
2239
+ isValid: hasTitle && hasDisplayName && hasIcon,
2240
+ errors: {
2241
+ title: !hasTitle ? "Title is required" : null,
2242
+ displayName: !hasDisplayName ? "Display name is required" : null,
2243
+ icon: !hasIcon ? "Icon is required" : null
2244
+ }
2245
+ };
2246
+ }
2247
+ case "fields":
2248
+ {
2249
+ const hasTitleField = form.fields && form.fields.some(field => field.id === "mandatory-title");
2250
+ const hasImageField = form.fields && form.fields.some(field => field.id === "mandatory-feature-image");
2341
2251
 
2342
- /**
2343
- * Action creator to set form display name and update menu
2344
- * Updates both form state and external menu string
2345
- * Title cases the display name before updating the strings store
2346
- *
2347
- * @param {string} displayName - New display name for form
2348
- * @returns {Function} Thunk function for Redux
2349
- *
2350
- * @example
2351
- * dispatch(setDisplayName('Contact Information'));
2352
- */
2353
- const setDisplayName = displayName => {
2354
- return dispatch => {
2355
- // Update menu string when display name changes (will be title cased in updateFeatureBuilderString)
2356
- dispatch(updateFeatureBuilderString(displayName));
2357
-
2358
- // Dispatch the actual action
2359
- dispatch({
2360
- type: actionsTypes.SET_DISPLAY_NAME,
2361
- payload: displayName
2362
- });
2252
+ // Check each field for missing labels and create field-specific errors
2253
+ const fieldErrors = {};
2254
+ let allFieldsHaveLabels = true;
2255
+ if (form.fields) {
2256
+ form.fields.forEach(field => {
2257
+ 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) {
2258
+ if (!field.values.label || field.values.label.trim().length === 0) {
2259
+ fieldErrors[field.id] = "Field label is required";
2260
+ allFieldsHaveLabels = false;
2261
+ }
2262
+ }
2263
+ });
2264
+ }
2265
+ return {
2266
+ isValid: hasTitleField && hasImageField && allFieldsHaveLabels,
2267
+ errors: _objectSpread$8({
2268
+ missingTitle: !hasTitleField ? "Title field is required" : null,
2269
+ missingImage: !hasImageField ? "Feature image field is required" : null
2270
+ }, fieldErrors)
2271
+ };
2272
+ }
2273
+ case "layout":
2274
+ {
2275
+ const hasLayoutType = form.layout && form.layout.type && form.layout.type.length > 0;
2276
+ return {
2277
+ isValid: hasLayoutType,
2278
+ errors: {
2279
+ layoutType: !hasLayoutType ? "Layout type is required" : null
2280
+ }
2281
+ };
2282
+ }
2283
+ default:
2284
+ return {
2285
+ isValid: true,
2286
+ errors: {}
2287
+ };
2288
+ }
2289
+ };
2290
+ const setCurrentStepAndSave = function (step) {
2291
+ let previousStep = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
2292
+ return dispatch => {
2293
+ dispatch(setCurrentStep(step, previousStep));
2363
2294
  };
2364
2295
  };
2365
2296
 
2297
+ var css$d = ".SidebarLayout_module_fullWidthContent__0d6658dd {\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.SidebarLayout_module_fullWidthContainer__0d6658dd {\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.SidebarLayout_module_contentContainer__0d6658dd {\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.SidebarLayout_module_container__0d6658dd{\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.SidebarLayout_module_contentContainer__0d6658dd {\n\t\tpadding: 1.5rem 1.5rem 2.5rem 1.5rem; /* Slightly reduced but still adequate */\n\t}\n\t.SidebarLayout_module_container__0d6658dd {\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.SidebarLayout_module_contentContainer__0d6658dd {\n\t\tpadding: 1rem 1rem 2rem 1rem; /* Maintain padding on small screens */\n\t}\n\t.SidebarLayout_module_container__0d6658dd {\n\t\tpadding: 1rem 1rem 2rem 1rem; /* Legacy container support */\n\t}\n}\n\n/* Enhanced sidebar navigation for progress indication */\n\n/* Enhanced progress section */\n.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.hub-sideBar-section:last-child {\n\tborder-bottom: none;\n}\n";
2298
+ var modules_d5b6badf = {"fullWidthContent":"SidebarLayout_module_fullWidthContent__0d6658dd","fullWidthContainer":"SidebarLayout_module_fullWidthContainer__0d6658dd","contentContainer":"SidebarLayout_module_contentContainer__0d6658dd","container":"SidebarLayout_module_container__0d6658dd"};
2299
+ n(css$d,{});
2300
+
2366
2301
  /**
2367
- * Action creator to set form icon and update menu
2368
- * Updates both form state and external menu icon
2302
+ * Sidebar Layout component for feature builder wizard
2303
+ * Provides navigation sidebar with step progression, completion tracking
2304
+ * Manages step accessibility based on wizard mode and validation state
2369
2305
  *
2370
- * @param {string} icon - Icon identifier or FontAwesome icon class
2371
- * @returns {Function} Thunk function for Redux
2306
+ * @param {Object} props - Component props
2307
+ * @param {React.ReactNode} props.children - Child components to render in main content area
2308
+ * @param {Object} props.history - React Router history object for navigation
2309
+ * @returns {React.ReactElement} Layout component with sidebar navigation
2372
2310
  *
2373
2311
  * @example
2374
- * dispatch(setIcon('fa-user'));
2312
+ * <SidebarLayout history={historyObject}>
2313
+ * <YourMainContent />
2314
+ * </SidebarLayout>
2375
2315
  */
2376
- const setIcon = icon => {
2377
- return dispatch => {
2378
- // Update menu icon when icon changes
2379
- dispatch(updateFeatureBuilderIcon(icon));
2316
+ const SideBarInner = props => {
2317
+ const {
2318
+ children,
2319
+ history
2320
+ } = props;
2321
+ const dispatch = reactRedux.useDispatch();
2380
2322
 
2381
- // Dispatch the actual action
2382
- dispatch({
2383
- type: actionsTypes.SET_ICON,
2384
- payload: icon
2385
- });
2323
+ // Get wizard state
2324
+ const mode = reactRedux.useSelector(selectWizardMode);
2325
+ const isEditMode = reactRedux.useSelector(selectIsEditMode);
2326
+ const isCreateMode = reactRedux.useSelector(selectIsCreateMode);
2327
+ const currentStep = reactRedux.useSelector(selectCurrentStep);
2328
+ const goTo = url => history.push(url);
2329
+ const isSelected = url => {
2330
+ return history.location.pathname === url;
2386
2331
  };
2387
- };
2388
2332
 
2389
- /**
2390
- * Action creator to add a new field to the form
2391
- * Generates unique ID and supports custom field types
2392
- *
2393
- * @param {string} [fieldType="text"] - Type of field to add (text, title, description, image, gallery, file, cta, feature-image)
2394
- * @returns {Object} Redux action object with field ID and type
2395
- *
2396
- * @example
2397
- * dispatch(addField('text'));
2398
- * dispatch(addField('image'));
2399
- */
2400
- const addField = function () {
2401
- let fieldType = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : "text";
2402
- // Generate a unique ID for the new field
2403
- const fieldId = "custom-field-".concat(Date.now(), "-").concat(Math.random().toString(36).substr(2, 9));
2404
- return {
2405
- type: actionsTypes.ADD_FIELD,
2406
- payload: {
2407
- id: fieldId,
2408
- type: fieldType
2333
+ // Define step configuration with dynamic URL based on mode
2334
+ const getStepUrl = stepKey => {
2335
+ // Use routes from values.config to support different variants (-a, -b, -c, -d)
2336
+ switch (stepKey) {
2337
+ case "overview":
2338
+ return values.routeFormOverviewStep;
2339
+ case "fields":
2340
+ return values.routeFormFieldsStep;
2341
+ case "layout":
2342
+ return values.routeFormLayoutStep;
2343
+ default:
2344
+ return "";
2409
2345
  }
2410
2346
  };
2411
- };
2412
- const deleteField = id => {
2413
- return {
2414
- type: actionsTypes.DELETE_FIELD,
2415
- payload: id
2347
+ const steps = [{
2348
+ key: "overview",
2349
+ text: "Feature Overview",
2350
+ icon: "info-circle",
2351
+ url: getStepUrl("overview")
2352
+ }, {
2353
+ key: "fields",
2354
+ text: "Configure Fields",
2355
+ icon: "edit",
2356
+ url: getStepUrl("fields")
2357
+ }, {
2358
+ key: "layout",
2359
+ text: "Choose Layout",
2360
+ icon: "columns",
2361
+ url: getStepUrl("layout")
2362
+ }];
2363
+
2364
+ // Build sidebar items based on mode
2365
+ const buildSidebarItems = () => {
2366
+ const isWizardMode = mode === "create" || mode === "edit";
2367
+ return steps.map((step, index) => {
2368
+ const isCompleted = selectIsStepComplete(step.key);
2369
+ const isAccessible = selectIsStepAccessible(step.key);
2370
+
2371
+ // Add step number to text for better clarity
2372
+ const stepText = "".concat(index + 1, ". ").concat(step.text);
2373
+ const itemProps = {
2374
+ type: "navItem",
2375
+ text: stepText,
2376
+ icon: step.icon,
2377
+ selected: isSelected(step.url),
2378
+ onclick: isWizardMode ? null : isAccessible ? () => {
2379
+ goTo(step.url);
2380
+ dispatch(goToStep(step.key));
2381
+ } : null,
2382
+ isFontAwesome: true,
2383
+ // Enhanced completion indicator
2384
+ completed: isCompleted,
2385
+ // Disable all navigation in wizard mode
2386
+ disabled: isWizardMode || mode === "create" && !isAccessible
2387
+ };
2388
+ return itemProps;
2389
+ });
2416
2390
  };
2417
- };
2418
- const updateFieldById = (id, updatedField) => {
2419
- return {
2420
- type: actionsTypes.UPDATE_FIELD,
2421
- payload: {
2422
- id,
2423
- updatedField
2424
- }
2391
+
2392
+ // Determine sidebar title - always use "Build Your Feature" now
2393
+ const getSidebarTitle = () => {
2394
+ return "Build Your Feature";
2425
2395
  };
2426
- };
2427
2396
 
2428
- /**
2429
- * Action creator to set a description field as the summary field
2430
- * Ensures only one description field can be marked as summary by
2431
- * automatically unsetting all other description fields when a new one is selected
2432
- *
2433
- * @param {string} fieldId - ID of the description field to set as summary
2434
- * @returns {Object} Redux action object for summary field selection
2435
- *
2436
- * @example
2437
- * dispatch(setSummaryField('field-description-123'));
2438
- */
2439
- const setSummaryField = fieldId => {
2440
- return {
2441
- type: actionsTypes.SET_SUMMARY_FIELD,
2442
- payload: fieldId
2443
- };
2444
- };
2445
- const setLayoutType = layoutType => {
2446
- return {
2447
- type: actionsTypes.SET_LAYOUT_TYPE,
2448
- payload: layoutType
2449
- };
2450
- };
2451
- const submitFormRequest = () => {
2452
- return {
2453
- type: actionsTypes.SUBMIT_FORM_REQUEST
2397
+ // Simple help text
2398
+ const getHelpText = () => {
2399
+ return "Get help with feature builder";
2454
2400
  };
2401
+
2402
+ // Build sidebar sections - simplified without progress section
2403
+ const sidebarSections = [{
2404
+ title: getSidebarTitle(),
2405
+ items: buildSidebarItems()
2406
+ }];
2407
+
2408
+ // Add effect to manually attach click handlers since HubSidebar might not use onclick properly
2409
+ React.useEffect(() => {
2410
+ const isWizardMode = mode === "create" || mode === "edit";
2411
+ const attachClickHandlers = () => {
2412
+ const stepsWithUrls = [{
2413
+ key: "overview",
2414
+ url: getStepUrl("overview")
2415
+ }, {
2416
+ key: "fields",
2417
+ url: getStepUrl("fields")
2418
+ }, {
2419
+ key: "layout",
2420
+ url: getStepUrl("layout")
2421
+ }];
2422
+ stepsWithUrls.forEach((step, index) => {
2423
+ const navItem = Array.from(document.querySelectorAll(".hub-wrapperContainer .sideNav-item")).find(item => item.textContent && item.textContent.includes("".concat(index + 1, ".")));
2424
+ if (navItem) {
2425
+ // Remove any existing click listeners
2426
+ navItem.onclick = null;
2427
+
2428
+ // Check if this step is accessible
2429
+ const isAccessible = selectIsStepAccessible(step.key)({
2430
+ [values.reducerKey]: {
2431
+ wizard: {
2432
+ mode: mode,
2433
+ navigation: {
2434
+ currentStep: currentStep
2435
+ }
2436
+ }
2437
+ }
2438
+ });
2439
+
2440
+ // Check if this is the current step (selected)
2441
+ const isCurrentStep = isSelected(step.url);
2442
+
2443
+ // In wizard mode, don't attach any click handlers
2444
+ // Only attach click handler if not in wizard mode AND accessible
2445
+ if (!isWizardMode && isAccessible) {
2446
+ navItem.onclick = event => {
2447
+ event.preventDefault();
2448
+ event.stopPropagation();
2449
+ history.push(step.url);
2450
+ dispatch(goToStep(step.key));
2451
+ };
2452
+ }
2453
+
2454
+ // Make sure it's styled appropriately
2455
+ // In wizard mode: block clicks, but keep current step visible
2456
+ navItem.style.pointerEvents = isWizardMode ? "none" : "auto";
2457
+ if (isWizardMode) {
2458
+ // Current step: full opacity, not-allowed cursor
2459
+ // Other steps: reduced opacity
2460
+ navItem.style.opacity = isCurrentStep ? "1" : "0.4";
2461
+ navItem.style.cursor = "not-allowed";
2462
+ } else {
2463
+ // Not in wizard mode: normal styling based on accessibility
2464
+ navItem.style.cursor = isAccessible ? "pointer" : "not-allowed";
2465
+ navItem.style.opacity = isAccessible ? "1" : "0.5";
2466
+ }
2467
+ }
2468
+ });
2469
+ };
2470
+
2471
+ // Initial attachment
2472
+ attachClickHandlers();
2473
+
2474
+ // Re-attach after a short delay to ensure HubSidebar has rendered
2475
+ const timeoutId = setTimeout(attachClickHandlers, 100);
2476
+
2477
+ // Also try to re-attach when DOM changes (observe for mutations)
2478
+ const observer = new MutationObserver(() => {
2479
+ setTimeout(attachClickHandlers, 50);
2480
+ });
2481
+
2482
+ // Start observing the document body for changes
2483
+ observer.observe(document.body, {
2484
+ childList: true,
2485
+ subtree: true
2486
+ });
2487
+ return () => {
2488
+ clearTimeout(timeoutId);
2489
+ observer.disconnect();
2490
+ };
2491
+ }, [history, dispatch, isEditMode, isCreateMode, currentStep]);
2492
+ return /*#__PURE__*/React__default["default"].createElement("div", {
2493
+ className: "hub-wrapperContainer"
2494
+ }, /*#__PURE__*/React__default["default"].createElement(HubSidebar, {
2495
+ sections: sidebarSections,
2496
+ helpGuide: {
2497
+ text: getHelpText(),
2498
+ url: "https://www.plusscommunities.com/user-guide"
2499
+ }
2500
+ }), /*#__PURE__*/React__default["default"].createElement("div", {
2501
+ className: "hub-contentWrapper"
2502
+ }, /*#__PURE__*/React__default["default"].createElement("div", {
2503
+ className: modules_d5b6badf.fullWidthContainer
2504
+ }, /*#__PURE__*/React__default["default"].createElement("div", {
2505
+ className: modules_d5b6badf.contentContainer
2506
+ }, children))));
2455
2507
  };
2456
- const submitFormSuccess = () => {
2457
- return {
2458
- type: actionsTypes.SUBMIT_FORM_SUCCESS
2459
- };
2508
+ const SidebarLayout = reactRouter.withRouter(SideBarInner);
2509
+
2510
+ var css$c = ".Form_module_content__d62303b4 {\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.Form_module_pageWrapper__d62303b4 {\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.Form_module_pageWrapper__d62303b4 {\n\t\tpadding: 3rem 0;\n\t}\n}\n\n/* Form container styling when using CenteredContainer */\n.Form_module_formContainer__d62303b4 {\n\tbackground-color: var(--bg-white);\n}\n\n/* Section styling */\n.Form_module_section__d62303b4 {\n\tmargin-bottom: 2rem;\n\t\tpadding: 0 0 2em;\n}\n\n.Form_module_section_NoBorder__d62303b4 {\n\t\tborder-bottom: none;\n}\n\n.Form_module_subtitle__d62303b4 {\n\tcolor: var(--text-bluegrey, #6c7a90);\n}\n\n.Form_module_sectionHeader__d62303b4 {\n\t\tdisplay: flex;\n\t\tjustify-content: space-between;\n}\n\n/* Add Field Button styling */\n.Form_module_addFieldButton__d62303b4 {\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.Form_module_addFieldButton__d62303b4: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.Form_module_addFieldButton__d62303b4:focus {\n\toutline: 2px solid var(--colour-branding-action, #5c90df);\n\toutline-offset: 2px;\n}\n\n.Form_module_addFieldButton__d62303b4:active {\n\ttransform: translateY(0);\n}\n\n/* Refresh button styling */\n.Form_module_refreshButton__d62303b4 {\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.Form_module_refreshButton__d62303b4:hover {\n\tbackground: var(--colour-dusk-hover, #485968);\n}\n\n/* Error message container */\n.Form_module_errorMessageContainer__d62303b4 {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 0.5rem;\n}\n\n/* Add Field Container - matches field structure */\n.Form_module_addFieldContainer__d62303b4 {\n\tdisplay: flex;\n\tmargin-top: 32px;\n}\n\n.Form_module_fieldNumberContainer__d62303b4 {\n\tdisplay: flex;\n\twidth: 40px;\n}\n\n/* Add Field Section */\n.Form_module_addFieldSection__d62303b4 {\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.Form_module_addFieldSection__d62303b4 {\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.Form_module_helpSection__d62303b4 {\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.Form_module_helpTitle__d62303b4 {\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.Form_module_helpText__d62303b4 {\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.Form_module_fieldTypeCards__d62303b4 {\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.Form_module_fieldTypeCards__d62303b4 {\n\t\tgap: 1.5rem; /* Slightly larger gap on desktop */\n\t}\n}\n\n.Form_module_fieldTypeCard__d62303b4 {\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.Form_module_fieldTypeCard__d62303b4: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.Form_module_fieldTypeCard__d62303b4:focus {\n\toutline: 2px solid var(--text-light);\n\toutline-offset: 2px;\n}\n\n.Form_module_fieldTypeCard__d62303b4:active {\n\ttransform: scale(0.98);\n\ttransition: transform 0.1s ease;\n}\n\n.Form_module_fieldTypeCardIcon__d62303b4 {\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.Form_module_fieldTypeCardContent__d62303b4 {\n\tflex: 1;\n\twidth: 100%;\n\tmin-width: 0; /* Prevent content from overflowing */\n}\n\n.Form_module_fieldTypeCardTitle__d62303b4 {\n\tmargin: 0 0 0.25rem 0;\n\tfont-weight: 600;\n\tfont-size: 1.4rem; /* Increased for better readability */\n}\n\n.Form_module_fieldTypeCardDescription__d62303b4 {\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.Form_module_fieldTypeCardUseCase__d62303b4 {\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.Form_module_addFieldSection__d62303b4> 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.Form_module_addFieldButtonContainer__d62303b4 {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: flex-end;\n}\n\n/* Empty State */\n.Form_module_emptyState__d62303b4 {\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.Form_module_emptyStateText__d62303b4 {\n\tcolor: var(--text-bluegrey, #6c7a90);\n\tfont-style: italic;\n}\n\n.Form_module_fieldTypeSelector__d62303b4 {\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.Form_module_addFieldSection__d62303b4 {\n\t\talign-items: stretch;\n\t}\n\t\n\t.Form_module_fieldTypeSelector__d62303b4 {\n\t\tmax-width: none;\n\t}\n}\n\n.Form_module_grid__four__d62303b4 {\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.Form_module_grid__four__d62303b4 {\n\t\tgrid-template-columns: 1fr;\n\t\tgap: 2rem;\n\t}\n}\n\n/* Navigation styles */\n.Form_module_navigation__d62303b4 {\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.Form_module_navigation__d62303b4> :only-child {\n\tmargin-left: auto;\n\tmargin-right: 0;\n}\n\n/* Two button alignment - one left, one right */\n.Form_module_navigation__d62303b4> :first-child:not(:only-child) {\n\tmargin-right: auto;\n}\n\n.Form_module_navigation__d62303b4> :last-child:not(:only-child) {\n\tmargin-left: auto;\n}\n\n/* Special case for overview step - align save button to right */\n.Form_module_overviewStep__d62303b4 .Form_module_navigation__d62303b4> :only-child {\n\tmargin-left: auto;\n\tmargin-right: 0;\n}\n\n.Form_module_previousButton__d62303b4 {\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.Form_module_previousButton__d62303b4:hover {\n\tbackground: #5a6268;\n}\n\n.Form_module_saveButton__d62303b4 {\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.Form_module_saveButton__d62303b4: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.Form_module_saveButton__d62303b4:focus {\n\toutline: 2px solid var(--colour-purple, #6e79c5);\n\toutline-offset: 2px;\n}\n\n.Form_module_saveButton__d62303b4:disabled {\n\tbackground: #6c757d;\n\tcursor: not-allowed;\n\topacity: 0.65;\n\ttransform: none;\n\tbox-shadow: none;\n}\n\n.Form_module_nextButton__d62303b4 {\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.Form_module_nextButton__d62303b4:hover:not(:disabled) {\n\ttransform: translateY(-2px);\n\tbox-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);\n}\n\n.Form_module_nextButton__d62303b4:disabled {\n\tbackground: #6c757d;\n\tcursor: not-allowed;\n\topacity: 0.65;\n\ttransform: none;\n\tbox-shadow: none;\n}\n\n\n\n.Form_module_errorMessage__d62303b4 {\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.Form_module_layoutOption__d62303b4 {\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\topacity: 0.6;\n}\n\n.Form_module_layoutOption__d62303b4:hover {\n\topacity: 1;\n}\n\n.Form_module_layoutOptionImage__d62303b4 {\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.Form_module_layoutOption__d62303b4.Form_module_selected__d62303b4 .Form_module_layoutOptionImage__d62303b4 {\n\tborder-color: var(--colour-purple, #6e79c5);\n\tbox-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n}\n\n.Form_module_layoutOption__d62303b4.Form_module_selected__d62303b4 {\n\topacity: 1;\n}\n\n.Form_module_layoutOptionImg__d62303b4 {\n\twidth: 100%;\n\theight: 100%;\n\tobject-fit: cover;\n}\n\n.Form_module_layoutOptionContent__d62303b4 {\n\tmax-width: 250px;\n\tmargin-bottom: 0.5rem;\n}\n\n.Form_module_layoutOptionTitle__d62303b4 {\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.Form_module_layoutOptionDescription__d62303b4 {\n\tfont-size: 1.2rem;\n\tmargin: 0;\n\tline-height: 1.3;\n\tcolor: #6c757d;\n}\n\n.Form_module_layoutOption__d62303b4.Form_module_hasError__d62303b4 .Form_module_layoutOptionImage__d62303b4 {\n\tborder-color: var(--colour-red, #dc3545);\n}\n\n/* Field error styles */\n.Form_module_fieldError__d62303b4 {\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.Form_module_errorIcon__d62303b4 {\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 Form_module_errorShake__d62303b4 {\n\t0%, 100% { transform: translateX(0); }\n\t25% { transform: translateX(-3px); }\n\t75% { transform: translateX(3px); }\n}\n\n@keyframes Form_module_errorPulse__d62303b4 {\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.Form_module_successMessage__d62303b4 {\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.Form_module_createMode__d62303b4 {\n\tcolor: var(--colour-purple, #6e79c5);\n}\n\n.Form_module_editMode__d62303b4 {\n\tcolor: var(--colour-branding-dark, #364196);\n}\n\n/* Responsive Design */\n@media (max-width: 768px) {\n\t.Form_module_content__d62303b4 {\n\t\tpadding: 1.5rem 0 2.5rem 0; /* Maintain proper padding on mobile */\n\t}\n\t\n\t.Form_module_section__d62303b4 {\n\t\tmargin-bottom: 1.5rem;\n\t}\n\t\n\t/* Mobile: vertical layout for field selector and button */\n\t.Form_module_addFieldSection__d62303b4 {\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.Form_module_addFieldSection__d62303b4 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.Form_module_addFieldButtonContainer__d62303b4 {\n\t\talign-items: stretch;\n\t\tjustify-content: stretch;\n\t\tmin-height: auto;\n\t}\n\t\n\t.Form_module_navigation__d62303b4 {\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.Form_module_navigation__d62303b4> :only-child {\n\t\tmargin-left: auto;\n\t\tmargin-right: 0;\n\t}\n\t\n\t.Form_module_navigation__d62303b4> :first-child:not(:only-child),\n\t.Form_module_navigation__d62303b4> :last-child:not(:only-child) {\n\t\tmargin-left: 0;\n\t\tmargin-right: 0;\n\t}\n\t\n\t.Form_module_previousButton__d62303b4,\n\t.Form_module_nextButton__d62303b4,\n\t.Form_module_saveButton__d62303b4 {\n\t\twidth: 100%;\n\t\tpadding: 1rem;\n\t}\n}\n\n@media (max-width: 480px) {\n\t.Form_module_content__d62303b4 {\n\t\tpadding: 1rem 0 2rem 0; /* Still maintain padding on small screens */\n\t}\n\t\n\t.Form_module_section__d62303b4 {\n\t\tmargin-bottom: 1rem;\n\t}\n\t\n\t.Form_module_navigation__d62303b4 {\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.Form_module_navigation__d62303b4> :only-child {\n\t\tmargin-left: auto;\n\t\tmargin-right: 0;\n\t}\n\t\n\t.Form_module_navigation__d62303b4> :first-child:not(:only-child),\n\t.Form_module_navigation__d62303b4> :last-child:not(:only-child) {\n\t\tmargin-left: 0;\n\t\tmargin-right: 0;\n\t}\n\t\n\t.Form_module_errorMessageContainer__d62303b4 {\n\t\tflex-direction: column;\n\t\talign-items: flex-start;\n\t\tgap: 0.75rem;\n\t}\n\t\n\t.Form_module_refreshButton__d62303b4 {\n\t\talign-self: flex-end;\n\t}\n}\n\n/* Full width content when sidebar is hidden */\n.hub-contentWrapper.fullWidthContent {\n\tmax-width: 1000px;\n\tmargin: 0 auto;\n\tpadding: 2rem;\n}\n\n.Form_module_hubContentWrapper_Col__d62303b4 {\n flex-flow: column;\n}\n\n/* Validation error message - displayed at top of form */\n.Form_module_validationErrorMessage__d62303b4 {\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.Form_module_validationErrorMessage__d62303b4::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.Form_module_formHeader__d62303b4 {\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.Form_module_fieldsLoadingContainer__d62303b4 {\n\ttext-align: center;\n\tpadding: 60px 20px;\n}\n\n/* Help note styling */\n.Form_module_helpNote__d62303b4 {\n\tfont-size: 13px;\n\tcolor: #6c757d;\n}\n\n/* Loading container for overview */\n.Form_module_overviewLoadingContainer__d62303b4 {\n\ttext-align: center;\n\tpadding: 60px 20px;\n}\n\n/* Form layout header */\n.Form_module_formLayoutHeader__d62303b4 {\n\tdisplay: flex;\n\tjustify-content: space-between;\n\talign-items: flex-start;\n\tmargin-bottom: 16px;\n}\n\n/* Section margins */\n.Form_module_gridIconSection__d62303b4 {\n\tmargin-bottom: 3rem;\n}\n\n.Form_module_layoutSection__d62303b4 {\n\tmargin-bottom: 2rem;\n}\n\n/* Grid icon loading state */\n.Form_module_gridIconLoading__d62303b4 {\n\tgrid-column: 1 / -1;\n}\n\n/* Hide upload button in grid icon section */\n.Form_module_gridIconSection__d62303b4 .iconLoader__buttonOverlay button:first-child {\n\tdisplay: none !important;\n}\n";
2511
+ var modules_cd65a764 = {"content":"Form_module_content__d62303b4","pageWrapper":"Form_module_pageWrapper__d62303b4","formContainer":"Form_module_formContainer__d62303b4","section":"Form_module_section__d62303b4","section--no-border":"Form_module_section_NoBorder__d62303b4","subtitle":"Form_module_subtitle__d62303b4","sectionHeader":"Form_module_sectionHeader__d62303b4","addFieldButton":"Form_module_addFieldButton__d62303b4","refreshButton":"Form_module_refreshButton__d62303b4","errorMessageContainer":"Form_module_errorMessageContainer__d62303b4","addFieldContainer":"Form_module_addFieldContainer__d62303b4","fieldNumberContainer":"Form_module_fieldNumberContainer__d62303b4","addFieldSection":"Form_module_addFieldSection__d62303b4","helpSection":"Form_module_helpSection__d62303b4","helpTitle":"Form_module_helpTitle__d62303b4","helpText":"Form_module_helpText__d62303b4","fieldTypeCards":"Form_module_fieldTypeCards__d62303b4","fieldTypeCard":"Form_module_fieldTypeCard__d62303b4","fieldTypeCardIcon":"Form_module_fieldTypeCardIcon__d62303b4","fieldTypeCardContent":"Form_module_fieldTypeCardContent__d62303b4","fieldTypeCardTitle":"Form_module_fieldTypeCardTitle__d62303b4","fieldTypeCardDescription":"Form_module_fieldTypeCardDescription__d62303b4","fieldTypeCardUseCase":"Form_module_fieldTypeCardUseCase__d62303b4","addFieldButtonContainer":"Form_module_addFieldButtonContainer__d62303b4","emptyState":"Form_module_emptyState__d62303b4","emptyStateText":"Form_module_emptyStateText__d62303b4","fieldTypeSelector":"Form_module_fieldTypeSelector__d62303b4","grid__four":"Form_module_grid__four__d62303b4","navigation":"Form_module_navigation__d62303b4","overviewStep":"Form_module_overviewStep__d62303b4","previousButton":"Form_module_previousButton__d62303b4","saveButton":"Form_module_saveButton__d62303b4","nextButton":"Form_module_nextButton__d62303b4","errorMessage":"Form_module_errorMessage__d62303b4","layoutOption":"Form_module_layoutOption__d62303b4","layoutOptionImage":"Form_module_layoutOptionImage__d62303b4","selected":"Form_module_selected__d62303b4","layoutOptionImg":"Form_module_layoutOptionImg__d62303b4","layoutOptionContent":"Form_module_layoutOptionContent__d62303b4","layoutOptionTitle":"Form_module_layoutOptionTitle__d62303b4","layoutOptionDescription":"Form_module_layoutOptionDescription__d62303b4","hasError":"Form_module_hasError__d62303b4","fieldError":"Form_module_fieldError__d62303b4","errorIcon":"Form_module_errorIcon__d62303b4","successMessage":"Form_module_successMessage__d62303b4","createMode":"Form_module_createMode__d62303b4","editMode":"Form_module_editMode__d62303b4","hub-contentWrapper--col":"Form_module_hubContentWrapper_Col__d62303b4","validationErrorMessage":"Form_module_validationErrorMessage__d62303b4","formHeader":"Form_module_formHeader__d62303b4","fieldsLoadingContainer":"Form_module_fieldsLoadingContainer__d62303b4","helpNote":"Form_module_helpNote__d62303b4","overviewLoadingContainer":"Form_module_overviewLoadingContainer__d62303b4","formLayoutHeader":"Form_module_formLayoutHeader__d62303b4","gridIconSection":"Form_module_gridIconSection__d62303b4","layoutSection":"Form_module_layoutSection__d62303b4","gridIconLoading":"Form_module_gridIconLoading__d62303b4","errorShake":"Form_module_errorShake__d62303b4","errorPulse":"Form_module_errorPulse__d62303b4"};
2512
+ n(css$c,{});
2513
+
2514
+ 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; }
2515
+ 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; }
2516
+ const {
2517
+ Helper: Helper$1,
2518
+ Session: Session$1
2519
+ } = PlussCore__namespace;
2520
+ const {
2521
+ getUrl: getUrl$1
2522
+ } = Helper$1;
2523
+ const {
2524
+ authedFunction: authedFunction$1
2525
+ } = Session$1;
2526
+ const featureDefinitionActions = {
2527
+ /**
2528
+ * Get the single feature definition by ID
2529
+ * Path: {id}
2530
+ */
2531
+ getSingle: async (id, site) => {
2532
+ const query = {
2533
+ id,
2534
+ site
2535
+ };
2536
+ return authedFunction$1({
2537
+ method: "GET",
2538
+ url: getUrl$1("feature-builder", "definition/get/single", query)
2539
+ });
2540
+ },
2541
+ /**
2542
+ * Creates a new feature definition with the provided configuration
2543
+ *
2544
+ * @param {string} id - The unique identifier for the new feature definition
2545
+ * @param {string} site - The site ID where the feature definition will be created
2546
+ * @param {FeatureDefinition} featureDefinition - The feature definition data to create
2547
+ * @returns {Promise<ApiResponse>} Promise resolving to API response with created feature definition
2548
+ * @throws {Error} When creation fails due to validation or API errors
2549
+ *
2550
+ */
2551
+ create: async (id, site, featureDefinition) => {
2552
+ return authedFunction$1({
2553
+ method: "POST",
2554
+ url: getUrl$1("feature-builder", "definition/update/create"),
2555
+ data: {
2556
+ id,
2557
+ site,
2558
+ featureDefinition
2559
+ }
2560
+ });
2561
+ },
2562
+ /**
2563
+ * Updates an existing feature definition with new configuration
2564
+ *
2565
+ * @param {FeatureDefinition} featureDefinitionData - The updated feature definition data
2566
+ * @param {string} featureDefinitionData.id - The unique identifier of the feature definition to update
2567
+ * @param {string} [featureDefinitionData.displayName] - Updated display name
2568
+ * @param {Field[]} [featureDefinitionData.fields] - Updated field definitions
2569
+ * @param {Object} [featureDefinitionData.layout] - Updated layout configuration
2570
+ * @param {string} site - The site ID where the feature definition exists
2571
+ * @returns {Promise<ApiResponse>} Promise resolving to API response with updated feature definition
2572
+ * @throws {Error} When update fails due to validation or API errors
2573
+ *
2574
+ * @example
2575
+ * try {
2576
+ * const response = await featureDefinitionActions.edit(
2577
+ * {
2578
+ * id: 'feature-123',
2579
+ * displayName: 'Updated Form',
2580
+ * fields: [...]
2581
+ * },
2582
+ * 'site-123'
2583
+ * );
2584
+ */
2585
+ edit: async (featureDefinitionData, site) => {
2586
+ // Ensure site is included in the request body
2587
+ const dataWithSite = _objectSpread$7({
2588
+ site: site
2589
+ }, featureDefinitionData);
2590
+ return authedFunction$1({
2591
+ method: "POST",
2592
+ url: getUrl$1("feature-builder", "definition/update/edit"),
2593
+ data: dataWithSite
2594
+ });
2595
+ },
2596
+ /**
2597
+ * Soft deletes a feature definition (marks as deleted but doesn't permanently remove)
2598
+ *
2599
+ * @param {string} id - The unique identifier of the feature definition to delete
2600
+ * @param {string} site - The site ID where the feature definition exists
2601
+ * @returns {Promise<ApiResponse>} Promise resolving to API response confirming deletion
2602
+ * @throws {Error} When deletion fails or feature definition is not found
2603
+ *
2604
+ */
2605
+ delete: async (id, site) => {
2606
+ return authedFunction$1({
2607
+ method: "POST",
2608
+ url: getUrl$1("feature-builder", "definition/update/delete"),
2609
+ data: {
2610
+ id,
2611
+ site
2612
+ }
2613
+ });
2614
+ }
2460
2615
  };
2461
- const submitFormFailure = error => {
2462
- return {
2463
- type: actionsTypes.SUBMIT_FORM_FAILURE,
2464
- payload: error
2465
- };
2616
+
2617
+ 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; }
2618
+ 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; }
2619
+
2620
+ // IMPORTANT: Using local UPDATE_STRINGS action type to make extension self-contained
2621
+ // The main app's StringsReducer will handle this action type the same way
2622
+ const UPDATE_STRINGS = "UPDATE_STRINGS";
2623
+ const updateFeatureBuilderString = title => (dispatch, getState) => {
2624
+ var _getState$strings;
2625
+ const currentStrings = ((_getState$strings = getState().strings) === null || _getState$strings === void 0 ? void 0 : _getState$strings.config) || {};
2626
+ const titleCased = toTitleCase(title) || values.textMenuTitle;
2627
+ const updatedStrings = _objectSpread$6(_objectSpread$6({}, currentStrings), {}, {
2628
+ sideNav: _objectSpread$6(_objectSpread$6({}, currentStrings.sideNav), {}, {
2629
+ [values.featureKey]: titleCased,
2630
+ [values.menuKey]: "Manage ".concat(titleCased)
2631
+ }),
2632
+ permission: _objectSpread$6(_objectSpread$6({}, currentStrings.permission), {}, {
2633
+ [values.permissionFeatureBuilderDefinition]: "Manage custom feature ".concat(titleCased),
2634
+ [values.permissionFeatureBuilderContent]: "Manage ".concat(titleCased, " content")
2635
+ })
2636
+ });
2637
+ dispatch({
2638
+ type: UPDATE_STRINGS,
2639
+ payload: updatedStrings
2640
+ });
2466
2641
  };
2467
- const clearFormSubmissionState = () => {
2468
- return {
2469
- type: actionsTypes.CLEAR_FORM_SUBMISSION_STATE
2470
- };
2642
+ const updateFeatureBuilderIcon = icon => (dispatch, getState) => {
2643
+ var _getState$strings2;
2644
+ const currentStrings = ((_getState$strings2 = getState().strings) === null || _getState$strings2 === void 0 ? void 0 : _getState$strings2.config) || {};
2645
+ const updatedStrings = _objectSpread$6(_objectSpread$6({}, currentStrings), {}, {
2646
+ sideNav: _objectSpread$6(_objectSpread$6({}, currentStrings.sideNav), {}, {
2647
+ [values.featureKey + "-icon"]: icon,
2648
+ [values.menuKey + "-icon"]: icon
2649
+ })
2650
+ });
2651
+ dispatch({
2652
+ type: UPDATE_STRINGS,
2653
+ payload: updatedStrings
2654
+ });
2471
2655
  };
2472
2656
 
2473
2657
  /**
2474
- * Submits the complete feature form to the server
2475
- * Handles form validation, API submission, and error handling
2658
+ * Form Actions for Feature Builder
2659
+ * Manages form state including title, icon, fields, layout, and submission
2660
+ * Handles CRUD operations for form fields and layout configuration
2661
+ * Coordinates with external menu updates and feature definition actions
2476
2662
  *
2477
- * @returns {Function} Async thunk function for Redux
2478
- * @throws {Error} When form validation fails or API submission encounters error
2663
+ * @namespace formActions
2479
2664
  */
2480
- function submitForm() {
2481
- return async (dispatch, getState) => {
2482
- const state = getState()[values.reducerKey];
2483
- const form = state && state.form;
2484
- if (!form) {
2485
- dispatch(submitFormFailure(new Error("Form data is missing. Please refresh the page and try again.")));
2486
- return;
2487
- }
2488
- dispatch(submitFormRequest());
2489
- try {
2490
- // Get site from auth store
2491
- const site = getState().auth.site;
2492
- if (!site) {
2493
- throw new Error("Authentication error: Site context not found. Please refresh and login again.");
2494
- }
2495
2665
 
2496
- // Use mode from fetch instead of checking definition ID
2497
- const definitionState = state && state.definition;
2498
- const mode = definitionState && definitionState.mode; // Use stored mode from fetch
2499
- const definitionId = definitionState && definitionState.id;
2500
- if (mode === "edit") {
2501
- // Always update when in edit mode
2502
- const updatedDefinition = {
2503
- id: definitionId,
2504
- site: site,
2505
- // Include site from auth store
2506
- featureDefinition: {
2507
- // Wrap in expected structure for backend
2508
- title: form.title,
2509
- icon: form.icon,
2510
- displayName: form.displayName,
2511
- layout: form.layout,
2512
- fields: form.fields
2513
- }
2514
- };
2515
- await featureDefinitionActions.edit(updatedDefinition, site);
2516
- } else {
2517
- // Always create when in create mode (or mode is undefined/null)
2518
- if (!values.featureId || !site) {
2519
- throw new Error("Authentication error: Missing required context (featureId or site).");
2520
- }
2521
- await featureDefinitionActions.create(values.featureId, site,
2522
- // Use actual site from auth store
2523
- {
2524
- title: form.title,
2525
- icon: form.icon,
2526
- displayName: form.displayName,
2527
- layout: form.layout,
2528
- fields: form.fields
2529
- });
2530
- }
2531
- dispatch(submitFormSuccess());
2532
- } catch (err) {
2533
- // Handle different types of errors
2534
- let errorToDisplay = err;
2535
- if (err.response) {
2536
- // API error (400, 401, 404, 500, etc.)
2537
- const {
2538
- status,
2539
- data
2540
- } = err.response;
2541
- if (status === 400 && data && data.error) {
2542
- errorToDisplay = new Error("Validation error: ".concat(data.error));
2543
- } else if (status === 401) {
2544
- errorToDisplay = new Error("You are not authorized to perform this action");
2545
- } else if (status === 404) {
2546
- errorToDisplay = new Error("Feature definition not found");
2547
- } else if (status >= 500) {
2548
- errorToDisplay = new Error("Server error. Please try again later.");
2549
- } else {
2550
- errorToDisplay = new Error(data && data.error || "Request failed with status ".concat(status));
2551
- }
2552
- } else if (err.request) {
2553
- // Network error (no response received)
2554
- errorToDisplay = new Error("Network error. Please check your connection and try again.");
2555
- } else if (err.message) {
2556
- // Other JavaScript errors
2557
- errorToDisplay = err;
2558
- } else {
2559
- // Unknown error
2560
- errorToDisplay = new Error("An unexpected error occurred while saving");
2561
- }
2562
- dispatch(submitFormFailure(errorToDisplay));
2563
- }
2564
- };
2565
- }
2666
+ /**
2667
+ * @typedef {Object} FieldValues
2668
+ * @property {string} [label] - Field label text
2669
+ * @property {string} [placeholder] - Placeholder text for input
2670
+ * @property {boolean} [isRequired] - Whether field is required
2671
+ * @property {string} [helpText] - Help text for field guidance
2672
+ * @property {boolean} [allowCaption] - Whether field allows captions
2673
+ * @property {boolean} [useAsSummary] - Whether field is used as summary
2674
+ */
2566
2675
 
2567
- 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; }
2568
- 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; }
2569
- const SET_NAVIGATION_STATE$1 = "SET_NAVIGATION_STATE";
2570
- const UPDATE_STEP_VALIDATION$1 = "UPDATE_STEP_VALIDATION";
2571
- const MARK_STEP_COMPLETE$1 = "MARK_STEP_COMPLETE";
2572
- const setCurrentStep = function (step) {
2573
- let previousStep = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
2676
+ /**
2677
+ * @typedef {Object} FormField
2678
+ * @property {string} id - Unique field identifier
2679
+ * @property {string} type - Field type (text, title, description, image, file, cta, feature-image)
2680
+ * @property {boolean} isMandatory - Whether field is mandatory system field
2681
+ * @property {FieldValues} values - Field configuration values
2682
+ */
2683
+
2684
+ /**
2685
+ * @typedef {Object} LayoutConfig
2686
+ * @property {string} type - Layout type (round, square, etc.)
2687
+ * @property {string} [gridIcon] - Background image for grid layout
2688
+ */
2689
+
2690
+ const actionsTypes = {
2691
+ SET_INITIAL_VALUES: "".concat(values.reducerKey.toUpperCase(), "_SET_INITIAL_VALUES"),
2692
+ SET_TITLE: "".concat(values.reducerKey.toUpperCase(), "_SET_TITLE"),
2693
+ SET_ICON: "".concat(values.reducerKey.toUpperCase(), "_SET_ICON"),
2694
+ SET_DISPLAY_NAME: "".concat(values.reducerKey.toUpperCase(), "_SET_DISPLAY_NAME"),
2695
+ ADD_FIELD: "".concat(values.reducerKey.toUpperCase(), "_ADD_FIELD"),
2696
+ DELETE_FIELD: "".concat(values.reducerKey.toUpperCase(), "_DELETE_FIELD"),
2697
+ UPDATE_FIELD: "".concat(values.reducerKey.toUpperCase(), "_UPDATE_FIELD"),
2698
+ SET_LAYOUT_GRID_ICON: "".concat(values.reducerKey.toUpperCase(), "_SET_LAYOUT_GRID_ICON"),
2699
+ SET_LAYOUT_TYPE: "".concat(values.reducerKey.toUpperCase(), "_SET_LAYOUT_TYPE"),
2700
+ SUBMIT_FORM_REQUEST: "".concat(values.reducerKey.toUpperCase(), "_SUBMIT_FORM_REQUEST"),
2701
+ SUBMIT_FORM_SUCCESS: "".concat(values.reducerKey.toUpperCase(), "_SUBMIT_FORM_SUCCESS"),
2702
+ SUBMIT_FORM_FAILURE: "".concat(values.reducerKey.toUpperCase(), "_SUBMIT_FORM_FAILURE"),
2703
+ CLEAR_FORM_SUBMISSION_STATE: "".concat(values.reducerKey.toUpperCase(), "_CLEAR_FORM_SUBMISSION_STATE"),
2704
+ SET_SUMMARY_FIELD: "".concat(values.reducerKey.toUpperCase(), "_SET_SUMMARY_FIELD")
2705
+ };
2706
+
2707
+ /**
2708
+ * Action creator to set initial form values
2709
+ * Initializes form state with existing feature definition data
2710
+ *
2711
+ * @param {Object} initialValues - Initial form values object
2712
+ * @param {string} initialValues.title - Feature title
2713
+ * @param {string} initialValues.icon - Feature icon
2714
+ * @param {string} initialValues.displayName - Feature display name
2715
+ * @param {LayoutConfig} initialValues.layout - Layout configuration
2716
+ * @param {FormField[]} initialValues.fields - Form fields array
2717
+ * @returns {Object} Redux action object with type and payload
2718
+ *
2719
+ * @example
2720
+ * dispatch(setInitialValues({
2721
+ * title: 'Contact Form',
2722
+ * icon: 'envelope',
2723
+ * fields: []
2724
+ * }));
2725
+ */
2726
+ const setInitialValues = initialValues => {
2574
2727
  return {
2575
- type: SET_NAVIGATION_STATE$1,
2576
- payload: {
2577
- currentStep: step,
2578
- previousStep,
2579
- canGoBack: step !== "welcome",
2580
- canGoForward: true
2581
- }
2728
+ type: actionsTypes.SET_INITIAL_VALUES,
2729
+ payload: initialValues
2582
2730
  };
2583
2731
  };
2584
- const goToStep = step => (dispatch, getState) => {
2585
- const state = getState()[values.reducerKey];
2586
- const currentStep = state && state.wizard && state.wizard.navigation && state.wizard.navigation.currentStep;
2587
-
2588
- // Clear form submission state when changing steps
2589
- dispatch(clearFormSubmissionState());
2590
- dispatch(setCurrentStep(step, currentStep));
2591
- };
2592
- const updateStepValidation = function (step, isValid) {
2593
- let errors = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
2732
+ const setTitle = title => {
2594
2733
  return {
2595
- type: UPDATE_STEP_VALIDATION$1,
2596
- payload: {
2597
- step,
2598
- isValid,
2599
- errors
2600
- }
2734
+ type: actionsTypes.SET_TITLE,
2735
+ payload: title
2601
2736
  };
2602
2737
  };
2603
- const validateAndUpdateStep = step => (dispatch, getState) => {
2604
- // Use existing selectors to get form data
2605
- const state = getState();
2606
- const title = selectFormTitle(state);
2607
- const icon = selectFormIcon(state);
2608
- const displayName = selectFormDisplayName(state);
2609
- const layout = selectFormLayout(state);
2610
- const fields = selectFormFields(state);
2611
- const form = {
2612
- title,
2613
- icon,
2614
- displayName,
2615
- layout,
2616
- fields
2617
- };
2618
- const {
2619
- isValid,
2620
- errors
2621
- } = getFormValidation(form, step);
2622
- dispatch(updateStepValidation(step, isValid, errors));
2623
2738
 
2624
- // If valid, mark as complete
2625
- if (isValid) {
2739
+ /**
2740
+ * Action creator to set form display name and update menu
2741
+ * Updates both form state and external menu string
2742
+ * Title cases the display name before updating the strings store
2743
+ *
2744
+ * @param {string} displayName - New display name for form
2745
+ * @returns {Function} Thunk function for Redux
2746
+ *
2747
+ * @example
2748
+ * dispatch(setDisplayName('Contact Information'));
2749
+ */
2750
+ const setDisplayName = displayName => {
2751
+ return dispatch => {
2752
+ // Update menu string when display name changes (will be title cased in updateFeatureBuilderString)
2753
+ dispatch(updateFeatureBuilderString(displayName));
2754
+
2755
+ // Dispatch the actual action
2626
2756
  dispatch({
2627
- type: MARK_STEP_COMPLETE$1,
2628
- payload: step
2757
+ type: actionsTypes.SET_DISPLAY_NAME,
2758
+ payload: displayName
2629
2759
  });
2630
- }
2631
- return {
2632
- isValid,
2633
- errors
2634
2760
  };
2635
2761
  };
2636
- const getFormValidation = (form, step) => {
2637
- // Return validation results for undefined form (prevent crashes)
2638
- if (!form) {
2639
- switch (step) {
2640
- case "overview":
2641
- return {
2642
- isValid: false,
2643
- errors: {
2644
- title: "Title is required",
2645
- displayName: "Display name is required",
2646
- icon: "Icon is required"
2647
- }
2648
- };
2649
- case "fields":
2650
- return {
2651
- isValid: false,
2652
- errors: {
2653
- missingTitle: "Title field is required",
2654
- missingImage: "Feature image field is required",
2655
- fieldLabels: "Some fields are missing labels"
2656
- }
2657
- };
2658
- case "layout":
2659
- return {
2660
- isValid: false,
2661
- errors: {
2662
- layoutType: "Layout type is required"
2663
- }
2664
- };
2665
- default:
2666
- return {
2667
- isValid: false,
2668
- errors: {}
2669
- };
2670
- }
2671
- }
2672
- switch (step) {
2673
- case "overview":
2674
- {
2675
- const hasTitle = form.title && form.title.trim().length > 0;
2676
- const hasDisplayName = form.displayName && form.displayName.trim().length > 0;
2677
- const hasIcon = form.icon && form.icon.length > 0;
2678
- return {
2679
- isValid: hasTitle && hasDisplayName && hasIcon,
2680
- errors: {
2681
- title: !hasTitle ? "Title is required" : null,
2682
- displayName: !hasDisplayName ? "Display name is required" : null,
2683
- icon: !hasIcon ? "Icon is required" : null
2684
- }
2685
- };
2686
- }
2687
- case "fields":
2688
- {
2689
- const hasTitleField = form.fields && form.fields.some(field => field.id === "mandatory-title");
2690
- const hasImageField = form.fields && form.fields.some(field => field.id === "mandatory-feature-image");
2691
2762
 
2692
- // Check each field for missing labels and create field-specific errors
2693
- const fieldErrors = {};
2694
- let allFieldsHaveLabels = true;
2695
- if (form.fields) {
2696
- form.fields.forEach(field => {
2697
- 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) {
2698
- if (!field.values.label || field.values.label.trim().length === 0) {
2699
- fieldErrors[field.id] = "Field label is required";
2700
- allFieldsHaveLabels = false;
2701
- }
2702
- }
2703
- });
2704
- }
2705
- return {
2706
- isValid: hasTitleField && hasImageField && allFieldsHaveLabels,
2707
- errors: _objectSpread$6({
2708
- missingTitle: !hasTitleField ? "Title field is required" : null,
2709
- missingImage: !hasImageField ? "Feature image field is required" : null
2710
- }, fieldErrors)
2711
- };
2712
- }
2713
- case "layout":
2714
- {
2715
- const hasLayoutType = form.layout && form.layout.type && form.layout.type.length > 0;
2716
- return {
2717
- isValid: hasLayoutType,
2718
- errors: {
2719
- layoutType: !hasLayoutType ? "Layout type is required" : null
2720
- }
2721
- };
2722
- }
2723
- default:
2724
- return {
2725
- isValid: true,
2726
- errors: {}
2727
- };
2728
- }
2729
- };
2730
- const setCurrentStepAndSave = function (step) {
2731
- let previousStep = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
2763
+ /**
2764
+ * Action creator to set form icon and update menu
2765
+ * Updates both form state and external menu icon
2766
+ *
2767
+ * @param {string} icon - Icon identifier or FontAwesome icon class
2768
+ * @returns {Function} Thunk function for Redux
2769
+ *
2770
+ * @example
2771
+ * dispatch(setIcon('fa-user'));
2772
+ */
2773
+ const setIcon = icon => {
2732
2774
  return dispatch => {
2733
- dispatch(setCurrentStep(step, previousStep));
2775
+ // Update menu icon when icon changes
2776
+ dispatch(updateFeatureBuilderIcon(icon));
2777
+
2778
+ // Dispatch the actual action
2779
+ dispatch({
2780
+ type: actionsTypes.SET_ICON,
2781
+ payload: icon
2782
+ });
2734
2783
  };
2735
2784
  };
2736
2785
 
2737
- var css$d = ".SidebarLayout_module_fullWidthContent__ba777567 {\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.SidebarLayout_module_fullWidthContainer__ba777567 {\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.SidebarLayout_module_contentContainer__ba777567 {\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.SidebarLayout_module_container__ba777567{\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.SidebarLayout_module_contentContainer__ba777567 {\n\t\tpadding: 1.5rem 1.5rem 2.5rem 1.5rem; /* Slightly reduced but still adequate */\n\t}\n\t.SidebarLayout_module_container__ba777567 {\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.SidebarLayout_module_contentContainer__ba777567 {\n\t\tpadding: 1rem 1rem 2rem 1rem; /* Maintain padding on small screens */\n\t}\n\t.SidebarLayout_module_container__ba777567 {\n\t\tpadding: 1rem 1rem 2rem 1rem; /* Legacy container support */\n\t}\n}\n\n/* Enhanced sidebar navigation for progress indication */\n.hub-sideBar {\n\t/* Make sidebar more prominent during creation mode */\n}\n\n.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.sideNav-item.isCurrent i {\n\tcolor: var(--colour-white, #ffffff) !important;\n}\n\n.sideNav-item.isCompleted {\n\tcolor: var(--colour-branding-dark, #364196) !important;\n\tposition: relative;\n}\n\n.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.sideNav-item.isDisabled {\n\topacity: 0.4;\n\tcursor: not-allowed;\n}\n\n/* Enhanced progress section */\n.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.hub-sideBar-section:last-child {\n\tborder-bottom: none;\n}\n";
2738
- var modules_d5b6badf = {"fullWidthContent":"SidebarLayout_module_fullWidthContent__ba777567","fullWidthContainer":"SidebarLayout_module_fullWidthContainer__ba777567","contentContainer":"SidebarLayout_module_contentContainer__ba777567","container":"SidebarLayout_module_container__ba777567"};
2739
- n(css$d,{});
2740
-
2741
2786
  /**
2742
- * Sidebar Layout component for feature builder wizard
2743
- * Provides navigation sidebar with step progression, completion tracking
2744
- * Manages step accessibility based on wizard mode and validation state
2787
+ * Action creator to add a new field to the form
2788
+ * Generates unique ID and supports custom field types
2745
2789
  *
2746
- * @param {Object} props - Component props
2747
- * @param {React.ReactNode} props.children - Child components to render in main content area
2748
- * @param {Object} props.history - React Router history object for navigation
2749
- * @returns {React.ReactElement} Layout component with sidebar navigation
2790
+ * @param {string} [fieldType="text"] - Type of field to add (text, title, description, image, gallery, file, cta, feature-image)
2791
+ * @returns {Object} Redux action object with field ID and type
2750
2792
  *
2751
2793
  * @example
2752
- * <SidebarLayout history={historyObject}>
2753
- * <YourMainContent />
2754
- * </SidebarLayout>
2794
+ * dispatch(addField('text'));
2795
+ * dispatch(addField('image'));
2755
2796
  */
2756
- const SideBarInner = props => {
2757
- const {
2758
- children,
2759
- history
2760
- } = props;
2761
- const dispatch = reactRedux.useDispatch();
2797
+ const addField = function () {
2798
+ let fieldType = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : "text";
2799
+ // Generate a unique ID for the new field
2800
+ const fieldId = "custom-field-".concat(Date.now(), "-").concat(Math.random().toString(36).substr(2, 9));
2801
+ return {
2802
+ type: actionsTypes.ADD_FIELD,
2803
+ payload: {
2804
+ id: fieldId,
2805
+ type: fieldType
2806
+ }
2807
+ };
2808
+ };
2809
+ const deleteField = id => {
2810
+ return {
2811
+ type: actionsTypes.DELETE_FIELD,
2812
+ payload: id
2813
+ };
2814
+ };
2815
+ const updateFieldById = (id, updatedField) => {
2816
+ return {
2817
+ type: actionsTypes.UPDATE_FIELD,
2818
+ payload: {
2819
+ id,
2820
+ updatedField
2821
+ }
2822
+ };
2823
+ };
2762
2824
 
2763
- // Get wizard state
2764
- const mode = reactRedux.useSelector(selectWizardMode);
2765
- const isEditMode = reactRedux.useSelector(selectIsEditMode);
2766
- const isCreateMode = reactRedux.useSelector(selectIsCreateMode);
2767
- const currentStep = reactRedux.useSelector(selectCurrentStep);
2768
- const goTo = url => history.push(url);
2769
- const isSelected = url => {
2770
- return history.location.pathname === url;
2825
+ /**
2826
+ * Action creator to set a description field as the summary field
2827
+ * Ensures only one description field can be marked as summary by
2828
+ * automatically unsetting all other description fields when a new one is selected
2829
+ *
2830
+ * @param {string} fieldId - ID of the description field to set as summary
2831
+ * @returns {Object} Redux action object for summary field selection
2832
+ *
2833
+ * @example
2834
+ * dispatch(setSummaryField('field-description-123'));
2835
+ */
2836
+ const setSummaryField = fieldId => {
2837
+ return {
2838
+ type: actionsTypes.SET_SUMMARY_FIELD,
2839
+ payload: fieldId
2771
2840
  };
2772
-
2773
- // Define step configuration with dynamic URL based on mode
2774
- const getStepUrl = stepKey => {
2775
- // Always use /definition/ in the URL since that's how routes are registered
2776
- return "/feature-builder/definition/".concat(stepKey);
2841
+ };
2842
+ const setLayoutType = layoutType => {
2843
+ return {
2844
+ type: actionsTypes.SET_LAYOUT_TYPE,
2845
+ payload: layoutType
2777
2846
  };
2778
- const steps = [{
2779
- key: "overview",
2780
- text: "Feature Overview",
2781
- icon: "info-circle",
2782
- url: getStepUrl("overview")
2783
- }, {
2784
- key: "fields",
2785
- text: "Configure Fields",
2786
- icon: "edit",
2787
- url: getStepUrl("fields")
2788
- }, {
2789
- key: "layout",
2790
- text: "Choose Layout",
2791
- icon: "columns",
2792
- url: getStepUrl("layout")
2793
- }];
2794
-
2795
- // Build sidebar items based on mode
2796
- const buildSidebarItems = () => {
2797
- return steps.map((step, index) => {
2798
- const isCompleted = selectIsStepComplete(step.key);
2799
- const isAccessible = selectIsStepAccessible(step.key);
2800
-
2801
- // Add step number to text for better clarity
2802
- const stepText = "".concat(index + 1, ". ").concat(step.text);
2803
- const itemProps = {
2804
- type: "navItem",
2805
- text: stepText,
2806
- icon: step.icon,
2807
- selected: isSelected(step.url),
2808
- onclick: isAccessible ? () => {
2809
- goTo(step.url);
2810
- dispatch(goToStep(step.key));
2811
- } : null,
2812
- isFontAwesome: true,
2813
- // Enhanced completion indicator
2814
- completed: isCompleted,
2815
- // Allow navigation to completed steps even in create mode
2816
- disabled: mode === "create" && !isAccessible
2817
- };
2818
- return itemProps;
2819
- });
2847
+ };
2848
+ const submitFormRequest = () => {
2849
+ return {
2850
+ type: actionsTypes.SUBMIT_FORM_REQUEST
2820
2851
  };
2821
-
2822
- // Determine sidebar title - always use "Build Your Feature" now
2823
- const getSidebarTitle = () => {
2824
- return "Build Your Feature";
2852
+ };
2853
+ const submitFormSuccess = () => {
2854
+ return {
2855
+ type: actionsTypes.SUBMIT_FORM_SUCCESS
2825
2856
  };
2826
-
2827
- // Simple help text
2828
- const getHelpText = () => {
2829
- return "Get help with feature builder";
2857
+ };
2858
+ const submitFormFailure = error => {
2859
+ return {
2860
+ type: actionsTypes.SUBMIT_FORM_FAILURE,
2861
+ payload: error
2830
2862
  };
2863
+ };
2864
+ const clearFormSubmissionState = () => {
2865
+ return {
2866
+ type: actionsTypes.CLEAR_FORM_SUBMISSION_STATE
2867
+ };
2868
+ };
2831
2869
 
2832
- // Build sidebar sections - simplified without progress section
2833
- const sidebarSections = [{
2834
- title: getSidebarTitle(),
2835
- items: buildSidebarItems()
2836
- }];
2837
-
2838
- // Add effect to manually attach click handlers since HubSidebar might not use onclick properly
2839
- React.useEffect(() => {
2840
- const attachClickHandlers = () => {
2841
- const stepsWithUrls = [{
2842
- key: "overview",
2843
- url: getStepUrl("overview")
2844
- }, {
2845
- key: "fields",
2846
- url: getStepUrl("fields")
2847
- }, {
2848
- key: "layout",
2849
- url: getStepUrl("layout")
2850
- }];
2851
- stepsWithUrls.forEach(step => {
2852
- 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"));
2853
- if (navItem) {
2854
- // Remove any existing click listeners
2855
- navItem.onclick = null;
2856
-
2857
- // Check if this step is accessible
2858
- const isAccessible = selectIsStepAccessible(step.key)({
2859
- [values.reducerKey]: {
2860
- wizard: {
2861
- mode: mode,
2862
- navigation: {
2863
- currentStep: currentStep
2864
- }
2865
- }
2866
- }
2867
- });
2870
+ /**
2871
+ * Submits the complete feature form to the server
2872
+ * Handles form validation, API submission, and error handling
2873
+ *
2874
+ * @returns {Function} Async thunk function for Redux
2875
+ * @throws {Error} When form validation fails or API submission encounters error
2876
+ */
2877
+ function submitForm() {
2878
+ return async (dispatch, getState) => {
2879
+ const state = getState()[values.reducerKey];
2880
+ const form = state && state.form;
2881
+ if (!form) {
2882
+ dispatch(submitFormFailure(new Error("Form data is missing. Please refresh the page and try again.")));
2883
+ return;
2884
+ }
2885
+ dispatch(submitFormRequest());
2886
+ try {
2887
+ // Get site from auth store
2888
+ const site = getState().auth.site;
2889
+ if (!site) {
2890
+ throw new Error("Authentication error: Site context not found. Please refresh and login again.");
2891
+ }
2868
2892
 
2869
- // Add our click handler only if accessible
2870
- if (isAccessible) {
2871
- navItem.onclick = event => {
2872
- event.preventDefault();
2873
- event.stopPropagation();
2874
- history.push(step.url);
2875
- dispatch(goToStep(step.key));
2876
- };
2893
+ // Use mode from fetch instead of checking definition ID
2894
+ const definitionState = state && state.definition;
2895
+ const mode = definitionState && definitionState.mode; // Use stored mode from fetch
2896
+ const definitionId = definitionState && definitionState.id;
2897
+ if (mode === "edit") {
2898
+ // Always update when in edit mode
2899
+ const updatedDefinition = {
2900
+ id: definitionId,
2901
+ site: site,
2902
+ // Include site from auth store
2903
+ featureDefinition: {
2904
+ // Wrap in expected structure for backend
2905
+ title: form.title,
2906
+ icon: form.icon,
2907
+ displayName: form.displayName,
2908
+ layout: form.layout,
2909
+ fields: form.fields
2877
2910
  }
2878
-
2879
- // Make sure it's styled appropriately
2880
- navItem.style.cursor = isAccessible ? "pointer" : "not-allowed";
2881
- navItem.style.pointerEvents = "auto";
2882
- navItem.style.opacity = isAccessible ? "1" : "0.5";
2911
+ };
2912
+ await featureDefinitionActions.edit(updatedDefinition, site);
2913
+ } else {
2914
+ // Always create when in create mode (or mode is undefined/null)
2915
+ if (!values.featureId || !site) {
2916
+ throw new Error("Authentication error: Missing required context (featureId or site).");
2883
2917
  }
2884
- });
2885
- };
2886
-
2887
- // Initial attachment
2888
- attachClickHandlers();
2889
-
2890
- // Re-attach after a short delay to ensure HubSidebar has rendered
2891
- const timeoutId = setTimeout(attachClickHandlers, 100);
2892
-
2893
- // Also try to re-attach when DOM changes (observe for mutations)
2894
- const observer = new MutationObserver(() => {
2895
- setTimeout(attachClickHandlers, 50);
2896
- });
2897
-
2898
- // Start observing the document body for changes
2899
- observer.observe(document.body, {
2900
- childList: true,
2901
- subtree: true
2902
- });
2903
- return () => {
2904
- clearTimeout(timeoutId);
2905
- observer.disconnect();
2906
- };
2907
- }, [history, dispatch, isEditMode, isCreateMode, currentStep]);
2908
- return /*#__PURE__*/React__default["default"].createElement("div", {
2909
- className: "hub-wrapperContainer"
2910
- }, /*#__PURE__*/React__default["default"].createElement(HubSidebar, {
2911
- sections: sidebarSections,
2912
- helpGuide: {
2913
- text: getHelpText(),
2914
- url: "https://www.plusscommunities.com/user-guide"
2918
+ await featureDefinitionActions.create(values.featureId, site,
2919
+ // Use actual site from auth store
2920
+ {
2921
+ title: form.title,
2922
+ icon: form.icon,
2923
+ displayName: form.displayName,
2924
+ layout: form.layout,
2925
+ fields: form.fields
2926
+ });
2927
+ }
2928
+ dispatch(submitFormSuccess());
2929
+ } catch (err) {
2930
+ // Handle different types of errors
2931
+ let errorToDisplay = err;
2932
+ if (err.response) {
2933
+ // API error (400, 401, 404, 500, etc.)
2934
+ const {
2935
+ status,
2936
+ data
2937
+ } = err.response;
2938
+ if (status === 400 && data && data.error) {
2939
+ errorToDisplay = new Error("Validation error: ".concat(data.error));
2940
+ } else if (status === 401) {
2941
+ errorToDisplay = new Error("You are not authorized to perform this action");
2942
+ } else if (status === 404) {
2943
+ errorToDisplay = new Error("Feature definition not found");
2944
+ } else if (status >= 500) {
2945
+ errorToDisplay = new Error("Server error. Please try again later.");
2946
+ } else {
2947
+ errorToDisplay = new Error(data && data.error || "Request failed with status ".concat(status));
2948
+ }
2949
+ } else if (err.request) {
2950
+ // Network error (no response received)
2951
+ errorToDisplay = new Error("Network error. Please check your connection and try again.");
2952
+ } else if (err.message) {
2953
+ // Other JavaScript errors
2954
+ errorToDisplay = err;
2955
+ } else {
2956
+ // Unknown error
2957
+ errorToDisplay = new Error("An unexpected error occurred while saving");
2958
+ }
2959
+ dispatch(submitFormFailure(errorToDisplay));
2915
2960
  }
2916
- }), /*#__PURE__*/React__default["default"].createElement("div", {
2917
- className: "hub-contentWrapper"
2918
- }, /*#__PURE__*/React__default["default"].createElement("div", {
2919
- className: modules_d5b6badf.fullWidthContainer
2920
- }, /*#__PURE__*/React__default["default"].createElement("div", {
2921
- className: modules_d5b6badf.contentContainer
2922
- }, children))));
2923
- };
2924
- const SidebarLayout = reactRouter.withRouter(SideBarInner);
2925
-
2926
- var css$c = ".Form_module_content__dfb952c8 {\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.Form_module_pageWrapper__dfb952c8 {\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.Form_module_pageWrapper__dfb952c8 {\n\t\tpadding: 3rem 0;\n\t}\n}\n\n/* Form container styling when using CenteredContainer */\n.Form_module_formContainer__dfb952c8 {\n\tbackground-color: var(--bg-white);\n}\n\n/* Section styling */\n.Form_module_section__dfb952c8 {\n\tmargin-bottom: 2rem;\n\t\tpadding: 0 0 2em;\n}\n\n.Form_module_section_NoBorder__dfb952c8 {\n\t\tborder-bottom: none;\n}\n\n.Form_module_subtitle__dfb952c8 {\n\tcolor: var(--text-bluegrey, #6c7a90);\n}\n\n.Form_module_sectionHeader__dfb952c8 {\n\t\tdisplay: flex;\n\t\tjustify-content: space-between;\n}\n\n/* Add Field Button styling */\n.Form_module_addFieldButton__dfb952c8 {\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.Form_module_addFieldButton__dfb952c8: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.Form_module_addFieldButton__dfb952c8:focus {\n\toutline: 2px solid var(--colour-branding-action, #5c90df);\n\toutline-offset: 2px;\n}\n\n.Form_module_addFieldButton__dfb952c8:active {\n\ttransform: translateY(0);\n}\n\n/* Refresh button styling */\n.Form_module_refreshButton__dfb952c8 {\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.Form_module_refreshButton__dfb952c8:hover {\n\tbackground: var(--colour-dusk-hover, #485968);\n}\n\n/* Error message container */\n.Form_module_errorMessageContainer__dfb952c8 {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 0.5rem;\n}\n\n/* Add Field Container - matches field structure */\n.Form_module_addFieldContainer__dfb952c8 {\n\tdisplay: flex;\n\tmargin-top: 32px;\n}\n\n.Form_module_fieldNumberContainer__dfb952c8 {\n\tdisplay: flex;\n\twidth: 40px;\n}\n\n/* Add Field Section */\n.Form_module_addFieldSection__dfb952c8 {\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.Form_module_addFieldSection__dfb952c8 {\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.Form_module_helpSection__dfb952c8 {\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.Form_module_helpTitle__dfb952c8 {\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.Form_module_helpText__dfb952c8 {\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.Form_module_fieldTypeCards__dfb952c8 {\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.Form_module_fieldTypeCards__dfb952c8 {\n\t\tgap: 1.5rem; /* Slightly larger gap on desktop */\n\t}\n}\n\n.Form_module_fieldTypeCard__dfb952c8 {\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.Form_module_fieldTypeCard__dfb952c8: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.Form_module_fieldTypeCard__dfb952c8:focus {\n\toutline: 2px solid var(--text-light);\n\toutline-offset: 2px;\n}\n\n.Form_module_fieldTypeCard__dfb952c8:active {\n\ttransform: scale(0.98);\n\ttransition: transform 0.1s ease;\n}\n\n.Form_module_fieldTypeCardIcon__dfb952c8 {\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.Form_module_fieldTypeCardContent__dfb952c8 {\n\tflex: 1;\n\twidth: 100%;\n\tmin-width: 0; /* Prevent content from overflowing */\n}\n\n.Form_module_fieldTypeCardTitle__dfb952c8 {\n\tmargin: 0 0 0.25rem 0;\n\tfont-weight: 600;\n\tfont-size: 1.4rem; /* Increased for better readability */\n}\n\n.Form_module_fieldTypeCardDescription__dfb952c8 {\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.Form_module_fieldTypeCardUseCase__dfb952c8 {\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.Form_module_addFieldSection__dfb952c8> 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.Form_module_addFieldButtonContainer__dfb952c8 {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: flex-end;\n}\n\n/* Empty State */\n.Form_module_emptyState__dfb952c8 {\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.Form_module_emptyStateText__dfb952c8 {\n\tcolor: var(--text-bluegrey, #6c7a90);\n\tfont-style: italic;\n}\n\n.Form_module_fieldTypeSelector__dfb952c8 {\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.Form_module_addFieldSection__dfb952c8 {\n\t\talign-items: stretch;\n\t}\n\t\n\t.Form_module_fieldTypeSelector__dfb952c8 {\n\t\tmax-width: none;\n\t}\n}\n\n.Form_module_grid__four__dfb952c8 {\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.Form_module_grid__four__dfb952c8 {\n\t\tgrid-template-columns: 1fr;\n\t\tgap: 2rem;\n\t}\n}\n\n/* Navigation styles */\n.Form_module_navigation__dfb952c8 {\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.Form_module_navigation__dfb952c8> :only-child {\n\tmargin-left: auto;\n\tmargin-right: 0;\n}\n\n/* Two button alignment - one left, one right */\n.Form_module_navigation__dfb952c8> :first-child:not(:only-child) {\n\tmargin-right: auto;\n}\n\n.Form_module_navigation__dfb952c8> :last-child:not(:only-child) {\n\tmargin-left: auto;\n}\n\n/* Special case for overview step - align save button to right */\n.Form_module_overviewStep__dfb952c8 .Form_module_navigation__dfb952c8> :only-child {\n\tmargin-left: auto;\n\tmargin-right: 0;\n}\n\n.Form_module_previousButton__dfb952c8 {\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.Form_module_previousButton__dfb952c8:hover {\n\tbackground: #5a6268;\n}\n\n.Form_module_saveButton__dfb952c8 {\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.Form_module_saveButton__dfb952c8: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.Form_module_saveButton__dfb952c8:focus {\n\toutline: 2px solid var(--colour-purple, #6e79c5);\n\toutline-offset: 2px;\n}\n\n.Form_module_saveButton__dfb952c8:disabled {\n\tbackground: #6c757d;\n\tcursor: not-allowed;\n\topacity: 0.65;\n\ttransform: none;\n\tbox-shadow: none;\n}\n\n.Form_module_nextButton__dfb952c8 {\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.Form_module_nextButton__dfb952c8:hover:not(:disabled) {\n\ttransform: translateY(-2px);\n\tbox-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);\n}\n\n.Form_module_nextButton__dfb952c8:disabled {\n\tbackground: #6c757d;\n\tcursor: not-allowed;\n\topacity: 0.65;\n\ttransform: none;\n\tbox-shadow: none;\n}\n\n\n\n.Form_module_errorMessage__dfb952c8 {\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.Form_module_layoutOption__dfb952c8 {\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.Form_module_layoutOptionImage__dfb952c8 {\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.Form_module_layoutOption__dfb952c8.Form_module_selected__dfb952c8 .Form_module_layoutOptionImage__dfb952c8 {\n\tborder-color: var(--border-line-grey, #dbddf1);\n\tbox-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n}\n\n.Form_module_layoutOptionImg__dfb952c8 {\n\twidth: 100%;\n\theight: 100%;\n\tobject-fit: cover;\n}\n\n.Form_module_layoutOptionContent__dfb952c8 {\n\tmax-width: 250px;\n}\n\n.Form_module_layoutOptionTitle__dfb952c8 {\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.Form_module_layoutOptionDescription__dfb952c8 {\n\tfont-size: 1.2rem;\n\tmargin: 0;\n\tline-height: 1.3;\n\tcolor: #6c757d;\n}\n\n.Form_module_layoutOption__dfb952c8.Form_module_hasError__dfb952c8 .Form_module_layoutOptionImage__dfb952c8 {\n\tborder-color: var(--colour-red, #dc3545);\n}\n\n/* Field error styles */\n.Form_module_fieldError__dfb952c8 {\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.Form_module_errorIcon__dfb952c8 {\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 Form_module_errorShake__dfb952c8 {\n\t0%, 100% { transform: translateX(0); }\n\t25% { transform: translateX(-3px); }\n\t75% { transform: translateX(3px); }\n}\n\n@keyframes Form_module_errorPulse__dfb952c8 {\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.Form_module_successMessage__dfb952c8 {\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.Form_module_createMode__dfb952c8 {\n\tcolor: var(--colour-purple, #6e79c5);\n}\n\n.Form_module_editMode__dfb952c8 {\n\tcolor: var(--colour-branding-dark, #364196);\n}\n\n/* Responsive Design */\n@media (max-width: 768px) {\n\t.Form_module_content__dfb952c8 {\n\t\tpadding: 1.5rem 0 2.5rem 0; /* Maintain proper padding on mobile */\n\t}\n\t\n\t.Form_module_section__dfb952c8 {\n\t\tmargin-bottom: 1.5rem;\n\t}\n\t\n\t/* Mobile: vertical layout for field selector and button */\n\t.Form_module_addFieldSection__dfb952c8 {\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.Form_module_addFieldSection__dfb952c8 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.Form_module_addFieldButtonContainer__dfb952c8 {\n\t\talign-items: stretch;\n\t\tjustify-content: stretch;\n\t\tmin-height: auto;\n\t}\n\t\n\t.Form_module_navigation__dfb952c8 {\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.Form_module_navigation__dfb952c8> :only-child {\n\t\tmargin-left: auto;\n\t\tmargin-right: 0;\n\t}\n\t\n\t.Form_module_navigation__dfb952c8> :first-child:not(:only-child),\n\t.Form_module_navigation__dfb952c8> :last-child:not(:only-child) {\n\t\tmargin-left: 0;\n\t\tmargin-right: 0;\n\t}\n\t\n\t.Form_module_previousButton__dfb952c8,\n\t.Form_module_nextButton__dfb952c8,\n\t.Form_module_saveButton__dfb952c8 {\n\t\twidth: 100%;\n\t\tpadding: 1rem;\n\t}\n}\n\n@media (max-width: 480px) {\n\t.Form_module_content__dfb952c8 {\n\t\tpadding: 1rem 0 2rem 0; /* Still maintain padding on small screens */\n\t}\n\t\n\t.Form_module_section__dfb952c8 {\n\t\tmargin-bottom: 1rem;\n\t}\n\t\n\t.Form_module_navigation__dfb952c8 {\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.Form_module_navigation__dfb952c8> :only-child {\n\t\tmargin-left: auto;\n\t\tmargin-right: 0;\n\t}\n\t\n\t.Form_module_navigation__dfb952c8> :first-child:not(:only-child),\n\t.Form_module_navigation__dfb952c8> :last-child:not(:only-child) {\n\t\tmargin-left: 0;\n\t\tmargin-right: 0;\n\t}\n\t\n\t.Form_module_errorMessageContainer__dfb952c8 {\n\t\tflex-direction: column;\n\t\talign-items: flex-start;\n\t\tgap: 0.75rem;\n\t}\n\t\n\t.Form_module_refreshButton__dfb952c8 {\n\t\talign-self: flex-end;\n\t}\n}\n\n/* Full width content when sidebar is hidden */\n.hub-contentWrapper.fullWidthContent {\n\tmax-width: 1000px;\n\tmargin: 0 auto;\n\tpadding: 2rem;\n}\n\n.Form_module_hubContentWrapper_Col__dfb952c8 {\n flex-flow: column;\n}\n\n/* Validation error message - displayed at top of form */\n.Form_module_validationErrorMessage__dfb952c8 {\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.Form_module_validationErrorMessage__dfb952c8::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.Form_module_formHeader__dfb952c8 {\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.Form_module_fieldsLoadingContainer__dfb952c8 {\n\ttext-align: center;\n\tpadding: 60px 20px;\n}\n\n/* Help note styling */\n.Form_module_helpNote__dfb952c8 {\n\tfont-size: 13px;\n\tcolor: #6c757d;\n}\n\n/* Loading container for overview */\n.Form_module_overviewLoadingContainer__dfb952c8 {\n\ttext-align: center;\n\tpadding: 60px 20px;\n}\n\n/* Form layout header */\n.Form_module_formLayoutHeader__dfb952c8 {\n\tdisplay: flex;\n\tjustify-content: space-between;\n\talign-items: flex-start;\n\tmargin-bottom: 16px;\n}\n\n/* Section margins */\n.Form_module_gridIconSection__dfb952c8 {\n\tmargin-bottom: 3rem;\n}\n\n.Form_module_layoutSection__dfb952c8 {\n\tmargin-bottom: 2rem;\n}\n\n/* Grid icon loading state */\n.Form_module_gridIconLoading__dfb952c8 {\n\tgrid-column: 1 / -1;\n}\n\n/* Hide upload button in grid icon section */\n.Form_module_gridIconSection__dfb952c8 .iconLoader__buttonOverlay button:first-child {\n\tdisplay: none !important;\n}\n";
2927
- var modules_cd65a764 = {"content":"Form_module_content__dfb952c8","pageWrapper":"Form_module_pageWrapper__dfb952c8","formContainer":"Form_module_formContainer__dfb952c8","section":"Form_module_section__dfb952c8","section--no-border":"Form_module_section_NoBorder__dfb952c8","subtitle":"Form_module_subtitle__dfb952c8","sectionHeader":"Form_module_sectionHeader__dfb952c8","addFieldButton":"Form_module_addFieldButton__dfb952c8","refreshButton":"Form_module_refreshButton__dfb952c8","errorMessageContainer":"Form_module_errorMessageContainer__dfb952c8","addFieldContainer":"Form_module_addFieldContainer__dfb952c8","fieldNumberContainer":"Form_module_fieldNumberContainer__dfb952c8","addFieldSection":"Form_module_addFieldSection__dfb952c8","helpSection":"Form_module_helpSection__dfb952c8","helpTitle":"Form_module_helpTitle__dfb952c8","helpText":"Form_module_helpText__dfb952c8","fieldTypeCards":"Form_module_fieldTypeCards__dfb952c8","fieldTypeCard":"Form_module_fieldTypeCard__dfb952c8","fieldTypeCardIcon":"Form_module_fieldTypeCardIcon__dfb952c8","fieldTypeCardContent":"Form_module_fieldTypeCardContent__dfb952c8","fieldTypeCardTitle":"Form_module_fieldTypeCardTitle__dfb952c8","fieldTypeCardDescription":"Form_module_fieldTypeCardDescription__dfb952c8","fieldTypeCardUseCase":"Form_module_fieldTypeCardUseCase__dfb952c8","addFieldButtonContainer":"Form_module_addFieldButtonContainer__dfb952c8","emptyState":"Form_module_emptyState__dfb952c8","emptyStateText":"Form_module_emptyStateText__dfb952c8","fieldTypeSelector":"Form_module_fieldTypeSelector__dfb952c8","grid__four":"Form_module_grid__four__dfb952c8","navigation":"Form_module_navigation__dfb952c8","overviewStep":"Form_module_overviewStep__dfb952c8","previousButton":"Form_module_previousButton__dfb952c8","saveButton":"Form_module_saveButton__dfb952c8","nextButton":"Form_module_nextButton__dfb952c8","errorMessage":"Form_module_errorMessage__dfb952c8","layoutOption":"Form_module_layoutOption__dfb952c8","layoutOptionImage":"Form_module_layoutOptionImage__dfb952c8","selected":"Form_module_selected__dfb952c8","layoutOptionImg":"Form_module_layoutOptionImg__dfb952c8","layoutOptionContent":"Form_module_layoutOptionContent__dfb952c8","layoutOptionTitle":"Form_module_layoutOptionTitle__dfb952c8","layoutOptionDescription":"Form_module_layoutOptionDescription__dfb952c8","hasError":"Form_module_hasError__dfb952c8","fieldError":"Form_module_fieldError__dfb952c8","errorIcon":"Form_module_errorIcon__dfb952c8","successMessage":"Form_module_successMessage__dfb952c8","createMode":"Form_module_createMode__dfb952c8","editMode":"Form_module_editMode__dfb952c8","hub-contentWrapper--col":"Form_module_hubContentWrapper_Col__dfb952c8","validationErrorMessage":"Form_module_validationErrorMessage__dfb952c8","formHeader":"Form_module_formHeader__dfb952c8","fieldsLoadingContainer":"Form_module_fieldsLoadingContainer__dfb952c8","helpNote":"Form_module_helpNote__dfb952c8","overviewLoadingContainer":"Form_module_overviewLoadingContainer__dfb952c8","formLayoutHeader":"Form_module_formLayoutHeader__dfb952c8","gridIconSection":"Form_module_gridIconSection__dfb952c8","layoutSection":"Form_module_layoutSection__dfb952c8","gridIconLoading":"Form_module_gridIconLoading__dfb952c8","errorShake":"Form_module_errorShake__dfb952c8","errorPulse":"Form_module_errorPulse__dfb952c8"};
2928
- n(css$c,{});
2961
+ };
2962
+ }
2929
2963
 
2930
2964
  /*
2931
2965
  * Icon categories and definitions for the feature builder
@@ -3249,6 +3283,9 @@ const FormOverviewStepInner = props => {
3249
3283
 
3250
3284
  // Clear submission state after showing toast (only for non-edit cases)
3251
3285
  if (!isEditMode) {
3286
+ const {
3287
+ clearFormSubmissionState
3288
+ } = require("../actions/formActions");
3252
3289
  dispatch(clearFormSubmissionState());
3253
3290
  }
3254
3291
  }
@@ -3864,21 +3901,22 @@ const FileField = props => {
3864
3901
  });
3865
3902
  };
3866
3903
 
3867
- const FETCH_FEATURES_REQUEST = "FETCH_FEATURES_REQUEST";
3868
- const FETCH_FEATURES_SUCCESS = "FETCH_FEATURES_SUCCESS";
3869
- const FETCH_FEATURES_FAILURE = "FETCH_FEATURES_FAILURE";
3870
- const FEATURE_CREATE_REQUEST = "FEATURE_CREATE_REQUEST";
3871
- const FEATURE_CREATE_SUCCESS = "FEATURE_CREATE_SUCCESS";
3872
- const FEATURE_CREATE_FAILURE = "FEATURE_CREATE_FAILURE";
3873
- const FEATURE_EDIT_REQUEST = "FEATURE_EDIT_REQUEST";
3874
- const FEATURE_EDIT_SUCCESS = "FEATURE_EDIT_SUCCESS";
3875
- const FEATURE_EDIT_FAILURE = "FEATURE_EDIT_FAILURE";
3876
- const FEATURE_DELETE_REQUEST = "FEATURE_DELETE_REQUEST";
3877
- const FEATURE_DELETE_FAILURE = "FEATURE_DELETE_FAILURE";
3904
+ const REDUCER_PREFIX$1 = values.reducerKey.toUpperCase();
3905
+ const FETCH_FEATURES_REQUEST = "".concat(REDUCER_PREFIX$1, "_FETCH_FEATURES_REQUEST");
3906
+ const FETCH_FEATURES_SUCCESS = "".concat(REDUCER_PREFIX$1, "_FETCH_FEATURES_SUCCESS");
3907
+ const FETCH_FEATURES_FAILURE = "".concat(REDUCER_PREFIX$1, "_FETCH_FEATURES_FAILURE");
3908
+ const FEATURE_CREATE_REQUEST = "".concat(REDUCER_PREFIX$1, "_FEATURE_CREATE_REQUEST");
3909
+ const FEATURE_CREATE_SUCCESS = "".concat(REDUCER_PREFIX$1, "_FEATURE_CREATE_SUCCESS");
3910
+ const FEATURE_CREATE_FAILURE = "".concat(REDUCER_PREFIX$1, "_FEATURE_CREATE_FAILURE");
3911
+ const FEATURE_EDIT_REQUEST = "".concat(REDUCER_PREFIX$1, "_FEATURE_EDIT_REQUEST");
3912
+ const FEATURE_EDIT_SUCCESS = "".concat(REDUCER_PREFIX$1, "_FEATURE_EDIT_SUCCESS");
3913
+ const FEATURE_EDIT_FAILURE = "".concat(REDUCER_PREFIX$1, "_FEATURE_EDIT_FAILURE");
3914
+ const FEATURE_DELETE_REQUEST = "".concat(REDUCER_PREFIX$1, "_FEATURE_DELETE_REQUEST");
3915
+ const FEATURE_DELETE_FAILURE = "".concat(REDUCER_PREFIX$1, "_FEATURE_DELETE_FAILURE");
3878
3916
 
3879
3917
  // Wizard synchronization action types
3880
- const SYNC_WIZARD_MODE_FROM_DEFINITION = "SYNC_WIZARD_MODE_FROM_DEFINITION";
3881
- const SET_WIZARD_MODE = "SET_WIZARD_MODE";
3918
+ const SYNC_WIZARD_MODE_FROM_DEFINITION = "".concat(REDUCER_PREFIX$1, "_SYNC_WIZARD_MODE_FROM_DEFINITION");
3919
+ const SET_WIZARD_MODE = "".concat(REDUCER_PREFIX$1, "_SET_WIZARD_MODE");
3882
3920
  function fetchFeaturesRequest() {
3883
3921
  return {
3884
3922
  type: FETCH_FEATURES_REQUEST
@@ -3990,9 +4028,12 @@ const useFeatureDefinitionLoader = function () {
3990
4028
  const definition = reactRedux.useSelector(selectDefinition);
3991
4029
  const isLoading = reactRedux.useSelector(selectDefinitionIsLoading);
3992
4030
  const error = reactRedux.useSelector(selectDefinitionError);
4031
+ const mode = reactRedux.useSelector(selectDefinitionMode);
3993
4032
 
3994
4033
  // Determine if we need to load the definition
3995
- const shouldLoadDefinition = !definition || forceReload;
4034
+ // Only load if: definition is missing AND mode is not yet set (meaning we haven't tried to fetch yet)
4035
+ // OR forceReload is true
4036
+ const shouldLoadDefinition = !definition && !mode || forceReload;
3996
4037
 
3997
4038
  // Function to manually reload definition
3998
4039
  const reloadDefinition = () => {
@@ -5058,28 +5099,29 @@ const listingActions = {
5058
5099
  // Legacy exports have been removed as Redux actions now use the new listingActions object
5059
5100
 
5060
5101
  // Action Types
5061
- const FETCH_LISTING_REQUEST = "FETCH_LISTING_REQUEST";
5062
- const FETCH_LISTING_SUCCESS = "FETCH_LISTING_SUCCESS";
5063
- const FETCH_LISTING_FAILURE = "FETCH_LISTING_FAILURE";
5064
- const FETCH_LISTING_SILENT_SUCCESS = "FETCH_LISTING_SILENT_SUCCESS";
5065
- const FETCH_LISTING_SILENT_FAILURE = "FETCH_LISTING_SILENT_FAILURE";
5066
- const REORDER_LISTING_SUCCESS = "REORDER_LISTING_SUCCESS";
5067
- const DELETE_LISTING_REQUEST = "DELETE_LISTING_REQUEST";
5068
- const DELETE_LISTING_SUCCESS = "DELETE_LISTING_SUCCESS";
5069
- const DELETE_LISTING_FAILURE = "DELETE_LISTING_FAILURE";
5070
- const UNDELETE_LISTING_REQUEST = "UNDELETE_LISTING_REQUEST";
5071
- const UNDELETE_LISTING_SUCCESS = "UNDELETE_LISTING_SUCCESS";
5072
- const UNDELETE_LISTING_FAILURE = "UNDELETE_LISTING_FAILURE";
5073
- const CREATE_LISTING_REQUEST = "CREATE_LISTING_REQUEST";
5074
- const CREATE_LISTING_SUCCESS = "CREATE_LISTING_SUCCESS";
5075
- const CREATE_LISTING_FAILURE = "CREATE_LISTING_FAILURE";
5076
- const EDIT_LISTING_REQUEST = "EDIT_LISTING_REQUEST";
5077
- const EDIT_LISTING_SUCCESS = "EDIT_LISTING_SUCCESS";
5078
- const EDIT_LISTING_FAILURE = "EDIT_LISTING_FAILURE";
5079
- const TOGGLE_LISTING_SUCCESS = "TOGGLE_LISTING_SUCCESS";
5080
- const TOGGLE_LISTING_FAILURE = "TOGGLE_LISTING_FAILURE";
5081
- const SET_SORT_BY = "SET_SORT_BY";
5082
- const SET_SHOW_DELETED = "SET_SHOW_DELETED";
5102
+ const REDUCER_PREFIX = values.reducerKey.toUpperCase();
5103
+ const FETCH_LISTING_REQUEST = "".concat(REDUCER_PREFIX, "_FETCH_LISTING_REQUEST");
5104
+ const FETCH_LISTING_SUCCESS = "".concat(REDUCER_PREFIX, "_FETCH_LISTING_SUCCESS");
5105
+ const FETCH_LISTING_FAILURE = "".concat(REDUCER_PREFIX, "_FETCH_LISTING_FAILURE");
5106
+ const FETCH_LISTING_SILENT_SUCCESS = "".concat(REDUCER_PREFIX, "_FETCH_LISTING_SILENT_SUCCESS");
5107
+ const FETCH_LISTING_SILENT_FAILURE = "".concat(REDUCER_PREFIX, "_FETCH_LISTING_SILENT_FAILURE");
5108
+ const REORDER_LISTING_SUCCESS = "".concat(REDUCER_PREFIX, "_REORDER_LISTING_SUCCESS");
5109
+ const DELETE_LISTING_REQUEST = "".concat(REDUCER_PREFIX, "_DELETE_LISTING_REQUEST");
5110
+ const DELETE_LISTING_SUCCESS = "".concat(REDUCER_PREFIX, "_DELETE_LISTING_SUCCESS");
5111
+ const DELETE_LISTING_FAILURE = "".concat(REDUCER_PREFIX, "_DELETE_LISTING_FAILURE");
5112
+ const UNDELETE_LISTING_REQUEST = "".concat(REDUCER_PREFIX, "_UNDELETE_LISTING_REQUEST");
5113
+ const UNDELETE_LISTING_SUCCESS = "".concat(REDUCER_PREFIX, "_UNDELETE_LISTING_SUCCESS");
5114
+ const UNDELETE_LISTING_FAILURE = "".concat(REDUCER_PREFIX, "_UNDELETE_LISTING_FAILURE");
5115
+ const CREATE_LISTING_REQUEST = "".concat(REDUCER_PREFIX, "_CREATE_LISTING_REQUEST");
5116
+ const CREATE_LISTING_SUCCESS = "".concat(REDUCER_PREFIX, "_CREATE_LISTING_SUCCESS");
5117
+ const CREATE_LISTING_FAILURE = "".concat(REDUCER_PREFIX, "_CREATE_LISTING_FAILURE");
5118
+ const EDIT_LISTING_REQUEST = "".concat(REDUCER_PREFIX, "_EDIT_LISTING_REQUEST");
5119
+ const EDIT_LISTING_SUCCESS = "".concat(REDUCER_PREFIX, "_EDIT_LISTING_SUCCESS");
5120
+ const EDIT_LISTING_FAILURE = "".concat(REDUCER_PREFIX, "_EDIT_LISTING_FAILURE");
5121
+ const TOGGLE_LISTING_SUCCESS = "".concat(REDUCER_PREFIX, "_TOGGLE_LISTING_SUCCESS");
5122
+ const TOGGLE_LISTING_FAILURE = "".concat(REDUCER_PREFIX, "_TOGGLE_LISTING_FAILURE");
5123
+ const SET_SORT_BY = "".concat(REDUCER_PREFIX, "_SET_SORT_BY");
5124
+ const SET_SHOW_DELETED = "".concat(REDUCER_PREFIX, "_SET_SHOW_DELETED");
5083
5125
  const fetchListingRequest = () => ({
5084
5126
  type: FETCH_LISTING_REQUEST
5085
5127
  });
@@ -7718,12 +7760,12 @@ const Reducers = (() => {
7718
7760
  })();
7719
7761
  const Screens = (() => {
7720
7762
  const screens = {};
7721
- screens[values.screenFormOverviewStep] = FormOverviewStep;
7722
- screens[values.screenFormFieldsStep] = FormFieldsStep;
7723
- screens[values.screenFormLayoutStep] = FormLayoutStep;
7724
- screens[values.screenListingScreen] = ListingScreen$1;
7725
- screens[values.pageCreateListing] = CreateListingPage;
7726
- screens[values.pageEditListing] = EditListingPage;
7763
+ screens["FormOverviewStep"] = FormOverviewStep;
7764
+ screens["FormFieldsStep"] = FormFieldsStep;
7765
+ screens["FormLayoutStep"] = FormLayoutStep;
7766
+ screens["ListingScreen"] = ListingScreen$1;
7767
+ screens["CreateListingPage"] = CreateListingPage;
7768
+ screens["EditListingPage"] = EditListingPage;
7727
7769
  return screens;
7728
7770
  })();
7729
7771