@plusscommunities/pluss-feature-builder-app-c 1.0.1-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 (118) hide show
  1. package/dist/module/actions/featureBuilderActions.js +106 -0
  2. package/dist/module/actions/featureBuilderActions.js.map +1 -0
  3. package/dist/module/actions/featureBuilderStringsActions.js +106 -0
  4. package/dist/module/actions/featureBuilderStringsActions.js.map +1 -0
  5. package/dist/module/actions/index.js +12 -0
  6. package/dist/module/actions/index.js.map +1 -0
  7. package/dist/module/actions/types.js +7 -0
  8. package/dist/module/actions/types.js.map +1 -0
  9. package/dist/module/components/FeatureDetailScreen.js +725 -0
  10. package/dist/module/components/FeatureDetailScreen.js.map +1 -0
  11. package/dist/module/components/FeatureListItem.js +174 -0
  12. package/dist/module/components/FeatureListItem.js.map +1 -0
  13. package/dist/module/components/FeatureListScreen.js +159 -0
  14. package/dist/module/components/FeatureListScreen.js.map +1 -0
  15. package/dist/module/components/FieldRenderer.js +218 -0
  16. package/dist/module/components/FieldRenderer.js.map +1 -0
  17. package/dist/module/components/FileDownload.js +74 -0
  18. package/dist/module/components/FileDownload.js.map +1 -0
  19. package/dist/module/components/WidgetGrid.js +158 -0
  20. package/dist/module/components/WidgetGrid.js.map +1 -0
  21. package/dist/module/components/WidgetLarge.js +274 -0
  22. package/dist/module/components/WidgetLarge.js.map +1 -0
  23. package/dist/module/components/WidgetSmall.js +315 -0
  24. package/dist/module/components/WidgetSmall.js.map +1 -0
  25. package/dist/module/components/common/index.js +25 -0
  26. package/dist/module/components/common/index.js.map +1 -0
  27. package/dist/module/components/layouts/CondensedList.js +195 -0
  28. package/dist/module/components/layouts/CondensedList.js.map +1 -0
  29. package/dist/module/components/layouts/FeatureImageList.js +172 -0
  30. package/dist/module/components/layouts/FeatureImageList.js.map +1 -0
  31. package/dist/module/components/layouts/RoundImageList.js +198 -0
  32. package/dist/module/components/layouts/RoundImageList.js.map +1 -0
  33. package/dist/module/components/layouts/SquareImageList.js +185 -0
  34. package/dist/module/components/layouts/SquareImageList.js.map +1 -0
  35. package/dist/module/config/index.js +10 -0
  36. package/dist/module/config/index.js.map +1 -0
  37. package/dist/module/core.config.js +17 -0
  38. package/dist/module/core.config.js.map +1 -0
  39. package/dist/module/feature.config.js +113 -0
  40. package/dist/module/feature.config.js.map +1 -0
  41. package/dist/module/index.js +24 -0
  42. package/dist/module/index.js.map +1 -0
  43. package/dist/module/js/Colors.js +25 -0
  44. package/dist/module/js/Colors.js.map +1 -0
  45. package/dist/module/js/FieldTypes.js +123 -0
  46. package/dist/module/js/FieldTypes.js.map +1 -0
  47. package/dist/module/js/NavigationService.js +10 -0
  48. package/dist/module/js/NavigationService.js.map +1 -0
  49. package/dist/module/js/Styles.js +3 -0
  50. package/dist/module/js/Styles.js.map +1 -0
  51. package/dist/module/js/helpers.js +29 -0
  52. package/dist/module/js/helpers.js.map +1 -0
  53. package/dist/module/js/index.js +24 -0
  54. package/dist/module/js/index.js.map +1 -0
  55. package/dist/module/js/spacing.js +29 -0
  56. package/dist/module/js/spacing.js.map +1 -0
  57. package/dist/module/js/types.js +254 -0
  58. package/dist/module/js/types.js.map +1 -0
  59. package/dist/module/reducers/featureBuilderReducer.js +75 -0
  60. package/dist/module/reducers/featureBuilderReducer.js.map +1 -0
  61. package/dist/module/utils/featureSelectors.js +9 -0
  62. package/dist/module/utils/featureSelectors.js.map +1 -0
  63. package/dist/module/values.config.a.js +96 -0
  64. package/dist/module/values.config.a.js.map +1 -0
  65. package/dist/module/values.config.b.js +96 -0
  66. package/dist/module/values.config.b.js.map +1 -0
  67. package/dist/module/values.config.c.js +96 -0
  68. package/dist/module/values.config.c.js.map +1 -0
  69. package/dist/module/values.config.d.js +96 -0
  70. package/dist/module/values.config.d.js.map +1 -0
  71. package/dist/module/values.config.js +96 -0
  72. package/dist/module/values.config.js.map +1 -0
  73. package/dist/module/webapi/featureBuilderAPI.js +59 -0
  74. package/dist/module/webapi/featureBuilderAPI.js.map +1 -0
  75. package/dist/module/webapi/helper.js +4 -0
  76. package/dist/module/webapi/helper.js.map +1 -0
  77. package/dist/module/webapi/index.js +8 -0
  78. package/dist/module/webapi/index.js.map +1 -0
  79. package/package.json +62 -0
  80. package/src/actions/featureBuilderActions.js +112 -0
  81. package/src/actions/featureBuilderStringsActions.js +114 -0
  82. package/src/actions/index.js +12 -0
  83. package/src/actions/types.js +7 -0
  84. package/src/components/FeatureDetailScreen.js +817 -0
  85. package/src/components/FeatureListItem.js +198 -0
  86. package/src/components/FeatureListScreen.js +160 -0
  87. package/src/components/FieldRenderer.js +272 -0
  88. package/src/components/FileDownload.js +79 -0
  89. package/src/components/WidgetGrid.js +181 -0
  90. package/src/components/WidgetLarge.js +305 -0
  91. package/src/components/WidgetSmall.js +344 -0
  92. package/src/components/common/index.js +25 -0
  93. package/src/components/layouts/CondensedList.js +230 -0
  94. package/src/components/layouts/FeatureImageList.js +193 -0
  95. package/src/components/layouts/RoundImageList.js +219 -0
  96. package/src/components/layouts/SquareImageList.js +205 -0
  97. package/src/config/index.js +10 -0
  98. package/src/core.config.js +29 -0
  99. package/src/feature.config.js +127 -0
  100. package/src/index.js +27 -0
  101. package/src/js/Colors.js +30 -0
  102. package/src/js/FieldTypes.js +131 -0
  103. package/src/js/NavigationService.js +12 -0
  104. package/src/js/Styles.js +3 -0
  105. package/src/js/helpers.js +30 -0
  106. package/src/js/index.js +24 -0
  107. package/src/js/spacing.js +30 -0
  108. package/src/js/types.js +253 -0
  109. package/src/reducers/featureBuilderReducer.js +64 -0
  110. package/src/utils/featureSelectors.js +8 -0
  111. package/src/values.config.a.js +104 -0
  112. package/src/values.config.b.js +104 -0
  113. package/src/values.config.c.js +104 -0
  114. package/src/values.config.d.js +104 -0
  115. package/src/values.config.js +104 -0
  116. package/src/webapi/featureBuilderAPI.js +65 -0
  117. package/src/webapi/helper.js +4 -0
  118. package/src/webapi/index.js +8 -0
