@plusscommunities/pluss-feature-builder-web-d 1.0.7 → 1.0.9-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 (119) hide show
  1. package/dist/{index.cjs.js → index.js} +3803 -3504
  2. package/dist/index.js.map +1 -0
  3. package/package.json +20 -27
  4. package/.babelrc +0 -4
  5. package/rollup.config.js +0 -69
  6. package/src/actions/featureBuilderStringsActions.js +0 -88
  7. package/src/actions/featureDefinitionsIndex.js +0 -258
  8. package/src/actions/formActions.js +0 -301
  9. package/src/actions/index.js +0 -12
  10. package/src/actions/listingActions.js +0 -352
  11. package/src/actions/wizardActions.js +0 -228
  12. package/src/components/ActivityCardExample.jsx +0 -86
  13. package/src/components/ActivityCardExample.module.css +0 -130
  14. package/src/components/BackgroundLoader.jsx +0 -33
  15. package/src/components/BackgroundLoader.module.css +0 -46
  16. package/src/components/BaseFieldConfig.jsx +0 -305
  17. package/src/components/BaseFieldConfig.module.css +0 -42
  18. package/src/components/CenteredContainer.jsx +0 -29
  19. package/src/components/CenteredContainer.module.css +0 -171
  20. package/src/components/DeleteConfirmationPopup.jsx +0 -95
  21. package/src/components/DeleteConfirmationPopup.module.css +0 -12
  22. package/src/components/ErrorBoundary.jsx +0 -134
  23. package/src/components/ErrorBoundary.module.css +0 -77
  24. package/src/components/ErrorMessage.jsx +0 -85
  25. package/src/components/ErrorMessage.module.css +0 -116
  26. package/src/components/ExampleDisplay.jsx +0 -26
  27. package/src/components/ExampleDisplay.module.css +0 -3
  28. package/src/components/FeatureBuilderSidebar.jsx +0 -84
  29. package/src/components/FeatureBuilderSuccessPopup.jsx +0 -49
  30. package/src/components/FeatureBuilderSuccessPopup.module.css +0 -41
  31. package/src/components/FeatureBuilderWelcomePopup.jsx +0 -51
  32. package/src/components/FeatureBuilderWelcomePopup.module.css +0 -21
  33. package/src/components/FeatureListingCard.jsx +0 -104
  34. package/src/components/FeatureListingCard.module.css +0 -62
  35. package/src/components/Fields.jsx +0 -423
  36. package/src/components/Fields.module.css +0 -159
  37. package/src/components/IconLoader.jsx +0 -153
  38. package/src/components/IconLoader.module.css +0 -92
  39. package/src/components/IconSelector.jsx +0 -111
  40. package/src/components/IconSelector.module.css +0 -197
  41. package/src/components/ListingEditor.jsx +0 -405
  42. package/src/components/ListingEditor.module.css +0 -14
  43. package/src/components/ListingSuccessPopup.jsx +0 -52
  44. package/src/components/LoadingScreen.jsx +0 -54
  45. package/src/components/LoadingScreen.module.css +0 -103
  46. package/src/components/LoadingState.jsx +0 -40
  47. package/src/components/LoadingState.module.css +0 -18
  48. package/src/components/PreviewFull.js +0 -24
  49. package/src/components/PreviewFull.module.css +0 -11
  50. package/src/components/PreviewGrid.js +0 -14
  51. package/src/components/PreviewWidget.js +0 -27
  52. package/src/components/PreviewWidget.module.css +0 -15
  53. package/src/components/SidebarLayout.jsx +0 -252
  54. package/src/components/SidebarLayout.module.css +0 -71
  55. package/src/components/SkeletonLoader.jsx +0 -128
  56. package/src/components/SkeletonLoader.module.css +0 -295
  57. package/src/components/SortButtonGroup.jsx +0 -34
  58. package/src/components/SortButtonGroup.module.css +0 -51
  59. package/src/components/ToastContainer.jsx +0 -98
  60. package/src/components/ToastContainer.module.css +0 -156
  61. package/src/components/ToggleSwitch.js +0 -40
  62. package/src/components/ToggleSwitch.module.css +0 -48
  63. package/src/components/TwoColumnInput.jsx +0 -29
  64. package/src/components/TwoColumnInput.module.css +0 -32
  65. package/src/components/ViewFull.js +0 -139
  66. package/src/components/ViewFull.module.css +0 -71
  67. package/src/components/ViewWidget.js +0 -62
  68. package/src/components/ViewWidget.module.css +0 -28
  69. package/src/components/iconCategories.js +0 -135
  70. package/src/components/iconImports.js +0 -409
  71. package/src/components/index.js +0 -59
  72. package/src/components/listing/FileListItem.jsx +0 -86
  73. package/src/components/listing/GalleryDisplay.jsx +0 -330
  74. package/src/components/listing/GalleryDisplay.module.css +0 -309
  75. package/src/components/listing/ListingCTAInput.jsx +0 -82
  76. package/src/components/listing/ListingDescriptionInput.jsx +0 -73
  77. package/src/components/listing/ListingField.jsx +0 -101
  78. package/src/components/listing/ListingField.module.css +0 -106
  79. package/src/components/listing/ListingFileInput.jsx +0 -273
  80. package/src/components/listing/ListingFileInput.module.css +0 -189
  81. package/src/components/listing/ListingForm.jsx +0 -90
  82. package/src/components/listing/ListingForm.module.css +0 -38
  83. package/src/components/listing/ListingGalleryInput.jsx +0 -239
  84. package/src/components/listing/ListingGalleryInput.module.css +0 -132
  85. package/src/components/listing/ListingImageInput.jsx +0 -153
  86. package/src/components/listing/ListingTextInput.jsx +0 -72
  87. package/src/feature.config.js +0 -130
  88. package/src/helper/index.js +0 -135
  89. package/src/hooks/useFeatureDefinitionLoader.js +0 -66
  90. package/src/images/full.png +0 -0
  91. package/src/images/fullNoTitle.png +0 -0
  92. package/src/images/previewWidget.png +0 -0
  93. package/src/images/widget.png +0 -0
  94. package/src/index.js +0 -38
  95. package/src/pages/CreateListingPage.jsx +0 -49
  96. package/src/pages/EditListingPage.jsx +0 -58
  97. package/src/reducers/featureBuilderReducer.js +0 -739
  98. package/src/screens/CreateListing.module.css +0 -45
  99. package/src/screens/Form.module.css +0 -744
  100. package/src/screens/FormFieldsStep.jsx +0 -626
  101. package/src/screens/FormLayoutStep.jsx +0 -405
  102. package/src/screens/FormOverviewStep.jsx +0 -389
  103. package/src/screens/ListingScreen.jsx +0 -477
  104. package/src/screens/ListingScreen.module.css +0 -333
  105. package/src/selectors/featureBuilderSelectors.js +0 -533
  106. package/src/types/index.js +0 -91
  107. package/src/utils/textUtils.js +0 -89
  108. package/src/validators/galleryValidators.js +0 -345
  109. package/src/values.config.a.js +0 -49
  110. package/src/values.config.b.js +0 -49
  111. package/src/values.config.c.js +0 -49
  112. package/src/values.config.d.js +0 -49
  113. package/src/values.config.default.js +0 -49
  114. package/src/values.config.js +0 -49
  115. package/src/webapi/featureDefinitionActions.js +0 -0
  116. package/src/webapi/featuresActions.js +0 -90
  117. package/src/webapi/helper.js +0 -4
  118. package/src/webapi/index.js +0 -12
  119. package/src/webapi/listingActions.js +0 -176
