@plusscommunities/pluss-feature-builder-app-d 1.0.1-beta.3 → 1.0.1-beta.5

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 (55) hide show
  1. package/dist/module/actions/featureBuilderActions.js +6 -4
  2. package/dist/module/actions/featureBuilderActions.js.map +1 -1
  3. package/dist/module/actions/featureBuilderStringsActions.js +19 -34
  4. package/dist/module/actions/featureBuilderStringsActions.js.map +1 -1
  5. package/dist/module/components/FeatureDetailScreen.js +1 -1
  6. package/dist/module/components/FeatureDetailScreen.js.map +1 -1
  7. package/dist/module/components/WidgetGrid.js +5 -2
  8. package/dist/module/components/WidgetGrid.js.map +1 -1
  9. package/dist/module/components/WidgetLarge.js +1 -1
  10. package/dist/module/components/WidgetLarge.js.map +1 -1
  11. package/dist/module/components/WidgetSmall.js +1 -1
  12. package/dist/module/components/WidgetSmall.js.map +1 -1
  13. package/dist/module/components/common/index.js +1 -1
  14. package/dist/module/components/common/index.js.map +1 -1
  15. package/dist/module/feature.config.js +33 -24
  16. package/dist/module/feature.config.js.map +1 -1
  17. package/dist/module/index.js +11 -17
  18. package/dist/module/index.js.map +1 -1
  19. package/dist/module/reducers/featureBuilderReducer.js +0 -37
  20. package/dist/module/reducers/featureBuilderReducer.js.map +1 -1
  21. package/dist/module/utils/featureSelectors.js +1 -1
  22. package/dist/module/utils/featureSelectors.js.map +1 -1
  23. package/dist/module/utils/selectors.js +38 -0
  24. package/dist/module/utils/selectors.js.map +1 -0
  25. package/dist/module/values.config.a.js +2 -24
  26. package/dist/module/values.config.a.js.map +1 -1
  27. package/dist/module/values.config.b.js +2 -24
  28. package/dist/module/values.config.b.js.map +1 -1
  29. package/dist/module/values.config.c.js +2 -24
  30. package/dist/module/values.config.c.js.map +1 -1
  31. package/dist/module/values.config.d.js +2 -24
  32. package/dist/module/values.config.d.js.map +1 -1
  33. package/dist/module/values.config.js +16 -38
  34. package/dist/module/values.config.js.map +1 -1
  35. package/dist/module/webapi/featureBuilderAPI.js +3 -11
  36. package/dist/module/webapi/featureBuilderAPI.js.map +1 -1
  37. package/package.json +2 -2
  38. package/src/actions/featureBuilderActions.js +7 -4
  39. package/src/actions/featureBuilderStringsActions.js +13 -35
  40. package/src/components/FeatureDetailScreen.js +1 -1
  41. package/src/components/WidgetGrid.js +6 -2
  42. package/src/components/WidgetLarge.js +6 -8
  43. package/src/components/WidgetSmall.js +7 -11
  44. package/src/components/common/index.js +1 -1
  45. package/src/feature.config.js +35 -29
  46. package/src/index.js +11 -17
  47. package/src/reducers/featureBuilderReducer.js +0 -32
  48. package/src/utils/featureSelectors.js +1 -1
  49. package/src/utils/selectors.js +34 -0
  50. package/src/values.config.a.js +4 -35
  51. package/src/values.config.b.js +4 -35
  52. package/src/values.config.c.js +4 -35
  53. package/src/values.config.d.js +4 -35
  54. package/src/values.config.js +4 -35
  55. package/src/webapi/featureBuilderAPI.js +3 -11
@@ -1,15 +1,9 @@
1
1
  import { getUrl } from "./helper";
2
2
  import { authedFunction } from "../js";
3
3
  import { values } from "../values.config";
