swoop-common 2.2.206 → 2.2.207

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.
@@ -1,6 +1,7 @@
1
1
  import React from "react";
2
2
  import { DTOComponentRead, DTORegionRead, DTOTemplateRead } from "../../api/generated/core/exports";
3
3
  import { PartnerSwoop } from "../../api/generated/swoop/exports";
4
+ import { ComponentSystem } from "../consts/system";
4
5
  export interface ComponentListItemProps {
5
6
  component: DTOComponentRead;
6
7
  isSelected: boolean;
@@ -11,6 +12,7 @@ export interface ComponentListItemProps {
11
12
  };
12
13
  onSelect: (component: DTOComponentRead) => void;
13
14
  showDivider: boolean;
15
+ system?: ComponentSystem;
14
16
  }
15
17
  declare const ComponentListItem: React.FC<ComponentListItemProps>;
16
18
  export default ComponentListItem;
@@ -1,13 +1,83 @@
1
- import React from "react";
2
- import { Box, Chip, Divider, ListItem, ListItemButton, ListItemText, Typography, } from "@mui/material";
3
- import { PARTNER_REGION_READABLE } from "../consts/region";
1
+ import React, { useEffect, useState } from "react";
2
+ import { Box, Button, Chip, Divider, ListItem, ListItemButton, ListItemText, Typography, } from "@mui/material";
4
3
  import { getComponentDataByTemplateName, TEMPLATE_NAMES } from "../../lib/config/templateStructure";