@@ -1,405 +0,0 @@
1
- import React, { useState, Fragment } from "react";
2
- import { useSelector, useDispatch } from "react-redux";
3
- import { PlussCore } from "../feature.config";
4
- import { capitalizeTextWithFallback } from "../utils/textUtils";
5
- import ListingForm from "../components/listing/ListingForm.jsx";
6
- import ToastContainer from "./ToastContainer.jsx";
7
- import styles from "./ListingEditor.module.css";
8
- import {
9
- OverlayPage,
10
- OverlayPageContents,
11
- OverlayPageBottomButtons,
12
- OverlayPageSection,
13
- Text,
14
- Button,
15
- SkeletonLoader,
16
- LoadingState,
17
- } from "../components";
18
- import {
19
- selectDefinition,
20
- selectDefinitionIsLoading,
21
- selectListingsIsLoading,
22
- selectDefinitionError,
23
- selectHasDefinition,
24
- selectFormDisplayName,
25
- selectListingById,
26
- } from "../selectors/featureBuilderSelectors";
27
- import {
28
- createListing,
29
- editListing,
30
- fetchSingleListing,
31
- } from "../actions/listingActions";
32
- import { fetchFeatureDefinitions } from "../actions/featureDefinitionsIndex.js";
33
- import { values } from "../values.config.js";
34
-
35
- const ListingEditor = ({ mode = "create", listingId, onSuccess, onCancel }) => {
36
- const isEditMode = mode === "edit";
37
- const dispatch = useDispatch();
38
- const auth = useSelector((state) => state.auth);
39
- const featureDefinition = useSelector(selectDefinition);
40
- const displayName =
41
- useSelector(selectFormDisplayName) || featureDefinition?.displayName;
42
- const isLoading = useSelector(selectDefinitionIsLoading);
43
- const listingsIsLoading = useSelector(selectListingsIsLoading);
44
- const error = useSelector(selectDefinitionError);
45
- const hasDefinition = useSelector(selectHasDefinition);
46
- const listing = useSelector(selectListingById(listingId));
47
-
48
- const [isSubmitting, setIsSubmitting] = React.useState(false);
49
- const [formData, setFormData] = React.useState({ fields: {} });
50
- const [hasUnsavedChanges, setHasUnsavedChanges] = React.useState(false);
51
- const [formErrors, setFormErrors] = React.useState({});
52
- const [showErrors, setShowErrors] = React.useState(false);
53
- const [formErrorMessage, setFormErrorMessage] = React.useState(null);
54
- const [toasts, setToasts] = React.useState([]);
55
- const formRef = React.useRef(null);
56
-
57
- // Toast management functions
58
- const addToast = (type, message) => {
59
- const id = Date.now();
60
- setToasts((prev) => [...prev, { id, type, message, isVisible: true }]);
61
- };
62
-
63
- const removeToast = (id) => {
64
- setToasts((prev) => prev.filter((toast) => toast.id !== id));
65
- };
66
-
67
- // Fetch listing data in edit mode
68
- React.useEffect(() => {
69
- if (isEditMode && listingId) {
70
- // Check if listing data already exists in Redux store
71
- if (!listing) {
72
- dispatch(fetchSingleListing(listingId));
73
- }
74
- }
75
- }, [dispatch, isEditMode, listingId, listing]);
76
-
77
- // Pre-fill form data in edit mode
78
- React.useEffect(() => {
79
- if (isEditMode && listing) {
80
- // Always use listing.fields for consistency - never use the entire listing object
81
- const fields = listing.fields || {};
82
- setFormData({ fields });
83
- }
84
- }, [isEditMode, listing]);
85
-
86
- const handleSubmit = (listingData) => {
87
- // listingData is expected to have the structure { fields: { fieldId: value, ... } }
88
- // After our fixes, form should always send { fields: {...} } structure
89
- if (!listingData || !listingData.fields) {
90
- return;
91
- }
92
-
93
- const newFormData = { fields: listingData.fields };
94
-
95
- setFormData(newFormData);
96
- setHasUnsavedChanges(true);
97
- // Clear errors when form data is updated
98
- setFormErrors({});
99
- setShowErrors(false);
100
- setFormErrorMessage(null);
101
- };
102
-
103
- const handleSave = () => {
104
- // Validate form - formData should always have structure { fields: {...} }
105
- if (!formData || !formData.fields) {
106
- setFormErrorMessage("Form data is invalid. Please try again.");
107
- addToast("error", "Form data is invalid. Please try again.");
108
- return;
109
- }
110
- setFormErrorMessage(null); // Clear previous error when validation passes
111
-
112
- const formFields = formData.fields;
113
- const errors = {};
114
-
115
- // Check all fields to collect all missing required fields
116
- featureDefinition.fields.forEach((field) => {
117
- // Use field.values.isRequired consistently (not field.isMandatory)
118
- if (field.values?.isRequired) {
119
- const fieldValue = formFields[field.id];
120
- let isEmpty = false;
121
-
122
- // Handle different field types based on their value structure
123
- if (field.type === "cta") {
124
- // CTA fields have object structure { label: "...", url: "..." }
125
- if (!fieldValue || typeof fieldValue !== "object") {
126
- isEmpty = true;
127
- } else {
128
- // Check if both label and url are provided and non-empty
129
- const hasLabel =
130
- fieldValue.label && fieldValue.label.trim().length > 0;
131
- const hasUrl = fieldValue.url && fieldValue.url.trim().length > 0;
132
- isEmpty = !hasLabel || !hasUrl;
133
- }
134
- } else if (field.type === "file") {
135
- // File fields can be arrays (multiple files) or objects (single file)
136
- if (!fieldValue) {
137
- isEmpty = true;
138
- } else if (Array.isArray(fieldValue)) {
139
- isEmpty = fieldValue.length === 0;
140
- } else if (typeof fieldValue === "object") {
141
- isEmpty = Object.keys(fieldValue).length === 0;
142
- } else {
143
- isEmpty =
144
- typeof fieldValue === "string" && fieldValue.trim() === "";
145
- }
146
- } else {
147
- // Handle string values for other field types
148
- isEmpty =
149
- !fieldValue ||
150
- (typeof fieldValue === "string" && fieldValue.trim() === "");
151
- }
152
-
153
- if (isEmpty) {
154
- errors[field.id] = `${field.values?.label || field.id} is required`;
155
- }
156
- }
157
- });
158
-
159
- const isValid = Object.keys(errors).length === 0;
160
-
161
- if (!isValid) {
162
- setFormErrors(errors);
163
- setShowErrors(true);
164
- return;
165
- }
166
-
167
- setIsSubmitting(true);
168
-
169
- // formData always has structure { fields: {...} } after our fixes
170
- const submissionData = isEditMode
171
- ? { id: listingId, fields: formData.fields, site: auth.site }
172
- : { fields: formData.fields, site: auth.site };
173
-
174
- const actionPromise = dispatch(
175
- isEditMode ? editListing(submissionData) : createListing(submissionData),
176
- );
177
-
178
- actionPromise
179
- .then((result) => {
180
- setIsSubmitting(false);
181
- setHasUnsavedChanges(false);
182
-
183
- // Call onSuccess callback with listing ID if available
184
- if (onSuccess) {
185
- const createdId = result?.data?.id || listingId;
186
- onSuccess(createdId);
187
- }
188
- })
189
- .catch((error) => {
190
- const errorMessage = `Failed to ${
191
- isEditMode ? "update" : "create"
192
- } listing. Please try again.`;
193
- addToast("error", errorMessage);
194
- setIsSubmitting(false);
195
- });
196
- };
197
-
198
- const handleClose = () => {
199
- if (hasUnsavedChanges && onCancel) {
200
- if (
201
- window.confirm(
202
- "You have unsaved changes. Are you sure you want to leave?",
203
- )
204
- ) {
205
- onCancel();
206
- }
207
- } else if (onCancel) {
208
- onCancel();
209
- }
210
- };
211
-
212
- // Check for content management permission
213
- if (
214
- !PlussCore.Session.validateAccess(
215
- auth.site,
216
- values.permissionFeatureBuilderContent,
217
- auth,
218
- )
219
- ) {
220
- return (
221
- <OverlayPage onClose={handleClose}>
222
- <OverlayPageContents>
223
- <OverlayPageSection className="pageSectionWrapper--newPopup1000">
224
- <div className={`padding-20 ${styles.centered}`}>
225
- <Text type="h2">Access Restricted</Text>
226
- <Text type="body">
227
- You don't have permission to {isEditMode ? "edit" : "create"}{" "}
228
- feature content.
229
- </Text>
230
- <Text type="body">
231
- Please contact your administrator if you need access.
232
- </Text>
233
- </div>
234
- </OverlayPageSection>
235
- </OverlayPageContents>
236
- <OverlayPageBottomButtons>
237
- <Button buttonType="secondary" onClick={handleClose}>
238
- Close
239
- </Button>
240
- </OverlayPageBottomButtons>
241
- </OverlayPage>
242
- );
243
- }
244
-
245
- // Show loading state only when fetching initial feature definition
246
- // Don't show skeleton when creating new listings since we have the definition in the store
247
- if (!hasDefinition && isLoading) {
248
- return (
249
- <OverlayPage onClose={handleClose}>
250
- <OverlayPageContents>
251
- <OverlayPageSection className="pageSectionWrapper--newPopup1000">
252
- <div className="padding-20">
253
- <SkeletonLoader type="form-input" count={4} />
254
- </div>
255
- </OverlayPageSection>
256
- </OverlayPageContents>
257
- <OverlayPageBottomButtons>
258
- <SkeletonLoader type="button" count={2} />
259
- </OverlayPageBottomButtons>
260
- </OverlayPage>
261
- );
262
- }
263
-
264
- if (error) {
265
- return (
266
- <OverlayPage onClose={handleClose}>
267
- <OverlayPageContents>
268
- <OverlayPageSection className="pageSectionWrapper--newPopup1000">
269
- <div className={`padding-20 ${styles.centered}`}>
270
- <Text type="body">Error loading data: {error}</Text>
271
- </div>
272
- </OverlayPageSection>
273
- </OverlayPageContents>
274
- <OverlayPageBottomButtons>
275
- <Button
276
- buttonType="primary"
277
- onClick={() => {
278
- dispatch(fetchFeatureDefinitions());
279
- if (isEditMode && listingId) {
280
- dispatch(fetchSingleListing(listingId));
281
- }
282
- }}
283
- >
284
- Retry
285
- </Button>
286
- <Button buttonType="secondary" onClick={handleClose}>
287
- Close
288
- </Button>
289
- </OverlayPageBottomButtons>
290
- </OverlayPage>
291
- );
292
- }
293
-
294
- if (!hasDefinition) {
295
- return (
296
- <OverlayPage onClose={handleClose}>
297
- <OverlayPageContents>
298
- <OverlayPageSection className="pageSectionWrapper--newPopup1000">
299
- <div className={`padding-20 ${styles.centered}`}>
300
- <Text type="h2">No Feature Definition</Text>
301
- <Text type="body">
302
- No feature definition exists. Please create a feature definition
303
- first.
304
- </Text>
305
- </div>
306
- </OverlayPageSection>
307
- </OverlayPageContents>
308
- <OverlayPageBottomButtons>
309
- <Button buttonType="secondary" onClick={handleClose}>
310
- Close
311
- </Button>
312
- </OverlayPageBottomButtons>
313
- </OverlayPage>
314
- );
315
- }
316
-
317
- return (
318
- <Fragment>
319
- <OverlayPage onClose={handleClose}>
320
- <OverlayPageContents>
321
- <OverlayPageSection className="pageSectionWrapper--newPopup">
322
- <Text type="formTitleLarge" className="marginBottom-16">
323
- {isEditMode
324
- ? `Edit ${capitalizeTextWithFallback(displayName, values.singularName)} listing`
325
- : `Create new ${capitalizeTextWithFallback(displayName, "listing")}`}
326
- </Text>
327
-
328
- {/* Form Section */}
329
- <div ref={formRef}>
330
- {isEditMode && !listing && listingsIsLoading ? (
331
- <div
332
- className={`padding-20 ${styles.centered} ${styles.loadingContainer}`}
333
- >
334
- <LoadingState message="Loading listing..." />
335
- </div>
336
- ) : isEditMode && !listing ? (
337
- <div
338
- className={`padding-20 ${styles.centered} ${styles.loadingContainer}`}
339
- >
340
- <Text type="body">
341
- Listing not found or has been deleted.
342
- </Text>
343
- <Button
344
- buttonType="secondary"
345
- onClick={handleClose}
346
- className={styles.backButton}
347
- >
348
- Go Back
349
- </Button>
350
- </div>
351
- ) : (
352
- <ListingForm
353
- featureDefinition={featureDefinition}
354
- onSubmit={handleSubmit}
355
- mode={isEditMode ? "edit" : "create"}
356
- initialData={
357
- isEditMode && listing
358
- ? { fields: listing.fields || {} }
359
- : undefined
360
- }
361
- onChange={() => setHasUnsavedChanges(true)}
362
- disabled={isSubmitting}
363
- hideSubmitButton={true}
364
- formData={formData.fields}
365
- setFormData={(fields) => setFormData({ fields })}
366
- formErrors={formErrors}
367
- showErrors={showErrors}
368
- formErrorMessage={formErrorMessage}
369
- />
370
- )}
371
- </div>
372
- </OverlayPageSection>
373
- </OverlayPageContents>
374
-
375
- <OverlayPageBottomButtons>
376
- <Button
377
- buttonType="secondary"
378
- onClick={handleClose}
379
- disabled={isSubmitting}
380
- className="marginRight-16"
381
- >
382
- Cancel
383
- </Button>
384
- <Button
385
- buttonType="primary"
386
- onClick={handleSave}
387
- disabled={isSubmitting}
388
- isActive
389
- >
390
- {isSubmitting
391
- ? isEditMode
392
- ? "Saving..."
393
- : "Creating..."
394
- : isEditMode
395
- ? "Save Changes"
396
- : "Create Listing"}
397
- </Button>
398
- </OverlayPageBottomButtons>
399
- </OverlayPage>
400
- <ToastContainer toasts={toasts} onDismiss={removeToast} />
401
- </Fragment>
402
- );
403
- };
404
-
405
- export default ListingEditor;
@@ -1,14 +0,0 @@
1
- /* Centered content */
2
- .centered {
3
- text-align: center;
4
- }
5
-
6
- /* Loading container */
7
- .loadingContainer {
8
- padding: 60px 20px;
9
- }
10
-
11
- /* Back button margin */
12
- .backButton {
13
- margin-top: 16px;
14
- }
@@ -1,52 +0,0 @@
1
- import React from "react";
2
- import { PlussCore } from "../feature.config";
3
- const { Components } = PlussCore;
4
- const { Popup } = Components;
5
-
6
- const ListingSuccessPopup = ({ isOpen, onClose, mode, listingId }) => {
7
- if (!isOpen) {
8
- return null;
9
- }
10
-
11
- const isCreateMode = mode === "create";
12
- const title = isCreateMode ? "Listing Created" : "Listing Updated";
13
- const subtitle = isCreateMode
14
- ? "Your listing has been created and is now available."
15
- : "Your changes have been saved and are now reflected.";
16
-
17
- const buttons = [
18
- {
19
- type: "primary",
20
- text: "View All Listings",
21
- onClick: onClose,
22
- isActive: true,
23
- },
24
- ];
25
-
26
- // Add "Create Another" button for create mode
27
- if (isCreateMode) {
28
- buttons.unshift({
29
- type: "secondary",
30
- text: "Create Another",
31
- onClick: () => {
32
- // Navigate to create new listing
33
- onClose(); // Close the popup first
34
- window.location.href = "/feature-builder/listing/create";
35
- },
36
- });
37
- }
38
-
39
- return (
40
- <Popup
41
- title={title}
42
- subtitle={subtitle}
43
- onClose={onClose}
44
- buttons={buttons}
45
- hasPadding
46
- minWidth={400}
47
- maxWidth={500}
48
- ></Popup>
49
- );
50
- };
51
-
52
- export { ListingSuccessPopup };
@@ -1,54 +0,0 @@
1
- import React from "react";
2
- import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
3
- import { faSpinner } from "@fortawesome/free-solid-svg-icons";
4
- import styles from "./LoadingScreen.module.css";
5
-
6
- const LoadingScreen = ({
7
- message = "Loading...",
8
- size = "large",
9
- showBackground = true,
10
- type = "default", // default, feature-definition, listings, navigation
11
- }) => {
12
- // Get contextual message based on type
13
- const getContextualMessage = () => {
14
- if (message !== "Loading...") return message;
15
-
16
- switch (type) {
17
- case "feature-definition":
18
- return "Loading feature definition...";
19
- case "listings":
20
- return "Loading your listings...";
21
- case "navigation":
22
- return "Loading next step...";
23
- default:
24
- return "Loading...";
25
- }
26
- };
27
-
28
- const sizeClass =
29
- size === "small"
30
- ? styles.small
31
- : size === "medium"
32
- ? styles.medium
33
- : styles.large;
34
-
35
- return (
36
- <div
37
- className={`${styles.loadingScreen} ${showBackground ? styles.withBackground : ""}`}
38
- >
39
- <div className={styles.loadingContent}>
40
- <div className={`${styles.spinnerContainer} ${sizeClass}`}>
41
- <FontAwesomeIcon icon={faSpinner} spin className={styles.spinner} />
42
- </div>
43
- <div className={styles.loadingText}>{getContextualMessage()}</div>
44
- {type !== "default" && (
45
- <div className={styles.loadingSubtext}>
46
- This will only take a moment...
47
- </div>
48
- )}
49
- </div>
50
- </div>
51
- );
52
- };
53
-
54
- export default LoadingScreen;
@@ -1,103 +0,0 @@
1
- .loadingScreen {
2
- position: fixed;
3
- top: 0;
4
- left: 0;
5
- right: 0;
6
- bottom: 0;
7
- display: flex;
8
- align-items: center;
9
- justify-content: center;
10
- z-index: 9999;
11
- pointer-events: none;
12
- }
13
-
14
- .loadingScreen.withBackground {
15
- background: rgba(255, 255, 255, 0.95);
16
- backdrop-filter: blur(4px);
17
- pointer-events: all;
18
- }
19
-
20
- .loadingContent {
21
- display: flex;
22
- flex-direction: column;
23
- align-items: center;
24
- justify-content: center;
25
- text-align: center;
26
- padding: 3rem 2rem;
27
- max-width: 400px;
28
- }
29
-
30
- .spinnerContainer {
31
- display: flex;
32
- align-items: center;
33
- justify-content: center;
34
- margin-bottom: 1.5rem;
35
- }
36
-
37
- .spinnerContainer.small {
38
- width: 32px;
39
- height: 32px;
40
- }
41
-
42
- .spinnerContainer.medium {
43
- width: 48px;
44
- height: 48px;
45
- }
46
-
47
- .spinnerContainer.large {
48
- width: 64px;
49
- height: 64px;
50
- }
51
-
52
- .spinner {
53
- color: var(--colour-branding-main, #4a57b7);
54
- width: 100%;
55
- height: 100%;
56
- font-size: inherit;
57
- }
58
-
59
- .loadingText {
60
- font-size: var(--font-size-lg);
61
- font-weight: 500;
62
- color: var(--text-dark, #181c4a);
63
- margin-bottom: 0.5rem;
64
- line-height: 1.4;
65
- }
66
-
67
- .loadingSubtext {
68
- font-size: var(--font-size-sm);
69
- color: var(--text-light, #717b81);
70
- margin-top: 0.5rem;
71
- line-height: 1.4;
72
- }
73
-
74
- /* Animation for smooth transitions */
75
- .loadingScreen {
76
- opacity: 0;
77
- transition: opacity 0.3s ease-in-out;
78
- }
79
-
80
- .loadingScreen.withBackground {
81
- opacity: 1;
82
- }
83
-
84
- /* Responsive adjustments */
85
- @media (max-width: 768px) {
86
- .loadingContent {
87
- padding: 2rem 1rem;
88
- }
89
-
90
- .loadingText {
91
- font-size: var(--font-size-base);
92
- }
93
-
94
- .spinnerContainer.large {
95
- width: 48px;
96
- height: 48px;
97
- }
98
-
99
- .spinnerContainer.medium {
100
- width: 40px;
101
- height: 40px;
102
- }
103
- }
@@ -1,40 +0,0 @@
1
- import React from "react";
2
- import FontAwesome from "react-fontawesome";
3
- import styles from "./LoadingState.module.css";
4
-
5
- /**
6
- * Loading State component for async operations
7
- * Displays a spinning loader with optional message text
8
- * Uses FontAwesome spinner with hardcoded pixel sizing to avoid rem scaling issues
9
- * Consistent styling with info page loader pattern
10
- *
11
- * @param {Object} props - Component props
12
- * @param {string} props.message - Descriptive message to display alongside the spinner
13
- * @param {number} [props.size=48] - Size of the spinner in pixels
14
- * @returns {React.ReactElement} Loading state interface with spinner and message
15
- *
16
- * @example
17
- * <LoadingState message="Loading feature data..." />
18
- *
19
- * @example
20
- * <LoadingState
21
- * message="Processing your request..."
22
- * size={64}
23
- * />
24
- */
25
- export function LoadingState({ message, size = 48 }) {
26
- return (
27
- <div className={styles.loadingState}>
28
- <FontAwesome
29
- style={{
30
- fontSize: size,
31
- color: "var(--colour-branding-main, #4a57b7)",
32
- }}
33
- name="spinner fa-pulse fa-fw"
34
- />
35
- <p>{message}</p>
36
- </div>
37
- );
38
- }
39
-
40
- export default LoadingState;
@@ -1,18 +0,0 @@
1
- /* LoadingState CSS Module - Based on info page loader pattern */
2
-
3
- .loadingState {
4
- display: flex;
5
- flex-direction: column;
6
- align-items: center;
7
- justify-content: center;
8
- gap: 2rem;
9
- height: 50vh;
10
- text-align: center;
11
- }
12
-
13
- .loadingState p {
14
- color: var(--text-bluegrey, #6c7a90);
15
- font-size: 1.6rem;
16
- font-weight: 300;
17
- margin-bottom: 0;
18
- }