@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.
- package/dist/module/actions/featureBuilderActions.js +106 -0
- package/dist/module/actions/featureBuilderActions.js.map +1 -0
- package/dist/module/actions/featureBuilderStringsActions.js +106 -0
- package/dist/module/actions/featureBuilderStringsActions.js.map +1 -0
- package/dist/module/actions/index.js +12 -0
- package/dist/module/actions/index.js.map +1 -0
- package/dist/module/actions/types.js +7 -0
- package/dist/module/actions/types.js.map +1 -0
- package/dist/module/components/FeatureDetailScreen.js +725 -0
- package/dist/module/components/FeatureDetailScreen.js.map +1 -0
- package/dist/module/components/FeatureListItem.js +174 -0
- package/dist/module/components/FeatureListItem.js.map +1 -0
- package/dist/module/components/FeatureListScreen.js +159 -0
- package/dist/module/components/FeatureListScreen.js.map +1 -0
- package/dist/module/components/FieldRenderer.js +218 -0
- package/dist/module/components/FieldRenderer.js.map +1 -0
- package/dist/module/components/FileDownload.js +74 -0
- package/dist/module/components/FileDownload.js.map +1 -0
- package/dist/module/components/WidgetGrid.js +158 -0
- package/dist/module/components/WidgetGrid.js.map +1 -0
- package/dist/module/components/WidgetLarge.js +274 -0
- package/dist/module/components/WidgetLarge.js.map +1 -0
- package/dist/module/components/WidgetSmall.js +315 -0
- package/dist/module/components/WidgetSmall.js.map +1 -0
- package/dist/module/components/common/index.js +25 -0
- package/dist/module/components/common/index.js.map +1 -0
- package/dist/module/components/layouts/CondensedList.js +195 -0
- package/dist/module/components/layouts/CondensedList.js.map +1 -0
- package/dist/module/components/layouts/FeatureImageList.js +172 -0
- package/dist/module/components/layouts/FeatureImageList.js.map +1 -0
- package/dist/module/components/layouts/RoundImageList.js +198 -0
- package/dist/module/components/layouts/RoundImageList.js.map +1 -0
- package/dist/module/components/layouts/SquareImageList.js +185 -0
- package/dist/module/components/layouts/SquareImageList.js.map +1 -0
- package/dist/module/config/index.js +10 -0
- package/dist/module/config/index.js.map +1 -0
- package/dist/module/core.config.js +17 -0
- package/dist/module/core.config.js.map +1 -0
- package/dist/module/feature.config.js +113 -0
- package/dist/module/feature.config.js.map +1 -0
- package/dist/module/index.js +24 -0
- package/dist/module/index.js.map +1 -0
- package/dist/module/js/Colors.js +25 -0
- package/dist/module/js/Colors.js.map +1 -0
- package/dist/module/js/FieldTypes.js +123 -0
- package/dist/module/js/FieldTypes.js.map +1 -0
- package/dist/module/js/NavigationService.js +10 -0
- package/dist/module/js/NavigationService.js.map +1 -0
- package/dist/module/js/Styles.js +3 -0
- package/dist/module/js/Styles.js.map +1 -0
- package/dist/module/js/helpers.js +29 -0
- package/dist/module/js/helpers.js.map +1 -0
- package/dist/module/js/index.js +24 -0
- package/dist/module/js/index.js.map +1 -0
- package/dist/module/js/spacing.js +29 -0
- package/dist/module/js/spacing.js.map +1 -0
- package/dist/module/js/types.js +254 -0
- package/dist/module/js/types.js.map +1 -0
- package/dist/module/reducers/featureBuilderReducer.js +75 -0
- package/dist/module/reducers/featureBuilderReducer.js.map +1 -0
- package/dist/module/utils/featureSelectors.js +9 -0
- package/dist/module/utils/featureSelectors.js.map +1 -0
- package/dist/module/values.config.a.js +96 -0
- package/dist/module/values.config.a.js.map +1 -0
- package/dist/module/values.config.b.js +96 -0
- package/dist/module/values.config.b.js.map +1 -0
- package/dist/module/values.config.c.js +96 -0
- package/dist/module/values.config.c.js.map +1 -0
- package/dist/module/values.config.d.js +96 -0
- package/dist/module/values.config.d.js.map +1 -0
- package/dist/module/values.config.js +96 -0
- package/dist/module/values.config.js.map +1 -0
- package/dist/module/webapi/featureBuilderAPI.js +59 -0
- package/dist/module/webapi/featureBuilderAPI.js.map +1 -0
- package/dist/module/webapi/helper.js +4 -0
- package/dist/module/webapi/helper.js.map +1 -0
- package/dist/module/webapi/index.js +8 -0
- package/dist/module/webapi/index.js.map +1 -0
- package/package.json +62 -0
- package/src/actions/featureBuilderActions.js +112 -0
- package/src/actions/featureBuilderStringsActions.js +114 -0
- package/src/actions/index.js +12 -0
- package/src/actions/types.js +7 -0
- package/src/components/FeatureDetailScreen.js +817 -0
- package/src/components/FeatureListItem.js +198 -0
- package/src/components/FeatureListScreen.js +160 -0
- package/src/components/FieldRenderer.js +272 -0
- package/src/components/FileDownload.js +79 -0
- package/src/components/WidgetGrid.js +181 -0
- package/src/components/WidgetLarge.js +305 -0
- package/src/components/WidgetSmall.js +344 -0
- package/src/components/common/index.js +25 -0
- package/src/components/layouts/CondensedList.js +230 -0
- package/src/components/layouts/FeatureImageList.js +193 -0
- package/src/components/layouts/RoundImageList.js +219 -0
- package/src/components/layouts/SquareImageList.js +205 -0
- package/src/config/index.js +10 -0
- package/src/core.config.js +29 -0
- package/src/feature.config.js +127 -0
- package/src/index.js +27 -0
- package/src/js/Colors.js +30 -0
- package/src/js/FieldTypes.js +131 -0
- package/src/js/NavigationService.js +12 -0
- package/src/js/Styles.js +3 -0
- package/src/js/helpers.js +30 -0
- package/src/js/index.js +24 -0
- package/src/js/spacing.js +30 -0
- package/src/js/types.js +253 -0
- package/src/reducers/featureBuilderReducer.js +64 -0
- package/src/utils/featureSelectors.js +8 -0
- package/src/values.config.a.js +104 -0
- package/src/values.config.b.js +104 -0
- package/src/values.config.c.js +104 -0
- package/src/values.config.d.js +104 -0
- package/src/values.config.js +104 -0
- package/src/webapi/featureBuilderAPI.js +65 -0
- package/src/webapi/helper.js +4 -0
- package/src/webapi/index.js +8 -0
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import _ from "lodash";
|
|
2
|
+
// import * as PlussCore from '../../pluss-core/src';
|
|
3
|
+
import * as PlussCore from "@plusscommunities/pluss-core-app";
|
|
4
|
+
import { values } from "./values.config";
|
|
5
|
+
|
|
6
|
+
export { PlussCore };
|
|
7
|
+
|
|
8
|
+
export const Services = {
|
|
9
|
+
navigation: null,
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const BaseComponents = {
|
|
13
|
+
NotificationBell: null,
|
|
14
|
+
};
|
|
15
|
+
|
|
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
|
+
const fetchFeatured = async (site, store, props) => {
|
|
24
|
+
// No-op: listings are already loaded by loadTargetFeature()
|
|
25
|
+
return [];
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const getFeatured = (state, features) => {
|
|
29
|
+
if (!_.includes(features, values.featureKey)) return null;
|
|
30
|
+
|
|
31
|
+
const listings = state[values.reducerKey]?.listings || [];
|
|
32
|
+
if (!listings || listings.length === 0) return [];
|
|
33
|
+
|
|
34
|
+
// 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);
|
|
40
|
+
|
|
41
|
+
return latestListings.map((listing) => {
|
|
42
|
+
const title = listing.fields?.[values.mandatoryFields.title] || values.labels.defaultTitle;
|
|
43
|
+
const imageField = listing.fields?.[values.mandatoryFields.featureImage];
|
|
44
|
+
let imageSource = null;
|
|
45
|
+
|
|
46
|
+
if (imageField) {
|
|
47
|
+
if (typeof imageField === "string") {
|
|
48
|
+
imageSource = imageField;
|
|
49
|
+
} else if (imageField?.url) {
|
|
50
|
+
imageSource = imageField.url;
|
|
51
|
+
} else if (imageField?.uri) {
|
|
52
|
+
imageSource = imageField.uri;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
id: listing.id,
|
|
58
|
+
type: values.featureKey,
|
|
59
|
+
title: title,
|
|
60
|
+
description: "",
|
|
61
|
+
image: imageSource,
|
|
62
|
+
date: listing.createdAt || listing.updatedAt || 0,
|
|
63
|
+
data: listing,
|
|
64
|
+
};
|
|
65
|
+
});
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const FeatureConfig = {
|
|
69
|
+
key: values.featureKey,
|
|
70
|
+
aliases: ["feature-builder", "custom-features"],
|
|
71
|
+
title: values.featureName,
|
|
72
|
+
|
|
73
|
+
gridMenu: {
|
|
74
|
+
icon: values.navigation.gridMenu.icon,
|
|
75
|
+
viewBox: values.navigation.gridMenu.viewBox,
|
|
76
|
+
navigate: values.navigation.gridMenu.navigate,
|
|
77
|
+
options: values.navigation.gridMenu.options,
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
// For now, keep other menus minimal since this is primarily a display feature
|
|
81
|
+
addMenu: null,
|
|
82
|
+
moreMenu: null,
|
|
83
|
+
kioskAction: null,
|
|
84
|
+
|
|
85
|
+
notification: null, // No notifications for MVP
|
|
86
|
+
submission: null, // No submissions for MVP
|
|
87
|
+
|
|
88
|
+
featured: {
|
|
89
|
+
type: values.featureKey,
|
|
90
|
+
prop: values.featureKey,
|
|
91
|
+
reducer: {
|
|
92
|
+
key: values.featureKey,
|
|
93
|
+
prop: "list",
|
|
94
|
+
},
|
|
95
|
+
updateKey: values.featureKey,
|
|
96
|
+
fetch: fetchFeatured,
|
|
97
|
+
get: getFeatured,
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
hideTabBar: [], // No special tab bar hiding needed
|
|
101
|
+
|
|
102
|
+
env: {
|
|
103
|
+
baseStage: "",
|
|
104
|
+
baseAPIUrl: "",
|
|
105
|
+
hasGradientHeader: false,
|
|
106
|
+
defaultProfileImage: "",
|
|
107
|
+
tinyChatDefault: "",
|
|
108
|
+
baseUploadsUrl: "",
|
|
109
|
+
allowMediaDownload: false,
|
|
110
|
+
allowMediaSharing: false,
|
|
111
|
+
awsUploadsBucket: "",
|
|
112
|
+
awsStorageBucket: "",
|
|
113
|
+
preferredSite: "",
|
|
114
|
+
strings: {},
|
|
115
|
+
newEventDefaults: "",
|
|
116
|
+
defaultAllowComments: true,
|
|
117
|
+
},
|
|
118
|
+
|
|
119
|
+
init: (environment, navigation, notificationBell) => {
|
|
120
|
+
FeatureConfig.env = environment;
|
|
121
|
+
Services.navigation = navigation;
|
|
122
|
+
BaseComponents.NotificationBell = notificationBell;
|
|
123
|
+
PlussCore.Config.init(environment, navigation);
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
export default FeatureConfig;
|
package/src/index.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
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);
|
|
12
|
+
|
|
13
|
+
export const Reducers = {
|
|
14
|
+
[values.reducerKey]: featureBuilderReducer,
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const Screens = {
|
|
18
|
+
FeatureBuilderList: FeatureListScreen,
|
|
19
|
+
FeatureBuilderDetail: FeatureDetailScreen,
|
|
20
|
+
};
|
|
21
|
+
|
|
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";
|
|
26
|
+
|
|
27
|
+
export { FeatureConfig };
|
package/src/js/Colors.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Colours } from "../core.config";
|
|
2
|
+
import { values } from "../values.config";
|
|
3
|
+
|
|
4
|
+
export const getMainBrandingColourFromState =
|
|
5
|
+
typeof Colours?.getMainBrandingColourFromState === "function"
|
|
6
|
+
? Colours.getMainBrandingColourFromState
|
|
7
|
+
: (state) =>
|
|
8
|
+
state?.user?.siteBranding?.MainBrandingColour ||
|
|
9
|
+
values.colors.defaultBrandMain;
|
|
10
|
+
|
|
11
|
+
export const getLightBrandingColourFromState =
|
|
12
|
+
typeof Colours?.getLightBrandingColourFromState === "function"
|
|
13
|
+
? Colours.getLightBrandingColourFromState
|
|
14
|
+
: (state) =>
|
|
15
|
+
state?.user?.siteBranding?.LightBrandingColour ||
|
|
16
|
+
values.colors.defaultBrandLight;
|
|
17
|
+
export const LINEGREY = Colours.LINEGREY;
|
|
18
|
+
export const TEXT_DARKEST = Colours.TEXT_DARKEST;
|
|
19
|
+
export const TEXT_DARK = Colours.TEXT_DARK;
|
|
20
|
+
export const TEXT_MID = Colours.TEXT_MID;
|
|
21
|
+
export const TEXT_LIGHT = Colours.TEXT_LIGHT;
|
|
22
|
+
export const TEXT_LIGHTER = Colours.TEXT_LIGHTER;
|
|
23
|
+
export const TEXT_BLUEGREY = Colours.TEXT_BLUEGREY;
|
|
24
|
+
export const COLOUR_GREEN = Colours.COLOUR_GREEN;
|
|
25
|
+
export const COLOUR_TRANSPARENT = Colours.COLOUR_TRANSPARENT;
|
|
26
|
+
export const COLOUR_TEAL = Colours.COLOUR_TEAL;
|
|
27
|
+
export const COLOUR_PURPLE = Colours.COLOUR_PURPLE;
|
|
28
|
+
export const COLOUR_TANGERINE = Colours.COLOUR_TANGERINE;
|
|
29
|
+
|
|
30
|
+
// TODO: Add required references
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Field Types Constants for Feature Builder
|
|
3
|
+
* Defines all supported field types and their properties
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export const FIELD_TYPES = {
|
|
7
|
+
TITLE: "title",
|
|
8
|
+
TEXT: "text",
|
|
9
|
+
DESCRIPTION: "description",
|
|
10
|
+
IMAGE: "image",
|
|
11
|
+
GALLERY: "gallery",
|
|
12
|
+
FEATURE_IMAGE: "feature-image",
|
|
13
|
+
EMAIL: "email",
|
|
14
|
+
NUMBER: "number",
|
|
15
|
+
DATE: "date",
|
|
16
|
+
CTA: "cta",
|
|
17
|
+
FILE: "file",
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Field type display labels for UI
|
|
22
|
+
*/
|
|
23
|
+
export const FIELD_TYPE_LABELS = {
|
|
24
|
+
[FIELD_TYPES.TITLE]: "Title",
|
|
25
|
+
[FIELD_TYPES.TEXT]: "Text",
|
|
26
|
+
[FIELD_TYPES.DESCRIPTION]: "Description",
|
|
27
|
+
[FIELD_TYPES.IMAGE]: "Image",
|
|
28
|
+
[FIELD_TYPES.GALLERY]: "Gallery",
|
|
29
|
+
[FIELD_TYPES.FEATURE_IMAGE]: "Feature Image",
|
|
30
|
+
[FIELD_TYPES.EMAIL]: "Email",
|
|
31
|
+
[FIELD_TYPES.NUMBER]: "Number",
|
|
32
|
+
[FIELD_TYPES.DATE]: "Date",
|
|
33
|
+
[FIELD_TYPES.CTA]: "CTA Button",
|
|
34
|
+
[FIELD_TYPES.FILE]: "File",
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Field type validation rules
|
|
39
|
+
*/
|
|
40
|
+
export const FIELD_VALIDATION = {
|
|
41
|
+
[FIELD_TYPES.TITLE]: {
|
|
42
|
+
required: true,
|
|
43
|
+
minLength: 1,
|
|
44
|
+
},
|
|
45
|
+
[FIELD_TYPES.TEXT]: {
|
|
46
|
+
required: true,
|
|
47
|
+
minLength: 1,
|
|
48
|
+
},
|
|
49
|
+
[FIELD_TYPES.DESCRIPTION]: {
|
|
50
|
+
required: false,
|
|
51
|
+
minLength: 0,
|
|
52
|
+
},
|
|
53
|
+
[FIELD_TYPES.IMAGE]: {
|
|
54
|
+
required: false,
|
|
55
|
+
allowedTypes: ["image/jpeg", "image/png", "image/gif"],
|
|
56
|
+
},
|
|
57
|
+
[FIELD_TYPES.GALLERY]: {
|
|
58
|
+
required: false,
|
|
59
|
+
allowedTypes: ["image/jpeg", "image/png", "image/gif"],
|
|
60
|
+
allowMultiple: true,
|
|
61
|
+
},
|
|
62
|
+
[FIELD_TYPES.FEATURE_IMAGE]: {
|
|
63
|
+
required: true,
|
|
64
|
+
allowedTypes: ["image/jpeg", "image/png", "image/gif"],
|
|
65
|
+
},
|
|
66
|
+
[FIELD_TYPES.EMAIL]: {
|
|
67
|
+
required: false,
|
|
68
|
+
pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
|
|
69
|
+
},
|
|
70
|
+
[FIELD_TYPES.NUMBER]: {
|
|
71
|
+
required: false,
|
|
72
|
+
pattern: /^\d+\.?\d*$/,
|
|
73
|
+
},
|
|
74
|
+
[FIELD_TYPES.DATE]: {
|
|
75
|
+
required: false,
|
|
76
|
+
},
|
|
77
|
+
[FIELD_TYPES.CTA]: {
|
|
78
|
+
required: false,
|
|
79
|
+
fields: ["label", "url"],
|
|
80
|
+
},
|
|
81
|
+
[FIELD_TYPES.FILE]: {
|
|
82
|
+
required: false,
|
|
83
|
+
allowMultiple: true,
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Get display label for field type
|
|
89
|
+
*/
|
|
90
|
+
export const getFieldTypeLabel = (fieldType) => {
|
|
91
|
+
return FIELD_TYPE_LABELS[fieldType] || fieldType;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Check if field type is a CTA (has label and url structure)
|
|
96
|
+
*/
|
|
97
|
+
export const isCTAField = (fieldValue) => {
|
|
98
|
+
return (
|
|
99
|
+
fieldValue &&
|
|
100
|
+
typeof fieldValue === "object" &&
|
|
101
|
+
fieldValue.label &&
|
|
102
|
+
fieldValue.url
|
|
103
|
+
);
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Check if field value represents an image
|
|
108
|
+
*/
|
|
109
|
+
export const isImageField = (fieldValue) => {
|
|
110
|
+
if (typeof fieldValue === "string") {
|
|
111
|
+
return (
|
|
112
|
+
fieldValue.startsWith("http://") || fieldValue.startsWith("https://")
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
if (typeof fieldValue === "object" && (fieldValue.uri || fieldValue.url)) {
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
return false;
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Check if field value represents a link/URL
|
|
123
|
+
*/
|
|
124
|
+
export const isLinkField = (fieldValue) => {
|
|
125
|
+
if (typeof fieldValue === "string") {
|
|
126
|
+
return (
|
|
127
|
+
fieldValue.startsWith("http://") || fieldValue.startsWith("https://")
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
return false;
|
|
131
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Services } from "../feature.config";
|
|
2
|
+
|
|
3
|
+
const navService = {
|
|
4
|
+
navigate: (routeName, params) =>
|
|
5
|
+
Services.navigation.navigate(routeName, params),
|
|
6
|
+
goBack: (routeName) => Services.navigation.goBack(routeName),
|
|
7
|
+
getParentRoute: () => Services.navigation.getParentRoute(),
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export default navService;
|
|
11
|
+
|
|
12
|
+
// TODO: Add required references
|
package/src/js/Styles.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helper functions for Feature Builder
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Get the summary field value from a listing based on feature definition
|
|
7
|
+
* Looks for a field marked with useAsSummary: true, then falls back to first description field
|
|
8
|
+
* @param {Object} listing - The listing object containing fields
|
|
9
|
+
* @param {Object} featureDefinition - The feature definition containing field definitions
|
|
10
|
+
* @returns {string} The summary field value or empty string
|
|
11
|
+
*/
|
|
12
|
+
export const getSummaryFieldValue = (listing, featureDefinition) => {
|
|
13
|
+
// First, look for field marked with useAsSummary: true
|
|
14
|
+
const summaryField = featureDefinition?.fields?.find(
|
|
15
|
+
(field) => field.useAsSummary === true,
|
|
16
|
+
);
|
|
17
|
+
if (summaryField) {
|
|
18
|
+
return listing.fields?.[summaryField.id] || "";
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Fall back to first description-type field
|
|
22
|
+
const descriptionField = featureDefinition?.fields?.find(
|
|
23
|
+
(field) => field.type === "description",
|
|
24
|
+
);
|
|
25
|
+
if (descriptionField) {
|
|
26
|
+
return listing.fields?.[descriptionField.id] || "";
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return "";
|
|
30
|
+
};
|
package/src/js/index.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Session, Helper } from "../core.config";
|
|
2
|
+
|
|
3
|
+
// Session
|
|
4
|
+
export const authedFunction = async (request) => {
|
|
5
|
+
const result = await Session.authedFunction(request);
|
|
6
|
+
return result;
|
|
7
|
+
};
|
|
8
|
+
export const getSessionTokenAWS = Session.getSessionTokenAWS;
|
|
9
|
+
export const getSessionUidAWS = Session.getSessionUidAWS;
|
|
10
|
+
|
|
11
|
+
// Helper
|
|
12
|
+
export const getFirstName = Helper.getFirstName;
|
|
13
|
+
export const getImageSource = Helper.getImageSource;
|
|
14
|
+
export const StatusBarHeight = Helper.StatusBarHeight;
|
|
15
|
+
export const getPluralS = Helper.getPluralS;
|
|
16
|
+
export const getShadowStyle = Helper.getShadowStyle;
|
|
17
|
+
export const getSite = Helper.getSite;
|
|
18
|
+
export const getValueOrDefault = Helper.getValueOrDefault;
|
|
19
|
+
export const usersToSearchResult = Helper.usersToSearchResult;
|
|
20
|
+
export const searchUsers = Helper.searchUsers;
|
|
21
|
+
|
|
22
|
+
export * from "./Colors";
|
|
23
|
+
|
|
24
|
+
// TODO: Add required references
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export const SPACING = {
|
|
2
|
+
// Base spacing unit (4px)
|
|
3
|
+
XS: 4,
|
|
4
|
+
// Small spacing (8px)
|
|
5
|
+
SM: 8,
|
|
6
|
+
// Medium spacing (16px)
|
|
7
|
+
MD: 16,
|
|
8
|
+
// Large spacing (24px)
|
|
9
|
+
LG: 24,
|
|
10
|
+
// Extra large spacing (32px)
|
|
11
|
+
XL: 32,
|
|
12
|
+
// Double large spacing (48px)
|
|
13
|
+
XXL: 48,
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const commonSpacing = {
|
|
17
|
+
marginTiny: SPACING.XS,
|
|
18
|
+
marginSmall: SPACING.SM,
|
|
19
|
+
marginMedium: SPACING.MD,
|
|
20
|
+
marginLarge: SPACING.LG,
|
|
21
|
+
marginXLarge: SPACING.XL,
|
|
22
|
+
marginXXLarge: SPACING.XXL,
|
|
23
|
+
|
|
24
|
+
paddingTiny: SPACING.XS,
|
|
25
|
+
paddingSmall: SPACING.SM,
|
|
26
|
+
paddingMedium: SPACING.MD,
|
|
27
|
+
paddingLarge: SPACING.LG,
|
|
28
|
+
paddingXLarge: SPACING.XL,
|
|
29
|
+
paddingXXLarge: SPACING.XXL,
|
|
30
|
+
};
|
package/src/js/types.js
ADDED
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core type definitions for Feature Builder Mobile Extension
|
|
3
|
+
* This file contains JSDoc type definitions for all major entities used throughout the feature builder
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @typedef {Object} FieldDefinition
|
|
8
|
+
* @property {string} id - Unique identifier for the field (e.g., 'mandatory-title', 'mandatory-feature-image')
|
|
9
|
+
* @property {string} type - Field data type ('string', 'number', 'boolean', 'image', 'pdf', 'link')
|
|
10
|
+
* @property {string} label - Human-readable label for the field
|
|
11
|
+
* @property {boolean} isMandatory - Whether this field is required for all feature definitions
|
|
12
|
+
* @property {Object} [validation] - Validation rules for the field
|
|
13
|
+
* @property {string} [validation.required] - Required validation message
|
|
14
|
+
* @property {string} [validation.min] - Minimum length/value validation
|
|
15
|
+
* @property {string} [validation.max] - Maximum length/value validation
|
|
16
|
+
* @property {string} [validation.pattern] - Regex pattern for validation
|
|
17
|
+
* @property {Object} [options] - Additional options specific to field type
|
|
18
|
+
* @property {string} [options.placeholder] - Placeholder text for input fields
|
|
19
|
+
* @property {string} [options.helpText] - Help text displayed below field
|
|
20
|
+
* @property {string[]} [options.allowedTypes] - Allowed file types for file fields
|
|
21
|
+
* @property {number} [options.maxSize] - Maximum file size in bytes
|
|
22
|
+
* @property {number} order - Display order of the field in forms
|
|
23
|
+
* @example
|
|
24
|
+
* const titleField = {
|
|
25
|
+
* id: 'mandatory-title',
|
|
26
|
+
* type: 'string',
|
|
27
|
+
* label: 'Title',
|
|
28
|
+
* isMandatory: true,
|
|
29
|
+
* validation: { required: 'Title is required' },
|
|
30
|
+
* options: { placeholder: 'Enter feature title' },
|
|
31
|
+
* order: 1
|
|
32
|
+
* };
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @typedef {Object} FeatureDefinition
|
|
37
|
+
* @property {string} id - Unique identifier for the feature definition
|
|
38
|
+
* @property {string} site - Site identifier this feature belongs to
|
|
39
|
+
* @property {string} title - Human-readable title for the feature
|
|
40
|
+
* @property {string} displayName - Display name shown in UI
|
|
41
|
+
* @property {string} icon - Icon identifier for the feature
|
|
42
|
+
* @property {FieldDefinition[]} fields - Array of field definitions that make up this feature
|
|
43
|
+
* @property {LayoutConfig} layout - Layout configuration for displaying listings
|
|
44
|
+
* @property {string} createdAt - ISO timestamp when feature was created
|
|
45
|
+
* @property {string} editedAt - ISO timestamp when feature was last edited
|
|
46
|
+
* @property {string|null} deletedAt - ISO timestamp when feature was deleted (null if active)
|
|
47
|
+
* @example
|
|
48
|
+
* const featureDefinition = {
|
|
49
|
+
* id: 'restaurant-menu',
|
|
50
|
+
* site: 'pluss60-demo',
|
|
51
|
+
* title: 'Restaurant Menu',
|
|
52
|
+
* displayName: 'Menu Items',
|
|
53
|
+
* icon: 'restaurant',
|
|
54
|
+
* fields: [titleField, imageField, descriptionField],
|
|
55
|
+
* layout: { type: 'round', iconGrid: false },
|
|
56
|
+
* createdAt: '2023-12-01T10:00:00Z',
|
|
57
|
+
* editedAt: '2023-12-01T10:00:00Z',
|
|
58
|
+
* deletedAt: null
|
|
59
|
+
* };
|
|
60
|
+
*/
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* @typedef {Object} LayoutConfig
|
|
64
|
+
* @property {string} type - Layout type ('round', 'square', 'condensed', 'feature')
|
|
65
|
+
* @property {boolean} [iconGrid] - Whether to use icon grid layout (for round/square types)
|
|
66
|
+
* @property {Object} [options] - Additional layout-specific options
|
|
67
|
+
* @property {number} [options.columns] - Number of columns for grid layouts
|
|
68
|
+
* @property {number} [options.itemSpacing] - Spacing between items
|
|
69
|
+
* @property {Object} [options.textStyling] - Text styling options
|
|
70
|
+
* @example
|
|
71
|
+
* const layoutConfig = {
|
|
72
|
+
* type: 'round',
|
|
73
|
+
* iconGrid: true,
|
|
74
|
+
* options: {
|
|
75
|
+
* columns: 2,
|
|
76
|
+
* itemSpacing: 16,
|
|
77
|
+
* textStyling: { showTitle: true, showDescription: false }
|
|
78
|
+
* }
|
|
79
|
+
* };
|
|
80
|
+
*/
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* @typedef {Object} Listing
|
|
84
|
+
* @property {string} id - Unique identifier for the listing (UUID)
|
|
85
|
+
* @property {string} featureDefinitionId - ID of the feature definition this listing belongs to
|
|
86
|
+
* @property {Object} fields - Key-value pairs of field data
|
|
87
|
+
* @property {string} [fields.mandatory-title] - Title field value
|
|
88
|
+
* @property {string} [fields.mandatory-feature-image] - Feature image URL
|
|
89
|
+
* @property {*} [fields.*] - Additional field values based on feature definition
|
|
90
|
+
* @property {string} createdAt - ISO timestamp when listing was created
|
|
91
|
+
* @property {string} editedAt - ISO timestamp when listing was last edited
|
|
92
|
+
* @property {string|null} deletedAt - ISO timestamp when listing was deleted (null if active)
|
|
93
|
+
* @property {Object} [metadata] - Additional metadata about the listing
|
|
94
|
+
* @property {string} [metadata.createdBy] - User ID who created the listing
|
|
95
|
+
* @property {string} [metadata.updatedBy] - User ID who last updated the listing
|
|
96
|
+
* @example
|
|
97
|
+
* const listing = {
|
|
98
|
+
* id: '123e4567-e89b-12d3-a456-426614174000',
|
|
99
|
+
* featureDefinitionId: 'restaurant-menu',
|
|
100
|
+
* fields: {
|
|
101
|
+
* 'mandatory-title': 'Margherita Pizza',
|
|
102
|
+
* 'mandatory-feature-image': 'https://example.com/pizza.jpg',
|
|
103
|
+
* 'description': 'Classic margherita with fresh basil',
|
|
104
|
+
* 'price': 12.99
|
|
105
|
+
* },
|
|
106
|
+
* createdAt: '2023-12-01T10:00:00Z',
|
|
107
|
+
* editedAt: '2023-12-01T10:00:00Z',
|
|
108
|
+
* deletedAt: null
|
|
109
|
+
* };
|
|
110
|
+
*/
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* @typedef {Object} ListingItem
|
|
114
|
+
* @property {string} id - Listing ID
|
|
115
|
+
* @property {string} title - Listing title (extracted from mandatory-title field)
|
|
116
|
+
* @property {string} [image] - Listing image URL (extracted from mandatory-feature-image field)
|
|
117
|
+
* @property {Object} fields - All field values for the listing
|
|
118
|
+
* @property {string} featureDefinitionId - ID of parent feature definition
|
|
119
|
+
* @property {FeatureDefinition} [featureDefinition] - Parent feature definition (if loaded)
|
|
120
|
+
* @property {boolean} isActive - Whether listing is active (not deleted)
|
|
121
|
+
* @property {string} createdAt - Creation timestamp
|
|
122
|
+
* @property {string} editedAt - Last edit timestamp
|
|
123
|
+
* @example
|
|
124
|
+
* const listItem = {
|
|
125
|
+
* id: '123e4567-e89b-12d3-a456-426614174000',
|
|
126
|
+
* title: 'Margherita Pizza',
|
|
127
|
+
* image: 'https://example.com/pizza.jpg',
|
|
128
|
+
* fields: { 'mandatory-title': 'Margherita Pizza', ... },
|
|
129
|
+
* featureDefinitionId: 'restaurant-menu',
|
|
130
|
+
* featureDefinition: restaurantMenuDefinition,
|
|
131
|
+
* isActive: true,
|
|
132
|
+
* createdAt: '2023-12-01T10:00:00Z',
|
|
133
|
+
* editedAt: '2023-12-01T10:00:00Z'
|
|
134
|
+
* };
|
|
135
|
+
*/
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* @typedef {Object} FeatureBuilderState
|
|
139
|
+
* @property {FeatureDefinition|null} feature - Currently loaded feature definition
|
|
140
|
+
* @property {Listing[]} listings - Listings for the current feature
|
|
141
|
+
* @property {boolean} loading - Global loading state
|
|
142
|
+
* @property {string|null} error - Global error message
|
|
143
|
+
* @example
|
|
144
|
+
* const featureBuilderState = {
|
|
145
|
+
* feature: restaurantMenuDefinition,
|
|
146
|
+
* listings: [pizzaListing, pastaListing],
|
|
147
|
+
* loading: false,
|
|
148
|
+
* error: null
|
|
149
|
+
* };
|
|
150
|
+
*/
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* @typedef {Object} NavigationParams
|
|
154
|
+
* @property {Object} [listing] - Listing data for detail view
|
|
155
|
+
* @property {FeatureDefinition} [featureDefinition] - Feature definition object
|
|
156
|
+
* @property {string} [featureTitle] - Feature title for header display
|
|
157
|
+
* @property {string} [mode] - Navigation mode ('view', 'edit', 'create')
|
|
158
|
+
* @property {Object} [metadata] - Additional navigation metadata
|
|
159
|
+
* @example
|
|
160
|
+
* const navParams = {
|
|
161
|
+
* listing: pizzaListing,
|
|
162
|
+
* featureDefinition: restaurantMenuDefinition,
|
|
163
|
+
* featureTitle: 'Restaurant Menu',
|
|
164
|
+
* mode: 'view',
|
|
165
|
+
* metadata: { source: 'widget' }
|
|
166
|
+
* };
|
|
167
|
+
*/
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* @typedef {Object} WidgetProps
|
|
171
|
+
* @property {Listing[]} items - Listings to display in widget
|
|
172
|
+
* @property {FeatureDefinition} featureDefinition - Feature definition for styling
|
|
173
|
+
* @property {string} colourBrandingMain - Main branding color
|
|
174
|
+
* @property {Function} onItemPress - Callback when item is pressed
|
|
175
|
+
* @property {Function} [onRefresh] - Callback for pull-to-refresh
|
|
176
|
+
* @property {boolean} refreshing - Whether widget is currently refreshing
|
|
177
|
+
* @property {Object} [style] - Additional styles for widget
|
|
178
|
+
* @property {number} [maxItems] - Maximum number of items to display
|
|
179
|
+
* @example
|
|
180
|
+
* const widgetProps = {
|
|
181
|
+
* items: [pizzaListing, pastaListing],
|
|
182
|
+
* featureDefinition: restaurantMenuDefinition,
|
|
183
|
+
* colourBrandingMain: '#FF6363',
|
|
184
|
+
* onItemPress: (item) => navigation.navigate('Detail', { listing: item }),
|
|
185
|
+
* refreshing: false,
|
|
186
|
+
* onRefresh: handleRefresh,
|
|
187
|
+
* maxItems: 2
|
|
188
|
+
* };
|
|
189
|
+
*/
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* @typedef {Object} ApiResponse
|
|
193
|
+
* @property {boolean} success - Whether the API call was successful
|
|
194
|
+
* @property {*} data - Response data payload
|
|
195
|
+
* @property {string} [message] - Optional message from the server
|
|
196
|
+
* @property {string} [error] - Error message if the call failed
|
|
197
|
+
* @property {number} [status] - HTTP status code
|
|
198
|
+
* @property {Object} [metadata] - Additional response metadata
|
|
199
|
+
* @example
|
|
200
|
+
* const apiResponse = {
|
|
201
|
+
* success: true,
|
|
202
|
+
* data: { listings: [pizzaListing, pastaListing] },
|
|
203
|
+
* message: 'Listings fetched successfully',
|
|
204
|
+
* status: 200,
|
|
205
|
+
* metadata: { count: 2, hasMore: false }
|
|
206
|
+
* };
|
|
207
|
+
*/
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* @typedef {Object} FieldComponentProps
|
|
211
|
+
* @property {FieldDefinition} fieldDefinition - Field definition configuration
|
|
212
|
+
* @property {*} value - Current field value
|
|
213
|
+
* @property {Function} onChange - Callback when field value changes
|
|
214
|
+
* @property {string} [error] - Validation error message
|
|
215
|
+
* @property {boolean} [disabled] - Whether field is disabled
|
|
216
|
+
* @property {Object} [style] - Additional styles for field component
|
|
217
|
+
* @property {Object} [theme] - Theme configuration
|
|
218
|
+
* @example
|
|
219
|
+
* const fieldProps = {
|
|
220
|
+
* fieldDefinition: titleField,
|
|
221
|
+
* value: 'Margherita Pizza',
|
|
222
|
+
* onChange: (newValue) => setFieldValue('title', newValue),
|
|
223
|
+
* error: null,
|
|
224
|
+
* disabled: false,
|
|
225
|
+
* style: { marginBottom: 16 },
|
|
226
|
+
* theme: { primaryColor: '#FF6363' }
|
|
227
|
+
* };
|
|
228
|
+
*/
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* @typedef {Object} LayoutComponentProps
|
|
232
|
+
* @property {Listing[]} listings - Array of listings to display
|
|
233
|
+
* @property {FeatureDefinition} featureDefinition - Feature definition for layout configuration
|
|
234
|
+
* @property {string} colourBrandingMain - Main branding color
|
|
235
|
+
* @property {Function} onItemPress - Callback when listing item is pressed
|
|
236
|
+
* @property {Function} [onRefresh] - Callback for pull-to-refresh
|
|
237
|
+
* @property {boolean} [refreshing] - Whether layout is currently refreshing
|
|
238
|
+
* @property {Object} [style] - Additional styles for layout component
|
|
239
|
+
* @property {Object} [layoutOptions] - Layout-specific options
|
|
240
|
+
* @example
|
|
241
|
+
* const layoutProps = {
|
|
242
|
+
* listings: [pizzaListing, pastaListing],
|
|
243
|
+
* featureDefinition: restaurantMenuDefinition,
|
|
244
|
+
* colourBrandingMain: '#FF6363',
|
|
245
|
+
* onItemPress: handleItemPress,
|
|
246
|
+
* refreshing: false,
|
|
247
|
+
* onRefresh: handleRefresh,
|
|
248
|
+
* layoutOptions: { showImages: true, columns: 2 }
|
|
249
|
+
* };
|
|
250
|
+
*/
|
|
251
|
+
|
|
252
|
+
// Export empty object to make this a module
|
|
253
|
+
module.exports = {};
|