@plusscommunities/pluss-feature-builder-web-d 1.0.2-beta.0

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 (117) hide show
  1. package/.babelrc +4 -0
  2. package/dist/index.cjs.js +7792 -0
  3. package/package.json +54 -0
  4. package/rollup.config.js +68 -0
  5. package/src/actions/featureBuilderStringsActions.js +88 -0
  6. package/src/actions/featureDefinitionsIndex.js +258 -0
  7. package/src/actions/formActions.js +311 -0
  8. package/src/actions/index.js +12 -0
  9. package/src/actions/listingActions.js +350 -0
  10. package/src/actions/wizardActions.js +240 -0
  11. package/src/components/ActivityCardExample.jsx +86 -0
  12. package/src/components/ActivityCardExample.module.css +130 -0
  13. package/src/components/BackgroundLoader.jsx +33 -0
  14. package/src/components/BackgroundLoader.module.css +46 -0
  15. package/src/components/BaseFieldConfig.jsx +305 -0
  16. package/src/components/BaseFieldConfig.module.css +42 -0
  17. package/src/components/CenteredContainer.jsx +29 -0
  18. package/src/components/CenteredContainer.module.css +171 -0
  19. package/src/components/DeleteConfirmationPopup.jsx +95 -0
  20. package/src/components/DeleteConfirmationPopup.module.css +12 -0
  21. package/src/components/ErrorBoundary.jsx +134 -0
  22. package/src/components/ErrorBoundary.module.css +77 -0
  23. package/src/components/ErrorMessage.jsx +85 -0
  24. package/src/components/ErrorMessage.module.css +116 -0
  25. package/src/components/ExampleDisplay.jsx +26 -0
  26. package/src/components/ExampleDisplay.module.css +3 -0
  27. package/src/components/FeatureBuilderSidebar.jsx +84 -0
  28. package/src/components/FeatureBuilderSuccessPopup.jsx +55 -0
  29. package/src/components/FeatureBuilderSuccessPopup.module.css +43 -0
  30. package/src/components/FeatureBuilderWelcomePopup.jsx +51 -0
  31. package/src/components/FeatureBuilderWelcomePopup.module.css +21 -0
  32. package/src/components/FeatureListingCard.jsx +104 -0
  33. package/src/components/FeatureListingCard.module.css +62 -0
  34. package/src/components/Fields.jsx +460 -0
  35. package/src/components/Fields.module.css +159 -0
  36. package/src/components/IconLoader.jsx +153 -0
  37. package/src/components/IconLoader.module.css +92 -0
  38. package/src/components/IconSelector.jsx +112 -0
  39. package/src/components/IconSelector.module.css +197 -0
  40. package/src/components/ListingEditor.jsx +406 -0
  41. package/src/components/ListingEditor.module.css +14 -0
  42. package/src/components/ListingSuccessPopup.jsx +52 -0
  43. package/src/components/LoadingScreen.jsx +54 -0
  44. package/src/components/LoadingScreen.module.css +103 -0
  45. package/src/components/LoadingState.jsx +40 -0
  46. package/src/components/LoadingState.module.css +18 -0
  47. package/src/components/PreviewFull.js +24 -0
  48. package/src/components/PreviewFull.module.css +11 -0
  49. package/src/components/PreviewGrid.js +14 -0
  50. package/src/components/PreviewWidget.js +27 -0
  51. package/src/components/PreviewWidget.module.css +15 -0
  52. package/src/components/SidebarLayout.jsx +292 -0
  53. package/src/components/SidebarLayout.module.css +145 -0
  54. package/src/components/SkeletonLoader.jsx +128 -0
  55. package/src/components/SkeletonLoader.module.css +295 -0
  56. package/src/components/SortButtonGroup.jsx +34 -0
  57. package/src/components/SortButtonGroup.module.css +51 -0
  58. package/src/components/ToastContainer.jsx +98 -0
  59. package/src/components/ToastContainer.module.css +156 -0
  60. package/src/components/ToggleSwitch.js +40 -0
  61. package/src/components/ToggleSwitch.module.css +48 -0
  62. package/src/components/TwoColumnInput.jsx +29 -0
  63. package/src/components/TwoColumnInput.module.css +32 -0
  64. package/src/components/ViewFull.js +139 -0
  65. package/src/components/ViewFull.module.css +71 -0
  66. package/src/components/ViewWidget.js +62 -0
  67. package/src/components/ViewWidget.module.css +28 -0
  68. package/src/components/iconCategories.js +135 -0
  69. package/src/components/iconImports.js +409 -0
  70. package/src/components/index.js +61 -0
  71. package/src/components/listing/FileListItem.jsx +86 -0
  72. package/src/components/listing/GalleryDisplay.jsx +331 -0
  73. package/src/components/listing/GalleryDisplay.module.css +309 -0
  74. package/src/components/listing/ListingCTAInput.jsx +82 -0
  75. package/src/components/listing/ListingDescriptionInput.jsx +73 -0
  76. package/src/components/listing/ListingField.jsx +101 -0
  77. package/src/components/listing/ListingField.module.css +106 -0
  78. package/src/components/listing/ListingFileInput.jsx +255 -0
  79. package/src/components/listing/ListingFileInput.module.css +192 -0
  80. package/src/components/listing/ListingForm.jsx +90 -0
  81. package/src/components/listing/ListingForm.module.css +38 -0
  82. package/src/components/listing/ListingGalleryInput.jsx +236 -0
  83. package/src/components/listing/ListingGalleryInput.module.css +131 -0
  84. package/src/components/listing/ListingImageInput.jsx +153 -0
  85. package/src/components/listing/ListingTextInput.jsx +72 -0
  86. package/src/feature.config.js +130 -0
  87. package/src/helper/index.js +135 -0
  88. package/src/hooks/useFeatureDefinitionLoader.js +62 -0
  89. package/src/images/full.png +0 -0
  90. package/src/images/fullNoTitle.png +0 -0
  91. package/src/images/previewWidget.png +0 -0
  92. package/src/images/widget.png +0 -0
  93. package/src/index.js +38 -0
  94. package/src/pages/CreateListingPage.jsx +49 -0
  95. package/src/pages/EditListingPage.jsx +58 -0
  96. package/src/reducers/featureBuilderReducer.js +744 -0
  97. package/src/screens/CreateListing.module.css +45 -0
  98. package/src/screens/Form.module.css +734 -0
  99. package/src/screens/FormFieldsStep.jsx +689 -0
  100. package/src/screens/FormLayoutStep.jsx +445 -0
  101. package/src/screens/FormOverviewStep.jsx +396 -0
  102. package/src/screens/ListingScreen.jsx +478 -0
  103. package/src/screens/ListingScreen.module.css +333 -0
  104. package/src/selectors/featureBuilderSelectors.js +529 -0
  105. package/src/types/index.js +91 -0
  106. package/src/utils/textUtils.js +89 -0
  107. package/src/validators/galleryValidators.js +345 -0
  108. package/src/values.config.a.js +49 -0
  109. package/src/values.config.b.js +49 -0
  110. package/src/values.config.c.js +49 -0
  111. package/src/values.config.d.js +49 -0
  112. package/src/values.config.js +49 -0
  113. package/src/webapi/featureDefinitionActions.js +0 -0
  114. package/src/webapi/featuresActions.js +90 -0
  115. package/src/webapi/helper.js +4 -0
  116. package/src/webapi/index.js +12 -0
  117. package/src/webapi/listingActions.js +176 -0
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "@plusscommunities/pluss-feature-builder-web-d",
3
+ "version": "1.0.2-beta.0",
4
+ "description": "Contains the feature builder extension for the pluss communities ",
5
+ "main": "dist/index.cjs.js",
6
+ "scripts": {
7
+ "build": "npm i && rollup -c",
8
+ "betapatch": "npm version prepatch --preid=beta",
9
+ "patch": "npm version patch",
10
+ "betaupload": "npm run build && npm publish --access public --tag beta && rm -rf node_modules",
11
+ "betaupload:p": "npm run betapatch && npm run betaupload",
12
+ "upload": "npm run build && npm publish --access public && rm -rf node_modules",
13
+ "upload:p": "npm run patch && npm run upload",
14
+ "copy:add": "run(){ ext=${1:-default}; test -f src/values.config.$ext.js || cp src/values.config.default.js src/values.config.$ext.js; }; run",
15
+ "copy:get": "echo $npm_package_name",
16
+ "copy:set": "run(){ target='@plusscommunities/pluss-feature-builder-web'; ext=${1:-default}; [ $ext == 'default' ] && replace=$target || replace=$target'-'$ext; echo 'Setting target to '$replace; test -f src/values.config.$ext.js && cp -f src/values.config.$ext.js src/values.config.js; REPLACE=$replace node -e 'const pkg=require(\"./package.json\"); pkg.name=process.env.REPLACE; require(\"fs\").writeFileSync(\"./package.json\", JSON.stringify(pkg, null, 2))'; }; run",
17
+ "copy:betaupload": "npm run betapatch; for file in `ls ./src/values.config.*.js`; do dup=`echo $file | sed 's/.*values\\.config\\.\\(.*\\)\\.js/\\1/'`; npm run copy:set $dup; npm run betaupload; done; npm run copy:set;",
18
+ "copy:upload": "npm run patch; for file in `ls ./src/values.config.*.js`; do dup=`echo $file | sed 's/.*values\\.config\\.\\(.*\\)\\.js/\\1/'`; npm run copy:set $dup; npm run upload; done; npm run copy:set;"
19
+ },
20
+ "author": "Phillip Suh",
21
+ "devDependencies": {
22
+ "@babel/cli": "^7.14.3",
23
+ "@babel/core": "^7.14.3",
24
+ "@babel/plugin-transform-class-properties": "^7.27.1",
25
+ "@babel/plugin-transform-optional-chaining": "^7.14.5",
26
+ "@babel/plugin-transform-react-jsx": "^7.14.3",
27
+ "@babel/plugin-transform-runtime": "^7.14.3",
28
+ "@babel/preset-env": "^7.14.2",
29
+ "@babel/preset-react": "^7.13.13",
30
+ "@rollup/plugin-babel": "^5.3.0",
31
+ "@rollup/plugin-image": "^2.0.6",
32
+ "@rollup/plugin-json": "^4.1.0",
33
+ "autoprefixer": "^10.2.6",
34
+ "rollup": "^2.50.1",
35
+ "rollup-plugin-local-resolve": "^1.0.7",
36
+ "rollup-plugin-styles": "^3.14.1"
37
+ },
38
+ "dependencies": {
39
+ "@babel/runtime": "^7.14.0"
40
+ },
41
+ "peerDependencies": {
42
+ "@fortawesome/fontawesome-svg-core": "^6.4.0",
43
+ "@fortawesome/free-solid-svg-icons": "^6.4.0",
44
+ "@fortawesome/react-fontawesome": "^0.2.0",
45
+ "@plusscommunities/pluss-core-web": "^1.6.15-beta.1",
46
+ "lodash": "^4.17.4",
47
+ "moment": "^2.30.1",
48
+ "react": "^16.14.0",
49
+ "react-bootstrap": "^0.31.2",
50
+ "react-dom": "^16.14.0",
51
+ "react-fontawesome": "^1.6.1"
52
+ },
53
+ "keywords": []
54
+ }
@@ -0,0 +1,68 @@
1
+ import styles from "rollup-plugin-styles";
2
+ const autoprefixer = require("autoprefixer");
3
+ import babel from "@rollup/plugin-babel";
4
+ import json from "@rollup/plugin-json";
5
+ import image from "@rollup/plugin-image";
6
+ import localResolve from "rollup-plugin-local-resolve";
7
+
8
+ // the entry point for the library
9
+ const input = "src/index.js";
10
+
11
+ var MODE = [
12
+ {
13
+ format: "cjs",
14
+ },
15
+ ];
16
+
17
+ var config = [];
18
+
19
+ MODE.map((m) => {
20
+ var conf = {
21
+ input: input,
22
+ output: {
23
+ // then name of your package
24
+ name: "@plusscommunities/pluss-feature-builder-web",
25
+ file: `dist/index.${m.format}.js`,
26
+ format: m.format,
27
+ exports: "auto",
28
+ },
29
+ // this externelizes react to prevent rollup from compiling it
30
+ external: [
31
+ "react",
32
+ "react-dom",
33
+ "react-redux",
34
+ "react-router-dom",
35
+ "@fortawesome/react-fontawesome",
36
+ "@fortawesome/fontawesome-svg-core",
37
+ "@fortawesome/free-solid-svg-icons",
38
+ "@plusscommunities/pluss-core-web",
39
+ /@babel\/runtime/,
40
+ ],
41
+ plugins: [
42
+ // these are babel comfigurations
43
+ babel({
44
+ exclude: "node_modules/**",
45
+ presets: ["@babel/preset-react", "@babel/preset-env"],
46
+ plugins: [
47
+ "@babel/transform-runtime",
48
+ "@babel/plugin-transform-react-jsx",
49
+ "@babel/plugin-transform-optional-chaining",
50
+ ["@babel/plugin-transform-class-properties", { loose: true }],
51
+ ],
52
+ babelHelpers: "runtime",
53
+ }),
54
+ // this adds support for styles
55
+ styles({
56
+ postcss: {
57
+ plugins: [autoprefixer()],
58
+ },
59
+ }),
60
+ json(),
61
+ image(),
62
+ localResolve(),
63
+ ],
64
+ };
65
+ config.push(conf);
66
+ });
67
+
68
+ export default [...config];
@@ -0,0 +1,88 @@
1
+ import {
2
+ selectFormTitle,
3
+ selectFormIcon,
4
+ } from "../selectors/featureBuilderSelectors";
5
+ import { values } from "../values.config";
6
+ import { capitalizeTextWithFallback, toTitleCase } from "../utils/textUtils";
7
+
8
+ // IMPORTANT: Using local UPDATE_STRINGS action type to make extension self-contained
9
+ // The main app's StringsReducer will handle this action type the same way
10
+ export const UPDATE_STRINGS = "UPDATE_STRINGS";
11
+
12
+ export const updateFeatureBuilderString = (title) => (dispatch, getState) => {
13
+ const currentStrings = getState().strings?.config || {};
14
+ const titleCased = toTitleCase(title) || values.textMenuTitle;
15
+ const updatedStrings = {
16
+ ...currentStrings,
17
+ sideNav: {
18
+ ...currentStrings.sideNav,
19
+ [values.featureKey]: titleCased,
20
+ [values.menuKey]: `Manage ${titleCased}`,
21
+ },
22
+ permission: {
23
+ ...currentStrings.permission,
24
+ [values.permissionFeatureBuilderDefinition]: `Manage custom feature ${titleCased}`,
25
+ [values.permissionFeatureBuilderContent]: `Manage ${titleCased} content`,
26
+ },
27
+ };
28
+
29
+ dispatch({
30
+ type: UPDATE_STRINGS,
31
+ payload: updatedStrings,
32
+ });
33
+ };
34
+
35
+ export const updateFeatureBuilderStringFromStore =
36
+ () => (dispatch, getState) => {
37
+ const title = selectFormTitle(getState());
38
+ dispatch(updateFeatureBuilderString(title));
39
+ };
40
+
41
+ export const updateFeatureBuilderIcon = (icon) => (dispatch, getState) => {
42
+ const currentStrings = getState().strings?.config || {};
43
+ const updatedStrings = {
44
+ ...currentStrings,
45
+ sideNav: {
46
+ ...currentStrings.sideNav,
47
+ [values.featureKey + "-icon"]: icon,
48
+ [values.menuKey + "-icon"]: icon,
49
+ },
50
+ };
51
+
52
+ dispatch({
53
+ type: UPDATE_STRINGS,
54
+ payload: updatedStrings,
55
+ });
56
+ };
57
+
58
+ export const updateFeatureBuilderStringAndIcon = () => (dispatch, getState) => {
59
+ const title = selectFormTitle(getState());
60
+ const icon = selectFormIcon(getState());
61
+
62
+ // Update title separately (will be title cased in updateFeatureBuilderString)
63
+ dispatch(updateFeatureBuilderString(title));
64
+
65
+ // Update icon as string
66
+ dispatch(updateFeatureBuilderIcon(icon));
67
+ };
68
+
69
+ export const updatePermissionNames = (featureTitle) => (dispatch, getState) => {
70
+ const currentStrings = getState().strings?.config || {};
71
+
72
+ const titleCasedFeature = toTitleCase(featureTitle, "");
73
+ const updatedStrings = {
74
+ ...currentStrings,
75
+ permission: {
76
+ ...currentStrings.permission,
77
+ [values.permissionFeatureBuilderDefinition]: `Create and Edit ${titleCasedFeature} Features`,
78
+ [values.permissionFeatureBuilderContent]: `Manage ${titleCasedFeature} Content`,
79
+ },
80
+ };
81
+
82
+ const result = dispatch({
83
+ type: UPDATE_STRINGS,
84
+ payload: updatedStrings,
85
+ });
86
+
87
+ return result;
88
+ };
@@ -0,0 +1,258 @@
1
+ import { featureDefinitionActions } from "../webapi/featuresActions";
2
+ import { values } from "../values.config.js";
3
+ import {
4
+ updateFeatureBuilderString,
5
+ updateFeatureBuilderIcon,
6
+ updatePermissionNames,
7
+ } from "./featureBuilderStringsActions";
8
+
9
+ export const FETCH_FEATURES_REQUEST = "FETCH_FEATURES_REQUEST";
10
+ export const FETCH_FEATURES_SUCCESS = "FETCH_FEATURES_SUCCESS";
11
+ export const FETCH_FEATURES_FAILURE = "FETCH_FEATURES_FAILURE";
12
+
13
+ export const FEATURE_CREATE_REQUEST = "FEATURE_CREATE_REQUEST";
14
+ export const FEATURE_CREATE_SUCCESS = "FEATURE_CREATE_SUCCESS";
15
+ export const FEATURE_CREATE_FAILURE = "FEATURE_CREATE_FAILURE";
16
+
17
+ export const FEATURE_EDIT_REQUEST = "FEATURE_EDIT_REQUEST";
18
+ export const FEATURE_EDIT_SUCCESS = "FEATURE_EDIT_SUCCESS";
19
+ export const FEATURE_EDIT_FAILURE = "FEATURE_EDIT_FAILURE";
20
+
21
+ export const FEATURE_DELETE_REQUEST = "FEATURE_DELETE_REQUEST";
22
+ export const FEATURE_DELETE_FAILURE = "FEATURE_DELETE_FAILURE";
23
+
24
+ // Wizard synchronization action types
25
+ export const SYNC_WIZARD_MODE_FROM_DEFINITION =
26
+ "SYNC_WIZARD_MODE_FROM_DEFINITION";
27
+ export const SET_WIZARD_MODE = "SET_WIZARD_MODE";
28
+
29
+ function fetchFeaturesRequest() {
30
+ return { type: FETCH_FEATURES_REQUEST };
31
+ }
32
+
33
+ function fetchFeaturesSuccess(data, mode) {
34
+ return { type: FETCH_FEATURES_SUCCESS, payload: { data, mode } };
35
+ }
36
+
37
+ function fetchFeaturesFailure(data) {
38
+ return { type: FETCH_FEATURES_FAILURE, payload: data };
39
+ }
40
+
41
+ function deleteFeatureRequest(id) {
42
+ return { type: FEATURE_DELETE_REQUEST, payload: id };
43
+ }
44
+
45
+ function deleteFeatureFailure(id) {
46
+ return { type: FEATURE_DELETE_FAILURE, payload: id };
47
+ }
48
+
49
+ // Action creators for wizard synchronization
50
+ function syncWizardModeFromDefinition(mode) {
51
+ return { type: SYNC_WIZARD_MODE_FROM_DEFINITION, payload: mode };
52
+ }
53
+
54
+ function setWizardMode(mode) {
55
+ return { type: SET_WIZARD_MODE, payload: mode };
56
+ }
57
+
58
+ // Action creators for create operations
59
+ function createFeatureRequest() {
60
+ return { type: FEATURE_CREATE_REQUEST };
61
+ }
62
+
63
+ function createFeatureSuccess(featureDefinition) {
64
+ return (dispatch) => {
65
+ // Update menu string with title and icon from created definition
66
+ const title = featureDefinition?.title;
67
+ const icon = featureDefinition?.icon;
68
+
69
+ dispatch(updateFeatureBuilderString(title));
70
+ dispatch(updateFeatureBuilderIcon(icon));
71
+
72
+ // Update permission names based on feature title
73
+ dispatch(updatePermissionNames(title));
74
+
75
+ // Dispatch the actual success action
76
+ dispatch({
77
+ type: FEATURE_CREATE_SUCCESS,
78
+ payload: featureDefinition,
79
+ });
80
+
81
+ // After creation, switch wizard to edit mode
82
+ dispatch(setWizardMode("edit"));
83
+ };
84
+ }
85
+
86
+ function createFeatureFailure(error) {
87
+ return { type: FEATURE_CREATE_FAILURE, payload: error };
88
+ }
89
+
90
+ // Action creators for edit operations
91
+ function editFeatureRequest() {
92
+ return { type: FEATURE_EDIT_REQUEST };
93
+ }
94
+
95
+ function editFeatureSuccess(featureDefinition) {
96
+ return (dispatch) => {
97
+ // Update menu string with title and icon from edited definition
98
+ const title = featureDefinition?.title;
99
+ const icon = featureDefinition?.icon;
100
+
101
+ dispatch(updateFeatureBuilderString(title));
102
+ dispatch(updateFeatureBuilderIcon(icon));
103
+
104
+ // Update permission names based on feature title
105
+ dispatch(updatePermissionNames(title));
106
+
107
+ // Dispatch the actual success action
108
+ dispatch({
109
+ type: FEATURE_EDIT_SUCCESS,
110
+ payload: featureDefinition,
111
+ });
112
+ };
113
+ }
114
+
115
+ function editFeatureFailure(error) {
116
+ return { type: FEATURE_EDIT_FAILURE, payload: error };
117
+ }
118
+
119
+ // Thunk actions using the new webapi
120
+ export function fetchFeatureDefinitions() {
121
+ return async (dispatch, getState) => {
122
+ dispatch(fetchFeaturesRequest());
123
+ try {
124
+ // Get site from auth store
125
+ const site = getState().auth.site;
126
+ if (!site) {
127
+ throw new Error(
128
+ "Authentication error: Site context not found. Please refresh and login again.",
129
+ );
130
+ }
131
+ const response = await featureDefinitionActions.getSingle(
132
+ values.featureId,
133
+ site,
134
+ );
135
+
136
+ // Check HTTP status to determine mode
137
+ if (response.status >= 200 && response.status < 300) {
138
+ // 2XX: Feature exists → edit mode
139
+ dispatch(fetchFeaturesSuccess(response.data, "edit"));
140
+
141
+ // Update menu string with title and icon from fetched definition
142
+ const featureDefinitionWrapper =
143
+ response.data?.featureDefinition || response.data;
144
+ const definition =
145
+ featureDefinitionWrapper?.definition || featureDefinitionWrapper;
146
+ const title = definition?.title;
147
+ const icon = definition?.icon;
148
+ dispatch(updateFeatureBuilderString(title));
149
+ dispatch(updateFeatureBuilderIcon(icon));
150
+
151
+ // Synchronize wizard mode with definition
152
+ dispatch(syncWizardModeFromDefinition("edit"));
153
+ } else {
154
+ // Unexpected success status → treat as error
155
+ dispatch(
156
+ fetchFeaturesFailure(
157
+ new Error(`Unexpected response status: ${response.status}`),
158
+ ),
159
+ );
160
+ }
161
+ } catch (err) {
162
+ // Check if it's a 404 (feature doesn't exist)
163
+ if (err.response && err.response.status === 404) {
164
+ // 404: Feature doesn't exist → create mode
165
+ dispatch(fetchFeaturesSuccess(null, "create"));
166
+
167
+ // Reset menu string to default when no feature exists
168
+ dispatch(updateFeatureBuilderString(null));
169
+
170
+ // Synchronize wizard mode with definition
171
+ dispatch(syncWizardModeFromDefinition("create"));
172
+ } else {
173
+ // Other errors: network, server, auth
174
+ dispatch(fetchFeaturesFailure(err));
175
+ }
176
+ }
177
+ };
178
+ }
179
+
180
+ export function fetchFeatureDefinition(id) {
181
+ return async (dispatch, getState) => {
182
+ dispatch(fetchFeaturesRequest());
183
+ try {
184
+ // Get site from auth store
185
+ const site = getState().auth.site;
186
+ if (!site) {
187
+ throw new Error(
188
+ "Authentication error: Site context not found. Please refresh and login again.",
189
+ );
190
+ }
191
+ const { data } = await featureDefinitionActions.getSingle(id, site);
192
+ dispatch(fetchFeaturesSuccess(data));
193
+ } catch (err) {
194
+ dispatch(fetchFeaturesFailure(err));
195
+ }
196
+ };
197
+ }
198
+
199
+ export function createFeatureDefinition(id, site, featureDefinition) {
200
+ return async (dispatch) => {
201
+ dispatch(createFeatureRequest());
202
+ try {
203
+ if (!values.featureId || !site) {
204
+ throw new Error(
205
+ "Authentication error: Missing required context (featureId or site).",
206
+ );
207
+ }
208
+ const { data } = await featureDefinitionActions.create(
209
+ id,
210
+ site,
211
+ featureDefinition,
212
+ );
213
+ dispatch(createFeatureSuccess(data));
214
+ } catch (err) {
215
+ dispatch(createFeatureFailure(err));
216
+ }
217
+ };
218
+ }
219
+
220
+ export function editFeatureDefinition(featureDefinitionData) {
221
+ return async (dispatch, getState) => {
222
+ dispatch(editFeatureRequest());
223
+ try {
224
+ // Get site from auth store
225
+ const site = getState().auth.site;
226
+ if (!site) {
227
+ throw new Error(
228
+ "Authentication error: Site context not found. Please refresh and login again.",
229
+ );
230
+ }
231
+ const { data } = await featureDefinitionActions.edit(
232
+ featureDefinitionData,
233
+ site,
234
+ );
235
+ dispatch(editFeatureSuccess(data));
236
+ } catch (err) {
237
+ dispatch(editFeatureFailure(err));
238
+ }
239
+ };
240
+ }
241
+
242
+ export function deleteFeatureDefinition(id) {
243
+ return async (dispatch, getState) => {
244
+ dispatch(deleteFeatureRequest({ id }));
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
+ await featureDefinitionActions.delete(id, site);
254
+ } catch (err) {
255
+ dispatch(deleteFeatureFailure({ id }));
256
+ }
257
+ };
258
+ }