@plusscommunities/pluss-feature-builder-web-a 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,301 +0,0 @@
1
- import { featureDefinitionActions } from "../webapi/featuresActions";
2
- import { values } from "../values.config";
3
- import {
4
- updateFeatureBuilderString,
5
- updateFeatureBuilderIcon,
6
- } from "./featureBuilderStringsActions";
7
-
8
- /**
9
- * @typedef {Object} FieldValues
10
- * @property {string} [label] - Field label text
11
- * @property {string} [placeholder] - Placeholder text for input
12
- * @property {boolean} [isRequired] - Whether field is required
13
- * @property {string} [helpText] - Help text for field guidance
14
- * @property {boolean} [allowCaption] - Whether field allows captions
15
- * @property {boolean} [useAsSummary] - Whether field is used as summary
16
- */
17
-
18
- /**
19
- * @typedef {Object} FormField
20
- * @property {string} id - Unique field identifier
21
- * @property {string} type - Field type (text, title, description, image, file, cta, feature-image)
22
- * @property {boolean} isMandatory - Whether field is mandatory system field
23
- * @property {FieldValues} values - Field configuration values
24
- */
25
-
26
- /**
27
- * @typedef {Object} LayoutConfig
28
- * @property {string} type - Layout type (round, square, etc.)
29
- * @property {string} [gridIcon] - Background image for grid layout
30
- */
31
-
32
- export const actionsTypes = {
33
- SET_INITIAL_VALUES: `${values.reducerKey.toUpperCase()}_SET_INITIAL_VALUES`,
34
- SET_TITLE: `${values.reducerKey.toUpperCase()}_SET_TITLE`,
35
- SET_ICON: `${values.reducerKey.toUpperCase()}_SET_ICON`,
36
- SET_DISPLAY_NAME: `${values.reducerKey.toUpperCase()}_SET_DISPLAY_NAME`,
37
- ADD_FIELD: `${values.reducerKey.toUpperCase()}_ADD_FIELD`,
38
- DELETE_FIELD: `${values.reducerKey.toUpperCase()}_DELETE_FIELD`,
39
- UPDATE_FIELD: `${values.reducerKey.toUpperCase()}_UPDATE_FIELD`,
40
- SET_LAYOUT_GRID_ICON: `${values.reducerKey.toUpperCase()}_SET_LAYOUT_GRID_ICON`,
41
- SET_LAYOUT_TYPE: `${values.reducerKey.toUpperCase()}_SET_LAYOUT_TYPE`,
42
- SUBMIT_FORM_REQUEST: `${values.reducerKey.toUpperCase()}_SUBMIT_FORM_REQUEST`,
43
- SUBMIT_FORM_SUCCESS: `${values.reducerKey.toUpperCase()}_SUBMIT_FORM_SUCCESS`,
44
- SUBMIT_FORM_FAILURE: `${values.reducerKey.toUpperCase()}_SUBMIT_FORM_FAILURE`,
45
- CLEAR_FORM_SUBMISSION_STATE: `${values.reducerKey.toUpperCase()}_CLEAR_FORM_SUBMISSION_STATE`,
46
- SET_SUMMARY_FIELD: `${values.reducerKey.toUpperCase()}_SET_SUMMARY_FIELD`,
47
- };
48
-
49
- /**
50
- * Action creator to set initial form values
51
- * Initializes form state with existing feature definition data
52
- *
53
- * @param {Object} initialValues - Initial form values object
54
- * @param {string} initialValues.title - Feature title
55
- * @param {string} initialValues.icon - Feature icon
56
- * @param {string} initialValues.displayName - Feature display name
57
- * @param {LayoutConfig} initialValues.layout - Layout configuration
58
- * @param {FormField[]} initialValues.fields - Form fields array
59
- * @returns {Object} Redux action object with type and payload
60
- *
61
- * @example
62
- * dispatch(setInitialValues({
63
- * title: 'Contact Form',
64
- * icon: 'envelope',
65
- * fields: []
66
- * }));
67
- */
68
- export const setInitialValues = (initialValues) => {
69
- return { type: actionsTypes.SET_INITIAL_VALUES, payload: initialValues };
70
- };
71
-
72
- export const setTitle = (title) => {
73
- return { type: actionsTypes.SET_TITLE, payload: title };
74
- };
75
-
76
- /**
77
- * Action creator to set form display name and update menu
78
- * Updates both form state and external menu string
79
- * Title cases the display name before updating the strings store
80
- *
81
- * @param {string} displayName - New display name for form
82
- * @returns {Function} Thunk function for Redux
83
- *
84
- * @example
85
- * dispatch(setDisplayName('Contact Information'));
86
- */
87
- export const setDisplayName = (displayName) => {
88
- return (dispatch) => {
89
- // Update menu string when display name changes (will be title cased in updateFeatureBuilderString)
90
- dispatch(updateFeatureBuilderString(displayName));
91
-
92
- // Dispatch the actual action
93
- dispatch({
94
- type: actionsTypes.SET_DISPLAY_NAME,
95
- payload: displayName,
96
- });
97
- };
98
- };
99
-
100
- /**
101
- * Action creator to set form icon and update menu
102
- * Updates both form state and external menu icon
103
- *
104
- * @param {string} icon - Icon identifier or FontAwesome icon class
105
- * @returns {Function} Thunk function for Redux
106
- *
107
- * @example
108
- * dispatch(setIcon('fa-user'));
109
- */
110
- export const setIcon = (icon) => {
111
- return (dispatch) => {
112
- // Update menu icon when icon changes
113
- dispatch(updateFeatureBuilderIcon(icon));
114
-
115
- // Dispatch the actual action
116
- dispatch({
117
- type: actionsTypes.SET_ICON,
118
- payload: icon,
119
- });
120
- };
121
- };
122
-
123
- /**
124
- * Action creator to add a new field to the form
125
- * Generates unique ID and supports custom field types
126
- *
127
- * @param {string} [fieldType="text"] - Type of field to add (text, title, description, image, gallery, file, cta, feature-image)
128
- * @returns {Object} Redux action object with field ID and type
129
- *
130
- * @example
131
- * dispatch(addField('text'));
132
- * dispatch(addField('image'));
133
- */
134
- export const addField = (fieldType = "text") => {
135
- // Generate a unique ID for the new field
136
- const fieldId = `custom-field-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
137
- return {
138
- type: actionsTypes.ADD_FIELD,
139
- payload: { id: fieldId, type: fieldType },
140
- };
141
- };
142
-
143
- export const deleteField = (id) => {
144
- return { type: actionsTypes.DELETE_FIELD, payload: id };
145
- };
146
-
147
- export const updateFieldById = (id, updatedField) => {
148
- return { type: actionsTypes.UPDATE_FIELD, payload: { id, updatedField } };
149
- };
150
-
151
- /**
152
- * Action creator to set a description field as the summary field
153
- * Ensures only one description field can be marked as summary by
154
- * automatically unsetting all other description fields when a new one is selected
155
- *
156
- * @param {string} fieldId - ID of the description field to set as summary
157
- * @returns {Object} Redux action object for summary field selection
158
- *
159
- * @example
160
- * dispatch(setSummaryField('field-description-123'));
161
- */
162
- export const setSummaryField = (fieldId) => {
163
- return { type: actionsTypes.SET_SUMMARY_FIELD, payload: fieldId };
164
- };
165
-
166
- export const setGridLayoutIcon = (imageUrl) => {
167
- return { type: actionsTypes.SET_LAYOUT_GRID_ICON, payload: imageUrl };
168
- };
169
-
170
- export const setLayoutType = (layoutType) => {
171
- return { type: actionsTypes.SET_LAYOUT_TYPE, payload: layoutType };
172
- };
173
-
174
- const submitFormRequest = () => {
175
- return { type: actionsTypes.SUBMIT_FORM_REQUEST };
176
- };
177
-
178
- const submitFormSuccess = () => {
179
- return { type: actionsTypes.SUBMIT_FORM_SUCCESS };
180
- };
181
-
182
- const submitFormFailure = (error) => {
183
- return { type: actionsTypes.SUBMIT_FORM_FAILURE, payload: error };
184
- };
185
-
186
- export const clearFormSubmissionState = () => {
187
- return { type: actionsTypes.CLEAR_FORM_SUBMISSION_STATE };
188
- };
189
-
190
- /**
191
- * Submits the complete feature form to the server
192
- * Handles form validation, API submission, and error handling
193
- *
194
- * @returns {Function} Async thunk function for Redux
195
- * @throws {Error} When form validation fails or API submission encounters error
196
- */
197
- export function submitForm() {
198
- return async (dispatch, getState) => {
199
- const state = getState()[values.reducerKey];
200
- const form = state?.form;
201
- if (!form) {
202
- dispatch(
203
- submitFormFailure(
204
- new Error(
205
- "Form data is missing. Please refresh the page and try again.",
206
- ),
207
- ),
208
- );
209
- return;
210
- }
211
-
212
- dispatch(submitFormRequest());
213
- try {
214
- // Get site from auth store
215
- const site = getState().auth.site;
216
- if (!site) {
217
- throw new Error(
218
- "Authentication error: Site context not found. Please refresh and login again.",
219
- );
220
- }
221
-
222
- // Use mode from fetch instead of checking definition ID
223
- const definitionState = state?.definition;
224
- const mode = definitionState?.mode; // Use stored mode from fetch
225
- const definitionId = definitionState?.id;
226
-
227
- if (mode === "edit") {
228
- // Always update when in edit mode
229
- const updatedDefinition = {
230
- id: definitionId,
231
- site: site, // Include site from auth store
232
- featureDefinition: {
233
- // Wrap in expected structure for backend
234
- title: form.title,
235
- icon: form.icon,
236
- displayName: form.displayName,
237
- layout: form.layout,
238
- fields: form.fields,
239
- },
240
- };
241
- await featureDefinitionActions.edit(updatedDefinition, site);
242
- } else {
243
- // Always create when in create mode (or mode is undefined/null)
244
- if (!values.featureId || !site) {
245
- throw new Error(
246
- "Authentication error: Missing required context (featureId or site).",
247
- );
248
- }
249
- await featureDefinitionActions.create(
250
- values.featureId,
251
- site, // Use actual site from auth store
252
- {
253
- title: form.title,
254
- icon: form.icon,
255
- displayName: form.displayName,
256
- layout: form.layout,
257
- fields: form.fields,
258
- },
259
- );
260
- }
261
-
262
- dispatch(submitFormSuccess());
263
- } catch (err) {
264
- // Handle different types of errors
265
- let errorToDisplay = err;
266
-
267
- if (err.response) {
268
- // API error (400, 401, 404, 500, etc.)
269
- const { status, data } = err.response;
270
- if (status === 400 && data?.error) {
271
- errorToDisplay = new Error(`Validation error: ${data.error}`);
272
- } else if (status === 401) {
273
- errorToDisplay = new Error(
274
- "You are not authorized to perform this action",
275
- );
276
- } else if (status === 404) {
277
- errorToDisplay = new Error("Feature definition not found");
278
- } else if (status >= 500) {
279
- errorToDisplay = new Error("Server error. Please try again later.");
280
- } else {
281
- errorToDisplay = new Error(
282
- data?.error || `Request failed with status ${status}`,
283
- );
284
- }
285
- } else if (err.request) {
286
- // Network error (no response received)
287
- errorToDisplay = new Error(
288
- "Network error. Please check your connection and try again.",
289
- );
290
- } else if (err.message) {
291
- // Other JavaScript errors
292
- errorToDisplay = err;
293
- } else {
294
- // Unknown error
295
- errorToDisplay = new Error("An unexpected error occurred while saving");
296
- }
297
-
298
- dispatch(submitFormFailure(errorToDisplay));
299
- }
300
- };
301
- }
@@ -1,12 +0,0 @@
1
- import { PlussCore } from "../feature.config";
2
-
3
- const { Actions } = PlussCore;
4
- export const setAuth = Actions.setAuth;
5
- export const usersLoaded = Actions.usersLoaded;
6
- export const addRecentlyCreated = Actions.addRecentlyCreated;
7
- export const setNavData = Actions.setNavData;
8
-
9
- // Feature Builder specific actions
10
- export * from "./featureDefinitionsIndex";
11
- export * from "./listingActions";
12
- export * from "./formActions";
@@ -1,352 +0,0 @@
1
- import { arrayMove } from "@dnd-kit/sortable";
2
- import { listingActions as webApiActions } from "../webapi/listingActions";
3
- import { values } from "../values.config";
4
-
5
- // Action Types
6
- const REDUCER_PREFIX = values.reducerKey.toUpperCase();
7
- export const FETCH_LISTING_REQUEST = `${REDUCER_PREFIX}_FETCH_LISTING_REQUEST`;
8
- export const FETCH_LISTING_SUCCESS = `${REDUCER_PREFIX}_FETCH_LISTING_SUCCESS`;
9
- export const FETCH_LISTING_FAILURE = `${REDUCER_PREFIX}_FETCH_LISTING_FAILURE`;
10
- export const FETCH_LISTING_SILENT_SUCCESS = `${REDUCER_PREFIX}_FETCH_LISTING_SILENT_SUCCESS`;
11
- export const FETCH_LISTING_SILENT_FAILURE = `${REDUCER_PREFIX}_FETCH_LISTING_SILENT_FAILURE`;
12
-
13
- export const REORDER_LISTING_SUCCESS = `${REDUCER_PREFIX}_REORDER_LISTING_SUCCESS`;
14
- export const REORDER_LISTING_FAILURE = `${REDUCER_PREFIX}_REORDER_LISTING_FAILURE`;
15
-
16
- export const DELETE_LISTING_REQUEST = `${REDUCER_PREFIX}_DELETE_LISTING_REQUEST`;
17
- export const DELETE_LISTING_SUCCESS = `${REDUCER_PREFIX}_DELETE_LISTING_SUCCESS`;
18
- export const DELETE_LISTING_FAILURE = `${REDUCER_PREFIX}_DELETE_LISTING_FAILURE`;
19
-
20
- export const UNDELETE_LISTING_REQUEST = `${REDUCER_PREFIX}_UNDELETE_LISTING_REQUEST`;
21
- export const UNDELETE_LISTING_SUCCESS = `${REDUCER_PREFIX}_UNDELETE_LISTING_SUCCESS`;
22
- export const UNDELETE_LISTING_FAILURE = `${REDUCER_PREFIX}_UNDELETE_LISTING_FAILURE`;
23
-
24
- export const CREATE_LISTING_REQUEST = `${REDUCER_PREFIX}_CREATE_LISTING_REQUEST`;
25
- export const CREATE_LISTING_SUCCESS = `${REDUCER_PREFIX}_CREATE_LISTING_SUCCESS`;
26
- export const CREATE_LISTING_FAILURE = `${REDUCER_PREFIX}_CREATE_LISTING_FAILURE`;
27
-
28
- export const EDIT_LISTING_REQUEST = `${REDUCER_PREFIX}_EDIT_LISTING_REQUEST`;
29
- export const EDIT_LISTING_SUCCESS = `${REDUCER_PREFIX}_EDIT_LISTING_SUCCESS`;
30
- export const EDIT_LISTING_FAILURE = `${REDUCER_PREFIX}_EDIT_LISTING_FAILURE`;
31
-
32
- export const TOGGLE_LISTING_REQUEST = `${REDUCER_PREFIX}_TOGGLE_LISTING_REQUEST`;
33
- export const TOGGLE_LISTING_SUCCESS = `${REDUCER_PREFIX}_TOGGLE_LISTING_SUCCESS`;
34
- export const TOGGLE_LISTING_FAILURE = `${REDUCER_PREFIX}_TOGGLE_LISTING_FAILURE`;
35
-
36
- export const SET_SORT_BY = `${REDUCER_PREFIX}_SET_SORT_BY`;
37
- export const SET_SHOW_DELETED = `${REDUCER_PREFIX}_SET_SHOW_DELETED`;
38
-
39
- export const fetchListingRequest = () => ({ type: FETCH_LISTING_REQUEST });
40
- export const fetchListingSuccess = (listings) => ({
41
- type: FETCH_LISTING_SUCCESS,
42
- payload: listings,
43
- });
44
- export const fetchListingFailure = (error) => ({
45
- type: FETCH_LISTING_FAILURE,
46
- payload: error,
47
- });
48
-
49
- export const fetchListingSilentSuccess = (listings) => ({
50
- type: FETCH_LISTING_SILENT_SUCCESS,
51
- payload: listings,
52
- });
53
-
54
- export const fetchListingSilentFailure = (error) => ({
55
- type: FETCH_LISTING_SILENT_FAILURE,
56
- payload: error,
57
- });
58
-
59
- export const fetchListing = () => {
60
- return async (dispatch, getState) => {
61
- dispatch(fetchListingRequest());
62
- try {
63
- // Get site from auth store
64
- const site = getState().auth.site;
65
- if (!site) {
66
- throw new Error(
67
- "Authentication error: Site context not found. Please refresh and login again.",
68
- );
69
- }
70
- const response = await webApiActions.getAll(site);
71
- // Assuming your API returns the listings directly or in a `data` property
72
- const listings = response.data.listings;
73
-
74
- dispatch(fetchListingSuccess(listings));
75
- } catch (error) {
76
- dispatch(
77
- fetchListingFailure(error.message || "Failed to fetch listings"),
78
- );
79
- }
80
- };
81
- };
82
-
83
- export const fetchListingSilent = () => {
84
- return async (dispatch, getState) => {
85
- try {
86
- // Get site from auth store
87
- const site = getState().auth.site;
88
- if (!site) {
89
- throw new Error(
90
- "Authentication error: Site context not found. Please refresh and login again.",
91
- );
92
- }
93
- const response = await webApiActions.getAll(site);
94
- // Assuming your API returns the listings directly or in a `data` property
95
- const listings = response.data.listings;
96
-
97
- dispatch(fetchListingSilentSuccess(listings));
98
- } catch (error) {
99
- dispatch(
100
- fetchListingSilentFailure(error.message || "Failed to fetch listings"),
101
- );
102
- }
103
- };
104
- };
105
-
106
- export const fetchSingleListing = (id) => {
107
- return async (dispatch, getState) => {
108
- dispatch(fetchListingRequest());
109
- try {
110
- // Get site from auth store
111
- const site = getState().auth.site;
112
- if (!site) {
113
- throw new Error(
114
- "Authentication error: Site context not found. Please refresh and login again.",
115
- );
116
- }
117
- const response = await webApiActions.getSingle(id, site);
118
- dispatch(fetchListingSuccess([response.data])); // Return as array for consistency
119
- } catch (error) {
120
- dispatch(fetchListingFailure(error.message || "Failed to fetch listing"));
121
- }
122
- };
123
- };
124
-
125
- export const reorderListingSuccess = (listings) => ({
126
- type: REORDER_LISTING_SUCCESS,
127
- payload: listings,
128
- });
129
- export const reorderListingFailure = (error) => ({
130
- type: REORDER_LISTING_FAILURE,
131
- payload: error,
132
- });
133
-
134
- export const reorderListing = (listings, oldIndex, newIndex) => {
135
- return async (dispatch) => {
136
- // Optimistic UI update: update the state immediately
137
- const reorderedListings = arrayMove(listings, oldIndex, newIndex);
138
- dispatch(reorderListingSuccess(reorderedListings));
139
-
140
- try {
141
- // Note: Reorder functionality not available in current API spec
142
- // This would need to be implemented if needed
143
- // await listingActions.updateOrder(reorderedListings);
144
- } catch (error) {
145
- // On failure, you might want to revert the state.
146
- dispatch(
147
- reorderListingFailure(error.message || "Failed to save new order"),
148
- );
149
- }
150
- };
151
- };
152
-
153
- export const deleteListingRequest = (id) => ({
154
- type: DELETE_LISTING_REQUEST,
155
- payload: id,
156
- });
157
- export const deleteListingSuccess = (id) => ({
158
- type: DELETE_LISTING_SUCCESS,
159
- payload: id,
160
- });
161
- export const deleteListingFailure = (error) => ({
162
- type: DELETE_LISTING_FAILURE,
163
- payload: error,
164
- });
165
-
166
- export const deleteListing = (id) => {
167
- return async (dispatch, getState) => {
168
- dispatch(deleteListingRequest(id));
169
- try {
170
- // Get site from auth store
171
- const site = getState().auth.site;
172
- if (!site) {
173
- throw new Error(
174
- "Authentication error: Site context not found. Please refresh and login again.",
175
- );
176
- }
177
- await webApiActions.delete(id, site);
178
- dispatch(deleteListingSuccess(id));
179
- return { success: true };
180
- } catch (error) {
181
- const errorMessage = error.message || "Failed to delete listing";
182
- dispatch(deleteListingFailure(errorMessage));
183
- throw new Error(errorMessage);
184
- }
185
- };
186
- };
187
-
188
- export const undeleteListingRequest = (id) => ({
189
- type: UNDELETE_LISTING_REQUEST,
190
- payload: id,
191
- });
192
- export const undeleteListingSuccess = (listing) => ({
193
- type: UNDELETE_LISTING_SUCCESS,
194
- payload: listing,
195
- });
196
- export const undeleteListingFailure = (error) => ({
197
- type: UNDELETE_LISTING_FAILURE,
198
- payload: error,
199
- });
200
-
201
- export const undeleteListing = (id) => {
202
- return async (dispatch, getState) => {
203
- dispatch(undeleteListingRequest(id));
204
- try {
205
- // Get site from auth store
206
- const site = getState().auth.site;
207
- if (!site) {
208
- throw new Error(
209
- "Authentication error: Site context not found. Please refresh and login again.",
210
- );
211
- }
212
- const response = await webApiActions.undelete(id, site);
213
- // If API returns the restored listing, use it; otherwise we'll need to refetch
214
- const restoredListing = response.data;
215
- if (restoredListing?.id) {
216
- dispatch(undeleteListingSuccess(restoredListing));
217
- } else {
218
- // Trigger a refetch by dispatching the success with just ID, then fetch updated listings
219
- dispatch(undeleteListingSuccess(id));
220
- // Fetch updated listings to get the restored one
221
- dispatch(fetchListingSilent());
222
- }
223
- return { success: true };
224
- } catch (error) {
225
- const errorMessage = error.message || "Failed to restore listing";
226
- dispatch(undeleteListingFailure(errorMessage));
227
- throw new Error(errorMessage);
228
- }
229
- };
230
- };
231
-
232
- export const createListingRequest = () => ({ type: CREATE_LISTING_REQUEST });
233
- export const createListingSuccess = (listing) => ({
234
- type: CREATE_LISTING_SUCCESS,
235
- payload: listing,
236
- });
237
- export const createListingFailure = (error) => ({
238
- type: CREATE_LISTING_FAILURE,
239
- payload: error,
240
- });
241
-
242
- export const createListing = (listing) => {
243
- return async (dispatch, getState) => {
244
- dispatch(createListingRequest());
245
- try {
246
- // Get site from auth store
247
- const site = getState().auth.site;
248
- if (!site) {
249
- throw new Error(
250
- "Authentication error: Site context not found. Please refresh and login again.",
251
- );
252
- }
253
- const response = await webApiActions.create(listing, site);
254
-
255
- // Assuming the API returns the newly created listing
256
- const newListing = response.data || response;
257
- dispatch(createListingSuccess(newListing));
258
- return newListing;
259
- } catch (error) {
260
- const errorMessage =
261
- error.response?.data?.message ||
262
- error.message ||
263
- "Failed to create listing";
264
- dispatch(createListingFailure(errorMessage));
265
- throw error; // Re-throw to allow component to handle
266
- }
267
- };
268
- };
269
-
270
- export const editListingRequest = () => ({ type: EDIT_LISTING_REQUEST });
271
- export const editListingSuccess = (listing) => ({
272
- type: EDIT_LISTING_SUCCESS,
273
- payload: listing,
274
- });
275
- export const editListingFailure = (error) => ({
276
- type: EDIT_LISTING_FAILURE,
277
- payload: error,
278
- });
279
-
280
- export const editListing = (listing) => {
281
- return async (dispatch, getState) => {
282
- dispatch(editListingRequest());
283
- try {
284
- // Get site from auth store
285
- const site = getState().auth.site;
286
- if (!site) {
287
- throw new Error(
288
- "Authentication error: Site context not found. Please refresh and login again.",
289
- );
290
- }
291
- const response = await webApiActions.edit(listing, site);
292
- const updatedListing = response.data || response;
293
- dispatch(editListingSuccess(updatedListing));
294
- return updatedListing;
295
- } catch (error) {
296
- const errorMessage =
297
- error.response?.data?.message ||
298
- error.message ||
299
- "Failed to update listing";
300
- dispatch(editListingFailure(errorMessage));
301
- throw error; // Re-throw to allow component to handle
302
- }
303
- };
304
- };
305
-
306
- export const toggleListingRequest = (id) => ({
307
- type: TOGGLE_LISTING_REQUEST,
308
- payload: id,
309
- });
310
-
311
- export const toggleListingSuccess = (id, isActive) => ({
312
- type: TOGGLE_LISTING_SUCCESS,
313
- payload: { id, isActive },
314
- });
315
-
316
- export const toggleListingFailure = (error) => ({
317
- type: TOGGLE_LISTING_FAILURE,
318
- payload: error,
319
- });
320
-
321
- export const toggleListing = (id, isActive) => {
322
- return async (dispatch, getState) => {
323
- dispatch(toggleListingRequest(id));
324
- try {
325
- // Get site from auth store
326
- const site = getState().auth.site;
327
- if (!site) {
328
- throw new Error(
329
- "Authentication error: Site context not found. Please refresh and login again.",
330
- );
331
- }
332
- // Update the listing with the new active state
333
- await webApiActions.edit({ id, isActive }, site);
334
- dispatch(toggleListingSuccess(id, isActive));
335
- return { success: true };
336
- } catch (error) {
337
- const errorMessage = error.message || "Failed to toggle listing";
338
- dispatch(toggleListingFailure(errorMessage));
339
- throw new Error(errorMessage);
340
- }
341
- };
342
- };
343
-
344
- export const setSortBy = (sortBy) => ({
345
- type: SET_SORT_BY,
346
- payload: sortBy,
347
- });
348
-
349
- export const setShowDeleted = (showDeleted) => ({
350
- type: SET_SHOW_DELETED,
351
- payload: showDeleted,
352
- });