swoop-common 2.2.175 → 2.2.177
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/lib/config/templateStructure.d.ts +4 -0
- package/dist/lib/config/templateStructure.js +5 -2
- package/dist/rendering/components/ComponentPicker.js +27 -7
- package/dist/rendering/prebuild/generated/import_generated.d.ts +1 -0
- package/dist/rendering/prebuild/generated/import_generated.js +1 -0
- package/dist/rendering/renderers/NestedObjectArray.d.ts +1 -0
- package/dist/rendering/renderers/NestedObjectArray.js +77 -0
- package/package.json +1 -1
|
@@ -21,6 +21,7 @@ export declare const TEMPLATE_NAMES: {
|
|
|
21
21
|
readonly FEE: "Fee";
|
|
22
22
|
readonly PACKAGE: "Package";
|
|
23
23
|
readonly ROOM: "Room";
|
|
24
|
+
readonly GROUP_TOUR: "Group Tour";
|
|
24
25
|
};
|
|
25
26
|
/**
|
|
26
27
|
* Template Name to Template ID mapping
|
|
@@ -45,6 +46,7 @@ export declare const templateStructure: {
|
|
|
45
46
|
readonly Fee: "template_d15cc4ab72034fb8a098d9a9ec791a7d";
|
|
46
47
|
readonly "Cruise Activity": "template_12345678123456781234567812345678";
|
|
47
48
|
readonly Room: "template_bb0b09c544d242c9a9ef38147966979f";
|
|
49
|
+
readonly "Group Tour": "template_530f2f95c21147a08422bb714d771994";
|
|
48
50
|
};
|
|
49
51
|
export declare const packagesTemplateStructure: {
|
|
50
52
|
readonly Cruise: "template_63a57a90570c47b89f830d2c7618324f";
|
|
@@ -52,6 +54,7 @@ export declare const packagesTemplateStructure: {
|
|
|
52
54
|
readonly GuidedMultidayActivity: "template_a64e161de5824fcb9515274b0f67d698";
|
|
53
55
|
readonly AllInclusiveHotel: "template_ba7999ff957c4ca3a5e61496df6178ac";
|
|
54
56
|
readonly "Private Tour": "template_d9081bfcc3b7461987a3728e57ca7363";
|
|
57
|
+
readonly "Group Tour": "template_530f2f95c21147a08422bb714d771994";
|
|
55
58
|
};
|
|
56
59
|
export declare const topLevelTemplates: {
|
|
57
60
|
readonly Activity: "template_e2f0e9e5343349358037a0564a3366a0";
|
|
@@ -65,6 +68,7 @@ export declare const topLevelTemplates: {
|
|
|
65
68
|
readonly GuidedMultidayActivity: "template_a64e161de5824fcb9515274b0f67d698";
|
|
66
69
|
readonly AllInclusiveHotel: "template_ba7999ff957c4ca3a5e61496df6178ac";
|
|
67
70
|
readonly "Private Tour": "template_d9081bfcc3b7461987a3728e57ca7363";
|
|
71
|
+
readonly "Group Tour": "template_530f2f95c21147a08422bb714d771994";
|
|
68
72
|
};
|
|
69
73
|
export type TemplateName = keyof typeof templateStructure;
|
|
70
74
|
export type TemplateId = typeof templateStructure[TemplateName];
|
|
@@ -20,7 +20,8 @@ export const TEMPLATE_NAMES = {
|
|
|
20
20
|
ACTIVITY: "Activity",
|
|
21
21
|
FEE: "Fee",
|
|
22
22
|
PACKAGE: "Package",
|
|
23
|
-
ROOM: "Room"
|
|
23
|
+
ROOM: "Room",
|
|
24
|
+
GROUP_TOUR: "Group Tour",
|
|
24
25
|
};
|
|
25
26
|
/**
|
|
26
27
|
* Template Name to Template ID mapping
|
|
@@ -44,7 +45,8 @@ export const templateStructure = {
|
|
|
44
45
|
[TEMPLATE_NAMES.ACTIVITY]: "template_e2f0e9e5343349358037a0564a3366a0",
|
|
45
46
|
[TEMPLATE_NAMES.FEE]: "template_d15cc4ab72034fb8a098d9a9ec791a7d",
|
|
46
47
|
[TEMPLATE_NAMES.CRUISE_ACTIVITY]: "template_12345678123456781234567812345678",
|
|
47
|
-
[TEMPLATE_NAMES.ROOM]: "template_bb0b09c544d242c9a9ef38147966979f"
|
|
48
|
+
[TEMPLATE_NAMES.ROOM]: "template_bb0b09c544d242c9a9ef38147966979f",
|
|
49
|
+
[TEMPLATE_NAMES.GROUP_TOUR]: "template_530f2f95c21147a08422bb714d771994"
|
|
48
50
|
};
|
|
49
51
|
export const packagesTemplateStructure = {
|
|
50
52
|
[TEMPLATE_NAMES.CRUISE]: templateStructure[TEMPLATE_NAMES.CRUISE],
|
|
@@ -52,6 +54,7 @@ export const packagesTemplateStructure = {
|
|
|
52
54
|
[TEMPLATE_NAMES.GUIDED_MULTIDAY_ACTIVITY]: templateStructure[TEMPLATE_NAMES.GUIDED_MULTIDAY_ACTIVITY],
|
|
53
55
|
[TEMPLATE_NAMES.ALL_INCLUSIVE_HOTEL]: templateStructure[TEMPLATE_NAMES.ALL_INCLUSIVE_HOTEL],
|
|
54
56
|
[TEMPLATE_NAMES.PRIVATE_TOUR]: templateStructure[TEMPLATE_NAMES.PRIVATE_TOUR],
|
|
57
|
+
[TEMPLATE_NAMES.GROUP_TOUR]: templateStructure[TEMPLATE_NAMES.GROUP_TOUR],
|
|
55
58
|
};
|
|
56
59
|
export const topLevelTemplates = Object.assign(Object.assign({}, packagesTemplateStructure), { [TEMPLATE_NAMES.ACTIVITY]: templateStructure[TEMPLATE_NAMES.ACTIVITY], [TEMPLATE_NAMES.FLIGHT]: templateStructure[TEMPLATE_NAMES.FLIGHT], [TEMPLATE_NAMES.GROUND_ACCOMMODATION]: templateStructure[TEMPLATE_NAMES.GROUND_ACCOMMODATION], [TEMPLATE_NAMES.SHIP_ACCOMMODATION]: templateStructure[TEMPLATE_NAMES.SHIP_ACCOMMODATION], [TEMPLATE_NAMES.TRANSFER]: templateStructure[TEMPLATE_NAMES.TRANSFER], [TEMPLATE_NAMES.INDEPENDENT_ARRANGEMENTS]: templateStructure[TEMPLATE_NAMES.INDEPENDENT_ARRANGEMENTS] });
|
|
57
60
|
/**
|
|
@@ -8,12 +8,12 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
10
|
import React, { useState, useEffect, useMemo } from "react";
|
|
11
|
-
import { Dialog, DialogTitle, DialogContent, DialogActions, Button, TextField, MenuItem, Box, List, Typography, InputAdornment, IconButton, CircularProgress, Pagination, Autocomplete, } from "@mui/material";
|
|
11
|
+
import { Dialog, DialogTitle, DialogContent, DialogActions, Button, TextField, MenuItem, Box, List, Typography, InputAdornment, IconButton, CircularProgress, Pagination, Autocomplete, Checkbox, FormControlLabel, } from "@mui/material";
|
|
12
12
|
import { Search as SearchIcon, Clear as ClearIcon, FilterList as FilterIcon, } from "@mui/icons-material";
|
|
13
13
|
import { fetchComponents, getAllPartners } from "../util/api";
|
|
14
14
|
import { InternalServices } from "../../api/init";
|
|
15
15
|
import ComponentListItem from "./ComponentListItem";
|
|
16
|
-
import { TEMPLATE_NAMES, templateStructure } from "../../lib/config/templateStructure";
|
|
16
|
+
import { TEMPLATE_NAMES, templateStructure, } from "../../lib/config/templateStructure";
|
|
17
17
|
export default function ComponentPickerModal({ open, onClose, onSelect, selectedComponent, parentIdsFilter, }) {
|
|
18
18
|
const [searchTerm, setSearchTerm] = useState("");
|
|
19
19
|
const [selectedRegion, setSelectedRegion] = useState("");
|
|
@@ -31,11 +31,12 @@ export default function ComponentPickerModal({ open, onClose, onSelect, selected
|
|
|
31
31
|
const [debouncedSearchTerm, setDebouncedSearchTerm] = useState("");
|
|
32
32
|
const [activityTypes, setActivityTypes] = useState([]);
|
|
33
33
|
const [selectedActivityType, setSelectedActivityType] = useState("");
|
|
34
|
+
const [citiesOnly, setCitiesOnly] = useState(false);
|
|
34
35
|
const activityTemplateId = useMemo(() => {
|
|
35
36
|
var _a;
|
|
36
37
|
const cruiseActivityId = templateStructure[TEMPLATE_NAMES.CRUISE_ACTIVITY];
|
|
37
38
|
const activityId = templateStructure[TEMPLATE_NAMES.ACTIVITY];
|
|
38
|
-
return (_a = parentIdsFilter === null || parentIdsFilter === void 0 ? void 0 : parentIdsFilter.find((id) => id === cruiseActivityId || id === activityId)) !== null && _a !== void 0 ? _a : null;
|
|
39
|
+
return ((_a = parentIdsFilter === null || parentIdsFilter === void 0 ? void 0 : parentIdsFilter.find((id) => id === cruiseActivityId || id === activityId)) !== null && _a !== void 0 ? _a : null);
|
|
39
40
|
}, [parentIdsFilter]);
|
|
40
41
|
useEffect(() => {
|
|
41
42
|
const timer = setTimeout(() => {
|
|
@@ -69,13 +70,21 @@ export default function ComponentPickerModal({ open, onClose, onSelect, selected
|
|
|
69
70
|
selectedPartner,
|
|
70
71
|
selectedTemplate,
|
|
71
72
|
selectedActivityType,
|
|
73
|
+
citiesOnly,
|
|
72
74
|
page,
|
|
73
75
|
]);
|
|
74
76
|
useEffect(() => {
|
|
75
77
|
if (open) {
|
|
76
78
|
setPage(1);
|
|
77
79
|
}
|
|
78
|
-
}, [
|
|
80
|
+
}, [
|
|
81
|
+
debouncedSearchTerm,
|
|
82
|
+
selectedRegion,
|
|
83
|
+
selectedPartner,
|
|
84
|
+
selectedTemplate,
|
|
85
|
+
selectedActivityType,
|
|
86
|
+
citiesOnly,
|
|
87
|
+
]);
|
|
79
88
|
useEffect(() => {
|
|
80
89
|
if (open) {
|
|
81
90
|
setSearchTerm("");
|
|
@@ -83,6 +92,7 @@ export default function ComponentPickerModal({ open, onClose, onSelect, selected
|
|
|
83
92
|
setSelectedPartner("");
|
|
84
93
|
setSelectedTemplate("");
|
|
85
94
|
setSelectedActivityType("");
|
|
95
|
+
setCitiesOnly(false);
|
|
86
96
|
}
|
|
87
97
|
}, [open]);
|
|
88
98
|
const loadInitialData = () => __awaiter(this, void 0, void 0, function* () {
|
|
@@ -123,6 +133,8 @@ export default function ComponentPickerModal({ open, onClose, onSelect, selected
|
|
|
123
133
|
query.on = "name";
|
|
124
134
|
if (selectedActivityType)
|
|
125
135
|
query["componentFields.data.type"] = selectedActivityType;
|
|
136
|
+
if (citiesOnly)
|
|
137
|
+
query["componentFields.data.type"] = "City";
|
|
126
138
|
// Parent ids overwrites templateid filter
|
|
127
139
|
if (parentIdsFilter === null || parentIdsFilter === void 0 ? void 0 : parentIdsFilter.length)
|
|
128
140
|
query.templateId = parentIdsFilter.join(",");
|
|
@@ -159,7 +171,14 @@ export default function ComponentPickerModal({ open, onClose, onSelect, selected
|
|
|
159
171
|
const componentRegions = regions.filter((r) => { var _a; return (_a = component.regions) === null || _a === void 0 ? void 0 : _a.includes(r.id); });
|
|
160
172
|
return { template, partners: componentPartners, regions: componentRegions };
|
|
161
173
|
};
|
|
162
|
-
const hasActiveFilters = selectedRegion ||
|
|
174
|
+
const hasActiveFilters = selectedRegion ||
|
|
175
|
+
selectedPartner ||
|
|
176
|
+
selectedTemplate ||
|
|
177
|
+
selectedActivityType ||
|
|
178
|
+
citiesOnly;
|
|
179
|
+
const isLocationComponent = Array.isArray(parentIdsFilter) &&
|
|
180
|
+
parentIdsFilter.length > 0 &&
|
|
181
|
+
parentIdsFilter.includes(templateStructure[TEMPLATE_NAMES.LOCATION]);
|
|
163
182
|
if (initialLoading) {
|
|
164
183
|
return (React.createElement(Dialog, { open: open, onClose: onClose, maxWidth: "md", fullWidth: true, PaperProps: { sx: { height: "80vh", maxHeight: "800px" } } },
|
|
165
184
|
React.createElement(DialogContent, null,
|
|
@@ -170,7 +189,7 @@ export default function ComponentPickerModal({ open, onClose, onSelect, selected
|
|
|
170
189
|
return (React.createElement(Dialog, { open: open, onClose: onClose, maxWidth: "md", fullWidth: true, PaperProps: { sx: { height: "80vh", maxHeight: "800px" } } },
|
|
171
190
|
React.createElement(DialogTitle, null,
|
|
172
191
|
React.createElement(Box, { display: "flex", alignItems: "center", justifyContent: "space-between" },
|
|
173
|
-
React.createElement(Typography, { variant: "h6" }, "Select
|
|
192
|
+
React.createElement(Typography, { variant: "h6" }, "Select Components"),
|
|
174
193
|
hasActiveFilters && (React.createElement(Button, { size: "small", startIcon: React.createElement(ClearIcon, null), onClick: handleClearFilters }, "Clear Filters")))),
|
|
175
194
|
React.createElement(DialogContent, { dividers: true },
|
|
176
195
|
React.createElement(TextField, { fullWidth: true, placeholder: "Search components...", value: searchTerm, onChange: (e) => setSearchTerm(e.target.value), InputProps: {
|
|
@@ -193,7 +212,8 @@ export default function ComponentPickerModal({ open, onClose, onSelect, selected
|
|
|
193
212
|
template.revision))))),
|
|
194
213
|
activityTypes.length > 0 && (React.createElement(TextField, { select: true, size: "small", label: "Activity Type", value: selectedActivityType, onChange: (e) => setSelectedActivityType(e.target.value), sx: { minWidth: 160 } },
|
|
195
214
|
React.createElement(MenuItem, { value: "" }, "All Activity Types"),
|
|
196
|
-
activityTypes.map((type) => (React.createElement(MenuItem, { key: type, value: type }, type)))))
|
|
215
|
+
activityTypes.map((type) => (React.createElement(MenuItem, { key: type, value: type }, type))))),
|
|
216
|
+
isLocationComponent && (React.createElement(FormControlLabel, { control: React.createElement(Checkbox, { checked: citiesOnly, onChange: (e) => setCitiesOnly(e.target.checked) }), label: "Cities only" }))),
|
|
197
217
|
React.createElement(Box, { display: "flex", alignItems: "center", gap: 2, mb: 1 },
|
|
198
218
|
React.createElement(Typography, { variant: "body2", color: "text.secondary" },
|
|
199
219
|
totalCount,
|
|
@@ -6,6 +6,7 @@ import "../../renderers/Debug";
|
|
|
6
6
|
import "../../renderers/Example";
|
|
7
7
|
import "../../renderers/Image/ImageForm";
|
|
8
8
|
import "../../renderers/Image/ImagePresentation";
|
|
9
|
+
import "../../renderers/NestedObjectArray";
|
|
9
10
|
import "../../renderers/StagedText";
|
|
10
11
|
import "../../renderers/Subset";
|
|
11
12
|
import "../../renderers/TemplatePicker";
|
|
@@ -8,6 +8,7 @@ import "../../renderers/Debug";
|
|
|
8
8
|
import "../../renderers/Example";
|
|
9
9
|
import "../../renderers/Image/ImageForm";
|
|
10
10
|
import "../../renderers/Image/ImagePresentation";
|
|
11
|
+
import "../../renderers/NestedObjectArray";
|
|
11
12
|
import "../../renderers/StagedText";
|
|
12
13
|
import "../../renderers/Subset";
|
|
13
14
|
import "../../renderers/TemplatePicker";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { resolveSchema } from "@jsonforms/core";
|
|
2
|
+
import { JsonForms } from "@jsonforms/react";
|
|
3
|
+
import { materialCells, materialRenderers } from "@jsonforms/material-renderers";
|
|
4
|
+
import React, { useState } from "react";
|
|
5
|
+
import { Accordion, AccordionDetails, AccordionSummary, Box, Button, IconButton, Typography } from "@mui/material";
|
|
6
|
+
import AddIcon from "@mui/icons-material/Add";
|
|
7
|
+
import DeleteIcon from "@mui/icons-material/Delete";
|
|
8
|
+
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
|
|
9
|
+
import { ComponentPool } from "../registry/types";
|
|
10
|
+
import { getComponents, registerComponent } from "../registry/components";
|
|
11
|
+
const hasNestedArray = (schema) => {
|
|
12
|
+
if (schema.type !== "array")
|
|
13
|
+
return false;
|
|
14
|
+
const items = schema.items;
|
|
15
|
+
if (!items || items.type !== "object")
|
|
16
|
+
return false;
|
|
17
|
+
const props = items.properties;
|
|
18
|
+
if (!props)
|
|
19
|
+
return false;
|
|
20
|
+
return Object.values(props).some((p) => p.type === "array");
|
|
21
|
+
};
|
|
22
|
+
// Rank 5 — beats materialArrayLayout (rank 4), only fires on arrays of objects
|
|
23
|
+
// that contain at least one nested array property.
|
|
24
|
+
const nestedObjectArrayTester = (uischema, schema) => {
|
|
25
|
+
const ui = uischema;
|
|
26
|
+
if (!ui.scope)
|
|
27
|
+
return -1;
|
|
28
|
+
const scoped = resolveSchema(schema, ui.scope, schema);
|
|
29
|
+
if (!scoped)
|
|
30
|
+
return -1;
|
|
31
|
+
if (!hasNestedArray(scoped))
|
|
32
|
+
return -1;
|
|
33
|
+
return 5;
|
|
34
|
+
};
|
|
35
|
+
const getItemLabel = (item, index) => (item === null || item === void 0 ? void 0 : item.title) ||
|
|
36
|
+
(item === null || item === void 0 ? void 0 : item.name) ||
|
|
37
|
+
(item === null || item === void 0 ? void 0 : item.label) ||
|
|
38
|
+
`Item ${index + 1}`;
|
|
39
|
+
const NestedObjectArrayRenderer = ({ data, handleChange, path, schema, label, enabled }) => {
|
|
40
|
+
const items = Array.isArray(data) ? data : [];
|
|
41
|
+
const itemSchema = schema.items;
|
|
42
|
+
const [expanded, setExpanded] = useState(false);
|
|
43
|
+
const renderers = [...materialRenderers, ...getComponents(ComponentPool.FORM)];
|
|
44
|
+
const handleItemChange = (index, newData) => {
|
|
45
|
+
const next = [...items];
|
|
46
|
+
next[index] = newData;
|
|
47
|
+
handleChange(path, next);
|
|
48
|
+
};
|
|
49
|
+
const addItem = () => {
|
|
50
|
+
handleChange(path, [...items, {}]);
|
|
51
|
+
setExpanded(items.length);
|
|
52
|
+
};
|
|
53
|
+
const removeItem = (e, index) => {
|
|
54
|
+
e.stopPropagation();
|
|
55
|
+
const next = items.filter((_, i) => i !== index);
|
|
56
|
+
handleChange(path, next);
|
|
57
|
+
setExpanded(false);
|
|
58
|
+
};
|
|
59
|
+
return (React.createElement(Box, null,
|
|
60
|
+
label && (React.createElement(Typography, { variant: "subtitle2", sx: { mb: 1 } }, label)),
|
|
61
|
+
items.map((item, index) => (React.createElement(Accordion, { key: index, expanded: expanded === index, onChange: (_, isExpanded) => setExpanded(isExpanded ? index : false), sx: { mb: 1 } },
|
|
62
|
+
React.createElement(AccordionSummary, { expandIcon: React.createElement(ExpandMoreIcon, null) },
|
|
63
|
+
React.createElement(Box, { sx: {
|
|
64
|
+
display: "flex",
|
|
65
|
+
alignItems: "center",
|
|
66
|
+
width: "100%",
|
|
67
|
+
gap: 1
|
|
68
|
+
} },
|
|
69
|
+
React.createElement(Typography, { sx: { flex: 1 } }, getItemLabel(item, index)),
|
|
70
|
+
enabled && (React.createElement(IconButton, { size: "small", color: "error", onClick: (e) => removeItem(e, index), "aria-label": "Remove item" },
|
|
71
|
+
React.createElement(DeleteIcon, { fontSize: "small" }))))),
|
|
72
|
+
React.createElement(AccordionDetails, null,
|
|
73
|
+
React.createElement(JsonForms, { schema: itemSchema, data: item, onChange: ({ data: newData }) => handleItemChange(index, newData), renderers: renderers, cells: materialCells, readonly: !enabled }))))),
|
|
74
|
+
enabled && (React.createElement(Button, { size: "small", variant: "outlined", startIcon: React.createElement(AddIcon, null), onClick: addItem, sx: { mt: 1 } },
|
|
75
|
+
"Add ", label !== null && label !== void 0 ? label : "Item"))));
|
|
76
|
+
};
|
|
77
|
+
registerComponent("nested-object-array", NestedObjectArrayRenderer, ComponentPool.FORM, nestedObjectArrayTester);
|