@plusscommunities/pluss-feature-builder-web-b 1.0.2-beta.5 → 1.0.2-beta.6
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 +825 -823
- package/package.json +1 -1
- package/src/actions/featureDefinitionsIndex.js +2 -3
- package/src/actions/formActions.js +148 -158
- package/src/actions/listingActions.js +1 -1
- package/src/actions/wizardActions.js +169 -184
- package/src/components/BaseFieldConfig.jsx +234 -234
- package/src/components/IconLoader.jsx +138 -138
- package/src/components/IconSelector.jsx +0 -1
- package/src/components/ListingEditor.jsx +0 -1
- package/src/components/SidebarLayout.jsx +4 -1
- package/src/components/index.js +0 -2
- package/src/components/listing/GalleryDisplay.jsx +0 -1
- package/src/components/listing/ListingGalleryInput.jsx +4 -1
- package/src/reducers/featureBuilderReducer.js +14 -25
- package/src/screens/FormLayoutStep.jsx +10 -13
- package/src/screens/FormOverviewStep.jsx +351 -358
- package/src/screens/ListingScreen.jsx +0 -1
- package/src/selectors/featureBuilderSelectors.js +49 -44
package/dist/index.cjs.js
CHANGED
|
@@ -116,7 +116,7 @@ const selectFormState = state => getFeatureBuilderState(state).form;
|
|
|
116
116
|
*/
|
|
117
117
|
const selectFormTitle = state => {
|
|
118
118
|
const formState = selectFormState(state);
|
|
119
|
-
return formState
|
|
119
|
+
return formState === null || formState === void 0 ? void 0 : formState.title;
|
|
120
120
|
};
|
|
121
121
|
|
|
122
122
|
/**
|
|
@@ -127,7 +127,7 @@ const selectFormTitle = state => {
|
|
|
127
127
|
*/
|
|
128
128
|
const selectFormIcon = state => {
|
|
129
129
|
const formState = selectFormState(state);
|
|
130
|
-
return formState
|
|
130
|
+
return formState === null || formState === void 0 ? void 0 : formState.icon;
|
|
131
131
|
};
|
|
132
132
|
|
|
133
133
|
/**
|
|
@@ -138,7 +138,7 @@ const selectFormIcon = state => {
|
|
|
138
138
|
*/
|
|
139
139
|
const selectFormDisplayName = state => {
|
|
140
140
|
const formState = selectFormState(state);
|
|
141
|
-
return formState
|
|
141
|
+
return formState === null || formState === void 0 ? void 0 : formState.displayName;
|
|
142
142
|
};
|
|
143
143
|
|
|
144
144
|
/**
|
|
@@ -149,7 +149,7 @@ const selectFormDisplayName = state => {
|
|
|
149
149
|
*/
|
|
150
150
|
const selectFormLayout = state => {
|
|
151
151
|
const formState = selectFormState(state);
|
|
152
|
-
return formState
|
|
152
|
+
return formState === null || formState === void 0 ? void 0 : formState.layout;
|
|
153
153
|
};
|
|
154
154
|
|
|
155
155
|
/**
|
|
@@ -160,24 +160,24 @@ const selectFormLayout = state => {
|
|
|
160
160
|
*/
|
|
161
161
|
const selectFormFields = state => {
|
|
162
162
|
const formState = selectFormState(state);
|
|
163
|
-
return formState
|
|
163
|
+
return (formState === null || formState === void 0 ? void 0 : formState.fields) || [];
|
|
164
164
|
};
|
|
165
165
|
const selectFormField = fieldId => state => selectFormFields(state).find(field => field.id === fieldId);
|
|
166
166
|
const selectFormIsInitial = state => {
|
|
167
167
|
const formState = selectFormState(state);
|
|
168
|
-
return formState
|
|
168
|
+
return formState === null || formState === void 0 ? void 0 : formState._isInitial;
|
|
169
169
|
};
|
|
170
170
|
const selectFormIsSubmitting = state => {
|
|
171
171
|
const formState = selectFormState(state);
|
|
172
|
-
return formState
|
|
172
|
+
return formState === null || formState === void 0 ? void 0 : formState._isSubmitting;
|
|
173
173
|
};
|
|
174
174
|
const selectFormSubmitError = state => {
|
|
175
175
|
const formState = selectFormState(state);
|
|
176
|
-
return formState
|
|
176
|
+
return formState === null || formState === void 0 ? void 0 : formState._submitError;
|
|
177
177
|
};
|
|
178
178
|
const selectFormSubmitSuccess = state => {
|
|
179
179
|
const formState = selectFormState(state);
|
|
180
|
-
return formState
|
|
180
|
+
return formState === null || formState === void 0 ? void 0 : formState._submitSuccess;
|
|
181
181
|
};
|
|
182
182
|
|
|
183
183
|
// ============ DEFINITION SELECTORS ============
|
|
@@ -185,50 +185,59 @@ const selectFormSubmitSuccess = state => {
|
|
|
185
185
|
const selectDefinitionState = state => getFeatureBuilderState(state).definition;
|
|
186
186
|
const selectDefinition = state => {
|
|
187
187
|
const definitionState = selectDefinitionState(state);
|
|
188
|
-
return definitionState
|
|
188
|
+
return definitionState === null || definitionState === void 0 ? void 0 : definitionState.definition;
|
|
189
189
|
};
|
|
190
190
|
const selectDefinitionId = state => {
|
|
191
191
|
const definitionState = selectDefinitionState(state);
|
|
192
|
-
return definitionState
|
|
192
|
+
return definitionState === null || definitionState === void 0 ? void 0 : definitionState.id;
|
|
193
193
|
};
|
|
194
194
|
const selectDefinitionIsLoading = state => {
|
|
195
195
|
const definitionState = selectDefinitionState(state);
|
|
196
|
-
return definitionState
|
|
196
|
+
return definitionState === null || definitionState === void 0 ? void 0 : definitionState.isLoading;
|
|
197
197
|
};
|
|
198
198
|
const selectDefinitionError = state => {
|
|
199
199
|
const definitionState = selectDefinitionState(state);
|
|
200
|
-
return definitionState
|
|
200
|
+
return definitionState === null || definitionState === void 0 ? void 0 : definitionState.error;
|
|
201
201
|
};
|
|
202
202
|
const selectDefinitionMode = state => {
|
|
203
203
|
const definitionState = selectDefinitionState(state);
|
|
204
|
-
return definitionState
|
|
204
|
+
return definitionState === null || definitionState === void 0 ? void 0 : definitionState.mode;
|
|
205
205
|
};
|
|
206
206
|
|
|
207
|
-
// Check if we have a definition loaded
|
|
208
|
-
|
|
207
|
+
// Check if we have a definition loaded or if we've determined the mode
|
|
208
|
+
// This is used by hiddenFromFeaturePicker to determine if feature should be shown
|
|
209
|
+
// When mode is "create", definition is null but we've determined the feature doesn't exist
|
|
210
|
+
// When mode is "edit", definition exists and we've determined the feature exists
|
|
211
|
+
const selectHasDefinition = state => {
|
|
212
|
+
const definition = selectDefinition(state);
|
|
213
|
+
const mode = selectDefinitionMode(state);
|
|
214
|
+
// Consider feature "loaded" if definition exists OR mode is set
|
|
215
|
+
// Mode being set means we've successfully fetched and determined the state
|
|
216
|
+
return !!definition || !!mode;
|
|
217
|
+
};
|
|
209
218
|
|
|
210
219
|
// ============ LISTINGS SELECTORS ============
|
|
211
220
|
|
|
212
221
|
const selectListingsState = state => getFeatureBuilderState(state).listings;
|
|
213
222
|
const selectListings = state => {
|
|
214
223
|
const listingsState = selectListingsState(state);
|
|
215
|
-
return listingsState
|
|
224
|
+
return (listingsState === null || listingsState === void 0 ? void 0 : listingsState.listings) || [];
|
|
216
225
|
};
|
|
217
226
|
const selectListingsIsLoading = state => {
|
|
218
227
|
const listingsState = selectListingsState(state);
|
|
219
|
-
return listingsState
|
|
228
|
+
return listingsState === null || listingsState === void 0 ? void 0 : listingsState.isLoading;
|
|
220
229
|
};
|
|
221
230
|
const selectListingsError = state => {
|
|
222
231
|
const listingsState = selectListingsState(state);
|
|
223
|
-
return listingsState
|
|
232
|
+
return listingsState === null || listingsState === void 0 ? void 0 : listingsState.error;
|
|
224
233
|
};
|
|
225
234
|
const selectListingsIsRestoring = state => {
|
|
226
235
|
const listingsState = selectListingsState(state);
|
|
227
|
-
return listingsState
|
|
236
|
+
return listingsState === null || listingsState === void 0 ? void 0 : listingsState.isRestoring;
|
|
228
237
|
};
|
|
229
238
|
const selectListingsIsInitiallyLoaded = state => {
|
|
230
239
|
const listingsState = selectListingsState(state);
|
|
231
|
-
return listingsState
|
|
240
|
+
return listingsState === null || listingsState === void 0 ? void 0 : listingsState.isInitiallyLoaded;
|
|
232
241
|
};
|
|
233
242
|
|
|
234
243
|
// Get a specific listing by ID
|
|
@@ -261,7 +270,7 @@ const selectDeletedListings = state => {
|
|
|
261
270
|
const selectWizardState = state => getFeatureBuilderState(state).wizard;
|
|
262
271
|
const selectWizardMode = state => {
|
|
263
272
|
const wizardState = selectWizardState(state);
|
|
264
|
-
return wizardState
|
|
273
|
+
return wizardState === null || wizardState === void 0 ? void 0 : wizardState.mode;
|
|
265
274
|
};
|
|
266
275
|
|
|
267
276
|
// Mode detection selectors - wizard mode is the SINGLE SOURCE OF TRUTH for create/edit mode
|
|
@@ -270,35 +279,35 @@ const selectIsCreateMode = state => selectWizardMode(state) === "create";
|
|
|
270
279
|
const selectIsEditMode = state => selectWizardMode(state) === "edit";
|
|
271
280
|
const selectNavigationState = state => {
|
|
272
281
|
const wizardState = selectWizardState(state);
|
|
273
|
-
return wizardState
|
|
282
|
+
return wizardState === null || wizardState === void 0 ? void 0 : wizardState.navigation;
|
|
274
283
|
};
|
|
275
284
|
const selectCurrentStep = state => {
|
|
276
285
|
const navigationState = selectNavigationState(state);
|
|
277
|
-
return navigationState
|
|
286
|
+
return navigationState === null || navigationState === void 0 ? void 0 : navigationState.currentStep;
|
|
278
287
|
};
|
|
279
288
|
const selectStepValidation = state => {
|
|
280
289
|
const wizardState = selectWizardState(state);
|
|
281
|
-
return wizardState
|
|
290
|
+
return wizardState === null || wizardState === void 0 ? void 0 : wizardState.stepValidation;
|
|
282
291
|
};
|
|
283
292
|
const selectStepValidationState = step => state => {
|
|
284
293
|
const stepValidation = selectStepValidation(state);
|
|
285
|
-
return stepValidation
|
|
294
|
+
return stepValidation === null || stepValidation === void 0 ? void 0 : stepValidation[step];
|
|
286
295
|
};
|
|
287
296
|
const selectIsStepValid = step => state => {
|
|
288
297
|
const stepValidationState = selectStepValidationState(step)(state);
|
|
289
|
-
return stepValidationState
|
|
298
|
+
return stepValidationState === null || stepValidationState === void 0 ? void 0 : stepValidationState.isValid;
|
|
290
299
|
};
|
|
291
300
|
const selectStepErrors = step => state => {
|
|
292
301
|
const stepValidationState = selectStepValidationState(step)(state);
|
|
293
|
-
return stepValidationState ? stepValidationState.errors
|
|
302
|
+
return (stepValidationState === null || stepValidationState === void 0 ? void 0 : stepValidationState.errors) || {};
|
|
294
303
|
};
|
|
295
304
|
const selectStepCompletion = state => {
|
|
296
305
|
const wizardState = selectWizardState(state);
|
|
297
|
-
return wizardState
|
|
306
|
+
return wizardState === null || wizardState === void 0 ? void 0 : wizardState.stepCompletion;
|
|
298
307
|
};
|
|
299
308
|
const selectIsStepComplete = step => state => {
|
|
300
309
|
const stepCompletion = selectStepCompletion(state);
|
|
301
|
-
return stepCompletion
|
|
310
|
+
return stepCompletion === null || stepCompletion === void 0 ? void 0 : stepCompletion[step];
|
|
302
311
|
};
|
|
303
312
|
|
|
304
313
|
// Check if current step should be accessible in current mode
|
|
@@ -328,14 +337,16 @@ const selectIsStepAccessible = step => state => {
|
|
|
328
337
|
|
|
329
338
|
// Get current sort method
|
|
330
339
|
const selectSortBy = state => {
|
|
340
|
+
var _listingsState$sortBy;
|
|
331
341
|
const listingsState = selectListingsState(state);
|
|
332
|
-
return listingsState
|
|
342
|
+
return (_listingsState$sortBy = listingsState === null || listingsState === void 0 ? void 0 : listingsState.sortBy) !== null && _listingsState$sortBy !== void 0 ? _listingsState$sortBy : "newest";
|
|
333
343
|
};
|
|
334
344
|
|
|
335
345
|
// Get show deleted toggle state
|
|
336
346
|
const selectShowDeleted = state => {
|
|
347
|
+
var _listingsState$showDe;
|
|
337
348
|
const listingsState = selectListingsState(state);
|
|
338
|
-
return listingsState
|
|
349
|
+
return (_listingsState$showDe = listingsState === null || listingsState === void 0 ? void 0 : listingsState.showDeleted) !== null && _listingsState$showDe !== void 0 ? _listingsState$showDe : false;
|
|
339
350
|
};
|
|
340
351
|
|
|
341
352
|
// Get sorted and filtered listings
|
|
@@ -2120,846 +2131,838 @@ const Text$8 = Components$4.Text;
|
|
|
2120
2131
|
|
|
2121
2132
|
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; }
|
|
2122
2133
|
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; }
|
|
2134
|
+
const {
|
|
2135
|
+
Helper: Helper$1,
|
|
2136
|
+
Session: Session$1
|
|
2137
|
+
} = PlussCore__namespace;
|
|
2138
|
+
const {
|
|
2139
|
+
getUrl: getUrl$1
|
|
2140
|
+
} = Helper$1;
|
|
2141
|
+
const {
|
|
2142
|
+
authedFunction: authedFunction$1
|
|
2143
|
+
} = Session$1;
|
|
2144
|
+
const featureDefinitionActions = {
|
|
2145
|
+
/**
|
|
2146
|
+
* Get the single feature definition by ID
|
|
2147
|
+
* Path: {id}
|
|
2148
|
+
*/
|
|
2149
|
+
getSingle: async (id, site) => {
|
|
2150
|
+
const query = {
|
|
2151
|
+
id,
|
|
2152
|
+
site
|
|
2153
|
+
};
|
|
2154
|
+
return authedFunction$1({
|
|
2155
|
+
method: "GET",
|
|
2156
|
+
url: getUrl$1("feature-builder", "definition/get/single", query)
|
|
2157
|
+
});
|
|
2158
|
+
},
|
|
2159
|
+
/**
|
|
2160
|
+
* Creates a new feature definition with the provided configuration
|
|
2161
|
+
*
|
|
2162
|
+
* @param {string} id - The unique identifier for the new feature definition
|
|
2163
|
+
* @param {string} site - The site ID where the feature definition will be created
|
|
2164
|
+
* @param {FeatureDefinition} featureDefinition - The feature definition data to create
|
|
2165
|
+
* @returns {Promise<ApiResponse>} Promise resolving to API response with created feature definition
|
|
2166
|
+
* @throws {Error} When creation fails due to validation or API errors
|
|
2167
|
+
*
|
|
2168
|
+
*/
|
|
2169
|
+
create: async (id, site, featureDefinition) => {
|
|
2170
|
+
return authedFunction$1({
|
|
2171
|
+
method: "POST",
|
|
2172
|
+
url: getUrl$1("feature-builder", "definition/update/create"),
|
|
2173
|
+
data: {
|
|
2174
|
+
id,
|
|
2175
|
+
site,
|
|
2176
|
+
featureDefinition
|
|
2177
|
+
}
|
|
2178
|
+
});
|
|
2179
|
+
},
|
|
2180
|
+
/**
|
|
2181
|
+
* Updates an existing feature definition with new configuration
|
|
2182
|
+
*
|
|
2183
|
+
* @param {FeatureDefinition} featureDefinitionData - The updated feature definition data
|
|
2184
|
+
* @param {string} featureDefinitionData.id - The unique identifier of the feature definition to update
|
|
2185
|
+
* @param {string} [featureDefinitionData.displayName] - Updated display name
|
|
2186
|
+
* @param {Field[]} [featureDefinitionData.fields] - Updated field definitions
|
|
2187
|
+
* @param {Object} [featureDefinitionData.layout] - Updated layout configuration
|
|
2188
|
+
* @param {string} site - The site ID where the feature definition exists
|
|
2189
|
+
* @returns {Promise<ApiResponse>} Promise resolving to API response with updated feature definition
|
|
2190
|
+
* @throws {Error} When update fails due to validation or API errors
|
|
2191
|
+
*
|
|
2192
|
+
* @example
|
|
2193
|
+
* try {
|
|
2194
|
+
* const response = await featureDefinitionActions.edit(
|
|
2195
|
+
* {
|
|
2196
|
+
* id: 'feature-123',
|
|
2197
|
+
* displayName: 'Updated Form',
|
|
2198
|
+
* fields: [...]
|
|
2199
|
+
* },
|
|
2200
|
+
* 'site-123'
|
|
2201
|
+
* );
|
|
2202
|
+
*/
|
|
2203
|
+
edit: async (featureDefinitionData, site) => {
|
|
2204
|
+
// Ensure site is included in the request body
|
|
2205
|
+
const dataWithSite = _objectSpread$8({
|
|
2206
|
+
site: site
|
|
2207
|
+
}, featureDefinitionData);
|
|
2208
|
+
return authedFunction$1({
|
|
2209
|
+
method: "POST",
|
|
2210
|
+
url: getUrl$1("feature-builder", "definition/update/edit"),
|
|
2211
|
+
data: dataWithSite
|
|
2212
|
+
});
|
|
2213
|
+
},
|
|
2214
|
+
/**
|
|
2215
|
+
* Soft deletes a feature definition (marks as deleted but doesn't permanently remove)
|
|
2216
|
+
*
|
|
2217
|
+
* @param {string} id - The unique identifier of the feature definition to delete
|
|
2218
|
+
* @param {string} site - The site ID where the feature definition exists
|
|
2219
|
+
* @returns {Promise<ApiResponse>} Promise resolving to API response confirming deletion
|
|
2220
|
+
* @throws {Error} When deletion fails or feature definition is not found
|
|
2221
|
+
*
|
|
2222
|
+
*/
|
|
2223
|
+
delete: async (id, site) => {
|
|
2224
|
+
return authedFunction$1({
|
|
2225
|
+
method: "POST",
|
|
2226
|
+
url: getUrl$1("feature-builder", "definition/update/delete"),
|
|
2227
|
+
data: {
|
|
2228
|
+
id,
|
|
2229
|
+
site
|
|
2230
|
+
}
|
|
2231
|
+
});
|
|
2232
|
+
}
|
|
2233
|
+
};
|
|
2123
2234
|
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
const
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
}
|
|
2139
|
-
|
|
2235
|
+
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; }
|
|
2236
|
+
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; }
|
|
2237
|
+
|
|
2238
|
+
// IMPORTANT: Using local UPDATE_STRINGS action type to make extension self-contained
|
|
2239
|
+
// The main app's StringsReducer will handle this action type the same way
|
|
2240
|
+
const UPDATE_STRINGS = "UPDATE_STRINGS";
|
|
2241
|
+
const updateFeatureBuilderString = title => (dispatch, getState) => {
|
|
2242
|
+
var _getState$strings;
|
|
2243
|
+
const currentStrings = ((_getState$strings = getState().strings) === null || _getState$strings === void 0 ? void 0 : _getState$strings.config) || {};
|
|
2244
|
+
const titleCased = toTitleCase(title) || values.textMenuTitle;
|
|
2245
|
+
const updatedStrings = _objectSpread$7(_objectSpread$7({}, currentStrings), {}, {
|
|
2246
|
+
sideNav: _objectSpread$7(_objectSpread$7({}, currentStrings.sideNav), {}, {
|
|
2247
|
+
[values.featureKey]: titleCased,
|
|
2248
|
+
[values.menuKey]: "Manage ".concat(titleCased)
|
|
2249
|
+
}),
|
|
2250
|
+
permission: _objectSpread$7(_objectSpread$7({}, currentStrings.permission), {}, {
|
|
2251
|
+
[values.permissionFeatureBuilderDefinition]: "Manage custom feature ".concat(titleCased),
|
|
2252
|
+
[values.permissionFeatureBuilderContent]: "Manage ".concat(titleCased, " content")
|
|
2253
|
+
})
|
|
2254
|
+
});
|
|
2255
|
+
dispatch({
|
|
2256
|
+
type: UPDATE_STRINGS,
|
|
2257
|
+
payload: updatedStrings
|
|
2258
|
+
});
|
|
2259
|
+
};
|
|
2260
|
+
const updateFeatureBuilderIcon = icon => (dispatch, getState) => {
|
|
2261
|
+
var _getState$strings2;
|
|
2262
|
+
const currentStrings = ((_getState$strings2 = getState().strings) === null || _getState$strings2 === void 0 ? void 0 : _getState$strings2.config) || {};
|
|
2263
|
+
const updatedStrings = _objectSpread$7(_objectSpread$7({}, currentStrings), {}, {
|
|
2264
|
+
sideNav: _objectSpread$7(_objectSpread$7({}, currentStrings.sideNav), {}, {
|
|
2265
|
+
[values.featureKey + "-icon"]: icon,
|
|
2266
|
+
[values.menuKey + "-icon"]: icon
|
|
2267
|
+
})
|
|
2268
|
+
});
|
|
2269
|
+
dispatch({
|
|
2270
|
+
type: UPDATE_STRINGS,
|
|
2271
|
+
payload: updatedStrings
|
|
2272
|
+
});
|
|
2140
2273
|
};
|
|
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;
|
|
2144
2274
|
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2275
|
+
/**
|
|
2276
|
+
* @typedef {Object} FieldValues
|
|
2277
|
+
* @property {string} [label] - Field label text
|
|
2278
|
+
* @property {string} [placeholder] - Placeholder text for input
|
|
2279
|
+
* @property {boolean} [isRequired] - Whether field is required
|
|
2280
|
+
* @property {string} [helpText] - Help text for field guidance
|
|
2281
|
+
* @property {boolean} [allowCaption] - Whether field allows captions
|
|
2282
|
+
* @property {boolean} [useAsSummary] - Whether field is used as summary
|
|
2283
|
+
*/
|
|
2284
|
+
|
|
2285
|
+
/**
|
|
2286
|
+
* @typedef {Object} FormField
|
|
2287
|
+
* @property {string} id - Unique field identifier
|
|
2288
|
+
* @property {string} type - Field type (text, title, description, image, file, cta, feature-image)
|
|
2289
|
+
* @property {boolean} isMandatory - Whether field is mandatory system field
|
|
2290
|
+
* @property {FieldValues} values - Field configuration values
|
|
2291
|
+
*/
|
|
2292
|
+
|
|
2293
|
+
/**
|
|
2294
|
+
* @typedef {Object} LayoutConfig
|
|
2295
|
+
* @property {string} type - Layout type (round, square, etc.)
|
|
2296
|
+
* @property {string} [gridIcon] - Background image for grid layout
|
|
2297
|
+
*/
|
|
2298
|
+
|
|
2299
|
+
const actionsTypes = {
|
|
2300
|
+
SET_INITIAL_VALUES: "".concat(values.reducerKey.toUpperCase(), "_SET_INITIAL_VALUES"),
|
|
2301
|
+
SET_TITLE: "".concat(values.reducerKey.toUpperCase(), "_SET_TITLE"),
|
|
2302
|
+
SET_ICON: "".concat(values.reducerKey.toUpperCase(), "_SET_ICON"),
|
|
2303
|
+
SET_DISPLAY_NAME: "".concat(values.reducerKey.toUpperCase(), "_SET_DISPLAY_NAME"),
|
|
2304
|
+
ADD_FIELD: "".concat(values.reducerKey.toUpperCase(), "_ADD_FIELD"),
|
|
2305
|
+
DELETE_FIELD: "".concat(values.reducerKey.toUpperCase(), "_DELETE_FIELD"),
|
|
2306
|
+
UPDATE_FIELD: "".concat(values.reducerKey.toUpperCase(), "_UPDATE_FIELD"),
|
|
2307
|
+
SET_LAYOUT_GRID_ICON: "".concat(values.reducerKey.toUpperCase(), "_SET_LAYOUT_GRID_ICON"),
|
|
2308
|
+
SET_LAYOUT_TYPE: "".concat(values.reducerKey.toUpperCase(), "_SET_LAYOUT_TYPE"),
|
|
2309
|
+
SUBMIT_FORM_REQUEST: "".concat(values.reducerKey.toUpperCase(), "_SUBMIT_FORM_REQUEST"),
|
|
2310
|
+
SUBMIT_FORM_SUCCESS: "".concat(values.reducerKey.toUpperCase(), "_SUBMIT_FORM_SUCCESS"),
|
|
2311
|
+
SUBMIT_FORM_FAILURE: "".concat(values.reducerKey.toUpperCase(), "_SUBMIT_FORM_FAILURE"),
|
|
2312
|
+
CLEAR_FORM_SUBMISSION_STATE: "".concat(values.reducerKey.toUpperCase(), "_CLEAR_FORM_SUBMISSION_STATE"),
|
|
2313
|
+
SET_SUMMARY_FIELD: "".concat(values.reducerKey.toUpperCase(), "_SET_SUMMARY_FIELD")
|
|
2151
2314
|
};
|
|
2152
|
-
|
|
2153
|
-
|
|
2315
|
+
|
|
2316
|
+
/**
|
|
2317
|
+
* Action creator to set initial form values
|
|
2318
|
+
* Initializes form state with existing feature definition data
|
|
2319
|
+
*
|
|
2320
|
+
* @param {Object} initialValues - Initial form values object
|
|
2321
|
+
* @param {string} initialValues.title - Feature title
|
|
2322
|
+
* @param {string} initialValues.icon - Feature icon
|
|
2323
|
+
* @param {string} initialValues.displayName - Feature display name
|
|
2324
|
+
* @param {LayoutConfig} initialValues.layout - Layout configuration
|
|
2325
|
+
* @param {FormField[]} initialValues.fields - Form fields array
|
|
2326
|
+
* @returns {Object} Redux action object with type and payload
|
|
2327
|
+
*
|
|
2328
|
+
* @example
|
|
2329
|
+
* dispatch(setInitialValues({
|
|
2330
|
+
* title: 'Contact Form',
|
|
2331
|
+
* icon: 'envelope',
|
|
2332
|
+
* fields: []
|
|
2333
|
+
* }));
|
|
2334
|
+
*/
|
|
2335
|
+
const setInitialValues = initialValues => {
|
|
2154
2336
|
return {
|
|
2155
|
-
type:
|
|
2156
|
-
payload:
|
|
2157
|
-
step,
|
|
2158
|
-
isValid,
|
|
2159
|
-
errors
|
|
2160
|
-
}
|
|
2337
|
+
type: actionsTypes.SET_INITIAL_VALUES,
|
|
2338
|
+
payload: initialValues
|
|
2161
2339
|
};
|
|
2162
2340
|
};
|
|
2163
|
-
const
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
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
|
|
2341
|
+
const setTitle = title => {
|
|
2342
|
+
return {
|
|
2343
|
+
type: actionsTypes.SET_TITLE,
|
|
2344
|
+
payload: title
|
|
2177
2345
|
};
|
|
2178
|
-
|
|
2179
|
-
isValid,
|
|
2180
|
-
errors
|
|
2181
|
-
} = getFormValidation(form, step);
|
|
2182
|
-
dispatch(updateStepValidation(step, isValid, errors));
|
|
2346
|
+
};
|
|
2183
2347
|
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
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");
|
|
2251
|
-
|
|
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;
|
|
2348
|
+
/**
|
|
2349
|
+
* Action creator to set form display name and update menu
|
|
2350
|
+
* Updates both form state and external menu string
|
|
2351
|
+
* Title cases the display name before updating the strings store
|
|
2352
|
+
*
|
|
2353
|
+
* @param {string} displayName - New display name for form
|
|
2354
|
+
* @returns {Function} Thunk function for Redux
|
|
2355
|
+
*
|
|
2356
|
+
* @example
|
|
2357
|
+
* dispatch(setDisplayName('Contact Information'));
|
|
2358
|
+
*/
|
|
2359
|
+
const setDisplayName = displayName => {
|
|
2292
2360
|
return dispatch => {
|
|
2293
|
-
|
|
2361
|
+
// Update menu string when display name changes (will be title cased in updateFeatureBuilderString)
|
|
2362
|
+
dispatch(updateFeatureBuilderString(displayName));
|
|
2363
|
+
|
|
2364
|
+
// Dispatch the actual action
|
|
2365
|
+
dispatch({
|
|
2366
|
+
type: actionsTypes.SET_DISPLAY_NAME,
|
|
2367
|
+
payload: displayName
|
|
2368
|
+
});
|
|
2294
2369
|
};
|
|
2295
2370
|
};
|
|
2296
2371
|
|
|
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
|
-
|
|
2301
2372
|
/**
|
|
2302
|
-
*
|
|
2303
|
-
*
|
|
2304
|
-
* Manages step accessibility based on wizard mode and validation state
|
|
2373
|
+
* Action creator to set form icon and update menu
|
|
2374
|
+
* Updates both form state and external menu icon
|
|
2305
2375
|
*
|
|
2306
|
-
* @param {
|
|
2307
|
-
* @
|
|
2308
|
-
* @param {Object} props.history - React Router history object for navigation
|
|
2309
|
-
* @returns {React.ReactElement} Layout component with sidebar navigation
|
|
2376
|
+
* @param {string} icon - Icon identifier or FontAwesome icon class
|
|
2377
|
+
* @returns {Function} Thunk function for Redux
|
|
2310
2378
|
*
|
|
2311
2379
|
* @example
|
|
2312
|
-
*
|
|
2313
|
-
* <YourMainContent />
|
|
2314
|
-
* </SidebarLayout>
|
|
2380
|
+
* dispatch(setIcon('fa-user'));
|
|
2315
2381
|
*/
|
|
2316
|
-
const
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
} = props;
|
|
2321
|
-
const dispatch = reactRedux.useDispatch();
|
|
2382
|
+
const setIcon = icon => {
|
|
2383
|
+
return dispatch => {
|
|
2384
|
+
// Update menu icon when icon changes
|
|
2385
|
+
dispatch(updateFeatureBuilderIcon(icon));
|
|
2322
2386
|
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
const goTo = url => history.push(url);
|
|
2329
|
-
const isSelected = url => {
|
|
2330
|
-
return history.location.pathname === url;
|
|
2387
|
+
// Dispatch the actual action
|
|
2388
|
+
dispatch({
|
|
2389
|
+
type: actionsTypes.SET_ICON,
|
|
2390
|
+
payload: icon
|
|
2391
|
+
});
|
|
2331
2392
|
};
|
|
2393
|
+
};
|
|
2332
2394
|
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2395
|
+
/**
|
|
2396
|
+
* Action creator to add a new field to the form
|
|
2397
|
+
* Generates unique ID and supports custom field types
|
|
2398
|
+
*
|
|
2399
|
+
* @param {string} [fieldType="text"] - Type of field to add (text, title, description, image, gallery, file, cta, feature-image)
|
|
2400
|
+
* @returns {Object} Redux action object with field ID and type
|
|
2401
|
+
*
|
|
2402
|
+
* @example
|
|
2403
|
+
* dispatch(addField('text'));
|
|
2404
|
+
* dispatch(addField('image'));
|
|
2405
|
+
*/
|
|
2406
|
+
const addField = function () {
|
|
2407
|
+
let fieldType = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : "text";
|
|
2408
|
+
// Generate a unique ID for the new field
|
|
2409
|
+
const fieldId = "custom-field-".concat(Date.now(), "-").concat(Math.random().toString(36).substr(2, 9));
|
|
2410
|
+
return {
|
|
2411
|
+
type: actionsTypes.ADD_FIELD,
|
|
2412
|
+
payload: {
|
|
2413
|
+
id: fieldId,
|
|
2414
|
+
type: fieldType
|
|
2345
2415
|
}
|
|
2346
2416
|
};
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
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
|
-
});
|
|
2390
|
-
};
|
|
2391
|
-
|
|
2392
|
-
// Determine sidebar title - always use "Build Your Feature" now
|
|
2393
|
-
const getSidebarTitle = () => {
|
|
2394
|
-
return "Build Your Feature";
|
|
2417
|
+
};
|
|
2418
|
+
const deleteField = id => {
|
|
2419
|
+
return {
|
|
2420
|
+
type: actionsTypes.DELETE_FIELD,
|
|
2421
|
+
payload: id
|
|
2395
2422
|
};
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2423
|
+
};
|
|
2424
|
+
const updateFieldById = (id, updatedField) => {
|
|
2425
|
+
return {
|
|
2426
|
+
type: actionsTypes.UPDATE_FIELD,
|
|
2427
|
+
payload: {
|
|
2428
|
+
id,
|
|
2429
|
+
updatedField
|
|
2430
|
+
}
|
|
2400
2431
|
};
|
|
2432
|
+
};
|
|
2401
2433
|
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
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))));
|
|
2434
|
+
/**
|
|
2435
|
+
* Action creator to set a description field as the summary field
|
|
2436
|
+
* Ensures only one description field can be marked as summary by
|
|
2437
|
+
* automatically unsetting all other description fields when a new one is selected
|
|
2438
|
+
*
|
|
2439
|
+
* @param {string} fieldId - ID of the description field to set as summary
|
|
2440
|
+
* @returns {Object} Redux action object for summary field selection
|
|
2441
|
+
*
|
|
2442
|
+
* @example
|
|
2443
|
+
* dispatch(setSummaryField('field-description-123'));
|
|
2444
|
+
*/
|
|
2445
|
+
const setSummaryField = fieldId => {
|
|
2446
|
+
return {
|
|
2447
|
+
type: actionsTypes.SET_SUMMARY_FIELD,
|
|
2448
|
+
payload: fieldId
|
|
2449
|
+
};
|
|
2507
2450
|
};
|
|
2508
|
-
const
|
|
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
|
-
|
|
2512
|
-
|
|
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
|
-
}
|
|
2451
|
+
const setLayoutType = layoutType => {
|
|
2452
|
+
return {
|
|
2453
|
+
type: actionsTypes.SET_LAYOUT_TYPE,
|
|
2454
|
+
payload: layoutType
|
|
2455
|
+
};
|
|
2615
2456
|
};
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
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
|
-
});
|
|
2457
|
+
const submitFormRequest = () => {
|
|
2458
|
+
return {
|
|
2459
|
+
type: actionsTypes.SUBMIT_FORM_REQUEST
|
|
2460
|
+
};
|
|
2641
2461
|
};
|
|
2642
|
-
const
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2462
|
+
const submitFormSuccess = () => {
|
|
2463
|
+
return {
|
|
2464
|
+
type: actionsTypes.SUBMIT_FORM_SUCCESS
|
|
2465
|
+
};
|
|
2466
|
+
};
|
|
2467
|
+
const submitFormFailure = error => {
|
|
2468
|
+
return {
|
|
2469
|
+
type: actionsTypes.SUBMIT_FORM_FAILURE,
|
|
2470
|
+
payload: error
|
|
2471
|
+
};
|
|
2472
|
+
};
|
|
2473
|
+
const clearFormSubmissionState = () => {
|
|
2474
|
+
return {
|
|
2475
|
+
type: actionsTypes.CLEAR_FORM_SUBMISSION_STATE
|
|
2476
|
+
};
|
|
2655
2477
|
};
|
|
2656
2478
|
|
|
2657
2479
|
/**
|
|
2658
|
-
*
|
|
2659
|
-
*
|
|
2660
|
-
* Handles CRUD operations for form fields and layout configuration
|
|
2661
|
-
* Coordinates with external menu updates and feature definition actions
|
|
2480
|
+
* Submits the complete feature form to the server
|
|
2481
|
+
* Handles form validation, API submission, and error handling
|
|
2662
2482
|
*
|
|
2663
|
-
* @
|
|
2664
|
-
|
|
2665
|
-
|
|
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
|
|
2483
|
+
* @returns {Function} Async thunk function for Redux
|
|
2484
|
+
* @throws {Error} When form validation fails or API submission encounters error
|
|
2674
2485
|
*/
|
|
2486
|
+
function submitForm() {
|
|
2487
|
+
return async (dispatch, getState) => {
|
|
2488
|
+
const state = getState()[values.reducerKey];
|
|
2489
|
+
const form = state === null || state === void 0 ? void 0 : state.form;
|
|
2490
|
+
if (!form) {
|
|
2491
|
+
dispatch(submitFormFailure(new Error("Form data is missing. Please refresh the page and try again.")));
|
|
2492
|
+
return;
|
|
2493
|
+
}
|
|
2494
|
+
dispatch(submitFormRequest());
|
|
2495
|
+
try {
|
|
2496
|
+
// Get site from auth store
|
|
2497
|
+
const site = getState().auth.site;
|
|
2498
|
+
if (!site) {
|
|
2499
|
+
throw new Error("Authentication error: Site context not found. Please refresh and login again.");
|
|
2500
|
+
}
|
|
2675
2501
|
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2502
|
+
// Use mode from fetch instead of checking definition ID
|
|
2503
|
+
const definitionState = state === null || state === void 0 ? void 0 : state.definition;
|
|
2504
|
+
const mode = definitionState === null || definitionState === void 0 ? void 0 : definitionState.mode; // Use stored mode from fetch
|
|
2505
|
+
const definitionId = definitionState === null || definitionState === void 0 ? void 0 : definitionState.id;
|
|
2506
|
+
if (mode === "edit") {
|
|
2507
|
+
// Always update when in edit mode
|
|
2508
|
+
const updatedDefinition = {
|
|
2509
|
+
id: definitionId,
|
|
2510
|
+
site: site,
|
|
2511
|
+
// Include site from auth store
|
|
2512
|
+
featureDefinition: {
|
|
2513
|
+
// Wrap in expected structure for backend
|
|
2514
|
+
title: form.title,
|
|
2515
|
+
icon: form.icon,
|
|
2516
|
+
displayName: form.displayName,
|
|
2517
|
+
layout: form.layout,
|
|
2518
|
+
fields: form.fields
|
|
2519
|
+
}
|
|
2520
|
+
};
|
|
2521
|
+
await featureDefinitionActions.edit(updatedDefinition, site);
|
|
2522
|
+
} else {
|
|
2523
|
+
// Always create when in create mode (or mode is undefined/null)
|
|
2524
|
+
if (!values.featureId || !site) {
|
|
2525
|
+
throw new Error("Authentication error: Missing required context (featureId or site).");
|
|
2526
|
+
}
|
|
2527
|
+
await featureDefinitionActions.create(values.featureId, site,
|
|
2528
|
+
// Use actual site from auth store
|
|
2529
|
+
{
|
|
2530
|
+
title: form.title,
|
|
2531
|
+
icon: form.icon,
|
|
2532
|
+
displayName: form.displayName,
|
|
2533
|
+
layout: form.layout,
|
|
2534
|
+
fields: form.fields
|
|
2535
|
+
});
|
|
2536
|
+
}
|
|
2537
|
+
dispatch(submitFormSuccess());
|
|
2538
|
+
} catch (err) {
|
|
2539
|
+
// Handle different types of errors
|
|
2540
|
+
let errorToDisplay = err;
|
|
2541
|
+
if (err.response) {
|
|
2542
|
+
// API error (400, 401, 404, 500, etc.)
|
|
2543
|
+
const {
|
|
2544
|
+
status,
|
|
2545
|
+
data
|
|
2546
|
+
} = err.response;
|
|
2547
|
+
if (status === 400 && data !== null && data !== void 0 && data.error) {
|
|
2548
|
+
errorToDisplay = new Error("Validation error: ".concat(data.error));
|
|
2549
|
+
} else if (status === 401) {
|
|
2550
|
+
errorToDisplay = new Error("You are not authorized to perform this action");
|
|
2551
|
+
} else if (status === 404) {
|
|
2552
|
+
errorToDisplay = new Error("Feature definition not found");
|
|
2553
|
+
} else if (status >= 500) {
|
|
2554
|
+
errorToDisplay = new Error("Server error. Please try again later.");
|
|
2555
|
+
} else {
|
|
2556
|
+
errorToDisplay = new Error((data === null || data === void 0 ? void 0 : data.error) || "Request failed with status ".concat(status));
|
|
2557
|
+
}
|
|
2558
|
+
} else if (err.request) {
|
|
2559
|
+
// Network error (no response received)
|
|
2560
|
+
errorToDisplay = new Error("Network error. Please check your connection and try again.");
|
|
2561
|
+
} else if (err.message) {
|
|
2562
|
+
// Other JavaScript errors
|
|
2563
|
+
errorToDisplay = err;
|
|
2564
|
+
} else {
|
|
2565
|
+
// Unknown error
|
|
2566
|
+
errorToDisplay = new Error("An unexpected error occurred while saving");
|
|
2567
|
+
}
|
|
2568
|
+
dispatch(submitFormFailure(errorToDisplay));
|
|
2569
|
+
}
|
|
2570
|
+
};
|
|
2571
|
+
}
|
|
2683
2572
|
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
* @property {string} type - Layout type (round, square, etc.)
|
|
2687
|
-
* @property {string} [gridIcon] - Background image for grid layout
|
|
2688
|
-
*/
|
|
2573
|
+
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; }
|
|
2574
|
+
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; }
|
|
2689
2575
|
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2576
|
+
// Wizard action types
|
|
2577
|
+
const REDUCER_PREFIX$2 = values.reducerKey.toUpperCase();
|
|
2578
|
+
const SET_NAVIGATION_STATE$1 = "".concat(REDUCER_PREFIX$2, "_SET_NAVIGATION_STATE");
|
|
2579
|
+
const UPDATE_STEP_VALIDATION$1 = "".concat(REDUCER_PREFIX$2, "_UPDATE_STEP_VALIDATION");
|
|
2580
|
+
const MARK_STEP_COMPLETE$1 = "".concat(REDUCER_PREFIX$2, "_MARK_STEP_COMPLETE");
|
|
2581
|
+
const setCurrentStep = function (step) {
|
|
2582
|
+
let previousStep = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
|
|
2583
|
+
return {
|
|
2584
|
+
type: SET_NAVIGATION_STATE$1,
|
|
2585
|
+
payload: {
|
|
2586
|
+
currentStep: step,
|
|
2587
|
+
previousStep,
|
|
2588
|
+
canGoBack: step !== "welcome",
|
|
2589
|
+
canGoForward: true
|
|
2590
|
+
}
|
|
2591
|
+
};
|
|
2705
2592
|
};
|
|
2593
|
+
const goToStep = step => (dispatch, getState) => {
|
|
2594
|
+
var _state$wizard;
|
|
2595
|
+
const state = getState()[values.reducerKey];
|
|
2596
|
+
const currentStep = state === null || state === void 0 || (_state$wizard = state.wizard) === null || _state$wizard === void 0 || (_state$wizard = _state$wizard.navigation) === null || _state$wizard === void 0 ? void 0 : _state$wizard.currentStep;
|
|
2706
2597
|
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
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 => {
|
|
2598
|
+
// Clear form submission state when changing steps
|
|
2599
|
+
dispatch(clearFormSubmissionState());
|
|
2600
|
+
dispatch(setCurrentStep(step, currentStep));
|
|
2601
|
+
};
|
|
2602
|
+
const updateStepValidation = function (step, isValid) {
|
|
2603
|
+
let errors = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
|
2727
2604
|
return {
|
|
2728
|
-
type:
|
|
2729
|
-
payload:
|
|
2605
|
+
type: UPDATE_STEP_VALIDATION$1,
|
|
2606
|
+
payload: {
|
|
2607
|
+
step,
|
|
2608
|
+
isValid,
|
|
2609
|
+
errors
|
|
2610
|
+
}
|
|
2611
|
+
};
|
|
2612
|
+
};
|
|
2613
|
+
const validateAndUpdateStep = step => (dispatch, getState) => {
|
|
2614
|
+
// Use existing selectors to get form data
|
|
2615
|
+
const state = getState();
|
|
2616
|
+
const title = selectFormTitle(state);
|
|
2617
|
+
const icon = selectFormIcon(state);
|
|
2618
|
+
const displayName = selectFormDisplayName(state);
|
|
2619
|
+
const layout = selectFormLayout(state);
|
|
2620
|
+
const fields = selectFormFields(state);
|
|
2621
|
+
const form = {
|
|
2622
|
+
title,
|
|
2623
|
+
icon,
|
|
2624
|
+
displayName,
|
|
2625
|
+
layout,
|
|
2626
|
+
fields
|
|
2730
2627
|
};
|
|
2731
|
-
|
|
2732
|
-
|
|
2628
|
+
const {
|
|
2629
|
+
isValid,
|
|
2630
|
+
errors
|
|
2631
|
+
} = getFormValidation(form, step);
|
|
2632
|
+
dispatch(updateStepValidation(step, isValid, errors));
|
|
2633
|
+
|
|
2634
|
+
// If valid, mark as complete
|
|
2635
|
+
if (isValid) {
|
|
2636
|
+
dispatch({
|
|
2637
|
+
type: MARK_STEP_COMPLETE$1,
|
|
2638
|
+
payload: step
|
|
2639
|
+
});
|
|
2640
|
+
}
|
|
2733
2641
|
return {
|
|
2734
|
-
|
|
2735
|
-
|
|
2642
|
+
isValid,
|
|
2643
|
+
errors
|
|
2736
2644
|
};
|
|
2737
2645
|
};
|
|
2646
|
+
const getFormValidation = (form, step) => {
|
|
2647
|
+
// Return validation results for undefined form (prevent crashes)
|
|
2648
|
+
if (!form) {
|
|
2649
|
+
switch (step) {
|
|
2650
|
+
case "overview":
|
|
2651
|
+
return {
|
|
2652
|
+
isValid: false,
|
|
2653
|
+
errors: {
|
|
2654
|
+
title: "Title is required",
|
|
2655
|
+
displayName: "Display name is required",
|
|
2656
|
+
icon: "Icon is required"
|
|
2657
|
+
}
|
|
2658
|
+
};
|
|
2659
|
+
case "fields":
|
|
2660
|
+
return {
|
|
2661
|
+
isValid: false,
|
|
2662
|
+
errors: {
|
|
2663
|
+
missingTitle: "Title field is required",
|
|
2664
|
+
missingImage: "Feature image field is required",
|
|
2665
|
+
fieldLabels: "Some fields are missing labels"
|
|
2666
|
+
}
|
|
2667
|
+
};
|
|
2668
|
+
case "layout":
|
|
2669
|
+
return {
|
|
2670
|
+
isValid: false,
|
|
2671
|
+
errors: {
|
|
2672
|
+
layoutType: "Layout type is required"
|
|
2673
|
+
}
|
|
2674
|
+
};
|
|
2675
|
+
default:
|
|
2676
|
+
return {
|
|
2677
|
+
isValid: false,
|
|
2678
|
+
errors: {}
|
|
2679
|
+
};
|
|
2680
|
+
}
|
|
2681
|
+
}
|
|
2682
|
+
switch (step) {
|
|
2683
|
+
case "overview":
|
|
2684
|
+
{
|
|
2685
|
+
var _form$title, _form$displayName, _form$icon;
|
|
2686
|
+
const hasTitle = ((_form$title = form.title) === null || _form$title === void 0 ? void 0 : _form$title.trim().length) > 0;
|
|
2687
|
+
const hasDisplayName = ((_form$displayName = form.displayName) === null || _form$displayName === void 0 ? void 0 : _form$displayName.trim().length) > 0;
|
|
2688
|
+
const hasIcon = ((_form$icon = form.icon) === null || _form$icon === void 0 ? void 0 : _form$icon.length) > 0;
|
|
2689
|
+
return {
|
|
2690
|
+
isValid: hasTitle && hasDisplayName && hasIcon,
|
|
2691
|
+
errors: {
|
|
2692
|
+
title: !hasTitle ? "Title is required" : null,
|
|
2693
|
+
displayName: !hasDisplayName ? "Display name is required" : null,
|
|
2694
|
+
icon: !hasIcon ? "Icon is required" : null
|
|
2695
|
+
}
|
|
2696
|
+
};
|
|
2697
|
+
}
|
|
2698
|
+
case "fields":
|
|
2699
|
+
{
|
|
2700
|
+
var _form$fields, _form$fields2;
|
|
2701
|
+
const hasTitleField = (_form$fields = form.fields) === null || _form$fields === void 0 ? void 0 : _form$fields.some(field => field.id === "mandatory-title");
|
|
2702
|
+
const hasImageField = (_form$fields2 = form.fields) === null || _form$fields2 === void 0 ? void 0 : _form$fields2.some(field => field.id === "mandatory-feature-image");
|
|
2738
2703
|
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2704
|
+
// Check each field for missing labels and create field-specific errors
|
|
2705
|
+
const fieldErrors = {};
|
|
2706
|
+
let allFieldsHaveLabels = true;
|
|
2707
|
+
if (form.fields) {
|
|
2708
|
+
form.fields.forEach(field => {
|
|
2709
|
+
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) {
|
|
2710
|
+
if (!field.values.label || field.values.label.trim().length === 0) {
|
|
2711
|
+
fieldErrors[field.id] = "Field label is required";
|
|
2712
|
+
allFieldsHaveLabels = false;
|
|
2713
|
+
}
|
|
2714
|
+
}
|
|
2715
|
+
});
|
|
2716
|
+
}
|
|
2717
|
+
return {
|
|
2718
|
+
isValid: hasTitleField && hasImageField && allFieldsHaveLabels,
|
|
2719
|
+
errors: _objectSpread$6({
|
|
2720
|
+
missingTitle: !hasTitleField ? "Title field is required" : null,
|
|
2721
|
+
missingImage: !hasImageField ? "Feature image field is required" : null
|
|
2722
|
+
}, fieldErrors)
|
|
2723
|
+
};
|
|
2724
|
+
}
|
|
2725
|
+
case "layout":
|
|
2726
|
+
{
|
|
2727
|
+
var _form$layout;
|
|
2728
|
+
const hasLayoutType = ((_form$layout = form.layout) === null || _form$layout === void 0 || (_form$layout = _form$layout.type) === null || _form$layout === void 0 ? void 0 : _form$layout.length) > 0;
|
|
2729
|
+
return {
|
|
2730
|
+
isValid: hasLayoutType,
|
|
2731
|
+
errors: {
|
|
2732
|
+
layoutType: !hasLayoutType ? "Layout type is required" : null
|
|
2733
|
+
}
|
|
2734
|
+
};
|
|
2735
|
+
}
|
|
2736
|
+
default:
|
|
2737
|
+
return {
|
|
2738
|
+
isValid: true,
|
|
2739
|
+
errors: {}
|
|
2740
|
+
};
|
|
2741
|
+
}
|
|
2742
|
+
};
|
|
2743
|
+
const setCurrentStepAndSave = function (step) {
|
|
2744
|
+
let previousStep = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
|
|
2751
2745
|
return dispatch => {
|
|
2752
|
-
|
|
2753
|
-
dispatch(updateFeatureBuilderString(displayName));
|
|
2754
|
-
|
|
2755
|
-
// Dispatch the actual action
|
|
2756
|
-
dispatch({
|
|
2757
|
-
type: actionsTypes.SET_DISPLAY_NAME,
|
|
2758
|
-
payload: displayName
|
|
2759
|
-
});
|
|
2746
|
+
dispatch(setCurrentStep(step, previousStep));
|
|
2760
2747
|
};
|
|
2761
2748
|
};
|
|
2762
2749
|
|
|
2750
|
+
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";
|
|
2751
|
+
var modules_d5b6badf = {"fullWidthContent":"SidebarLayout_module_fullWidthContent__0d6658dd","fullWidthContainer":"SidebarLayout_module_fullWidthContainer__0d6658dd","contentContainer":"SidebarLayout_module_contentContainer__0d6658dd","container":"SidebarLayout_module_container__0d6658dd"};
|
|
2752
|
+
n(css$d,{});
|
|
2753
|
+
|
|
2763
2754
|
/**
|
|
2764
|
-
*
|
|
2765
|
-
*
|
|
2755
|
+
* Sidebar Layout component for feature builder wizard
|
|
2756
|
+
* Provides navigation sidebar with step progression, completion tracking
|
|
2757
|
+
* Manages step accessibility based on wizard mode and validation state
|
|
2766
2758
|
*
|
|
2767
|
-
* @param {
|
|
2768
|
-
* @
|
|
2759
|
+
* @param {Object} props - Component props
|
|
2760
|
+
* @param {React.ReactNode} props.children - Child components to render in main content area
|
|
2761
|
+
* @param {Object} props.history - React Router history object for navigation
|
|
2762
|
+
* @returns {React.ReactElement} Layout component with sidebar navigation
|
|
2769
2763
|
*
|
|
2770
2764
|
* @example
|
|
2771
|
-
*
|
|
2765
|
+
* <SidebarLayout history={historyObject}>
|
|
2766
|
+
* <YourMainContent />
|
|
2767
|
+
* </SidebarLayout>
|
|
2772
2768
|
*/
|
|
2773
|
-
const
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2769
|
+
const SideBarInner = props => {
|
|
2770
|
+
const {
|
|
2771
|
+
children,
|
|
2772
|
+
history
|
|
2773
|
+
} = props;
|
|
2774
|
+
const dispatch = reactRedux.useDispatch();
|
|
2777
2775
|
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2776
|
+
// Get wizard state
|
|
2777
|
+
const mode = reactRedux.useSelector(selectWizardMode);
|
|
2778
|
+
const isEditMode = reactRedux.useSelector(selectIsEditMode);
|
|
2779
|
+
const isCreateMode = reactRedux.useSelector(selectIsCreateMode);
|
|
2780
|
+
const currentStep = reactRedux.useSelector(selectCurrentStep);
|
|
2781
|
+
const goTo = url => history.push(url);
|
|
2782
|
+
const isSelected = url => {
|
|
2783
|
+
return history.location.pathname === url;
|
|
2783
2784
|
};
|
|
2784
|
-
};
|
|
2785
2785
|
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
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
|
|
2786
|
+
// Define step configuration with dynamic URL based on mode
|
|
2787
|
+
const getStepUrl = stepKey => {
|
|
2788
|
+
// Use routes from values.config to support different variants (-a, -b, -c, -d)
|
|
2789
|
+
switch (stepKey) {
|
|
2790
|
+
case "overview":
|
|
2791
|
+
return values.routeFormOverviewStep;
|
|
2792
|
+
case "fields":
|
|
2793
|
+
return values.routeFormFieldsStep;
|
|
2794
|
+
case "layout":
|
|
2795
|
+
return values.routeFormLayoutStep;
|
|
2796
|
+
default:
|
|
2797
|
+
return "";
|
|
2806
2798
|
}
|
|
2807
2799
|
};
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
}
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2800
|
+
const steps = [{
|
|
2801
|
+
key: "overview",
|
|
2802
|
+
text: "Feature Overview",
|
|
2803
|
+
icon: "info-circle",
|
|
2804
|
+
url: getStepUrl("overview")
|
|
2805
|
+
}, {
|
|
2806
|
+
key: "fields",
|
|
2807
|
+
text: "Configure Fields",
|
|
2808
|
+
icon: "edit",
|
|
2809
|
+
url: getStepUrl("fields")
|
|
2810
|
+
}, {
|
|
2811
|
+
key: "layout",
|
|
2812
|
+
text: "Choose Layout",
|
|
2813
|
+
icon: "columns",
|
|
2814
|
+
url: getStepUrl("layout")
|
|
2815
|
+
}];
|
|
2816
|
+
|
|
2817
|
+
// Build sidebar items based on mode
|
|
2818
|
+
const buildSidebarItems = () => {
|
|
2819
|
+
const isWizardMode = mode === "create" || mode === "edit";
|
|
2820
|
+
return steps.map((step, index) => {
|
|
2821
|
+
const isCompleted = selectIsStepComplete(step.key);
|
|
2822
|
+
const isAccessible = selectIsStepAccessible(step.key);
|
|
2823
|
+
|
|
2824
|
+
// Add step number to text for better clarity
|
|
2825
|
+
const stepText = "".concat(index + 1, ". ").concat(step.text);
|
|
2826
|
+
const itemProps = {
|
|
2827
|
+
type: "navItem",
|
|
2828
|
+
text: stepText,
|
|
2829
|
+
icon: step.icon,
|
|
2830
|
+
selected: isSelected(step.url),
|
|
2831
|
+
onclick: isWizardMode ? null : isAccessible ? () => {
|
|
2832
|
+
goTo(step.url);
|
|
2833
|
+
dispatch(goToStep(step.key));
|
|
2834
|
+
} : null,
|
|
2835
|
+
isFontAwesome: true,
|
|
2836
|
+
// Enhanced completion indicator
|
|
2837
|
+
completed: isCompleted,
|
|
2838
|
+
// Disable all navigation in wizard mode
|
|
2839
|
+
disabled: isWizardMode || mode === "create" && !isAccessible
|
|
2840
|
+
};
|
|
2841
|
+
return itemProps;
|
|
2842
|
+
});
|
|
2822
2843
|
};
|
|
2823
|
-
};
|
|
2824
2844
|
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
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
|
|
2840
|
-
};
|
|
2841
|
-
};
|
|
2842
|
-
const setLayoutType = layoutType => {
|
|
2843
|
-
return {
|
|
2844
|
-
type: actionsTypes.SET_LAYOUT_TYPE,
|
|
2845
|
-
payload: layoutType
|
|
2846
|
-
};
|
|
2847
|
-
};
|
|
2848
|
-
const submitFormRequest = () => {
|
|
2849
|
-
return {
|
|
2850
|
-
type: actionsTypes.SUBMIT_FORM_REQUEST
|
|
2851
|
-
};
|
|
2852
|
-
};
|
|
2853
|
-
const submitFormSuccess = () => {
|
|
2854
|
-
return {
|
|
2855
|
-
type: actionsTypes.SUBMIT_FORM_SUCCESS
|
|
2856
|
-
};
|
|
2857
|
-
};
|
|
2858
|
-
const submitFormFailure = error => {
|
|
2859
|
-
return {
|
|
2860
|
-
type: actionsTypes.SUBMIT_FORM_FAILURE,
|
|
2861
|
-
payload: error
|
|
2845
|
+
// Determine sidebar title - always use "Build Your Feature" now
|
|
2846
|
+
const getSidebarTitle = () => {
|
|
2847
|
+
return "Build Your Feature";
|
|
2862
2848
|
};
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2849
|
+
|
|
2850
|
+
// Simple help text
|
|
2851
|
+
const getHelpText = () => {
|
|
2852
|
+
return "Get help with feature builder";
|
|
2867
2853
|
};
|
|
2868
|
-
};
|
|
2869
2854
|
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
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
|
-
}
|
|
2855
|
+
// Build sidebar sections - simplified without progress section
|
|
2856
|
+
const sidebarSections = [{
|
|
2857
|
+
title: getSidebarTitle(),
|
|
2858
|
+
items: buildSidebarItems()
|
|
2859
|
+
}];
|
|
2892
2860
|
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2861
|
+
// Add effect to manually attach click handlers since HubSidebar might not use onclick properly
|
|
2862
|
+
React.useEffect(() => {
|
|
2863
|
+
const isWizardMode = mode === "create" || mode === "edit";
|
|
2864
|
+
const attachClickHandlers = () => {
|
|
2865
|
+
const stepsWithUrls = [{
|
|
2866
|
+
key: "overview",
|
|
2867
|
+
url: getStepUrl("overview")
|
|
2868
|
+
}, {
|
|
2869
|
+
key: "fields",
|
|
2870
|
+
url: getStepUrl("fields")
|
|
2871
|
+
}, {
|
|
2872
|
+
key: "layout",
|
|
2873
|
+
url: getStepUrl("layout")
|
|
2874
|
+
}];
|
|
2875
|
+
stepsWithUrls.forEach((step, index) => {
|
|
2876
|
+
const navItem = Array.from(document.querySelectorAll(".hub-wrapperContainer .sideNav-item")).find(item => item.textContent && item.textContent.includes("".concat(index + 1, ".")));
|
|
2877
|
+
if (navItem) {
|
|
2878
|
+
// Remove any existing click listeners
|
|
2879
|
+
navItem.onclick = null;
|
|
2880
|
+
|
|
2881
|
+
// Check if this step is accessible
|
|
2882
|
+
const isAccessible = selectIsStepAccessible(step.key)({
|
|
2883
|
+
[values.reducerKey]: {
|
|
2884
|
+
wizard: {
|
|
2885
|
+
mode: mode,
|
|
2886
|
+
navigation: {
|
|
2887
|
+
currentStep: currentStep
|
|
2888
|
+
}
|
|
2889
|
+
}
|
|
2890
|
+
}
|
|
2891
|
+
});
|
|
2892
|
+
|
|
2893
|
+
// Check if this is the current step (selected)
|
|
2894
|
+
const isCurrentStep = isSelected(step.url);
|
|
2895
|
+
|
|
2896
|
+
// In wizard mode, don't attach any click handlers
|
|
2897
|
+
// Only attach click handler if not in wizard mode AND accessible
|
|
2898
|
+
if (!isWizardMode && isAccessible) {
|
|
2899
|
+
navItem.onclick = event => {
|
|
2900
|
+
event.preventDefault();
|
|
2901
|
+
event.stopPropagation();
|
|
2902
|
+
history.push(step.url);
|
|
2903
|
+
dispatch(goToStep(step.key));
|
|
2904
|
+
};
|
|
2905
|
+
}
|
|
2906
|
+
|
|
2907
|
+
// Make sure it's styled appropriately
|
|
2908
|
+
// In wizard mode: block clicks, but keep current step visible
|
|
2909
|
+
navItem.style.pointerEvents = isWizardMode ? "none" : "auto";
|
|
2910
|
+
if (isWizardMode) {
|
|
2911
|
+
// Current step: full opacity, not-allowed cursor
|
|
2912
|
+
// Other steps: reduced opacity
|
|
2913
|
+
navItem.style.opacity = isCurrentStep ? "1" : "0.4";
|
|
2914
|
+
navItem.style.cursor = "not-allowed";
|
|
2915
|
+
} else {
|
|
2916
|
+
// Not in wizard mode: normal styling based on accessibility
|
|
2917
|
+
navItem.style.cursor = isAccessible ? "pointer" : "not-allowed";
|
|
2918
|
+
navItem.style.opacity = isAccessible ? "1" : "0.5";
|
|
2910
2919
|
}
|
|
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).");
|
|
2917
|
-
}
|
|
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
2920
|
}
|
|
2949
|
-
}
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2921
|
+
});
|
|
2922
|
+
};
|
|
2923
|
+
|
|
2924
|
+
// Initial attachment
|
|
2925
|
+
attachClickHandlers();
|
|
2926
|
+
|
|
2927
|
+
// Re-attach after a short delay to ensure HubSidebar has rendered
|
|
2928
|
+
const timeoutId = setTimeout(attachClickHandlers, 100);
|
|
2929
|
+
|
|
2930
|
+
// Also try to re-attach when DOM changes (observe for mutations)
|
|
2931
|
+
const observer = new MutationObserver(() => {
|
|
2932
|
+
setTimeout(attachClickHandlers, 50);
|
|
2933
|
+
});
|
|
2934
|
+
|
|
2935
|
+
// Start observing the document body for changes
|
|
2936
|
+
observer.observe(document.body, {
|
|
2937
|
+
childList: true,
|
|
2938
|
+
subtree: true
|
|
2939
|
+
});
|
|
2940
|
+
return () => {
|
|
2941
|
+
clearTimeout(timeoutId);
|
|
2942
|
+
observer.disconnect();
|
|
2943
|
+
};
|
|
2944
|
+
}, [history, dispatch, isEditMode, isCreateMode, currentStep]);
|
|
2945
|
+
return /*#__PURE__*/React__default["default"].createElement("div", {
|
|
2946
|
+
className: "hub-wrapperContainer"
|
|
2947
|
+
}, /*#__PURE__*/React__default["default"].createElement(HubSidebar, {
|
|
2948
|
+
sections: sidebarSections,
|
|
2949
|
+
helpGuide: {
|
|
2950
|
+
text: getHelpText(),
|
|
2951
|
+
url: "https://www.plusscommunities.com/user-guide"
|
|
2960
2952
|
}
|
|
2961
|
-
}
|
|
2962
|
-
|
|
2953
|
+
}), /*#__PURE__*/React__default["default"].createElement("div", {
|
|
2954
|
+
className: "hub-contentWrapper"
|
|
2955
|
+
}, /*#__PURE__*/React__default["default"].createElement("div", {
|
|
2956
|
+
className: modules_d5b6badf.fullWidthContainer
|
|
2957
|
+
}, /*#__PURE__*/React__default["default"].createElement("div", {
|
|
2958
|
+
className: modules_d5b6badf.contentContainer
|
|
2959
|
+
}, children))));
|
|
2960
|
+
};
|
|
2961
|
+
const SidebarLayout = reactRouter.withRouter(SideBarInner);
|
|
2962
|
+
|
|
2963
|
+
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";
|
|
2964
|
+
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"};
|
|
2965
|
+
n(css$c,{});
|
|
2963
2966
|
|
|
2964
2967
|
/*
|
|
2965
2968
|
* Icon categories and definitions for the feature builder
|
|
@@ -3283,9 +3286,6 @@ const FormOverviewStepInner = props => {
|
|
|
3283
3286
|
|
|
3284
3287
|
// Clear submission state after showing toast (only for non-edit cases)
|
|
3285
3288
|
if (!isEditMode) {
|
|
3286
|
-
const {
|
|
3287
|
-
clearFormSubmissionState
|
|
3288
|
-
} = require("../actions/formActions");
|
|
3289
3289
|
dispatch(clearFormSubmissionState());
|
|
3290
3290
|
}
|
|
3291
3291
|
}
|
|
@@ -3979,8 +3979,9 @@ function fetchFeatureDefinitions() {
|
|
|
3979
3979
|
dispatch(fetchFeaturesFailure(new Error("Unexpected response status: ".concat(response.status))));
|
|
3980
3980
|
}
|
|
3981
3981
|
} catch (err) {
|
|
3982
|
+
var _err$response;
|
|
3982
3983
|
// Check if it's a 404 (feature doesn't exist)
|
|
3983
|
-
if (
|
|
3984
|
+
if (((_err$response = err.response) === null || _err$response === void 0 ? void 0 : _err$response.status) === 404) {
|
|
3984
3985
|
// 404: Feature doesn't exist → create mode
|
|
3985
3986
|
dispatch(fetchFeaturesSuccess(null, "create"));
|
|
3986
3987
|
|
|
@@ -4636,8 +4637,7 @@ const FormLayoutStepInner = props => {
|
|
|
4636
4637
|
// Use custom hook to handle definition loading
|
|
4637
4638
|
const {
|
|
4638
4639
|
definition,
|
|
4639
|
-
definitionIsLoading
|
|
4640
|
-
reloadDefinition
|
|
4640
|
+
definitionIsLoading
|
|
4641
4641
|
} = useFeatureDefinitionLoader();
|
|
4642
4642
|
|
|
4643
4643
|
// Get form initialization state
|
|
@@ -5249,7 +5249,7 @@ const undeleteListing = id => {
|
|
|
5249
5249
|
const response = await listingActions.undelete(id, site);
|
|
5250
5250
|
// If API returns the restored listing, use it; otherwise we'll need to refetch
|
|
5251
5251
|
const restoredListing = response.data;
|
|
5252
|
-
if (restoredListing && restoredListing.id) {
|
|
5252
|
+
if (restoredListing !== null && restoredListing !== void 0 && restoredListing.id) {
|
|
5253
5253
|
dispatch(undeleteListingSuccess(restoredListing));
|
|
5254
5254
|
} else {
|
|
5255
5255
|
// Trigger a refetch by dispatching the success with just ID, then fetch updated listings
|
|
@@ -5565,20 +5565,21 @@ const formReducer = function () {
|
|
|
5565
5565
|
switch (type) {
|
|
5566
5566
|
case actionsTypes.SET_INITIAL_VALUES:
|
|
5567
5567
|
{
|
|
5568
|
+
var _payload$featureDefin, _definitionWrapper$de;
|
|
5568
5569
|
// The actual definition data is in payload.featureDefinition.definition
|
|
5569
|
-
const definitionWrapper = payload
|
|
5570
|
-
const definition = definitionWrapper
|
|
5570
|
+
const definitionWrapper = (_payload$featureDefin = payload === null || payload === void 0 ? void 0 : payload.featureDefinition) !== null && _payload$featureDefin !== void 0 ? _payload$featureDefin : payload;
|
|
5571
|
+
const definition = (_definitionWrapper$de = definitionWrapper === null || definitionWrapper === void 0 ? void 0 : definitionWrapper.definition) !== null && _definitionWrapper$de !== void 0 ? _definitionWrapper$de : definitionWrapper;
|
|
5571
5572
|
|
|
5572
5573
|
// Validate and map definition data to form state structure
|
|
5573
5574
|
const mappedValues = {
|
|
5574
|
-
title: definition
|
|
5575
|
-
icon: definition
|
|
5576
|
-
displayName: definition
|
|
5577
|
-
layout: definition
|
|
5575
|
+
title: (definition === null || definition === void 0 ? void 0 : definition.title) || "",
|
|
5576
|
+
icon: (definition === null || definition === void 0 ? void 0 : definition.icon) || "star",
|
|
5577
|
+
displayName: (definition === null || definition === void 0 ? void 0 : definition.displayName) || "",
|
|
5578
|
+
layout: (definition === null || definition === void 0 ? void 0 : definition.layout) || {
|
|
5578
5579
|
gridIcon: undefined,
|
|
5579
5580
|
type: "round"
|
|
5580
5581
|
},
|
|
5581
|
-
fields:
|
|
5582
|
+
fields: Array.isArray(definition === null || definition === void 0 ? void 0 : definition.fields) ? definition.fields : state.fields
|
|
5582
5583
|
};
|
|
5583
5584
|
const newState = _objectSpread$3(_objectSpread$3(_objectSpread$3({}, state), mappedValues), {}, {
|
|
5584
5585
|
_isInitial: false
|
|
@@ -5783,14 +5784,15 @@ const definitionReducer = function () {
|
|
|
5783
5784
|
let definitionId = values.featureId; // Always use hardcoded ID
|
|
5784
5785
|
|
|
5785
5786
|
if (mode === "edit" && data) {
|
|
5787
|
+
var _data$featureDefiniti, _featureDefinitionWra, _featureDefinitionWra2, _definition;
|
|
5786
5788
|
// Extract from API response for edit mode
|
|
5787
5789
|
// Handle nested structure: data.featureDefinition.definition
|
|
5788
|
-
const featureDefinitionWrapper = data
|
|
5789
|
-
definition = featureDefinitionWrapper
|
|
5790
|
-
definitionId = featureDefinitionWrapper
|
|
5790
|
+
const featureDefinitionWrapper = (_data$featureDefiniti = data === null || data === void 0 ? void 0 : data.featureDefinition) !== null && _data$featureDefiniti !== void 0 ? _data$featureDefiniti : data;
|
|
5791
|
+
definition = (_featureDefinitionWra = featureDefinitionWrapper === null || featureDefinitionWrapper === void 0 ? void 0 : featureDefinitionWrapper.definition) !== null && _featureDefinitionWra !== void 0 ? _featureDefinitionWra : featureDefinitionWrapper;
|
|
5792
|
+
definitionId = (_featureDefinitionWra2 = featureDefinitionWrapper === null || featureDefinitionWrapper === void 0 ? void 0 : featureDefinitionWrapper.id) !== null && _featureDefinitionWra2 !== void 0 ? _featureDefinitionWra2 : values.featureId;
|
|
5791
5793
|
|
|
5792
5794
|
// Ensure fields array exists and preserves order property
|
|
5793
|
-
if (definition &&
|
|
5795
|
+
if ((_definition = definition) !== null && _definition !== void 0 && _definition.fields) {
|
|
5794
5796
|
// Create a new array to ensure we preserve all field properties including order
|
|
5795
5797
|
definition.fields = definition.fields.map(field => _objectSpread$3(_objectSpread$3({}, field), {}, {
|
|
5796
5798
|
// Ensure order property exists, fallback to array index if missing
|
|
@@ -5829,7 +5831,7 @@ const definitionReducer = function () {
|
|
|
5829
5831
|
// Optimistically update with new definition
|
|
5830
5832
|
mode: "edit" // Switch to edit mode immediately
|
|
5831
5833
|
}),
|
|
5832
|
-
id: payload
|
|
5834
|
+
id: payload === null || payload === void 0 ? void 0 : payload.id,
|
|
5833
5835
|
error: null
|
|
5834
5836
|
});
|
|
5835
5837
|
case FEATURE_EDIT_REQUEST:
|