4
-
5
- // Helper function for constructing URLs
6
- const getFeatureBuilderUrl = (action, query = null) => {
7
- return getUrl(values.apiNamespace, action, query);
8
- };
9
4
  export const featureBuilderAPI = {
10
- // Fetch all feature definitions
11
5
  getFeatureDefinitions: site => {
12
- const url = getFeatureBuilderUrl(values.api.getDefinitions, {
6
+ const url = getUrl(values.serviceKey, values.api.getDefinitions, {
13
7
  site
14
8
  });
15
9
  const request = {
@@ -22,7 +16,6 @@ export const featureBuilderAPI = {
22
16
  throw error;
23
17
  });
24
18
  },
25
- // Fetch listings for all features or specific feature
26
19
  getFeatureListings: (site, featureDefinitionId = null) => {
27
20
  const queryParams = {
28
21
  site
@@ -30,7 +23,7 @@ export const featureBuilderAPI = {
30
23
  if (featureDefinitionId) {
31
24
  queryParams.featureDefinitionId = featureDefinitionId;
32
25
  }
33
- const url = getFeatureBuilderUrl(values.api.getListings, queryParams);
26
+ const url = getUrl(values.serviceKey, values.api.getListings, queryParams);
34
27
  const request = {
35
28
  method: "GET",
36
29
  url
@@ -41,12 +34,11 @@ export const featureBuilderAPI = {
41
34
  throw error;
42
35
  });
43
36
  },
44
- // Fetch a single listing by ID
45
37
  getSingleListing: (site, listingId) => {
46
38
  if (!listingId) {
47
39
  return Promise.reject(new Error("Listing ID is required"));
48
40
  }
49
- const url = getFeatureBuilderUrl(values.api.getSingleListing, {
41
+ const url = getUrl(values.serviceKey, values.api.getSingleListing, {
50
42
  site,
51
43
  id: listingId
52
44
  });
@@ -1 +1 @@
1
- {"version":3,"names":["getUrl","authedFunction","values","getFeatureBuilderUrl","action","query","apiNamespace","featureBuilderAPI","getFeatureDefinitions","site","url","api","getDefinitions","request","method","then","response","catch","error","getFeatureListings","featureDefinitionId","queryParams","getListings","getSingleListing","listingId","Promise","reject","Error","id"],"sources":["featureBuilderAPI.js"],"sourcesContent":["import { getUrl } from \"./helper\";\nimport { authedFunction } from \"../js\";\nimport { values } from \"../values.config\";\n\n// Helper function for constructing URLs\nconst getFeatureBuilderUrl = (action, query = null) => {\n\treturn getUrl(values.apiNamespace, action, query);\n};\n\nexport const featureBuilderAPI = {\n\t// Fetch all feature definitions\n\tgetFeatureDefinitions: (site) => {\n\t\tconst url = getFeatureBuilderUrl(values.api.getDefinitions, { site });\n\n\t\tconst request = {\n\t\t\tmethod: \"GET\",\n\t\t\turl,\n\t\t};\n\n\t\treturn authedFunction(request)\n\t\t\t.then((response) => {\n\t\t\t\treturn response;\n\t\t\t})\n\t\t\t.catch((error) => {\n\t\t\t\tthrow error;\n\t\t\t});\n\t},\n\n\t// Fetch listings for all features or specific feature\n\tgetFeatureListings: (site, featureDefinitionId = null) => {\n\t\tconst queryParams = { site };\n\t\tif (featureDefinitionId) {\n\t\t\tqueryParams.featureDefinitionId = featureDefinitionId;\n\t\t}\n\t\tconst url = getFeatureBuilderUrl(values.api.getListings, queryParams);\n\n\t\tconst request = {\n\t\t\tmethod: \"GET\",\n\t\t\turl,\n\t\t};\n\n\t\treturn authedFunction(request)\n\t\t\t.then((response) => {\n\t\t\t\treturn response;\n\t\t\t})\n\t\t\t.catch((error) => {\n\t\t\t\tthrow error;\n\t\t\t});\n\t},\n\n\t// Fetch a single listing by ID\n\tgetSingleListing: (site, listingId) => {\n\t\tif (!listingId) {\n\t\t\treturn Promise.reject(new Error(\"Listing ID is required\"));\n\t\t}\n\t\tconst url = getFeatureBuilderUrl(values.api.getSingleListing, {\n\t\t\tsite,\n\t\t\tid: listingId,\n\t\t});\n\t\treturn authedFunction({\n\t\t\tmethod: \"GET\",\n\t\t\turl,\n\t\t});\n\t},\n};\n"],"mappings":"AAAA,SAASA,MAAM,QAAQ,UAAU;AACjC,SAASC,cAAc,QAAQ,OAAO;AACtC,SAASC,MAAM,QAAQ,kBAAkB;;AAEzC;AACA,MAAMC,oBAAoB,GAAGA,CAACC,MAAM,EAAEC,KAAK,GAAG,IAAI,KAAK;EACtD,OAAOL,MAAM,CAACE,MAAM,CAACI,YAAY,EAAEF,MAAM,EAAEC,KAAK,CAAC;AAClD,CAAC;AAED,OAAO,MAAME,iBAAiB,GAAG;EAChC;EACAC,qBAAqB,EAAGC,IAAI,IAAK;IAChC,MAAMC,GAAG,GAAGP,oBAAoB,CAACD,MAAM,CAACS,GAAG,CAACC,cAAc,EAAE;MAAEH;IAAK,CAAC,CAAC;IAErE,MAAMI,OAAO,GAAG;MACfC,MAAM,EAAE,KAAK;MACbJ;IACD,CAAC;IAED,OAAOT,cAAc,CAACY,OAAO,CAAC,CAC5BE,IAAI,CAAEC,QAAQ,IAAK;MACnB,OAAOA,QAAQ;IAChB,CAAC,CAAC,CACDC,KAAK,CAAEC,KAAK,IAAK;MACjB,MAAMA,KAAK;IACZ,CAAC,CAAC;EACJ,CAAC;EAED;EACAC,kBAAkB,EAAEA,CAACV,IAAI,EAAEW,mBAAmB,GAAG,IAAI,KAAK;IACzD,MAAMC,WAAW,GAAG;MAAEZ;IAAK,CAAC;IAC5B,IAAIW,mBAAmB,EAAE;MACxBC,WAAW,CAACD,mBAAmB,GAAGA,mBAAmB;IACtD;IACA,MAAMV,GAAG,GAAGP,oBAAoB,CAACD,MAAM,CAACS,GAAG,CAACW,WAAW,EAAED,WAAW,CAAC;IAErE,MAAMR,OAAO,GAAG;MACfC,MAAM,EAAE,KAAK;MACbJ;IACD,CAAC;IAED,OAAOT,cAAc,CAACY,OAAO,CAAC,CAC5BE,IAAI,CAAEC,QAAQ,IAAK;MACnB,OAAOA,QAAQ;IAChB,CAAC,CAAC,CACDC,KAAK,CAAEC,KAAK,IAAK;MACjB,MAAMA,KAAK;IACZ,CAAC,CAAC;EACJ,CAAC;EAED;EACAK,gBAAgB,EAAEA,CAACd,IAAI,EAAEe,SAAS,KAAK;IACtC,IAAI,CAACA,SAAS,EAAE;MACf,OAAOC,OAAO,CAACC,MAAM,CAAC,IAAIC,KAAK,CAAC,wBAAwB,CAAC,CAAC;IAC3D;IACA,MAAMjB,GAAG,GAAGP,oBAAoB,CAACD,MAAM,CAACS,GAAG,CAACY,gBAAgB,EAAE;MAC7Dd,IAAI;MACJmB,EAAE,EAAEJ;IACL,CAAC,CAAC;IACF,OAAOvB,cAAc,CAAC;MACrBa,MAAM,EAAE,KAAK;MACbJ;IACD,CAAC,CAAC;EACH;AACD,CAAC","ignoreList":[]}
1
+ {"version":3,"names":["getUrl","authedFunction","values","featureBuilderAPI","getFeatureDefinitions","site","url","serviceKey","api","getDefinitions","request","method","then","response","catch","error","getFeatureListings","featureDefinitionId","queryParams","getListings","getSingleListing","listingId","Promise","reject","Error","id"],"sources":["featureBuilderAPI.js"],"sourcesContent":["import { getUrl } from \"./helper\";\nimport { authedFunction } from \"../js\";\nimport { values } from \"../values.config\";\n\nexport const featureBuilderAPI = {\n\tgetFeatureDefinitions: (site) => {\n\t\tconst url = getUrl(values.serviceKey, values.api.getDefinitions, { site });\n\n\t\tconst request = {\n\t\t\tmethod: \"GET\",\n\t\t\turl,\n\t\t};\n\n\t\treturn authedFunction(request)\n\t\t\t.then((response) => {\n\t\t\t\treturn response;\n\t\t\t})\n\t\t\t.catch((error) => {\n\t\t\t\tthrow error;\n\t\t\t});\n\t},\n\n\tgetFeatureListings: (site, featureDefinitionId = null) => {\n\t\tconst queryParams = { site };\n\t\tif (featureDefinitionId) {\n\t\t\tqueryParams.featureDefinitionId = featureDefinitionId;\n\t\t}\n\t\tconst url = getUrl(values.serviceKey, values.api.getListings, queryParams);\n\n\t\tconst request = {\n\t\t\tmethod: \"GET\",\n\t\t\turl,\n\t\t};\n\n\t\treturn authedFunction(request)\n\t\t\t.then((response) => {\n\t\t\t\treturn response;\n\t\t\t})\n\t\t\t.catch((error) => {\n\t\t\t\tthrow error;\n\t\t\t});\n\t},\n\n\tgetSingleListing: (site, listingId) => {\n\t\tif (!listingId) {\n\t\t\treturn Promise.reject(new Error(\"Listing ID is required\"));\n\t\t}\n\t\tconst url = getUrl(values.serviceKey, values.api.getSingleListing, {\n\t\t\tsite,\n\t\t\tid: listingId,\n\t\t});\n\t\treturn authedFunction({\n\t\t\tmethod: \"GET\",\n\t\t\turl,\n\t\t});\n\t},\n};\n"],"mappings":"AAAA,SAASA,MAAM,QAAQ,UAAU;AACjC,SAASC,cAAc,QAAQ,OAAO;AACtC,SAASC,MAAM,QAAQ,kBAAkB;AAEzC,OAAO,MAAMC,iBAAiB,GAAG;EAChCC,qBAAqB,EAAGC,IAAI,IAAK;IAChC,MAAMC,GAAG,GAAGN,MAAM,CAACE,MAAM,CAACK,UAAU,EAAEL,MAAM,CAACM,GAAG,CAACC,cAAc,EAAE;MAAEJ;IAAK,CAAC,CAAC;IAE1E,MAAMK,OAAO,GAAG;MACfC,MAAM,EAAE,KAAK;MACbL;IACD,CAAC;IAED,OAAOL,cAAc,CAACS,OAAO,CAAC,CAC5BE,IAAI,CAAEC,QAAQ,IAAK;MACnB,OAAOA,QAAQ;IAChB,CAAC,CAAC,CACDC,KAAK,CAAEC,KAAK,IAAK;MACjB,MAAMA,KAAK;IACZ,CAAC,CAAC;EACJ,CAAC;EAEDC,kBAAkB,EAAEA,CAACX,IAAI,EAAEY,mBAAmB,GAAG,IAAI,KAAK;IACzD,MAAMC,WAAW,GAAG;MAAEb;IAAK,CAAC;IAC5B,IAAIY,mBAAmB,EAAE;MACxBC,WAAW,CAACD,mBAAmB,GAAGA,mBAAmB;IACtD;IACA,MAAMX,GAAG,GAAGN,MAAM,CAACE,MAAM,CAACK,UAAU,EAAEL,MAAM,CAACM,GAAG,CAACW,WAAW,EAAED,WAAW,CAAC;IAE1E,MAAMR,OAAO,GAAG;MACfC,MAAM,EAAE,KAAK;MACbL;IACD,CAAC;IAED,OAAOL,cAAc,CAACS,OAAO,CAAC,CAC5BE,IAAI,CAAEC,QAAQ,IAAK;MACnB,OAAOA,QAAQ;IAChB,CAAC,CAAC,CACDC,KAAK,CAAEC,KAAK,IAAK;MACjB,MAAMA,KAAK;IACZ,CAAC,CAAC;EACJ,CAAC;EAEDK,gBAAgB,EAAEA,CAACf,IAAI,EAAEgB,SAAS,KAAK;IACtC,IAAI,CAACA,SAAS,EAAE;MACf,OAAOC,OAAO,CAACC,MAAM,CAAC,IAAIC,KAAK,CAAC,wBAAwB,CAAC,CAAC;IAC3D;IACA,MAAMlB,GAAG,GAAGN,MAAM,CAACE,MAAM,CAACK,UAAU,EAAEL,MAAM,CAACM,GAAG,CAACY,gBAAgB,EAAE;MAClEf,IAAI;MACJoB,EAAE,EAAEJ;IACL,CAAC,CAAC;IACF,OAAOpB,cAAc,CAAC;MACrBU,MAAM,EAAE,KAAK;MACbL;IACD,CAAC,CAAC;EACH;AACD,CAAC","ignoreList":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plusscommunities/pluss-feature-builder-app-d",
3
- "version": "1.0.1-beta.3",
3
+ "version": "1.0.1-beta.5",
4
4
  "description": "Feature Builder mobile extension for displaying custom features",
5
5
  "main": "dist/module/index.js",
6
6
  "module": "dist/module/index.js",
@@ -19,7 +19,7 @@
19
19
  "upload:p": "npm run patch && npm run upload",
20
20
  "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",
21
21
  "copy:get": "echo $npm_package_name",
22
- "copy:set": "run(){ target='@plusscommunities/pluss-feature-builder-app-a'; 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; sed -i '' -e 's/'$target'.*\"/'$replace'\"/g' package.json; }; run",
22
+ "copy:set": "run(){ target='@plusscommunities/pluss-feature-builder-app'; 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; sed -i '' -e 's/'$target'.*\"/'$replace'\"/g' package.json; }; run",
23
23
  "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;",
24
24
  "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;"
25
25
  },
@@ -43,6 +43,7 @@ export const loadTargetFeature = () => {
43
43
  const state = getState();
44
44
  const site = state.user?.site;
45
45
  const targetId = values.targetFeatureDefinitionId;
46
+
46
47
  const defResponse = await featureBuilderAPI.getFeatureDefinitions(site);
47
48
  const allDefinitions = defResponse.data.featureDefinitions || [];
48
49
 
@@ -69,17 +70,19 @@ export const loadTargetFeature = () => {
69
70
  // Filter out soft-deleted items at the Redux level
70
71
  const listings = rawListings.filter((listing) => !listing.deletedAt);
71
72
 
73
+ const payload = {
74
+ feature: flattenedFeature,
75
+ listings: listings,
76
+ };
72
77
  dispatch({
73
78
  type: FeatureBuilderActionTypes.LOAD_SUCCESS,
74
- payload: {
75
- feature: flattenedFeature,
76
- listings: listings,
77
- },
79
+ payload,
78
80
  });
79
81
 
80
82
  // Update strings store with feature definition title and icon
81
83
  dispatch(updateFeatureBuilderStringAndIconFromDefinition());
82
84
  } catch (error) {
85
+ console.error('FeatureBuilder Actions - Error in loadTargetFeature:', error);
83
86
  dispatch({
84
87
  type: FeatureBuilderActionTypes.FAILURE,
85
88
  payload: error.message || "Failed to load feature.",
@@ -1,4 +1,4 @@
1
- import { selectFeatureDefinition } from "../reducers/featureBuilderReducer";
1
+ import { selectFeatureDefinition } from "../utils/selectors";
2
2
  import { values } from "../values.config";
3
3
 
4
4
  // IMPORTANT: Using local UPDATE_STRINGS action type to make extension self-contained
@@ -9,20 +9,12 @@ export const UPDATE_STRINGS = "UPDATE_STRINGS";
9
9
  * Updates the feature builder's menu text in the global strings store
10
10
  * This ensures UI components use the dynamic title from feature definition
11
11
  */
12
- export const updateFeatureBuilderString = (title) => (dispatch, getState) => {
13
- const currentStrings = getState().strings?.config || {};
14
-
15
- const updatedStrings = {
16
- ...currentStrings,
17
- featureTitles: {
18
- ...currentStrings.featureTitles,
19
- [values.featureKey]: title || values.featureName,
20
- },
21
- };
22
-
12
+ export const updateFeatureBuilderString = (title) => (dispatch) => {
23
13
  dispatch({
24
14
  type: UPDATE_STRINGS,
25
- payload: updatedStrings,
15
+ payload: {
16
+ featureTitles: { [values.featureKey]: title || values.featureName }
17
+ },
26
18
  });
27
19
  };
28
20
 
@@ -45,19 +37,12 @@ export const updateFeatureBuilderStringFromDefinition =
45
37
  * Updates the feature builder icon in the global strings store
46
38
  * This ensures UI components use the dynamic icon from the feature definition
47
39
  */
48
- export const updateFeatureBuilderIcon = (icon) => (dispatch, getState) => {
49
- const currentStrings = getState().strings?.config || {};
50
- const updatedStrings = {
51
- ...currentStrings,
52
- featureIcons: {
53
- ...currentStrings.featureIcons,
54
- [values.featureKey]: icon,
55
- },
56
- };
57
-
40
+ export const updateFeatureBuilderIcon = (icon) => (dispatch) => {
58
41
  dispatch({
59
42
  type: UPDATE_STRINGS,
60
- payload: updatedStrings,
43
+ payload: {
44
+ featureIcons: { [values.featureKey]: icon }
45
+ },
61
46
  });
62
47
  };
63
48
 
@@ -77,19 +62,12 @@ export const updateFeatureBuilderIconFromDefinition =
77
62
  * This ensures UI components use the dynamic grid icon from the feature definition
78
63
  */
79
64
  export const updateFeatureBuilderGridIcon =
80
- (gridIcon) => (dispatch, getState) => {
81
- const currentStrings = getState().strings?.config || {};
82
- const updatedStrings = {
83
- ...currentStrings,
84
- featureIcons: {
85
- ...currentStrings.featureIcons,
86
- [values.featureKey]: gridIcon,
87
- },
88
- };
89
-
65
+ (gridIcon) => (dispatch) => {
90
66
  dispatch({
91
67
  type: UPDATE_STRINGS,
92
- payload: updatedStrings,
68
+ payload: {
69
+ featureIcons: { [values.featureKey]: gridIcon }
70
+ },
93
71
  });
94
72
  };
95
73
 
@@ -28,7 +28,7 @@ import {
28
28
  import { TEXT_MID, LINEGREY } from "../js/Colors";
29
29
  import FileDownload from "./FileDownload";
30
30
  import { values } from "../values.config";
31
- import { selectFeatureDefinition } from "../reducers/featureBuilderReducer";
31
+ import { selectFeatureDefinition } from "../utils/selectors";
32
32
 
33
33
  import Constants from "expo-constants";
34
34
  import { InlineButton } from "./common";
@@ -11,7 +11,7 @@ import _ from "lodash";
11
11
  import { Services } from "../feature.config";
12
12
  import { Components } from "../core.config";
13
13
  import { values } from "../values.config";
14
- import { selectActiveFeatureDefinitions } from "../reducers/featureBuilderReducer";
14
+ import { selectActiveFeatureDefinitions } from "../utils/selectors";
15
15
  import { getTargetFeatureDefinition } from "../utils/featureSelectors";
16
16
  import { SPACING } from "../js/spacing";
17
17
  import { getMainBrandingColourFromState } from "../js";
@@ -35,6 +35,10 @@ class WidgetGrid extends Component {
35
35
  }
36
36
  }
37
37
 
38
+ componentWillUnmount() {
39
+ // Component cleanup
40
+ }
41
+
38
42
  refresh = () => {
39
43
  this.onLoadingChanged(true, async () => {
40
44
  try {
@@ -46,7 +50,7 @@ class WidgetGrid extends Component {
46
50
  () => this.props.getState,
47
51
  );
48
52
  } catch (error) {
49
- // Silently handle refresh errors
53
+ console.error('FeatureBuilder WidgetGrid - Error in refresh:', error);
50
54
  } finally {
51
55
  this.onLoadingChanged(false);
52
56
  }
@@ -13,7 +13,10 @@ import { Icon } from "react-native-elements";
13
13
  import { Services } from "../feature.config";
14
14
  import { Components, Colours } from "../core.config";
15
15
  import { values } from "../values.config";
16
- import { selectListings, selectFeatureDefinition } from "../reducers/featureBuilderReducer";
16
+ import {
17
+ selectListings,
18
+ selectFeatureDefinition,
19
+ } from "../utils/selectors";
17
20
  import { SPACING } from "../js/spacing";
18
21
  import { getMainBrandingColourFromState } from "../js";
19
22
  import { getSummaryFieldValue } from "../js/helpers";
@@ -38,13 +41,8 @@ class WidgetLarge extends Component {
38
41
  const { options, featureDefinition } = this.props;
39
42
  if (options && !_.isEmpty(options.Title)) return options.Title;
40
43
  // Use feature definition title
41
- if (
42
- featureDefinition?.title ||
43
- featureDefinition?.displayName
44
- ) {
45
- return (
46
- featureDefinition.title || featureDefinition.displayName
47
- );
44
+ if (featureDefinition?.title || featureDefinition?.displayName) {
45
+ return featureDefinition.title || featureDefinition.displayName;
48
46
  }
49
47
  return values.featureName || "Custom Features";
50
48
  };
@@ -13,7 +13,10 @@ import { Icon } from "react-native-elements";
13
13
  import { Services } from "../feature.config";
14
14
  import { Components, Colours } from "../core.config";
15
15
  import { values } from "../values.config";
16
- import { selectListings, selectFeatureDefinition } from "../reducers/featureBuilderReducer";
16
+ import {
17
+ selectListings,
18
+ selectFeatureDefinition,
19
+ } from "../utils/selectors";
17
20
  import { SPACING } from "../js/spacing";
18
21
  import { getMainBrandingColourFromState } from "../js";
19
22
  import { getSummaryFieldValue } from "../js/helpers";
@@ -38,13 +41,8 @@ class WidgetSmall extends Component {
38
41
  const { options, featureDefinition } = this.props;
39
42
  if (options && !_.isEmpty(options.Title)) return options.Title;
40
43
  // Use feature definition title
41
- if (
42
- featureDefinition?.title ||
43
- featureDefinition?.displayName
44
- ) {
45
- return (
46
- featureDefinition.title || featureDefinition.displayName
47
- );
44
+ if (featureDefinition?.title || featureDefinition?.displayName) {
45
+ return featureDefinition.title || featureDefinition.displayName;
48
46
  }
49
47
  return values.featureName || "Features";
50
48
  };
@@ -196,9 +194,7 @@ class WidgetSmall extends Component {
196
194
  >
197
195
  <View style={[styles.borderContainer, styles.viewMoreContainer]}>
198
196
  <Text style={styles.viewMoreText}>View all</Text>
199
- <Text style={styles.viewMoreCount}>
200
- {listings.length} items
201
- </Text>
197
+ <Text style={styles.viewMoreCount}>{listings.length} items</Text>
202
198
  </View>
203
199
  </TouchableOpacity>
204
200
  )}
@@ -1,7 +1,7 @@
1
1
  import { Components } from "../../core.config";
2
2
 
3
3
  export const InlineButton = Components.InlineButton;
4
- export const ProfilePic = Components.ProfilePic;
4
+ // export const ProfilePic = Components.ProfilePic; // Disabled due to circular dependency in core-app package
5
5
  export const EmptyStateMain = Components.EmptyStateMain;
6
6
  export const Spinner = Components.Spinner;
7
7
  export const MiddlePopup = Components.MiddlePopup;
@@ -1,7 +1,9 @@
1
- import _ from "lodash";
1
+ import _ from 'lodash';
2
2
  // import * as PlussCore from '../../pluss-core/src';
3
- import * as PlussCore from "@plusscommunities/pluss-core-app";
4
- import { values } from "./values.config";
3
+ import * as PlussCore from '@plusscommunities/pluss-core-app';
4
+ import { values } from './values.config';
5
+ import { loadTargetFeature } from './actions/featureBuilderActions';
6
+ import { updateFeatureBuilderString, updateFeatureBuilderIcon } from './actions/featureBuilderStringsActions';
5
7
 
6
8
  export { PlussCore };
7
9
 
@@ -13,13 +15,6 @@ export const BaseComponents = {
13
15
  NotificationBell: null,
14
16
  };
15
17
 
16
- // These will be set when the components are imported in index.js
17
- export const setWidgets = (small, large, grid) => {
18
- WidgetSmall = small;
19
- WidgetLarge = large;
20
- WidgetGrid = grid;
21
- };
22
-
23
18
  const fetchFeatured = async (site, store, props) => {
24
19
  // No-op: listings are already loaded by loadTargetFeature()
25
20
  return [];
@@ -32,19 +27,15 @@ const getFeatured = (state, features) => {
32
27
  if (!listings || listings.length === 0) return [];
33
28
 
34
29
  // Get latest 3-4 listings, sorted by createdAt or updatedAt
35
- const latestListings = _.orderBy(
36
- listings,
37
- [(item) => item.createdAt || item.updatedAt],
38
- ["desc"]
39
- ).slice(0, 4);
30
+ const latestListings = _.orderBy(listings, [item => item.createdAt || item.updatedAt], ['desc']).slice(0, 4);
40
31
 
41
- return latestListings.map((listing) => {
32
+ return latestListings.map(listing => {
42
33
  const title = listing.fields?.[values.mandatoryFields.title] || values.labels.defaultTitle;
43
34
  const imageField = listing.fields?.[values.mandatoryFields.featureImage];
44
35
  let imageSource = null;
45
36
 
46
37
  if (imageField) {
47
- if (typeof imageField === "string") {
38
+ if (typeof imageField === 'string') {
48
39
  imageSource = imageField;
49
40
  } else if (imageField?.url) {
50
41
  imageSource = imageField.url;
@@ -57,7 +48,7 @@ const getFeatured = (state, features) => {
57
48
  id: listing.id,
58
49
  type: values.featureKey,
59
50
  title: title,
60
- description: "",
51
+ description: '',
61
52
  image: imageSource,
62
53
  date: listing.createdAt || listing.updatedAt || 0,
63
54
  data: listing,
@@ -67,7 +58,7 @@ const getFeatured = (state, features) => {
67
58
 
68
59
  const FeatureConfig = {
69
60
  key: values.featureKey,
70
- aliases: ["feature-builder", "custom-features"],
61
+ aliases: ['feature-builder', 'custom-features'],
71
62
  title: values.featureName,
72
63
 
73
64
  gridMenu: {
@@ -90,7 +81,7 @@ const FeatureConfig = {
90
81
  prop: values.featureKey,
91
82
  reducer: {
92
83
  key: values.featureKey,
93
- prop: "list",
84
+ prop: 'list',
94
85
  },
95
86
  updateKey: values.featureKey,
96
87
  fetch: fetchFeatured,
@@ -100,19 +91,19 @@ const FeatureConfig = {
100
91
  hideTabBar: [], // No special tab bar hiding needed
101
92
 
102
93
  env: {
103
- baseStage: "",
104
- baseAPIUrl: "",
94
+ baseStage: '',
95
+ baseAPIUrl: '',
105
96
  hasGradientHeader: false,
106
- defaultProfileImage: "",
107
- tinyChatDefault: "",
108
- baseUploadsUrl: "",
97
+ defaultProfileImage: '',
98
+ tinyChatDefault: '',
99
+ baseUploadsUrl: '',
109
100
  allowMediaDownload: false,
110
101
  allowMediaSharing: false,
111
- awsUploadsBucket: "",
112
- awsStorageBucket: "",
113
- preferredSite: "",
102
+ awsUploadsBucket: '',
103
+ awsStorageBucket: '',
104
+ preferredSite: '',
114
105
  strings: {},
115
- newEventDefaults: "",
106
+ newEventDefaults: '',
116
107
  defaultAllowComments: true,
117
108
  },
118
109
 
@@ -122,6 +113,21 @@ const FeatureConfig = {
122
113
  BaseComponents.NotificationBell = notificationBell;
123
114
  PlussCore.Config.init(environment, navigation);
124
115
  },
116
+
117
+ afterStoreInit: (dispatch, getState) => {
118
+ // Get the site from state (guaranteed to be available due to polling)
119
+ const site = getState().user?.site;
120
+
121
+ if (site) {
122
+ // Show loading state before fetching
123
+ dispatch(updateFeatureBuilderString('Loading...'));
124
+ dispatch(updateFeatureBuilderIcon('spinner'));
125
+
126
+ // Load the target feature definition and listings
127
+ // This will also update the title and icon from the definition
128
+ dispatch(loadTargetFeature());
129
+ }
130
+ },
125
131
  };
126
132
 
127
133
  export default FeatureConfig;
package/src/index.js CHANGED
@@ -1,27 +1,21 @@
1
- import FeatureConfig, { setWidgets } from "./feature.config";
2
- import featureBuilderReducer from "./reducers/featureBuilderReducer";
3
- import FeatureListScreen from "./components/FeatureListScreen";
4
- import FeatureDetailScreen from "./components/FeatureDetailScreen";
5
- import WidgetSmall from "./components/WidgetSmall";
6
- import WidgetLarge from "./components/WidgetLarge";
7
- import WidgetGrid from "./components/WidgetGrid";
8
- import { values } from "./values.config";
9
-
10
- // Set widgets in the config to avoid circular dependencies
11
- setWidgets(WidgetSmall, WidgetLarge, WidgetGrid);
1
+ import FeatureConfig from './feature.config';
2
+ import featureBuilderReducer from './reducers/featureBuilderReducer';
3
+ import FeatureListScreen from './components/FeatureListScreen';
4
+ import FeatureDetailScreen from './components/FeatureDetailScreen';
5
+ import { values } from './values.config';
12
6
 
13
7
  export const Reducers = {
14
8
  [values.reducerKey]: featureBuilderReducer,
15
9
  };
16
10
 
17
11
  export const Screens = {
18
- FeatureBuilderList: FeatureListScreen,
19
- FeatureBuilderDetail: FeatureDetailScreen,
12
+ [values.screens.featureList]: FeatureListScreen,
13
+ [values.screens.featureDetail]: FeatureDetailScreen,
20
14
  };
21
15
 
22
- export { default as Config } from "./feature.config";
23
- export { default as WidgetSmall } from "./components/WidgetSmall";
24
- export { default as WidgetLarge } from "./components/WidgetLarge";
25
- export { default as WidgetGrid } from "./components/WidgetGrid";
16
+ export { default as Config } from './feature.config';
17
+ export { default as WidgetSmall } from './components/WidgetSmall';
18
+ export { default as WidgetLarge } from './components/WidgetLarge';
19
+ export { default as WidgetGrid } from './components/WidgetGrid';
26
20
 
27
21
  export { FeatureConfig };
@@ -1,4 +1,3 @@
1
- import { values } from "../values.config";
2
1
  import { FeatureBuilderActionTypes } from "../actions/featureBuilderActions";
3
2
 
4
3
  const initialState = {
@@ -31,34 +30,3 @@ const featureBuilderReducer = (state = initialState, action) => {
31
30
  };
32
31
 
33
32
  export default featureBuilderReducer;
34
-
35
- // --- Selectors ---
36
-
37
- // Use values.reducerKey to access the correct state slice for multi-instance support
38
- export const selectFeature = (state) => {
39
- return state[values.reducerKey]?.feature;
40
- };
41
-
42
- export const selectFeatureDefinition = (state) => {
43
- return state[values.reducerKey]?.feature;
44
- };
45
-
46
- export const selectListings = (state) => {
47
- return state[values.reducerKey]?.listings || [];
48
- };
49
-
50
- export const selectListingsForFeature = (state) => {
51
- return state[values.reducerKey]?.listings || [];
52
- };
53
-
54
- // Legacy Helpers for Widgets
55
- export const selectActiveFeatureDefinitions = (state) => {
56
- return state[values.reducerKey]?.feature ? [state[values.reducerKey].feature] : [];
57
- };
58
-
59
- export const selectFeaturesWithListings = (state) => {
60
- const feature = state[values.reducerKey]?.feature;
61
- const listings = state[values.reducerKey]?.listings;
62
- if (!feature || !listings) return [];
63
- return [{ definition: feature, listings: listings }];
64
- };
@@ -1,4 +1,4 @@
1
- import { selectFeatureDefinition } from "../reducers/featureBuilderReducer";
1
+ import { selectFeatureDefinition } from "./selectors";
2
2
 
3
3
  /**
4
4
  * Get the target feature definition from the store
@@ -0,0 +1,34 @@
1
+ import { values } from "../values.config";
2
+
3
+ // --- Selectors ---
4
+ // Use values.reducerKey to access the correct state slice for multi-instance support
5
+
6
+ export const selectFeature = (state) => {
7
+ return state[values.reducerKey]?.feature;
8
+ };
9
+
10
+ export const selectFeatureDefinition = (state) => {
11
+ return state[values.reducerKey]?.feature;
12
+ };
13
+
14
+ export const selectListings = (state) => {
15
+ return state[values.reducerKey]?.listings || [];
16
+ };
17
+
18
+ export const selectListingsForFeature = (state) => {
19
+ return state[values.reducerKey]?.listings || [];
20
+ };
21
+
22
+ // Legacy Helpers for Widgets
23
+ export const selectActiveFeatureDefinitions = (state) => {
24
+ return state[values.reducerKey]?.feature
25
+ ? [state[values.reducerKey].feature]
26
+ : [];
27
+ };
28
+
29
+ export const selectFeaturesWithListings = (state) => {
30
+ const feature = state[values.reducerKey]?.feature;
31
+ const listings = state[values.reducerKey]?.listings;
32
+ if (!feature || !listings) return [];
33
+ return [{ definition: feature, listings: listings }];
34
+ };
@@ -1,47 +1,30 @@
1
1
  export const values = {
2
- // Feature identification
3
2
  featureKey: "feature-builder-a",
4
3
  featureName: "Feature A",
5
4
  targetFeatureDefinitionId: "feature-builder-a-default",
6
-
7
- // Redux state management
8
- reducerKey: "featureBuilderA", // Redux state slice name (must be unique per instance)
9
-
10
- // API routing
11
- apiNamespace: "feature-builder-a", // API endpoint prefix for multi-instance routing
12
-
13
- // Notifications
14
- notificationKey: "feature-builder-a", // Key for notifications.dataUpdated[...]
15
-
16
- // Default brand colors (single source of truth)
5
+ reducerKey: "featureBuilderA",
6
+ serviceKey: "feature-builder",
7
+ notificationKey: "feature-builder-a",
17
8
  colors: {
18
9
  defaultBrandMain: "#FF6363",
19
10
  defaultBrandDark: "#D13636",
20
11
  defaultBrandLight: "#FCE1E1",
21
12
  },
22
-
23
- // Screen names
24
13
  screens: {
25
14
  featureList: "FeatureBuilderListA",
26
15
  featureDetail: "FeatureBuilderDetailA",
27
16
  },
28
-
29
- // API endpoints
30
17
  api: {
31
18
  getDefinitions: "definition/get/all",
32
19
  getListings: "listing/get/all",
33
20
  getSingleListing: "listing/get/single",
34
21
  },
35
-
36
- // Layout types
37
22
  layoutTypes: {
38
23
  round: "round",
39
24
  square: "square",
40
25
  condensed: "condensed",
41
26
  feature: "feature",
42
27
  },
43
-
44
- // Field types
45
28
  fieldTypes: {
46
29
  string: "string",
47
30
  number: "number",
@@ -50,21 +33,15 @@ export const values = {
50
33
  pdf: "pdf",
51
34
  link: "link",
52
35
  },
53
-
54
- // Mandatory field IDs
55
36
  mandatoryFields: {
56
37
  title: "mandatory-title",
57
38
  featureImage: "mandatory-feature-image",
58
39
  },
59
-
60
- // Action types (with suffix to prevent collisions between instances)
61
40
  actions: {
62
41
  LOADING: "FB_LOADING_A",
63
42
  LOAD_SUCCESS: "FB_LOAD_SUCCESS_A",
64
43
  FAILURE: "FB_FAILURE_A",
65
44
  },
66
-
67
- // Navigation
68
45
  navigation: {
69
46
  gridMenu: {
70
47
  icon: "settings",
@@ -72,28 +49,20 @@ export const values = {
72
49
  navigate: "FeatureBuilderListA",
73
50
  },
74
51
  },
75
-
76
- // Default values
77
52
  defaults: {
78
53
  itemsPerWidget: 2,
79
- loadingTimeout: 30000, // 30 seconds
54
+ loadingTimeout: 30000,
80
55
  },
81
-
82
- // Widget configuration
83
56
  widget: {
84
57
  maxItems: 2,
85
58
  itemMinWidth: 120,
86
59
  itemMaxWidth: 150,
87
60
  },
88
-
89
- // UI dimensions
90
61
  dimensions: {
91
62
  cardHeight: 125,
92
63
  imageSize: 95,
93
64
  imageBorderRadius: 47.5,
94
65
  },
95
-
96
- // Common text labels
97
66
  labels: {
98
67
  emptyState: "No custom features available",
99
68
  defaultTitle: "Untitled",