@plusscommunities/pluss-feature-builder-web-d 1.0.7 → 1.0.9-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. package/dist/{index.cjs.js → index.js} +3803 -3504
  2. package/dist/index.js.map +1 -0
  3. package/package.json +20 -27
  4. package/.babelrc +0 -4
  5. package/rollup.config.js +0 -69
  6. package/src/actions/featureBuilderStringsActions.js +0 -88
  7. package/src/actions/featureDefinitionsIndex.js +0 -258
  8. package/src/actions/formActions.js +0 -301
  9. package/src/actions/index.js +0 -12
  10. package/src/actions/listingActions.js +0 -352
  11. package/src/actions/wizardActions.js +0 -228
  12. package/src/components/ActivityCardExample.jsx +0 -86
  13. package/src/components/ActivityCardExample.module.css +0 -130
  14. package/src/components/BackgroundLoader.jsx +0 -33
  15. package/src/components/BackgroundLoader.module.css +0 -46
  16. package/src/components/BaseFieldConfig.jsx +0 -305
  17. package/src/components/BaseFieldConfig.module.css +0 -42
  18. package/src/components/CenteredContainer.jsx +0 -29
  19. package/src/components/CenteredContainer.module.css +0 -171
  20. package/src/components/DeleteConfirmationPopup.jsx +0 -95
  21. package/src/components/DeleteConfirmationPopup.module.css +0 -12
  22. package/src/components/ErrorBoundary.jsx +0 -134
  23. package/src/components/ErrorBoundary.module.css +0 -77
  24. package/src/components/ErrorMessage.jsx +0 -85
  25. package/src/components/ErrorMessage.module.css +0 -116
  26. package/src/components/ExampleDisplay.jsx +0 -26
  27. package/src/components/ExampleDisplay.module.css +0 -3
  28. package/src/components/FeatureBuilderSidebar.jsx +0 -84
  29. package/src/components/FeatureBuilderSuccessPopup.jsx +0 -49
  30. package/src/components/FeatureBuilderSuccessPopup.module.css +0 -41
  31. package/src/components/FeatureBuilderWelcomePopup.jsx +0 -51
  32. package/src/components/FeatureBuilderWelcomePopup.module.css +0 -21
  33. package/src/components/FeatureListingCard.jsx +0 -104
  34. package/src/components/FeatureListingCard.module.css +0 -62
  35. package/src/components/Fields.jsx +0 -423
  36. package/src/components/Fields.module.css +0 -159
  37. package/src/components/IconLoader.jsx +0 -153
  38. package/src/components/IconLoader.module.css +0 -92
  39. package/src/components/IconSelector.jsx +0 -111
  40. package/src/components/IconSelector.module.css +0 -197
  41. package/src/components/ListingEditor.jsx +0 -405
  42. package/src/components/ListingEditor.module.css +0 -14
  43. package/src/components/ListingSuccessPopup.jsx +0 -52
  44. package/src/components/LoadingScreen.jsx +0 -54
  45. package/src/components/LoadingScreen.module.css +0 -103
  46. package/src/components/LoadingState.jsx +0 -40
  47. package/src/components/LoadingState.module.css +0 -18
  48. package/src/components/PreviewFull.js +0 -24
  49. package/src/components/PreviewFull.module.css +0 -11
  50. package/src/components/PreviewGrid.js +0 -14
  51. package/src/components/PreviewWidget.js +0 -27
  52. package/src/components/PreviewWidget.module.css +0 -15
  53. package/src/components/SidebarLayout.jsx +0 -252
  54. package/src/components/SidebarLayout.module.css +0 -71
  55. package/src/components/SkeletonLoader.jsx +0 -128
  56. package/src/components/SkeletonLoader.module.css +0 -295
  57. package/src/components/SortButtonGroup.jsx +0 -34
  58. package/src/components/SortButtonGroup.module.css +0 -51
  59. package/src/components/ToastContainer.jsx +0 -98
  60. package/src/components/ToastContainer.module.css +0 -156
  61. package/src/components/ToggleSwitch.js +0 -40
  62. package/src/components/ToggleSwitch.module.css +0 -48
  63. package/src/components/TwoColumnInput.jsx +0 -29
  64. package/src/components/TwoColumnInput.module.css +0 -32
  65. package/src/components/ViewFull.js +0 -139
  66. package/src/components/ViewFull.module.css +0 -71
  67. package/src/components/ViewWidget.js +0 -62
  68. package/src/components/ViewWidget.module.css +0 -28
  69. package/src/components/iconCategories.js +0 -135
  70. package/src/components/iconImports.js +0 -409
  71. package/src/components/index.js +0 -59
  72. package/src/components/listing/FileListItem.jsx +0 -86
  73. package/src/components/listing/GalleryDisplay.jsx +0 -330
  74. package/src/components/listing/GalleryDisplay.module.css +0 -309
  75. package/src/components/listing/ListingCTAInput.jsx +0 -82
  76. package/src/components/listing/ListingDescriptionInput.jsx +0 -73
  77. package/src/components/listing/ListingField.jsx +0 -101
  78. package/src/components/listing/ListingField.module.css +0 -106
  79. package/src/components/listing/ListingFileInput.jsx +0 -273
  80. package/src/components/listing/ListingFileInput.module.css +0 -189
  81. package/src/components/listing/ListingForm.jsx +0 -90
  82. package/src/components/listing/ListingForm.module.css +0 -38
  83. package/src/components/listing/ListingGalleryInput.jsx +0 -239
  84. package/src/components/listing/ListingGalleryInput.module.css +0 -132
  85. package/src/components/listing/ListingImageInput.jsx +0 -153
  86. package/src/components/listing/ListingTextInput.jsx +0 -72
  87. package/src/feature.config.js +0 -130
  88. package/src/helper/index.js +0 -135
  89. package/src/hooks/useFeatureDefinitionLoader.js +0 -66
  90. package/src/images/full.png +0 -0
  91. package/src/images/fullNoTitle.png +0 -0
  92. package/src/images/previewWidget.png +0 -0
  93. package/src/images/widget.png +0 -0
  94. package/src/index.js +0 -38
  95. package/src/pages/CreateListingPage.jsx +0 -49
  96. package/src/pages/EditListingPage.jsx +0 -58
  97. package/src/reducers/featureBuilderReducer.js +0 -739
  98. package/src/screens/CreateListing.module.css +0 -45
  99. package/src/screens/Form.module.css +0 -744
  100. package/src/screens/FormFieldsStep.jsx +0 -626
  101. package/src/screens/FormLayoutStep.jsx +0 -405
  102. package/src/screens/FormOverviewStep.jsx +0 -389
  103. package/src/screens/ListingScreen.jsx +0 -477
  104. package/src/screens/ListingScreen.module.css +0 -333
  105. package/src/selectors/featureBuilderSelectors.js +0 -533
  106. package/src/types/index.js +0 -91
  107. package/src/utils/textUtils.js +0 -89
  108. package/src/validators/galleryValidators.js +0 -345
  109. package/src/values.config.a.js +0 -49
  110. package/src/values.config.b.js +0 -49
  111. package/src/values.config.c.js +0 -49
  112. package/src/values.config.d.js +0 -49
  113. package/src/values.config.default.js +0 -49
  114. package/src/values.config.js +0 -49
  115. package/src/webapi/featureDefinitionActions.js +0 -0
  116. package/src/webapi/featuresActions.js +0 -90
  117. package/src/webapi/helper.js +0 -4
  118. package/src/webapi/index.js +0 -12
  119. package/src/webapi/listingActions.js +0 -176
