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