5
- const ComponentListItem = ({ component, isSelected, details, onSelect, showDivider, }) => {
6
- var _a;
4
+ import { getComponentsByIds } from "../util/api";
5
+ import { COMPONENT_SYSTEMS } from "../consts/system";
6
+ const TEMPLATE_CHIP_STYLES = {
7
+ [TEMPLATE_NAMES.PACKAGE]: { backgroundColor: "#E3F2FD", color: "#1565C0" }, // blue
8
+ [TEMPLATE_NAMES.PACKAGES]: { backgroundColor: "#E3F2FD", color: "#1565C0" }, // blue
9
+ [TEMPLATE_NAMES.TRANSFER]: { backgroundColor: "#F3E5F5", color: "#7B1FA2" }, // purple
10
+ [TEMPLATE_NAMES.ACTIVITY]: { backgroundColor: "#E8F5E9", color: "#2E7D32" }, // green
11
+ [TEMPLATE_NAMES.CRUISE_ACTIVITY]: { backgroundColor: "#DCEDC8", color: "#558B2F" }, // different shade of green
12
+ [TEMPLATE_NAMES.GROUND_ACCOMMODATION]: { backgroundColor: "#FFF9C4", color: "#F9A825" }, // yellow
13
+ [TEMPLATE_NAMES.SHIP_ACCOMMODATION]: { backgroundColor: "#FCE4EC", color: "#C2185B" }, // red/pink
14
+ };
15
+ const getTemplateChipStyle = (templateName) => { var _a; return (_a = TEMPLATE_CHIP_STYLES[templateName]) !== null && _a !== void 0 ? _a : { backgroundColor: "grey.200", color: "text.secondary" }; };
16
+ /*
17
+ Content per item type from ticket:
18
+ Package: Trip ID (hide on IB), Start city, Private, Guided
19
+
20
+ Transfer: Private, Guided, Transfer type, Start location (from journey), End location (from journey)
21
+ // lookup journey from component.componentFields.0.data.journey which contains a componentID, e.g. component_5a51acc53ab0b16da19f875c8cc148f2
22
+ // journey will contain a couple more componentFields which are start and end locations. These will need looking up and outputting.
23
+
24
+ Activity: Start location, End location, Type, Guided
25
+
26
+ Cruise activity: Type
27
+
28
+ Ground accommodation: City
29
+
30
+ Ship accommodation: Vessel ID (hide on IB)
31
+
32
+ Private and Guided render as a tick (✓) when true
33
+
34
+ When Private or Guided is false or unpopulated, the field is omitted entirely
35
+ */
36
+ const INLINE_CONTENT_BY_TEMPLATE = {
37
+ [TEMPLATE_NAMES.SHIP_ACCOMMODATION]: {
38
+ render: (component, _lookups, isTemplateEditor) => {
39
+ var _a;
40
+ if (!isTemplateEditor)
41
+ return null;
42
+ const vesselId = (_a = getComponentDataByTemplateName(TEMPLATE_NAMES.SHIP_ACCOMMODATION, component.componentFields)) === null || _a === void 0 ? void 0 : _a.vesselID;
43
+ if (!vesselId)
44
+ return null;
45
+ return (React.createElement(Typography, { variant: "body2", color: "text.secondary" },
46
+ "Vessel ID: ",
47
+ vesselId));
48
+ },
49
+ },
50
+ // plus the rest in from there...
51
+ };
52
+ const getInlineContentDefinition = (templateName) => templateName ? INLINE_CONTENT_BY_TEMPLATE[templateName] : undefined;
53
+ const ComponentListItem = ({ component, isSelected, details, onSelect, showDivider, system, }) => {
54
+ var _a, _b, _c, _d, _e;
7
55
  const activityData = (_a = getComponentDataByTemplateName(TEMPLATE_NAMES.CRUISE_ACTIVITY, component.componentFields)) !== null && _a !== void 0 ? _a : getComponentDataByTemplateName(TEMPLATE_NAMES.ACTIVITY, component.componentFields);
8
56
  const displayName = (activityData === null || activityData === void 0 ? void 0 : activityData.type)
9
57
  ? `${activityData.type} - ${component.name}`
10
58
  : component.name;
59
+ const convertToTitleCase = (str) => {
60
+ return str.replace(/\w\S*/g, (txt) => txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase());
61
+ };
62
+ const inlineContentDefinition = getInlineContentDefinition((_b = details.template) === null || _b === void 0 ? void 0 : _b.name);
63
+ const requiredComponentIds = (_d = (_c = inlineContentDefinition === null || inlineContentDefinition === void 0 ? void 0 : inlineContentDefinition.getRequiredComponentIds) === null || _c === void 0 ? void 0 : _c.call(inlineContentDefinition, component)) !== null && _d !== void 0 ? _d : [];
64
+ const requiredComponentIdsKey = requiredComponentIds.join(",");
65
+ const [lookups, setLookups] = useState({});
66
+ const isTemplateEditor = system === COMPONENT_SYSTEMS.TEMPLATE_EDITOR;
67
+ useEffect(() => {
68
+ const missingIds = requiredComponentIds.filter((id) => !lookups[id]);
69
+ if (!missingIds.length)
70
+ return;
71
+ let cancelled = false;
72
+ getComponentsByIds(missingIds).then((fetched) => {
73
+ if (cancelled)
74
+ return;
75
+ setLookups((prev) => (Object.assign(Object.assign({}, prev), Object.fromEntries(fetched.map((c) => [c.id, c])))));
76
+ });
77
+ return () => { cancelled = true; };
78
+ // eslint-disable-next-line react-hooks/exhaustive-deps
79
+ }, [requiredComponentIdsKey]);
80
+ const inlineContent = (_e = inlineContentDefinition === null || inlineContentDefinition === void 0 ? void 0 : inlineContentDefinition.render(component, lookups, isTemplateEditor)) !== null && _e !== void 0 ? _e : null;
11
81
  return (React.createElement(React.Fragment, null,
12
82
  React.createElement(ListItem, { disablePadding: true },
13
83
  React.createElement(ListItemButton, { onClick: () => onSelect(component), selected: isSelected, sx: {
@@ -20,21 +90,23 @@ const ComponentListItem = ({ component, isSelected, details, onSelect, showDivid
20
90
  borderColor: "primary.main",
21
91
  },
22
92
  } },
23
- React.createElement(Box, { width: "100%" },
24
- React.createElement(ListItemText, { primary: React.createElement(Typography, { variant: "subtitle1", fontWeight: "medium" }, displayName), secondary: React.createElement(Box, { mt: 1 },
25
- details.template && (React.createElement(Typography, { variant: "body2", color: "text.secondary" },
26
- "Type: ",
27
- details.template.name)),
28
- details.regions.length > 0 && (React.createElement(Box, { display: "flex", alignItems: "center", gap: 1, mt: 0.5 },
29
- React.createElement(Typography, { variant: "body2", color: "text.secondary" }, "Regions:"),
30
- details.regions.map((region) => (React.createElement(Chip, { key: region.id, label: region.name, size: "small", variant: "outlined" }))))),
31
- details.partners.length > 0 && (React.createElement(Box, { display: "flex", alignItems: "center", gap: 1, mt: 0.5 },
32
- React.createElement(Typography, { variant: "body2", color: "text.secondary" }, "Partners:"),
33
- details.partners.slice(0, 3).map((partner) => (React.createElement(Chip, { key: partner.id, label: `${partner.title} (${PARTNER_REGION_READABLE[(partner.id || "").split("-")[0]]})`, size: "small", variant: "outlined", color: "secondary" }))),
34
- details.partners.length > 3 && (React.createElement(Typography, { variant: "body2", color: "text.secondary" },
35
- "+",
36
- details.partners.length - 3,
37
- " more"))))) })))),
93
+ React.createElement(Box, { width: "100%", display: "flex", justifyContent: "space-between", alignItems: "flex-start", gap: 2 },
94
+ React.createElement(ListItemText, { primary: React.createElement(Box, { display: "flex", flexDirection: "column", gap: 0.5 },
95
+ details.template && (React.createElement(Box, null,
96
+ React.createElement(Chip, { label: convertToTitleCase(details.template.name), size: "small", sx: Object.assign(Object.assign({}, getTemplateChipStyle(details.template.name)), { fontWeight: 500 }) }))),
97
+ React.createElement(Typography, { variant: "subtitle1", fontWeight: "medium" }, displayName)), secondary: React.createElement(Box, { display: "flex", flexDirection: "column", gap: 0.5, mt: 1 },
98
+ React.createElement(Box, { display: "flex", alignItems: "center", gap: 3, flexWrap: "wrap" },
99
+ details.partners.length > 0 && (React.createElement(Typography, { variant: "body2", color: "text.secondary" },
100
+ "Partner: ",
101
+ details.partners[0].title)),
102
+ (component === null || component === void 0 ? void 0 : component.destination) && (React.createElement(Typography, { variant: "body2", color: "text.secondary" },
103
+ "Destination: ",
104
+ convertToTitleCase(component.destination))),
105
+ details.regions.length > 0 && (React.createElement(Typography, { variant: "body2", color: "text.secondary" },
106
+ "Region: ",
107
+ details.regions[0].name))),
108
+ inlineContent && (React.createElement(Box, { display: "flex", alignItems: "center", gap: 3, flexWrap: "wrap" }, inlineContent))) }),
109
+ isTemplateEditor && (React.createElement(Button, { component: "a", href: `${window.location.origin}/components/${component.id}`, target: "_blank", rel: "noopener noreferrer", variant: "outlined", size: "small", onClick: (e) => e.stopPropagation(), sx: { flexShrink: 0 } }, "View"))))),
38
110
  showDivider && React.createElement(Divider, null)));
39
111
  };