@@ -1,739 +0,0 @@
1
- /**
2
- * Feature Builder Redux Reducer
3
- * Manages state for feature creation wizard including:
4
- * - Form state (title, icon, fields, layout)
5
- * - Validation and step management
6
- * - Loading and error states
7
- * - Feature definitions CRUD operations
8
- *
9
- * @typedef {Object} FeatureBuilderState
10
- * @property {Object} formState - Current form configuration
11
- * @property {Array} featureDefinitions - Available feature definitions
12
- * @property {Object} wizardState - Wizard navigation and validation state
13
- * @property {boolean} loading - Loading state for async operations
14
- * @property {Error} error - Current error state
15
- *
16
- * @param {FeatureBuilderState} state - Current Redux state
17
- * @param {Object} action - Redux action to process
18
- * @returns {FeatureBuilderState} New Redux state
19
- *
20
- * @example
21
- * // Reducer is used automatically with Redux
22
- * const store = createStore(featureBuilderReducer, initialState);
23
- */
24
- // Import action types from existing files
25
- import { actionsTypes as formActionTypes } from "../actions/formActions";
26
- import {
27
- FETCH_FEATURES_REQUEST,
28
- FETCH_FEATURES_SUCCESS,
29
- FETCH_FEATURES_FAILURE,
30
- FEATURE_CREATE_REQUEST,
31
- FEATURE_CREATE_SUCCESS,
32
- FEATURE_CREATE_FAILURE,
33
- FEATURE_EDIT_REQUEST,
34
- FEATURE_EDIT_SUCCESS,
35
- FEATURE_EDIT_FAILURE,
36
- FEATURE_DELETE_REQUEST,
37
- FEATURE_DELETE_FAILURE,
38
- SYNC_WIZARD_MODE_FROM_DEFINITION,
39
- SET_WIZARD_MODE,
40
- } from "../actions/featureDefinitionsIndex";
41
-
42
- import {
43
- FETCH_LISTING_REQUEST,
44
- FETCH_LISTING_SUCCESS,
45
- FETCH_LISTING_FAILURE,
46
- FETCH_LISTING_SILENT_SUCCESS,
47
- FETCH_LISTING_SILENT_FAILURE,
48
- REORDER_LISTING_SUCCESS,
49
- REORDER_LISTING_FAILURE,
50
- DELETE_LISTING_REQUEST,
51
- DELETE_LISTING_SUCCESS,
52
- DELETE_LISTING_FAILURE,
53
- UNDELETE_LISTING_REQUEST,
54
- UNDELETE_LISTING_SUCCESS,
55
- UNDELETE_LISTING_FAILURE,
56
- CREATE_LISTING_REQUEST,
57
- CREATE_LISTING_SUCCESS,
58
- CREATE_LISTING_FAILURE,
59
- EDIT_LISTING_REQUEST,
60
- EDIT_LISTING_SUCCESS,
61
- EDIT_LISTING_FAILURE,
62
- TOGGLE_LISTING_REQUEST,
63
- TOGGLE_LISTING_SUCCESS,
64
- TOGGLE_LISTING_FAILURE,
65
- SET_SORT_BY,
66
- SET_SHOW_DELETED,
67
- } from "../actions/listingActions";
68
-
69
- import { values } from "../values.config";
70
-
71
- // Wizard action types (local declarations only)
72
- const SET_NAVIGATION_STATE = "SET_NAVIGATION_STATE";
73
- const UPDATE_STEP_VALIDATION = "UPDATE_STEP_VALIDATION";
74
- const MARK_STEP_COMPLETE = "MARK_STEP_COMPLETE";
75
- const RESET_WIZARD_STATE = "RESET_WIZARD_STATE";
76
-
77
- // Default new field definition
78
- const DEFAULT_NEW_FIELD = {
79
- type: "text",
80
- isMandatory: false,
81
- values: {
82
- label: "",
83
- placeholder: "",
84
- isRequired: false,
85
- helpText: "",
86
- allowCaption: false, // Default to not allowing captions for image fields
87
- useAsSummary: false, // Default to not using as summary for description fields
88
- minImages: 1, // Gallery-specific default
89
- maxImages: 16, // Gallery-specific default
90
- maxFileSize: "5MB", // Gallery-specific default
91
- allowedTypes: [], // Gallery-specific default
92
- },
93
- };
94
-
95
- function createNewField(id, type = "text", existingFields = []) {
96
- const baseField = {
97
- ...DEFAULT_NEW_FIELD,
98
- id,
99
- type,
100
- values: { ...DEFAULT_NEW_FIELD.values },
101
- };
102
-
103
- // Calculate order for new field
104
- const maxOrder = Math.max(...existingFields.map((f) => f.order || 0), 1); // Start from 1 since title is order 1
105
- baseField.order = maxOrder + 1;
106
-
107
- // Set default values based on field type
108
- switch (type) {
109
- case "title":
110
- baseField.values.label = "Title";
111
- baseField.values.placeholder = "Enter title";
112
- baseField.values.helpText = "The main title or heading for this item";
113
- baseField.values.isRequired = true;
114
- break;
115
-
116
- case "text":
117
- baseField.values.label = "Text";
118
- baseField.values.placeholder = "Enter text";
119
- baseField.values.helpText = "Provide additional text information";
120
- baseField.values.isRequired = true;
121
- break;
122
-
123
- case "description":
124
- baseField.values.label = "Description";
125
- baseField.values.placeholder = "Enter description";
126
- baseField.values.helpText = "Detailed description of the item";
127
- break;
128
-
129
- case "image": {
130
- baseField.values.label = "Image Upload";
131
- baseField.values.helpText = "Upload an image to display";
132
- const { placeholder, ...valuesWithoutPlaceholder } = baseField.values;
133
- baseField.values = valuesWithoutPlaceholder;
134
- break;
135
- }
136
-
137
- case "gallery": {
138
- baseField.values.label = "Image Gallery";
139
- baseField.values.helpText = "Upload multiple images to create a gallery";
140
- baseField.values.minImages = 1;
141
- baseField.values.maxImages = 16;
142
- baseField.values.maxFileSize = "5MB";
143
- baseField.values.allowedTypes = ["image/jpeg", "image/png", "image/webp"];
144
- const {
145
- placeholder: galleryPlaceholder,
146
- ...galleryValuesWithoutPlaceholder
147
- } = baseField.values;
148
- baseField.values = galleryValuesWithoutPlaceholder;
149
- break;
150
- }
151
-
152
- case "feature-image": {
153
- baseField.values.label = "Feature Image";
154
- baseField.values.helpText = "Main featured image for this item";
155
- baseField.values.isRequired = true;
156
- const {
157
- placeholder: ph,
158
- allowCaption,
159
- ...valuesWithoutCaptionAndPlaceholder
160
- } = baseField.values;
161
- baseField.values = valuesWithoutCaptionAndPlaceholder;
162
- break;
163
- }
164
-
165
- case "cta":
166
- baseField.values.label = "Action Button";
167
- baseField.values.placeholder = "Button text";
168
- baseField.values.helpText = "Text to display on the action button";
169
- break;
170
-
171
- case "file":
172
- baseField.values.label = "File";
173
- baseField.values.placeholder = "Select file";
174
- baseField.values.helpText = "Upload a file for users to download";
175
- break;
176
-
177
- default:
178
- baseField.values.label = "Field";
179
- baseField.values.placeholder = "Enter value";
180
- baseField.values.helpText = "Provide information for this field";
181
- break;
182
- }
183
-
184
- return baseField;
185
- }
186
-
187
- // Initial form state (from existing formReducer)
188
- const INITIAL_FORM_STATE = {
189
- _isInitial: true,
190
- title: "",
191
- icon: values.defaultIcon,
192
- displayName: "",
193
- layout: {
194
- gridIcon: undefined,
195
- type: "round",
196
- },
197
- fields: [
198
- {
199
- id: "mandatory-feature-image",
200
- type: "feature-image",
201
- isMandatory: true,
202
- order: 0,
203
- values: {
204
- label: "Feature Image",
205
- isRequired: true, // Feature image is always required
206
- helpText: "The main picture to display on the content",
207
- },
208
- },
209
- {
210
- id: "mandatory-title",
211
- type: "title",
212
- isMandatory: true,
213
- order: 1,
214
- values: {
215
- label: "Title",
216
- placeholder: "Enter title here",
217
- isRequired: true,
218
- helpText: "The title of your listing",
219
- },
220
- },
221
- ],
222
- };
223
-
224
- // Helper function to get initial wizard state
225
- const getInitialWizardState = () => {
226
- return {
227
- mode: "create", // 'create' | 'edit'
228
- navigation: {
229
- currentStep: "welcome", // 'welcome' | 'overview' | 'fields' | 'layout'
230
- previousStep: null,
231
- canGoBack: false,
232
- canGoForward: true,
233
- },
234
- stepValidation: {
235
- overview: { isValid: false, errors: {} },
236
- fields: { isValid: false, errors: {} },
237
- layout: { isValid: false, errors: {} },
238
- },
239
- stepCompletion: {
240
- overview: false,
241
- fields: false,
242
- layout: false,
243
- },
244
- };
245
- };
246
-
247
- // Initial consolidated state
248
- const INITIAL_STATE = {
249
- form: INITIAL_FORM_STATE,
250
- definition: {
251
- id: null,
252
- definition: null,
253
- isLoading: false,
254
- error: null,
255
- isCreating: false,
256
- isEditing: false,
257
- mode: null, // Add mode field
258
- },
259
- listings: {
260
- listings: [],
261
- isLoading: false,
262
- isCreating: false,
263
- isEditing: false,
264
- isRestoring: false,
265
- error: null,
266
- sortBy: "newest",
267
- showDeleted: false,
268
- isInitiallyLoaded: false,
269
- },
270
- wizard: getInitialWizardState(),
271
- };
272
-
273
- // Form reducer logic (from existing formReducer)
274
- const formReducer = (state = INITIAL_FORM_STATE, action) => {
275
- const { type, payload } = action;
276
-
277
- switch (type) {
278
- case formActionTypes.SET_INITIAL_VALUES: {
279
- // The actual definition data is in payload.featureDefinition.definition
280
- const definitionWrapper =
281
- (payload && payload.featureDefinition) || payload;
282
- const definition =
283
- (definitionWrapper && definitionWrapper.definition) ||
284
- definitionWrapper;
285
-
286
- // Validate and map definition data to form state structure
287
- const mappedValues = {
288
- title: definition?.title || "",
289
- icon: definition?.icon || "star",
290
- displayName: definition?.displayName || "",
291
- layout: definition?.layout || {
292
- gridIcon: undefined,
293
- type: "round",
294
- },
295
- fields: Array.isArray(definition?.fields)
296
- ? definition.fields
297
- : state.fields,
298
- };
299
-
300
- const newState = {
301
- ...state,
302
- ...mappedValues,
303
- _isInitial: false,
304
- };
305
- return newState;
306
- }
307
-
308
- case formActionTypes.SET_TITLE:
309
- return { ...state, title: payload };
310
-
311
- case formActionTypes.SET_ICON:
312
- return { ...state, icon: payload };
313
-
314
- case formActionTypes.SET_DISPLAY_NAME:
315
- return { ...state, displayName: payload };
316
-
317
- case formActionTypes.ADD_FIELD: {
318
- // Safety check for payload
319
- if (!payload || !payload.id) {
320
- return state;
321
- }
322
-
323
- const newField = createNewField(payload.id, payload.type, state.fields);
324
- const newFields = [...state.fields, newField];
325
- return { ...state, fields: newFields };
326
- }
327
-
328
- case formActionTypes.DELETE_FIELD: {
329
- const newFields = state.fields.filter((field) => field.id !== payload);
330
- return { ...state, fields: newFields };
331
- }
332
-
333
- case formActionTypes.UPDATE_FIELD: {
334
- const { id, updatedField } = payload;
335
-
336
- // Safety check: ensure payload is valid
337
- if (!id || !updatedField || typeof updatedField !== "object") {
338
- return state;
339
- }
340
-
341
- const newFields = state.fields.map((field) => {
342
- if (field.id === id) {
343
- // Preserve existing field structure and merge updates safely
344
- const updatedFieldWithSafety = {
345
- ...field, // Preserve all existing properties
346
- ...updatedField, // Merge updates (should be {values: {...}})
347
- };
348
- return updatedFieldWithSafety;
349
- }
350
- return field;
351
- });
352
-
353
- return { ...state, fields: newFields };
354
- }
355
-
356
- case formActionTypes.SET_SUMMARY_FIELD: {
357
- // Safety check: ensure payload is valid
358
- if (!payload) {
359
- return state;
360
- }
361
-
362
- const newFields = state.fields.map((field) => {
363
- if (field.id === payload) {
364
- // Set the selected field as summary
365
- return {
366
- ...field,
367
- values: {
368
- ...field.values,
369
- useAsSummary: true,
370
- },
371
- };
372
- } else if (field.type === "description") {
373
- // Unset all other description fields
374
- return {
375
- ...field,
376
- values: {
377
- ...field.values,
378
- useAsSummary: false,
379
- },
380
- };
381
- }
382
- return field;
383
- });
384
-
385
- return { ...state, fields: newFields };
386
- }
387
-
388
- case formActionTypes.SET_LAYOUT_GRID_ICON: {
389
- return {
390
- ...state,
391
- layout: { ...state.layout, gridIcon: payload },
392
- };
393
- }
394
-
395
- case formActionTypes.SET_LAYOUT_TYPE: {
396
- return {
397
- ...state,
398
- layout: { ...state.layout, type: payload },
399
- };
400
- }
401
-
402
- case formActionTypes.SUBMIT_FORM_REQUEST:
403
- return {
404
- ...state,
405
- _isSubmitting: true,
406
- _submitError: null,
407
- _submitSuccess: false,
408
- };
409
-
410
- case formActionTypes.SUBMIT_FORM_SUCCESS:
411
- return {
412
- ...state,
413
- _isSubmitting: false,
414
- _submitError: null,
415
- _submitSuccess: true,
416
- };
417
-
418
- case formActionTypes.SUBMIT_FORM_FAILURE:
419
- return {
420
- ...state,
421
- _isSubmitting: false,
422
- _submitError: payload,
423
- _submitSuccess: false,
424
- };
425
-
426
- case formActionTypes.CLEAR_FORM_SUBMISSION_STATE:
427
- return {
428
- ...state,
429
- _isSubmitting: false,
430
- _submitError: null,
431
- _submitSuccess: false,
432
- };
433
-
434
- default:
435
- return state;
436
- }
437
- };
438
-
439
- // Wizard reducer logic - now a pure function
440
- const wizardReducer = (state = INITIAL_STATE.wizard, action) => {
441
- const { type, payload } = action;
442
-
443
- switch (type) {
444
- case SYNC_WIZARD_MODE_FROM_DEFINITION:
445
- return {
446
- ...state,
447
- mode: payload,
448
- navigation: {
449
- ...state.navigation,
450
- currentStep: "overview",
451
- },
452
- };
453
-
454
- case SET_WIZARD_MODE:
455
- return {
456
- ...state,
457
- mode: payload,
458
- };
459
-
460
- case SET_NAVIGATION_STATE:
461
- return {
462
- ...state,
463
- navigation: {
464
- ...state.navigation,
465
- ...payload,
466
- },
467
- };
468
-
469
- case UPDATE_STEP_VALIDATION:
470
- return {
471
- ...state,
472
- stepValidation: {
473
- ...state.stepValidation,
474
- [payload.step]: {
475
- isValid: payload.isValid,
476
- errors: payload.errors || {},
477
- },
478
- },
479
- };
480
-
481
- case MARK_STEP_COMPLETE:
482
- return {
483
- ...state,
484
- stepCompletion: {
485
- ...state.stepCompletion,
486
- [payload]: true,
487
- },
488
- };
489
-
490
- case RESET_WIZARD_STATE:
491
- return INITIAL_STATE.wizard;
492
-
493
- default:
494
- return state;
495
- }
496
- };
497
-
498
- // Definition reducer logic (simplified for single definition)
499
- const definitionReducer = (state = INITIAL_STATE.definition, action) => {
500
- const { type, payload } = action;
501
-
502
- switch (type) {
503
- case FETCH_FEATURES_REQUEST:
504
- return { ...state, isLoading: true, error: null };
505
-
506
- case FETCH_FEATURES_SUCCESS: {
507
- // payload now includes mode: 'edit' or 'create'
508
- const { data, mode } = payload || {};
509
-
510
- let definition = null;
511
- let definitionId = values.featureId; // Always use hardcoded ID
512
-
513
- if (mode === "edit" && data) {
514
- // Extract from API response for edit mode
515
- // Handle nested structure: data.featureDefinition.definition
516
- const featureDefinitionWrapper =
517
- (data && data.featureDefinition) || data;
518
- definition =
519
- (featureDefinitionWrapper && featureDefinitionWrapper.definition) ||
520
- featureDefinitionWrapper;
521
- definitionId =
522
- (featureDefinitionWrapper && featureDefinitionWrapper.id) ||
523
- values.featureId;
524
-
525
- // Ensure fields array exists and preserves order property
526
- if (definition?.fields) {
527
- // Create a new array to ensure we preserve all field properties including order
528
- definition.fields = definition.fields.map((field) => ({
529
- ...field,
530
- // Ensure order property exists, fallback to array index if missing
531
- order: field.order !== undefined ? field.order : 999,
532
- }));
533
- }
534
- }
535
-
536
- return {
537
- ...state,
538
- definition,
539
- id: definitionId,
540
- isLoading: false,
541
- error: null,
542
- mode, // Store the mode from fetch
543
- };
544
- }
545
-
546
- case FETCH_FEATURES_FAILURE:
547
- return { ...state, isLoading: false, error: payload };
548
-
549
- case FEATURE_CREATE_REQUEST:
550
- return { ...state, isCreating: true, error: null };
551
-
552
- case FEATURE_CREATE_FAILURE:
553
- return { ...state, isCreating: false, error: payload };
554
-
555
- case FEATURE_CREATE_SUCCESS:
556
- return {
557
- ...state,
558
- isCreating: false,
559
- definition: {
560
- ...state.definition,
561
- definition: payload, // Optimistically update with new definition
562
- mode: "edit", // Switch to edit mode immediately
563
- },
564
- id: payload?.id,
565
- error: null,
566
- };
567
-
568
- case FEATURE_EDIT_REQUEST:
569
- return { ...state, isEditing: true, error: null };
570
-
571
- case FEATURE_EDIT_FAILURE:
572
- return { ...state, isEditing: false, error: payload };
573
-
574
- case FEATURE_EDIT_SUCCESS:
575
- return {
576
- ...state,
577
- isEditing: false,
578
- definition: payload,
579
- error: null,
580
- };
581
-
582
- case FEATURE_DELETE_REQUEST:
583
- return { ...state, isLoading: true };
584
-
585
- case FEATURE_DELETE_FAILURE:
586
- return { ...state, isLoading: false, error: payload };
587
-
588
- default:
589
- return state;
590
- }
591
- };
592
-
593
- // Listings reducer logic (updated to use 'listings' instead of 'items')
594
- const listingsReducer = (state = INITIAL_STATE.listings, action) => {
595
- const { type, payload } = action;
596
-
597
- switch (type) {
598
- case FETCH_LISTING_REQUEST:
599
- return { ...state, isLoading: true, error: null };
600
-
601
- case FETCH_LISTING_SUCCESS:
602
- return {
603
- ...state,
604
- isLoading: false,
605
- listings: payload,
606
- isInitiallyLoaded: true,
607
- };
608
-
609
- case FETCH_LISTING_FAILURE:
610
- return { ...state, isLoading: false, error: payload };
611
-
612
- case FETCH_LISTING_SILENT_SUCCESS:
613
- return { ...state, listings: payload, isInitiallyLoaded: true };
614
-
615
- case FETCH_LISTING_SILENT_FAILURE:
616
- return { ...state, error: payload };
617
-
618
- case REORDER_LISTING_SUCCESS:
619
- return { ...state, listings: payload };
620
-
621
- case DELETE_LISTING_SUCCESS:
622
- return {
623
- ...state,
624
- listings: state.listings.map((listing) =>
625
- listing.id === payload
626
- ? { ...listing, deletedAt: new Date().toISOString() }
627
- : listing,
628
- ),
629
- };
630
-
631
- case DELETE_LISTING_FAILURE:
632
- return { ...state, error: payload };
633
-
634
- case UNDELETE_LISTING_REQUEST:
635
- return { ...state, isRestoring: true, error: null };
636
-
637
- case UNDELETE_LISTING_SUCCESS:
638
- // Handle both scenarios: restored listing data or just ID
639
- if (typeof payload === "object" && payload.id) {
640
- // We have the restored listing data - add it back to the list
641
- const restoredListing = payload;
642
- const listingExists = state.listings.some(
643
- (listing) => listing.id === restoredListing.id,
644
- );
645
- if (!listingExists) {
646
- return {
647
- ...state,
648
- listings: [...state.listings, restoredListing],
649
- error: null,
650
- isRestoring: false,
651
- };
652
- }
653
- // Listing already exists, update it
654
- return {
655
- ...state,
656
- listings: state.listings.map((listing) =>
657
- listing.id === restoredListing.id ? restoredListing : listing,
658
- ),
659
- error: null,
660
- isRestoring: false,
661
- };
662
- } else {
663
- // We only have the ID - don't set loading as fetchListingSilent will handle the update
664
- return { ...state, error: null, isRestoring: false };
665
- }
666
-
667
- case UNDELETE_LISTING_FAILURE:
668
- return { ...state, error: payload, isRestoring: false };
669
-
670
- case CREATE_LISTING_REQUEST:
671
- return { ...state, isCreating: true, error: null };
672
-
673
- case CREATE_LISTING_SUCCESS:
674
- return {
675
- ...state,
676
- isCreating: false,
677
- listings: [...state.listings, payload],
678
- };
679
-
680
- case CREATE_LISTING_FAILURE:
681
- return { ...state, isCreating: false, error: payload };
682
-
683
- case EDIT_LISTING_REQUEST:
684
- return { ...state, isEditing: true, error: null };
685
-
686
- case EDIT_LISTING_SUCCESS:
687
- return {
688
- ...state,
689
- isEditing: false,
690
- listings: state.listings.map((listing) =>
691
- listing.id === payload.id ? payload : listing,
692
- ),
693
- error: null,
694
- };
695
-
696
- case EDIT_LISTING_FAILURE:
697
- return { ...state, isEditing: false, error: payload };
698
-
699
- case TOGGLE_LISTING_SUCCESS:
700
- return {
701
- ...state,
702
- listings: state.listings.map((listing) =>
703
- listing.id === payload.id
704
- ? { ...listing, isActive: payload.isActive }
705
- : listing,
706
- ),
707
- };
708
-
709
- case TOGGLE_LISTING_FAILURE:
710
- return { ...state, error: payload };
711
-
712
- case SET_SORT_BY:
713
- return { ...state, sortBy: payload };
714
-
715
- case SET_SHOW_DELETED:
716
- return { ...state, showDeleted: payload };
717
-
718
- default:
719
- return state;
720
- }
721
- };
722
-
723
- // Main consolidated reducer - now eliminates circular dependencies
724
- export function featureBuilderReducer(state = INITIAL_STATE, action) {
725
- // Calculate new states for all reducers independently
726
- const newForm = formReducer(state.form, action);
727
- const newDefinition = definitionReducer(state.definition, action);
728
- const newListings = listingsReducer(state.listings, action);
729
- const newWizard = wizardReducer(state.wizard, action);
730
-
731
- return {
732
- form: newForm,
733
- definition: newDefinition,
734
- listings: newListings,
735
- wizard: newWizard,
736
- };
737
- }
738
-
739
- export default featureBuilderReducer;