@plusscommunities/pluss-feature-builder-web-a 1.0.2-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.babelrc +4 -0
- package/dist/index.cjs.js +7792 -0
- package/package.json +54 -0
- package/rollup.config.js +68 -0
- package/src/actions/featureBuilderStringsActions.js +88 -0
- package/src/actions/featureDefinitionsIndex.js +258 -0
- package/src/actions/formActions.js +311 -0
- package/src/actions/index.js +12 -0
- package/src/actions/listingActions.js +350 -0
- package/src/actions/wizardActions.js +240 -0
- package/src/components/ActivityCardExample.jsx +86 -0
- package/src/components/ActivityCardExample.module.css +130 -0
- package/src/components/BackgroundLoader.jsx +33 -0
- package/src/components/BackgroundLoader.module.css +46 -0
- package/src/components/BaseFieldConfig.jsx +305 -0
- package/src/components/BaseFieldConfig.module.css +42 -0
- package/src/components/CenteredContainer.jsx +29 -0
- package/src/components/CenteredContainer.module.css +171 -0
- package/src/components/DeleteConfirmationPopup.jsx +95 -0
- package/src/components/DeleteConfirmationPopup.module.css +12 -0
- package/src/components/ErrorBoundary.jsx +134 -0
- package/src/components/ErrorBoundary.module.css +77 -0
- package/src/components/ErrorMessage.jsx +85 -0
- package/src/components/ErrorMessage.module.css +116 -0
- package/src/components/ExampleDisplay.jsx +26 -0
- package/src/components/ExampleDisplay.module.css +3 -0
- package/src/components/FeatureBuilderSidebar.jsx +84 -0
- package/src/components/FeatureBuilderSuccessPopup.jsx +55 -0
- package/src/components/FeatureBuilderSuccessPopup.module.css +43 -0
- package/src/components/FeatureBuilderWelcomePopup.jsx +51 -0
- package/src/components/FeatureBuilderWelcomePopup.module.css +21 -0
- package/src/components/FeatureListingCard.jsx +104 -0
- package/src/components/FeatureListingCard.module.css +62 -0
- package/src/components/Fields.jsx +460 -0
- package/src/components/Fields.module.css +159 -0
- package/src/components/IconLoader.jsx +153 -0
- package/src/components/IconLoader.module.css +92 -0
- package/src/components/IconSelector.jsx +112 -0
- package/src/components/IconSelector.module.css +197 -0
- package/src/components/ListingEditor.jsx +406 -0
- package/src/components/ListingEditor.module.css +14 -0
- package/src/components/ListingSuccessPopup.jsx +52 -0
- package/src/components/LoadingScreen.jsx +54 -0
- package/src/components/LoadingScreen.module.css +103 -0
- package/src/components/LoadingState.jsx +40 -0
- package/src/components/LoadingState.module.css +18 -0
- package/src/components/PreviewFull.js +24 -0
- package/src/components/PreviewFull.module.css +11 -0
- package/src/components/PreviewGrid.js +14 -0
- package/src/components/PreviewWidget.js +27 -0
- package/src/components/PreviewWidget.module.css +15 -0
- package/src/components/SidebarLayout.jsx +292 -0
- package/src/components/SidebarLayout.module.css +145 -0
- package/src/components/SkeletonLoader.jsx +128 -0
- package/src/components/SkeletonLoader.module.css +295 -0
- package/src/components/SortButtonGroup.jsx +34 -0
- package/src/components/SortButtonGroup.module.css +51 -0
- package/src/components/ToastContainer.jsx +98 -0
- package/src/components/ToastContainer.module.css +156 -0
- package/src/components/ToggleSwitch.js +40 -0
- package/src/components/ToggleSwitch.module.css +48 -0
- package/src/components/TwoColumnInput.jsx +29 -0
- package/src/components/TwoColumnInput.module.css +32 -0
- package/src/components/ViewFull.js +139 -0
- package/src/components/ViewFull.module.css +71 -0
- package/src/components/ViewWidget.js +62 -0
- package/src/components/ViewWidget.module.css +28 -0
- package/src/components/iconCategories.js +135 -0
- package/src/components/iconImports.js +409 -0
- package/src/components/index.js +61 -0
- package/src/components/listing/FileListItem.jsx +86 -0
- package/src/components/listing/GalleryDisplay.jsx +331 -0
- package/src/components/listing/GalleryDisplay.module.css +309 -0
- package/src/components/listing/ListingCTAInput.jsx +82 -0
- package/src/components/listing/ListingDescriptionInput.jsx +73 -0
- package/src/components/listing/ListingField.jsx +101 -0
- package/src/components/listing/ListingField.module.css +106 -0
- package/src/components/listing/ListingFileInput.jsx +255 -0
- package/src/components/listing/ListingFileInput.module.css +192 -0
- package/src/components/listing/ListingForm.jsx +90 -0
- package/src/components/listing/ListingForm.module.css +38 -0
- package/src/components/listing/ListingGalleryInput.jsx +236 -0
- package/src/components/listing/ListingGalleryInput.module.css +131 -0
- package/src/components/listing/ListingImageInput.jsx +153 -0
- package/src/components/listing/ListingTextInput.jsx +72 -0
- package/src/feature.config.js +130 -0
- package/src/helper/index.js +135 -0
- package/src/hooks/useFeatureDefinitionLoader.js +62 -0
- package/src/images/full.png +0 -0
- package/src/images/fullNoTitle.png +0 -0
- package/src/images/previewWidget.png +0 -0
- package/src/images/widget.png +0 -0
- package/src/index.js +38 -0
- package/src/pages/CreateListingPage.jsx +49 -0
- package/src/pages/EditListingPage.jsx +58 -0
- package/src/reducers/featureBuilderReducer.js +744 -0
- package/src/screens/CreateListing.module.css +45 -0
- package/src/screens/Form.module.css +734 -0
- package/src/screens/FormFieldsStep.jsx +689 -0
- package/src/screens/FormLayoutStep.jsx +445 -0
- package/src/screens/FormOverviewStep.jsx +396 -0
- package/src/screens/ListingScreen.jsx +478 -0
- package/src/screens/ListingScreen.module.css +333 -0
- package/src/selectors/featureBuilderSelectors.js +529 -0
- package/src/types/index.js +91 -0
- package/src/utils/textUtils.js +89 -0
- package/src/validators/galleryValidators.js +345 -0
- package/src/values.config.a.js +49 -0
- package/src/values.config.b.js +49 -0
- package/src/values.config.c.js +49 -0
- package/src/values.config.d.js +49 -0
- package/src/values.config.js +49 -0
- package/src/webapi/featureDefinitionActions.js +0 -0
- package/src/webapi/featuresActions.js +90 -0
- package/src/webapi/helper.js +4 -0
- package/src/webapi/index.js +12 -0
- package/src/webapi/listingActions.js +176 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { PlussCore } from "../feature.config";
|
|
3
|
+
|
|
4
|
+
const { Components } = PlussCore;
|
|
5
|
+
const { Text, Button } = Components;
|
|
6
|
+
import styles from "./ErrorBoundary.module.css";
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ErrorBoundary extends React.Component {
|
|
10
|
+
constructor(props) {
|
|
11
|
+
super(props);
|
|
12
|
+
this.state = {
|
|
13
|
+
hasError: false,
|
|
14
|
+
error: null,
|
|
15
|
+
errorInfo: null,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
static getDerivedStateFromError(error) {
|
|
20
|
+
return { hasError: true };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
componentDidCatch(error, errorInfo) {
|
|
24
|
+
this.setState({
|
|
25
|
+
error: error,
|
|
26
|
+
errorInfo: errorInfo,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
handleRetry = () => {
|
|
31
|
+
this.setState({
|
|
32
|
+
hasError: false,
|
|
33
|
+
error: null,
|
|
34
|
+
errorInfo: null,
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
if (this.props.onRetry) {
|
|
38
|
+
this.props.onRetry();
|
|
39
|
+
} else {
|
|
40
|
+
window.location.reload();
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
render() {
|
|
45
|
+
if (this.state.hasError) {
|
|
46
|
+
const {
|
|
47
|
+
title = "Oops! Something went wrong",
|
|
48
|
+
message = "We encountered an unexpected error. Please try again.",
|
|
49
|
+
} = this.props;
|
|
50
|
+
|
|
51
|
+
return (
|
|
52
|
+
<div className="text-center p-6">
|
|
53
|
+
<div className="mb-4">
|
|
54
|
+
<span className={styles.errorIcon}>
|
|
55
|
+
⚠
|
|
56
|
+
</span>
|
|
57
|
+
</div>
|
|
58
|
+
|
|
59
|
+
<Text
|
|
60
|
+
type="h2"
|
|
61
|
+
className="mb-4"
|
|
62
|
+
className={styles.errorTitle}
|
|
63
|
+
>
|
|
64
|
+
{title}
|
|
65
|
+
</Text>
|
|
66
|
+
|
|
67
|
+
<Text
|
|
68
|
+
type="body"
|
|
69
|
+
className="mb-6"
|
|
70
|
+
className={styles.errorMessage}
|
|
71
|
+
>
|
|
72
|
+
{message}
|
|
73
|
+
</Text>
|
|
74
|
+
|
|
75
|
+
<Button buttonType="primary" onClick={this.handleRetry}>
|
|
76
|
+
Try Again
|
|
77
|
+
</Button>
|
|
78
|
+
|
|
79
|
+
{process.env.NODE_ENV === "development" && this.state.error && (
|
|
80
|
+
<details className="mt-6 text-left">
|
|
81
|
+
<summary
|
|
82
|
+
className={`cursor-pointer font-bold p-2 bg-gray-50 border rounded mb-3 ${styles.errorDetails}`}
|
|
83
|
+
>
|
|
84
|
+
Error Details (Development Only)
|
|
85
|
+
</summary>
|
|
86
|
+
<div
|
|
87
|
+
className={`p-4 bg-gray-50 border rounded ${styles.errorDetailsContent}`}
|
|
88
|
+
>
|
|
89
|
+
<div className="mb-4">
|
|
90
|
+
<strong className={styles.errorDetailsTitle}>
|
|
91
|
+
Error:
|
|
92
|
+
</strong>
|
|
93
|
+
<pre
|
|
94
|
+
className={`${styles.errorStack}`}
|
|
95
|
+
>
|
|
96
|
+
{this.state.error.toString()}
|
|
97
|
+
</pre>
|
|
98
|
+
</div>
|
|
99
|
+
|
|
100
|
+
{this.state.errorInfo && (
|
|
101
|
+
<div className="mb-4">
|
|
102
|
+
<strong className={styles.componentStackTitle}>
|
|
103
|
+
Component Stack:
|
|
104
|
+
</strong>
|
|
105
|
+
<pre className={`${styles.componentStack}`}>
|
|
106
|
+
>
|
|
107
|
+
{this.state.errorInfo.componentStack}
|
|
108
|
+
</pre>
|
|
109
|
+
</div>
|
|
110
|
+
)}
|
|
111
|
+
|
|
112
|
+
{this.state.error.stack && (
|
|
113
|
+
<div>
|
|
114
|
+
<strong className={styles.componentStackTitle}>
|
|
115
|
+
Stack Trace:
|
|
116
|
+
</strong>
|
|
117
|
+
<pre className={`${styles.componentStack}`}>
|
|
118
|
+
>
|
|
119
|
+
{this.state.error.stack}
|
|
120
|
+
</pre>
|
|
121
|
+
</div>
|
|
122
|
+
)}
|
|
123
|
+
</div>
|
|
124
|
+
</details>
|
|
125
|
+
)}
|
|
126
|
+
</div>
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return this.props.children;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export { ErrorBoundary };
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/* Error icon */
|
|
2
|
+
.errorIcon {
|
|
3
|
+
font-size: 3rem;
|
|
4
|
+
color: var(--colour-red);
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
/* Error title */
|
|
8
|
+
.errorTitle {
|
|
9
|
+
color: var(--colour-red);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/* Error message */
|
|
13
|
+
.errorMessage {
|
|
14
|
+
color: var(--text-dark);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/* Error details section */
|
|
18
|
+
.errorDetails {
|
|
19
|
+
color: var(--text-dark);
|
|
20
|
+
background-color: var(--bg-bluegrey);
|
|
21
|
+
padding: 8px;
|
|
22
|
+
margin-bottom: 8px;
|
|
23
|
+
font-weight: bold;
|
|
24
|
+
cursor: pointer;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/* Error details content */
|
|
28
|
+
.errorDetailsContent {
|
|
29
|
+
background-color: var(--bg-bluegrey);
|
|
30
|
+
padding: 16px;
|
|
31
|
+
margin-bottom: 16px;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/* Error details title */
|
|
35
|
+
.errorDetailsTitle {
|
|
36
|
+
display: block;
|
|
37
|
+
margin-bottom: 8px;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/* Error stack trace */
|
|
41
|
+
.errorStack {
|
|
42
|
+
font-size: 0.75rem;
|
|
43
|
+
line-height: 1.4;
|
|
44
|
+
overflow-x: auto;
|
|
45
|
+
background: #f8f9fa;
|
|
46
|
+
padding: 12px;
|
|
47
|
+
border-radius: 4px;
|
|
48
|
+
margin: 0;
|
|
49
|
+
color: var(--text-mid);
|
|
50
|
+
background-color: var(--bg-white);
|
|
51
|
+
border: 1px solid var(--colour-branding-inactive);
|
|
52
|
+
white-space: pre-wrap;
|
|
53
|
+
word-wrap: break-word;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/* Component stack */
|
|
57
|
+
.componentStackTitle {
|
|
58
|
+
display: block;
|
|
59
|
+
margin-bottom: 8px;
|
|
60
|
+
color: var(--text-dark);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.componentStack {
|
|
64
|
+
font-size: 0.75rem;
|
|
65
|
+
line-height: 1.4;
|
|
66
|
+
font-family: monospace;
|
|
67
|
+
background: #f8f9fa;
|
|
68
|
+
padding: 12px;
|
|
69
|
+
border-radius: 4px;
|
|
70
|
+
margin: 0;
|
|
71
|
+
overflow-x: auto;
|
|
72
|
+
white-space: pre-wrap;
|
|
73
|
+
word-wrap: break-word;
|
|
74
|
+
color: var(--text-mid);
|
|
75
|
+
background-color: var(--bg-white);
|
|
76
|
+
border: 1px solid var(--colour-branding-inactive);
|
|
77
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { PlussCore } from "../feature.config";
|
|
3
|
+
import styles from "./ErrorMessage.module.css";
|
|
4
|
+
|
|
5
|
+
const { Components } = PlussCore;
|
|
6
|
+
const { FontAwesomeIcon } = Components;
|
|
7
|
+
|
|
8
|
+
const ErrorMessage = ({
|
|
9
|
+
message,
|
|
10
|
+
onClose,
|
|
11
|
+
variant = "error",
|
|
12
|
+
showIcon = true,
|
|
13
|
+
className = "",
|
|
14
|
+
style = {},
|
|
15
|
+
}) => {
|
|
16
|
+
if (!message) return null;
|
|
17
|
+
|
|
18
|
+
const getIcon = () => {
|
|
19
|
+
switch (variant) {
|
|
20
|
+
case "error":
|
|
21
|
+
return "exclamation-circle";
|
|
22
|
+
case "warning":
|
|
23
|
+
return "exclamation-triangle";
|
|
24
|
+
case "success":
|
|
25
|
+
return "check-circle";
|
|
26
|
+
case "info":
|
|
27
|
+
return "info-circle";
|
|
28
|
+
default:
|
|
29
|
+
return "exclamation-circle";
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const getTextIcon = () => {
|
|
34
|
+
switch (variant) {
|
|
35
|
+
case "error":
|
|
36
|
+
return "⚠";
|
|
37
|
+
case "warning":
|
|
38
|
+
return "⚠";
|
|
39
|
+
case "success":
|
|
40
|
+
return "✓";
|
|
41
|
+
case "info":
|
|
42
|
+
return "ℹ";
|
|
43
|
+
default:
|
|
44
|
+
return "⚠";
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const getClassName = () => {
|
|
49
|
+
const baseClassName = styles.root;
|
|
50
|
+
const variantClassName = styles[`root--${variant}`];
|
|
51
|
+
const closableClassName = onClose ? styles["root--closable"] : "";
|
|
52
|
+
return [baseClassName, variantClassName, closableClassName, className]
|
|
53
|
+
.filter(Boolean)
|
|
54
|
+
.join(" ");
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
return (
|
|
58
|
+
<div className={getClassName()} style={style}>
|
|
59
|
+
<div className={styles.content}>
|
|
60
|
+
{showIcon && (
|
|
61
|
+
<span className={styles.icon}>
|
|
62
|
+
{FontAwesomeIcon ? (
|
|
63
|
+
<FontAwesomeIcon icon={getIcon()} />
|
|
64
|
+
) : (
|
|
65
|
+
getTextIcon()
|
|
66
|
+
)}
|
|
67
|
+
</span>
|
|
68
|
+
)}
|
|
69
|
+
<span className={styles.text}>{message}</span>
|
|
70
|
+
</div>
|
|
71
|
+
{onClose && (
|
|
72
|
+
<button
|
|
73
|
+
type="button"
|
|
74
|
+
className={styles.close}
|
|
75
|
+
onClick={onClose}
|
|
76
|
+
aria-label="Dismiss error"
|
|
77
|
+
>
|
|
78
|
+
{FontAwesomeIcon ? <FontAwesomeIcon icon="times" /> : "×"}
|
|
79
|
+
</button>
|
|
80
|
+
)}
|
|
81
|
+
</div>
|
|
82
|
+
);
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export { ErrorMessage };
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/* ErrorMessage CSS Module */
|
|
2
|
+
|
|
3
|
+
/* Block */
|
|
4
|
+
.root {
|
|
5
|
+
display: flex;
|
|
6
|
+
align-items: center;
|
|
7
|
+
justify-content: space-between;
|
|
8
|
+
padding: 12px 16px;
|
|
9
|
+
border-radius: 4px;
|
|
10
|
+
margin-bottom: 16px;
|
|
11
|
+
border: 1px solid;
|
|
12
|
+
font-size: var(--font-size-sm);
|
|
13
|
+
line-height: 1.4;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/* Content wrapper */
|
|
17
|
+
.content {
|
|
18
|
+
display: flex;
|
|
19
|
+
align-items: center;
|
|
20
|
+
flex: 1;
|
|
21
|
+
gap: 8px;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.icon {
|
|
25
|
+
flex-shrink: 0;
|
|
26
|
+
font-size: var(--font-size-base);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.text {
|
|
30
|
+
flex: 1;
|
|
31
|
+
word-wrap: break-word;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/* Close button */
|
|
35
|
+
.close {
|
|
36
|
+
background: none;
|
|
37
|
+
border: none;
|
|
38
|
+
cursor: pointer;
|
|
39
|
+
padding: 4px;
|
|
40
|
+
margin-left: 8px;
|
|
41
|
+
display: flex;
|
|
42
|
+
align-items: center;
|
|
43
|
+
justify-content: center;
|
|
44
|
+
color: inherit;
|
|
45
|
+
opacity: 0.7;
|
|
46
|
+
transition: opacity 0.2s ease;
|
|
47
|
+
border-radius: 2px;
|
|
48
|
+
flex-shrink: 0;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.close:hover {
|
|
52
|
+
opacity: 1;
|
|
53
|
+
background-color: rgba(255, 255, 255, 0.1);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/* Variants */
|
|
57
|
+
.root--error {
|
|
58
|
+
background-color: #f8d7da;
|
|
59
|
+
color: #721c24;
|
|
60
|
+
border-color: #f5c6cb;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.root--error .close:hover {
|
|
64
|
+
background-color: rgba(114, 28, 36, 0.1);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.root--warning {
|
|
68
|
+
background-color: #fff3cd;
|
|
69
|
+
color: #856404;
|
|
70
|
+
border-color: #ffeaa7;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.root--warning .close:hover {
|
|
74
|
+
background-color: rgba(133, 100, 4, 0.1);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.root--success {
|
|
78
|
+
background-color: #d4edda;
|
|
79
|
+
color: #155724;
|
|
80
|
+
border-color: #c3e6cb;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.root--success .close:hover {
|
|
84
|
+
background-color: rgba(21, 87, 36, 0.1);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.root--info {
|
|
88
|
+
background-color: #d1ecf1;
|
|
89
|
+
color: #0c5460;
|
|
90
|
+
border-color: #bee5eb;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.root--info .close:hover {
|
|
94
|
+
background-color: rgba(12, 84, 96, 0.1);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/* Closable modifier for additional spacing */
|
|
98
|
+
.root--closable {
|
|
99
|
+
padding-right: 12px;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/* Responsive adjustments */
|
|
103
|
+
@media (max-width: 768px) {
|
|
104
|
+
.root {
|
|
105
|
+
padding: 10px 12px;
|
|
106
|
+
font-size: var(--font-size-sm);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.content {
|
|
110
|
+
gap: 6px;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.icon {
|
|
114
|
+
font-size: var(--font-size-sm);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import ActivityCardExample from "./ActivityCardExample.jsx";
|
|
3
|
+
import styles from "./ExampleDisplay.module.css";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Example Display component for feature preview
|
|
7
|
+
* Shows how feature cards will appear in the app
|
|
8
|
+
* Wraps ActivityCardExample with proper styling
|
|
9
|
+
*
|
|
10
|
+
* @param {Object} props - Component props
|
|
11
|
+
* @param {string} props.displayName - Feature name to display in preview
|
|
12
|
+
* @returns {React.ReactElement} Example feature card display
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* <ExampleDisplay displayName="Contact Form" />
|
|
16
|
+
*/
|
|
17
|
+
const ExampleDisplay = ({ displayName }) => {
|
|
18
|
+
return (
|
|
19
|
+
<div className={styles.exampleDisplay}>
|
|
20
|
+
{/* Activity Card Example */}
|
|
21
|
+
<ActivityCardExample displayName={displayName} />
|
|
22
|
+
</div>
|
|
23
|
+
);
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export default ExampleDisplay;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { withRouter } from "react-router";
|
|
3
|
+
import { useSelector } from "react-redux";
|
|
4
|
+
import { PlussCore } from "../feature.config";
|
|
5
|
+
import { values } from "../values.config.js";
|
|
6
|
+
import { selectDefinition } from "../selectors/featureBuilderSelectors";
|
|
7
|
+
import { capitalizeTextWithFallback } from "../utils/textUtils";
|
|
8
|
+
|
|
9
|
+
const { HubSidebar } = PlussCore.Components;
|
|
10
|
+
|
|
11
|
+
const FeatureBuilderSidebar = ({ history, onCreate }) => {
|
|
12
|
+
// Get the actual feature definition
|
|
13
|
+
const definition = useSelector(selectDefinition);
|
|
14
|
+
|
|
15
|
+
// Extract title and display name from definition
|
|
16
|
+
const featureTitle = definition && definition.title;
|
|
17
|
+
const featureDisplayName = definition && definition.displayName;
|
|
18
|
+
|
|
19
|
+
// Check if we're in edit mode (URL contains /edit/)
|
|
20
|
+
const isEditMode = history.location.pathname.includes("/edit/");
|
|
21
|
+
|
|
22
|
+
// Get listing title from URL for edit mode
|
|
23
|
+
const getListingTitleFromUrl = () => {
|
|
24
|
+
const pathParts = history.location.pathname.split("/");
|
|
25
|
+
const editIndex = pathParts.indexOf("edit");
|
|
26
|
+
if (editIndex !== -1 && pathParts.length > editIndex + 1) {
|
|
27
|
+
return pathParts[editIndex + 1]; // Get the ID after 'edit/'
|
|
28
|
+
}
|
|
29
|
+
return null;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// Use feature title for section title, fallback to values config
|
|
33
|
+
const sectionTitle = featureTitle || values.singularName;
|
|
34
|
+
|
|
35
|
+
// Navigate to create listing
|
|
36
|
+
const handleCreateListing = () => {
|
|
37
|
+
if (onCreate) {
|
|
38
|
+
onCreate();
|
|
39
|
+
} else {
|
|
40
|
+
history.push(values.routeCreateListing);
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
// Navigate to listing view
|
|
45
|
+
const handleViewListing = () => {
|
|
46
|
+
history.push(values.routeListingScreen);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
// Build sidebar sections
|
|
50
|
+
const sections = [
|
|
51
|
+
{
|
|
52
|
+
title: sectionTitle,
|
|
53
|
+
items: [
|
|
54
|
+
{
|
|
55
|
+
type: "newButton",
|
|
56
|
+
text: `Create new ${capitalizeTextWithFallback(featureDisplayName, "listing")}`,
|
|
57
|
+
onClick: handleCreateListing,
|
|
58
|
+
buttonType: "outlined",
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
type: "navItem",
|
|
62
|
+
text:
|
|
63
|
+
isEditMode && getListingTitleFromUrl()
|
|
64
|
+
? `Edit ${capitalizeTextWithFallback(featureDisplayName, "listing")}: ${getListingTitleFromUrl()}`
|
|
65
|
+
: `View ${capitalizeTextWithFallback(featureDisplayName, "listing")} listing`,
|
|
66
|
+
icon: "eye",
|
|
67
|
+
isFontAwesome: true,
|
|
68
|
+
selected: history.location.pathname === values.routeListingScreen,
|
|
69
|
+
onClick: handleViewListing,
|
|
70
|
+
},
|
|
71
|
+
],
|
|
72
|
+
},
|
|
73
|
+
];
|
|
74
|
+
|
|
75
|
+
// Help guide configuration
|
|
76
|
+
const helpGuide = {
|
|
77
|
+
text: "Help",
|
|
78
|
+
url: "https://www.plusscommunities.com/user-guide",
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
return <HubSidebar sections={sections} helpGuide={helpGuide} />;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
export default withRouter(FeatureBuilderSidebar);
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { PlussCore } from "../feature.config";
|
|
3
|
+
const { Components } = PlussCore;
|
|
4
|
+
const { Popup } = Components;
|
|
5
|
+
import styles from "./FeatureBuilderSuccessPopup.module.css";
|
|
6
|
+
|
|
7
|
+
const FeatureBuilderSuccessPopup = ({
|
|
8
|
+
isOpen,
|
|
9
|
+
onClose,
|
|
10
|
+
featureName,
|
|
11
|
+
displayName,
|
|
12
|
+
}) => {
|
|
13
|
+
if (!isOpen) {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const buttons = [
|
|
18
|
+
{
|
|
19
|
+
type: "primary",
|
|
20
|
+
text: "Got it",
|
|
21
|
+
onClick: onClose,
|
|
22
|
+
isActive: true,
|
|
23
|
+
},
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<Popup
|
|
28
|
+
title={`${featureName} Created`}
|
|
29
|
+
subtitle={
|
|
30
|
+
<>
|
|
31
|
+
<span>{featureName}</span> is now saved and available for your
|
|
32
|
+
communities!
|
|
33
|
+
</>
|
|
34
|
+
}
|
|
35
|
+
onClose={onClose}
|
|
36
|
+
buttons={buttons}
|
|
37
|
+
hasPadding
|
|
38
|
+
minWidth={500}
|
|
39
|
+
maxWidth={600}
|
|
40
|
+
>
|
|
41
|
+
<div className={styles.successContent}>
|
|
42
|
+
<div className={styles.successMessage}>
|
|
43
|
+
To activate this feature, please add it to your sites using the
|
|
44
|
+
Feature Picker.
|
|
45
|
+
</div>
|
|
46
|
+
<div className={styles.successMessage}>
|
|
47
|
+
Team members with the right permissions can start creating content for
|
|
48
|
+
the communities that have installed the feature.
|
|
49
|
+
</div>
|
|
50
|
+
</div>
|
|
51
|
+
</Popup>
|
|
52
|
+
);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export { FeatureBuilderSuccessPopup };
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
.successContent {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
align-items: center;
|
|
5
|
+
gap: 20px;
|
|
6
|
+
padding: 20px 0;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.gifPlaceholder {
|
|
10
|
+
width: 100%;
|
|
11
|
+
max-width: 400px;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.gifContainer {
|
|
15
|
+
width: 100%;
|
|
16
|
+
height: 200px;
|
|
17
|
+
background-color: #f5f5f5;
|
|
18
|
+
border: 2px dashed #ccc;
|
|
19
|
+
border-radius: 8px;
|
|
20
|
+
display: flex;
|
|
21
|
+
align-items: center;
|
|
22
|
+
justify-content: center;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.gifPlaceholderText {
|
|
26
|
+
color: #999;
|
|
27
|
+
font-size: 14px;
|
|
28
|
+
text-align: center;
|
|
29
|
+
padding: 20px;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.successMessage {
|
|
33
|
+
text-align: center;
|
|
34
|
+
color: #666;
|
|
35
|
+
font-size: 16px;
|
|
36
|
+
line-height: 1.5;
|
|
37
|
+
max-width: 400px;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.featureName {
|
|
41
|
+
font-weight: 600;
|
|
42
|
+
color: #333;
|
|
43
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { PlussCore } from "../feature.config";
|
|
3
|
+
const { Components } = PlussCore;
|
|
4
|
+
const { Popup } = Components;
|
|
5
|
+
import styles from "./FeatureBuilderWelcomePopup.module.css";
|
|
6
|
+
const { Text } = Components;
|
|
7
|
+
|
|
8
|
+
const FeatureBuilderWelcomePopup = ({ isOpen, onClose }) => {
|
|
9
|
+
if (!isOpen) {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const buttons = [
|
|
14
|
+
{
|
|
15
|
+
type: "primary",
|
|
16
|
+
text: "Get Started",
|
|
17
|
+
onClick: onClose,
|
|
18
|
+
isActive: true,
|
|
19
|
+
},
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<Popup
|
|
24
|
+
title="Create a Custom Feature"
|
|
25
|
+
onClose={onClose}
|
|
26
|
+
buttons={buttons}
|
|
27
|
+
hasPadding
|
|
28
|
+
minWidth={600}
|
|
29
|
+
maxWidth={700}
|
|
30
|
+
>
|
|
31
|
+
<div className={styles.welcomeContent}>
|
|
32
|
+
<div className={styles.welcomeMessage}>
|
|
33
|
+
<Text type="bodyLarge">
|
|
34
|
+
Use this form to set up a new feature, such as "Hospitality" or
|
|
35
|
+
"Meet the Team."
|
|
36
|
+
</Text>
|
|
37
|
+
<Text type="bodyLarge">
|
|
38
|
+
You will select the name, the icon, and the specific details
|
|
39
|
+
required. This ensures the feature looks the same at every site.
|
|
40
|
+
</Text>
|
|
41
|
+
<Text type="bodyLarge">
|
|
42
|
+
Once you finish, you can install the feature on any site and
|
|
43
|
+
authorized users can manage its content.
|
|
44
|
+
</Text>
|
|
45
|
+
</div>
|
|
46
|
+
</div>
|
|
47
|
+
</Popup>
|
|
48
|
+
);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export { FeatureBuilderWelcomePopup };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
.welcomeContent {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
gap: 20px;
|
|
5
|
+
padding: 20px 0;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.welcomeMessage {
|
|
9
|
+
color: #666;
|
|
10
|
+
font-size: 16px;
|
|
11
|
+
line-height: 1.6;
|
|
12
|
+
width: 100%;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.welcomeMessage p {
|
|
16
|
+
margin: 0 0 16px 0;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.welcomeMessage p:last-child {
|
|
20
|
+
margin-bottom: 0;
|
|
21
|
+
}
|