40
112
  export default ComponentListItem;
@@ -1,11 +1,13 @@
1
1
  import React from "react";
2
2
  import { DTOComponentRead } from "../../api/generated/core/exports";
3
+ import { ComponentSystem } from "../consts/system";
3
4
  interface ComponentPickerModalProps {
4
5
  open: boolean;
5
6
  onClose: () => void;
6
7
  onSelect: (component: DTOComponentRead) => void;
7
8
  selectedComponent?: DTOComponentRead | null;
8
9
  parentIdsFilter?: Array<string>;
10
+ system?: ComponentSystem;
9
11
  }
10
- export default function ComponentPickerModal({ open, onClose, onSelect, selectedComponent, parentIdsFilter, }: ComponentPickerModalProps): React.JSX.Element;
12
+ export default function ComponentPickerModal({ open, onClose, onSelect, selectedComponent, parentIdsFilter, system, }: ComponentPickerModalProps): React.JSX.Element;
11
13
  export {};
@@ -15,7 +15,7 @@ import { fetchComponents, getAllPartners } from "../util/api";
15
15
  import { InternalServices } from "../../api/init";
16
16
  import ComponentListItem from "./ComponentListItem";
17
17
  import { TEMPLATE_NAMES, templateStructure, } from "../../lib/config/templateStructure";
