@plusscommunities/pluss-feature-builder-app-c 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.
- package/dist/module/actions/featureBuilderActions.js +6 -4
- package/dist/module/actions/featureBuilderActions.js.map +1 -1
- package/dist/module/actions/featureBuilderStringsActions.js +19 -34
- package/dist/module/actions/featureBuilderStringsActions.js.map +1 -1
- package/dist/module/components/FeatureDetailScreen.js +1 -1
- package/dist/module/components/FeatureDetailScreen.js.map +1 -1
- package/dist/module/components/WidgetGrid.js +5 -2
- package/dist/module/components/WidgetGrid.js.map +1 -1
- package/dist/module/components/WidgetLarge.js +1 -1
- package/dist/module/components/WidgetLarge.js.map +1 -1
- package/dist/module/components/WidgetSmall.js +1 -1
- package/dist/module/components/WidgetSmall.js.map +1 -1
- package/dist/module/components/common/index.js +1 -1
- package/dist/module/components/common/index.js.map +1 -1
- package/dist/module/feature.config.js +33 -24
- package/dist/module/feature.config.js.map +1 -1
- package/dist/module/index.js +11 -17
- package/dist/module/index.js.map +1 -1
- package/dist/module/reducers/featureBuilderReducer.js +0 -37
- package/dist/module/reducers/featureBuilderReducer.js.map +1 -1
- package/dist/module/utils/featureSelectors.js +1 -1
- package/dist/module/utils/featureSelectors.js.map +1 -1
- package/dist/module/utils/selectors.js +38 -0
- package/dist/module/utils/selectors.js.map +1 -0
- package/dist/module/values.config.a.js +2 -24
- package/dist/module/values.config.a.js.map +1 -1
- package/dist/module/values.config.b.js +2 -24
- package/dist/module/values.config.b.js.map +1 -1
- package/dist/module/values.config.c.js +2 -24
- package/dist/module/values.config.c.js.map +1 -1
- package/dist/module/values.config.d.js +2 -24
- package/dist/module/values.config.d.js.map +1 -1
- package/dist/module/values.config.js +16 -38
- package/dist/module/values.config.js.map +1 -1
- package/dist/module/webapi/featureBuilderAPI.js +3 -11
- package/dist/module/webapi/featureBuilderAPI.js.map +1 -1
- package/package.json +2 -2
- package/src/actions/featureBuilderActions.js +7 -4
- package/src/actions/featureBuilderStringsActions.js +13 -35
- package/src/components/FeatureDetailScreen.js +1 -1
- package/src/components/WidgetGrid.js +6 -2
- package/src/components/WidgetLarge.js +6 -8
- package/src/components/WidgetSmall.js +7 -11
- package/src/components/common/index.js +1 -1
- package/src/feature.config.js +35 -29
- package/src/index.js +11 -17
- package/src/reducers/featureBuilderReducer.js +0 -32
- package/src/utils/featureSelectors.js +1 -1
- package/src/utils/selectors.js +34 -0
- package/src/values.config.a.js +4 -35
- package/src/values.config.b.js +4 -35
- package/src/values.config.c.js +4 -35
- package/src/values.config.d.js +4 -35
- package/src/values.config.js +4 -35
- 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 =
|
|
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 =
|
|
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 =
|
|
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","
|
|
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-c",
|
|
3
|
-
"version": "1.0.1-beta.
|
|
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
|
|
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 "../
|
|
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
|
|
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:
|
|
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
|
|
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:
|
|
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
|
|
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:
|
|
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 "../
|
|
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 "../
|
|
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
|
-
|
|
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 {
|
|
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
|
|
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 {
|
|
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
|
|
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;
|
package/src/feature.config.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import _ from
|
|
1
|
+
import _ from 'lodash';
|
|
2
2
|
// import * as PlussCore from '../../pluss-core/src';
|
|
3
|
-
import * as PlussCore from
|
|
4
|
-
import { values } from
|
|
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(
|
|
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 ===
|
|
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: [
|
|
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:
|
|
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
|
|
2
|
-
import featureBuilderReducer from
|
|
3
|
-
import FeatureListScreen from
|
|
4
|
-
import FeatureDetailScreen from
|
|
5
|
-
import
|
|
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
|
-
|
|
19
|
-
|
|
12
|
+
[values.screens.featureList]: FeatureListScreen,
|
|
13
|
+
[values.screens.featureDetail]: FeatureDetailScreen,
|
|
20
14
|
};
|
|
21
15
|
|
|
22
|
-
export { default as Config } from
|
|
23
|
-
export { default as WidgetSmall } from
|
|
24
|
-
export { default as WidgetLarge } from
|
|
25
|
-
export { default as WidgetGrid } from
|
|
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
|
-
};
|
|
@@ -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
|
+
};
|
package/src/values.config.a.js
CHANGED
|
@@ -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
|
-
|
|
8
|
-
|
|
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,
|
|
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",
|