@@ -0,0 +1,181 @@
1
+ import React, { Component } from "react";
2
+ import {
3
+ Text,
4
+ View,
5
+ StyleSheet,
6
+ TouchableOpacity,
7
+ Dimensions,
8
+ } from "react-native";
9
+ import { connect } from "react-redux";
10
+ import _ from "lodash";
11
+ import { Services } from "../feature.config";
12
+ import { Components } from "../core.config";
13
+ import { values } from "../values.config";
14
+ import { selectActiveFeatureDefinitions } from "../reducers/featureBuilderReducer";
15
+ import { getTargetFeatureDefinition } from "../utils/featureSelectors";
16
+ import { SPACING } from "../js/spacing";
17
+ import { getMainBrandingColourFromState } from "../js";
18
+ // Import SVGIcon directly from main app since extension has import issues
19
+ // Using a different approach - we'll use a simple text representation for now
20
+ // import { SVGIcon } from '../../../../../src/components/common';
21
+
22
+ class WidgetGrid extends Component {
23
+ constructor(props) {
24
+ super(props);
25
+ this.state = { loading: false };
26
+ }
27
+
28
+ componentDidMount() {
29
+ this.refresh();
30
+ }
31
+
32
+ componentDidUpdate(prevProps) {
33
+ if (!prevProps.dataUpdated && this.props.dataUpdated) {
34
+ this.refresh();
35
+ }
36
+ }
37
+
38
+ refresh = () => {
39
+ this.onLoadingChanged(true, async () => {
40
+ try {
41
+ const { initializeFeatureBuilder } = await import(
42
+ "../actions/featureBuilderActions"
43
+ );
44
+ await initializeFeatureBuilder()(
45
+ this.props.dispatch,
46
+ () => this.props.getState,
47
+ );
48
+ } catch (error) {
49
+ // Silently handle refresh errors
50
+ } finally {
51
+ this.onLoadingChanged(false);
52
+ }
53
+ });
54
+ };
55
+
56
+ onLoadingChanged = (loading, callback) => {
57
+ this.setState({ loading }, () => {
58
+ if (this.props.onLoadingChanged)
59
+ this.props.onLoadingChanged(this.state.loading);
60
+ if (callback) callback();
61
+ });
62
+ };
63
+
64
+ onPress = () => {
65
+ Services.navigation.navigate(values.screens.featureList, {
66
+ options: this.props.options,
67
+ });
68
+ };
69
+
70
+ render() {
71
+ const { colourBrandingMain, featureDefinitions, targetFeatureDefinition } =
72
+ this.props;
73
+
74
+ // Always show the card, even when loading or no features, with a loading state
75
+ const icon = "settings"; // Use a valid icon instead of 'cog'
76
+ // Use the feature definition title if available, otherwise fall back to default
77
+ const title =
78
+ targetFeatureDefinition?.title ||
79
+ targetFeatureDefinition?.displayName ||
80
+ values.featureName ||
81
+ "Feature Builder";
82
+
83
+ const isLoading = this.state.loading;
84
+ const featureCount = featureDefinitions?.length || 0;
85
+
86
+ return (
87
+ <TouchableOpacity onPress={this.onPress} style={[styles.container]}>
88
+ <View
89
+ style={[
90
+ styles.button,
91
+ {
92
+ backgroundColor:
93
+ colourBrandingMain || values.colors.defaultBrandMain,
94
+ },
95
+ ]}
96
+ >
97
+ <View style={styles.buttonTitleSection}>
98
+ <Text
99
+ style={[styles.buttonTitle, styles.buttonTitleLarge]}
100
+ numberOfLines={2}
101
+ >
102
+ {isLoading ? "Loading..." : title}
103
+ </Text>
104
+ <Text style={styles.subtitleText}>
105
+ {isLoading
106
+ ? "Please wait..."
107
+ : `${featureCount} Feature${featureCount !== 1 ? "s" : ""} Available`}
108
+ </Text>
109
+ </View>
110
+ <View style={styles.iconContainer}>
111
+ <Text style={styles.iconText}>⚙</Text>
112
+ </View>
113
+ </View>
114
+ </TouchableOpacity>
115
+ );
116
+ }
117
+ }
118
+
119
+ const mapStateToProps = (state) => {
120
+ const { user, notifications } = state;
121
+
122
+ const targetFeatureDefinition = getTargetFeatureDefinition(state);
123
+
124
+ return {
125
+ colourBrandingMain: getMainBrandingColourFromState(state),
126
+ featureDefinitions: selectActiveFeatureDefinitions(state),
127
+ targetFeatureDefinition,
128
+ site: user.site,
129
+ dataUpdated: notifications.dataUpdated[values.notificationKey],
130
+ strings: state.strings?.config || {},
131
+ };
132
+ };
133
+
134
+ const styles = StyleSheet.create({
135
+ container: {
136
+ marginHorizontal: SPACING.SM,
137
+ marginVertical: SPACING.SM,
138
+ },
139
+ button: {
140
+ borderRadius: SPACING.SM,
141
+ padding: SPACING.MD,
142
+ minHeight: 120,
143
+ justifyContent: "space-between",
144
+ alignItems: "flex-start",
145
+ flexDirection: "row",
146
+ },
147
+ buttonTitleSection: {
148
+ flex: 1,
149
+ marginRight: SPACING.MD,
150
+ justifyContent: "center",
151
+ },
152
+ buttonTitle: {
153
+ color: "#fff",
154
+ fontSize: 16,
155
+ fontWeight: "600",
156
+ },
157
+ buttonTitleLarge: {
158
+ fontSize: 18,
159
+ },
160
+ subtitleText: {
161
+ color: "rgba(255, 255, 255, 0.8)",
162
+ fontSize: 12,
163
+ marginTop: SPACING.XS,
164
+ },
165
+ iconContainer: {
166
+ width: 50,
167
+ height: 50,
168
+ borderRadius: 25,
169
+ backgroundColor: "rgba(255, 255, 255, 0.2)",
170
+ justifyContent: "center",
171
+ alignItems: "center",
172
+ },
173
+ iconText: {
174
+ fontSize: 24,
175
+ color: "#fff",
176
+ },
177
+ });
178
+
179
+ export default connect(mapStateToProps, null, null, { forwardRef: true })(
180
+ WidgetGrid,
181
+ );
@@ -0,0 +1,305 @@
1
+ import React, { Component } from "react";
2
+ import {
3
+ Text,
4
+ View,
5
+ ScrollView,
6
+ StyleSheet,
7
+ TouchableOpacity,
8
+ Image,
9
+ } from "react-native";
10
+ import { connect } from "react-redux";
11
+ import _ from "lodash";
12
+ import { Icon } from "react-native-elements";
13
+ import { Services } from "../feature.config";
14
+ import { Components, Colours } from "../core.config";
15
+ import { values } from "../values.config";
16
+ import { selectListings, selectFeatureDefinition } from "../reducers/featureBuilderReducer";
17
+ import { SPACING } from "../js/spacing";
18
+ import { getMainBrandingColourFromState } from "../js";
19
+ import { getSummaryFieldValue } from "../js/helpers";
20
+
21
+ class WidgetLarge extends Component {
22
+ constructor(props) {
23
+ super(props);
24
+ this.state = { loading: false };
25
+ }
26
+
27
+ componentDidMount() {
28
+ this.refresh();
29
+ }
30
+
31
+ componentDidUpdate(prevProps) {
32
+ if (!prevProps.dataUpdated && this.props.dataUpdated) {
33
+ this.refresh();
34
+ }
35
+ }
36
+
37
+ getTitle = () => {
38
+ const { options, featureDefinition } = this.props;
39
+ if (options && !_.isEmpty(options.Title)) return options.Title;
40
+ // Use feature definition title
41
+ if (
42
+ featureDefinition?.title ||
43
+ featureDefinition?.displayName
44
+ ) {
45
+ return (
46
+ featureDefinition.title || featureDefinition.displayName
47
+ );
48
+ }
49
+ return values.featureName || "Custom Features";
50
+ };
51
+
52
+ getEmptyStateText = () => {
53
+ const { options } = this.props;
54
+ if (options && !_.isEmpty(options.EmptyText)) return options.EmptyText;
55
+ return "No custom features available";
56
+ };
57
+
58
+ refresh = () => {
59
+ this.onLoadingChanged(true, async () => {
60
+ try {
61
+ const { initializeFeatureBuilder } = await import(
62
+ "../actions/featureBuilderActions"
63
+ );
64
+ await initializeFeatureBuilder()(
65
+ this.props.dispatch,
66
+ () => this.props.getState,
67
+ );
68
+ } finally {
69
+ this.onLoadingChanged(false);
70
+ }
71
+ });
72
+ };
73
+
74
+ onLoadingChanged = (loading, callback) => {
75
+ this.setState({ loading }, () => {
76
+ if (this.props.onLoadingChanged)
77
+ this.props.onLoadingChanged(this.state.loading);
78
+ if (callback) callback();
79
+ });
80
+ };
81
+
82
+ getImageSource = (imageField) => {
83
+ if (!imageField) return null;
84
+
85
+ if (typeof imageField === "string") {
86
+ return { uri: imageField };
87
+ }
88
+
89
+ if (typeof imageField === "object" && imageField.uri) {
90
+ return imageField;
91
+ }
92
+
93
+ if (typeof imageField === "object" && imageField.url) {
94
+ return { uri: imageField.url };
95
+ }
96
+
97
+ return null;
98
+ };
99
+
100
+ onListingPress = (listing) => {
101
+ const { featureDefinition } = this.props;
102
+ const title = this.getTitle();
103
+ Services.navigation.navigate(values.screens.featureDetail, {
104
+ listing: listing,
105
+ featureDefinition: featureDefinition,
106
+ featureTitle: title,
107
+ });
108
+ };
109
+
110
+ renderListingCard = (listing) => {
111
+ const { colourBrandingMain, featureDefinition } = this.props;
112
+ const title = listing.fields?.[values.mandatoryFields.title] || "Untitled";
113
+ const summary = getSummaryFieldValue(listing, featureDefinition);
114
+ const imageField = listing.fields?.[values.mandatoryFields.featureImage];
115
+ const imageSource = this.getImageSource(imageField);
116
+
117
+ return (
118
+ <TouchableOpacity
119
+ key={listing.id}
120
+ style={styles.listItem}
121
+ onPress={() => this.onListingPress(listing)}
122
+ >
123
+ {/* Small circular avatar image */}
124
+ <View style={styles.avatarContainer}>
125
+ {imageSource ? (
126
+ <Image
127
+ source={imageSource}
128
+ style={styles.avatar}
129
+ resizeMode="cover"
130
+ />
131
+ ) : (
132
+ <View style={[styles.avatar, styles.placeholderAvatar]}>
133
+ <Icon
134
+ name="image"
135
+ type="font-awesome"
136
+ size={16}
137
+ color={Colours.TEXT_LIGHT}
138
+ />
139
+ </View>
140
+ )}
141
+ </View>
142
+
143
+ {/* Text content */}
144
+ <View style={styles.textContainer}>
145
+ <Text style={styles.titleText} numberOfLines={1}>
146
+ {title}
147
+ </Text>
148
+ {summary && (
149
+ <Text style={styles.descriptionText} numberOfLines={1}>
150
+ {summary}
151
+ </Text>
152
+ )}
153
+ </View>
154
+
155
+ {/* Chevron icon */}
156
+ <View style={styles.chevronContainer}>
157
+ <Icon
158
+ name="chevron-right"
159
+ type="font-awesome"
160
+ size={14}
161
+ color={Colours.TEXT_LIGHT}
162
+ />
163
+ </View>
164
+ </TouchableOpacity>
165
+ );
166
+ };
167
+
168
+ renderContent() {
169
+ const { listings } = this.props;
170
+
171
+ // Don't render widget if no listings are available (and not loading)
172
+ if (_.isEmpty(listings) && !this.state.loading) {
173
+ return null;
174
+ }
175
+
176
+ if (_.isEmpty(listings)) {
177
+ if (this.state.loading) {
178
+ return (
179
+ <View style={styles.loadingPadding}>
180
+ <Components.LoadingStateWidget height={300} />
181
+ </View>
182
+ );
183
+ }
184
+ return null;
185
+ }
186
+
187
+ return (
188
+ <ScrollView
189
+ contentContainerStyle={styles.featuresContainer}
190
+ showsVerticalScrollIndicator={false}
191
+ >
192
+ {listings.map((listing) => this.renderListingCard(listing))}
193
+ </ScrollView>
194
+ );
195
+ }
196
+
197
+ render() {
198
+ const { colourBrandingMain } = this.props;
199
+
200
+ const content = this.renderContent();
201
+ // Don't render widget section if content is null (no features available)
202
+ if (content === null) {
203
+ return null;
204
+ }
205
+
206
+ return (
207
+ <View style={styles.sectionContainer}>
208
+ <View style={styles.sectionPadding}>
209
+ <View style={styles.sectionHeading}>
210
+ <Text style={styles.sectionTitle}>{this.getTitle()}</Text>
211
+ </View>
212
+ </View>
213
+ {content}
214
+ </View>
215
+ );
216
+ }
217
+ }
218
+
219
+ const styles = StyleSheet.create({
220
+ sectionContainer: {
221
+ backgroundColor: "#fff",
222
+ flex: 1,
223
+ },
224
+ sectionPadding: {
225
+ paddingHorizontal: SPACING.MD,
226
+ paddingBottom: SPACING.XS + 2,
227
+ paddingTop: SPACING.MD,
228
+ },
229
+ loadingPadding: {
230
+ paddingHorizontal: SPACING.MD,
231
+ },
232
+ sectionHeading: {
233
+ marginBottom: SPACING.MD,
234
+ },
235
+ sectionTitle: {
236
+ fontFamily: "sf-bold",
237
+ fontSize: 24,
238
+ color: "#000",
239
+ },
240
+ featuresContainer: {
241
+ paddingHorizontal: SPACING.MD,
242
+ paddingBottom: SPACING.MD,
243
+ },
244
+ // Listing item styles (similar to CondensedListItem)
245
+ listItem: {
246
+ flexDirection: "row",
247
+ alignItems: "center",
248
+ paddingHorizontal: 16,
249
+ paddingVertical: 8,
250
+ backgroundColor: "#fff",
251
+ },
252
+ avatarContainer: {
253
+ width: 40,
254
+ height: 40,
255
+ borderRadius: 20,
256
+ marginRight: 12,
257
+ },
258
+ avatar: {
259
+ width: 40,
260
+ height: 40,
261
+ borderRadius: 20,
262
+ backgroundColor: Colours.BACKGROUND_LIGHT || "#f0f0f0",
263
+ },
264
+ placeholderAvatar: {
265
+ backgroundColor: Colours.BACKGROUND_LIGHT || "#f0f0f0",
266
+ justifyContent: "center",
267
+ alignItems: "center",
268
+ },
269
+ textContainer: {
270
+ flex: 1,
271
+ justifyContent: "center",
272
+ },
273
+ titleText: {
274
+ fontFamily: "sf-semibold",
275
+ fontSize: 16,
276
+ color: Colours.TEXT_DARK || "#333",
277
+ marginBottom: 2,
278
+ },
279
+ descriptionText: {
280
+ fontFamily: "sf-regular",
281
+ fontSize: 14,
282
+ color: Colours.TEXT_LIGHT || "#666",
283
+ lineHeight: 18,
284
+ },
285
+ chevronContainer: {
286
+ paddingLeft: 8,
287
+ },
288
+ });
289
+
290
+ const mapStateToProps = (state) => {
291
+ const { user, notifications } = state;
292
+
293
+ return {
294
+ colourBrandingMain: getMainBrandingColourFromState(state),
295
+ listings: selectListings(state),
296
+ featureDefinition: selectFeatureDefinition(state),
297
+ site: user.site,
298
+ dataUpdated: notifications.dataUpdated[values.notificationKey],
299
+ strings: state.strings?.config || {},
300
+ };
301
+ };
302
+
303
+ export default connect(mapStateToProps, null, null, { forwardRef: true })(
304
+ WidgetLarge,
305
+ );