18
- export default function ComponentPickerModal({ open, onClose, onSelect, selectedComponent, parentIdsFilter, }) {
18
+ export default function ComponentPickerModal({ open, onClose, onSelect, selectedComponent, parentIdsFilter, system, }) {
19
19
  const [searchTerm, setSearchTerm] = useState("");
20
20
  const [selectedRegion, setSelectedRegion] = useState("");
21
21
  const [selectedPartner, setSelectedPartner] = useState("");
@@ -240,7 +240,7 @@ export default function ComponentPickerModal({ open, onClose, onSelect, selected
240
240
  React.createElement(Typography, { variant: "body1", color: "text.secondary" }, "Loading components..."))) : components.length === 0 ? (React.createElement(Box, { display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", py: 4 },
241
241
  React.createElement(FilterIcon, { sx: { fontSize: 48, color: "text.secondary", mb: 2 } }),
242
242
  React.createElement(Typography, { variant: "body1", color: "text.secondary" }, "No components match your search criteria"),
243
- React.createElement(Typography, { variant: "body2", color: "text.secondary" }, "Try adjusting your filters or search term"))) : (components.map((component, index) => (React.createElement(ComponentListItem, { key: component.id, component: component, isSelected: (selectedComponent === null || selectedComponent === void 0 ? void 0 : selectedComponent.id) === component.id, details: getComponentDetails(component), onSelect: handleSelect, showDivider: index < components.length - 1 }))))),
243
+ React.createElement(Typography, { variant: "body2", color: "text.secondary" }, "Try adjusting your filters or search term"))) : (components.map((component, index) => (React.createElement(ComponentListItem, { key: component.id, component: component, isSelected: (selectedComponent === null || selectedComponent === void 0 ? void 0 : selectedComponent.id) === component.id, details: getComponentDetails(component), onSelect: handleSelect, showDivider: index < components.length - 1, system: system }))))),
244
244
  components.length > 0 && (React.createElement(Box, { display: "flex", justifyContent: "center", mt: 2 },
245
245
  React.createElement(Pagination, { count: Math.ceil(totalCount / pageSize), page: page, onChange: (_, newPage) => setPage(newPage), color: "primary" })))),
246
246
  React.createElement(DialogActions, null,
@@ -0,0 +1,5 @@
1
+ export declare const COMPONENT_SYSTEMS: {
2
+ readonly TEMPLATE_EDITOR: "template-editor";
3
+ readonly ITINERARY_BUILDER: "itinerary-builder";
4
+ };
5
+ export type ComponentSystem = typeof COMPONENT_SYSTEMS[keyof typeof COMPONENT_SYSTEMS];
@@ -0,0 +1,4 @@
1
+ export const COMPONENT_SYSTEMS = {
2
+ TEMPLATE_EDITOR: "template-editor",
3
+ ITINERARY_BUILDER: "itinerary-builder",
4
+ };
@@ -1,10 +1,12 @@
1
1
  import React from 'react';
2
2
  import { DTOComponentRead } from '../../api/generated/core/exports';
3
+ import { ComponentSystem } from '../consts/system';
3
4
  interface ComponentPickerExports {
4
5
  get: (parentIds?: Array<string>) => Promise<DTOComponentRead>;
5
6
  }
6
7
  interface Props {
7
8
  children?: React.ReactNode;
9
+ system?: ComponentSystem;
8
10
  }
9
11
  export declare const useComponentPicker: () => ComponentPickerExports;
10
12
  export declare const ComponentPickerProvider: React.FC<Props>;
@@ -16,7 +16,7 @@ export const useComponentPicker = () => {
16
16
  throw new Error("useComponentPicker used from outside selector context");
17
17
  return picker;
18
18
  };
19
- export const ComponentPickerProvider = ({ children }) => {
19
+ export const ComponentPickerProvider = ({ children, system }) => {
20
20
  const [open, setOpen] = useState(false);
21
21
  const changeProp = useRef(() => { });
22
22
  const closeProp = useRef(() => { });
@@ -44,6 +44,6 @@ export const ComponentPickerProvider = ({ children }) => {
44
44
  });
45
45
  });
46
46
  return (React.createElement(ComponentSelectorContext.Provider, { value: { get: getSelectedComponent } },
47
- React.createElement(ComponentPickerModal, { parentIdsFilter: parentIds.length ? parentIds : undefined, open: open, onClose: closeProp.current, onSelect: changeProp.current }),
47
+ React.createElement(ComponentPickerModal, { parentIdsFilter: parentIds.length ? parentIds : undefined, open: open, onClose: closeProp.current, onSelect: changeProp.current, system: system }),
48
48
  children));
49
49
  };
@@ -10,3 +10,4 @@ export { MasterSchema, Stage } from "./schema/formSchemaTypes";
10
10
  export { FORM_BUILDER_JSON_SCHEMA } from "./schema/formBuilders/formBuilderJsonSchema";
11
11
  export { FORM_BUILDER_UI_SCHEMA } from "./schema/formBuilders/formBuilderUiSchema";
12
12
  export * from "./contexts/ComponentPickerContext";
13
+ export * from "./consts/system";
@@ -10,3 +10,4 @@ export { Stage } from "./schema/formSchemaTypes";
10
10
  export { FORM_BUILDER_JSON_SCHEMA } from "./schema/formBuilders/formBuilderJsonSchema";
11
11
  export { FORM_BUILDER_UI_SCHEMA } from "./schema/formBuilders/formBuilderUiSchema";
12
12
  export * from "./contexts/ComponentPickerContext";
13
+ export * from "./consts/system";
@@ -6,3 +6,4 @@ export declare const fetchComponents: (filters?: Record<string, string>) => Prom
6
6
  }>;
7
7
  pagination: Pagination;
8
8
  }>;
9
+ export declare const getComponentsByIds: (componentIds: string[]) => Promise<DTOComponentRead[]>;
@@ -50,3 +50,18 @@ export const fetchComponents = (filters) => __awaiter(void 0, void 0, void 0, fu
50
50
  });
51
51
  return { data: latestComponents, pagination: data.pagination };
52
52
  });
53
+ export const getComponentsByIds = (componentIds) => __awaiter(void 0, void 0, void 0, function* () {
54
+ const uniqueIds = Array.from(new Set(componentIds.filter(Boolean)));
55
+ if (!uniqueIds.length)
56
+ return [];
57
+ try {
58
+ const response = yield InternalServices.CoreService.componentBatchGet({
59
+ componentIds: uniqueIds,
60
+ });
61
+ return response.data || [];
62
+ }
63
+ catch (error) {
64
+ console.error("Failed to fetch components by id:", error);
65
+ return [];
66
+ }
67
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "swoop-common",
3
- "version": "2.2.206",
3
+ "version": "2.2.207",
4
4
  "main": "dist/api/index.js",
5
5
  "types": "dist/api/index.d.ts",
6
6
  "exports": {