@camunda/camunda-composite-components 0.11.0 → 0.12.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/lib/esm/package.json +2 -2
- package/lib/esm/src/components/c3-navigation/c3-navigation-appbar/c3-navigation-appbar.js +117 -6
- package/lib/esm/src/components/c3-navigation/c3-navigation-sidebar/c3-navigation-sidebar-element.js +10 -5
- package/lib/esm/src/components/c3-navigation/c3-navigation-sidebar/c3-navigation-sidebar.types.d.ts +3 -0
- package/lib/esm/src/components/c3-navigation/c3-navigation-sidebar/c3-notification-sidebar.js +2 -2
- package/lib/esm/src/components/c3-navigation/c3-navigation.js +3 -9
- package/lib/esm/src/components/c3-navigation/c3-navigation.types.d.ts +1 -0
- package/lib/esm/src/components/c3-navigation/c3-notification-provider/c3-notification-provider.d.ts +1 -0
- package/lib/esm/src/components/c3-navigation/c3-notification-provider/c3-notification-provider.js +28 -24
- package/lib/esm/src/components/c3-navigation/c3-org-name.d.ts +6 -0
- package/lib/esm/src/components/c3-navigation/c3-org-name.js +37 -0
- package/lib/esm/src/components/c3-navigation/c3-org-sidebar/c3-org-sidebar.js +102 -32
- package/lib/esm/src/components/c3-navigation/c3-org-sidebar/components.d.ts +10 -0
- package/lib/esm/src/components/c3-navigation/c3-org-sidebar/components.js +32 -6
- package/lib/esm/src/components/c3-user-configuration/c3-profile-provider/c3-profile-provider.d.ts +2 -0
- package/lib/esm/src/components/c3-user-configuration/c3-profile-provider/c3-profile-provider.js +11 -7
- package/lib/esm/src/components/styles.d.ts +1 -0
- package/lib/esm/src/components/styles.js +6 -0
- package/lib/esm/src/utils/camunda.types.d.ts +3 -0
- package/package.json +2 -2
package/lib/esm/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"type": "git",
|
|
5
5
|
"url": "git+https://github.com/camunda-cloud/camunda-composite-components.git"
|
|
6
6
|
},
|
|
7
|
-
"version": "0.
|
|
7
|
+
"version": "0.11.1-rc.1",
|
|
8
8
|
"scripts": {
|
|
9
9
|
"clean": "rimraf lib/",
|
|
10
10
|
"build": "yarn clean && tsc",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"@babel/preset-env": "7.25.7",
|
|
35
35
|
"@babel/preset-react": "7.25.7",
|
|
36
36
|
"@babel/preset-typescript": "7.25.7",
|
|
37
|
-
"@carbon/react": "1.
|
|
37
|
+
"@carbon/react": "1.66.0",
|
|
38
38
|
"@mdx-js/react": "3.0.1",
|
|
39
39
|
"@playwright/test": "1.45.2",
|
|
40
40
|
"@semantic-release/changelog": "6.0.3",
|
|
@@ -1,20 +1,116 @@
|
|
|
1
|
-
import React, { useEffect, useState } from "react";
|
|
1
|
+
import React, { useCallback, useEffect, useState } from "react";
|
|
2
2
|
import { Close } from "@carbon/react/icons";
|
|
3
3
|
import { useOnClickOutside } from "../helpers";
|
|
4
|
-
import { HeaderGlobalAction, HeaderMenuItem, HeaderSideNavItems, SideNavItems, SideNavLink, SideNavMenu, SideNavMenuItem, } from "@carbon/react";
|
|
4
|
+
import { FormLabel, HeaderGlobalAction, HeaderMenuItem, HeaderSideNavItems, Modal, SideNavItems, SideNavLink, SideNavMenu, SideNavMenuItem, } from "@carbon/react";
|
|
5
5
|
import { C3AppMenuIcon } from "../../../assets/c3-icons";
|
|
6
6
|
import { useC3Profile } from "../../c3-user-configuration/c3-profile-provider/c3-profile-provider";
|
|
7
7
|
import { useC3UserConfiguration } from "../../c3-user-configuration/c3-user-configuration-provider";
|
|
8
8
|
import { APPS } from "../../../utils/camunda";
|
|
9
9
|
import { NavWrapper, SideNav } from "./components";
|
|
10
|
+
const getOrgLink = (app, orgId, domain) => {
|
|
11
|
+
switch (app) {
|
|
12
|
+
case "console": {
|
|
13
|
+
return `https://console.${domain}/org/${orgId}`;
|
|
14
|
+
}
|
|
15
|
+
case "modeler": {
|
|
16
|
+
return `https://modeler.${domain}/login?returnUrl=/org/${orgId}`;
|
|
17
|
+
}
|
|
18
|
+
default: {
|
|
19
|
+
return "#";
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
const OrgPickerModal = ({ isOpen, onCancel, orgs, activeOrg, loadingStatus, appToNavigateTo, domain, }) => {
|
|
24
|
+
const hasNoTeams = Object.values(orgs ?? {})?.every(({ org }) => {
|
|
25
|
+
return !org?.name;
|
|
26
|
+
});
|
|
27
|
+
const getClustersToRender = (clusters) => {
|
|
28
|
+
const hasAppReadPermission = activeOrg?.permissions?.cluster?.[appToNavigateTo]?.read;
|
|
29
|
+
const clustersToRender = clusters?.filter(({ status, endpoints }) => status[appToNavigateTo] === "Healthy" &&
|
|
30
|
+
endpoints[appToNavigateTo] &&
|
|
31
|
+
hasAppReadPermission);
|
|
32
|
+
return clustersToRender;
|
|
33
|
+
};
|
|
34
|
+
const hasNoTeamsWithClusters = Object.values(orgs ?? {})?.every(({ clusters }) => {
|
|
35
|
+
const isConsoleOrModeler = [APPS[0], APPS[1]].includes(appToNavigateTo);
|
|
36
|
+
return !isConsoleOrModeler && getClustersToRender(clusters).length === 0;
|
|
37
|
+
});
|
|
38
|
+
return (React.createElement(Modal, { danger: true, open: isOpen, modalHeading: "Select a team", onRequestClose: onCancel, passiveModal: true, loadingStatus: loadingStatus, size: "sm" },
|
|
39
|
+
React.createElement("div", { style: { marginLeft: "-1rem" } },
|
|
40
|
+
React.createElement(FormLabel, { style: {
|
|
41
|
+
paddingTop: "15px",
|
|
42
|
+
paddingLeft: "1rem",
|
|
43
|
+
} }, hasNoTeams
|
|
44
|
+
? "No teams found"
|
|
45
|
+
: hasNoTeamsWithClusters
|
|
46
|
+
? "No teams with clusters found"
|
|
47
|
+
: "Teams"),
|
|
48
|
+
React.createElement("div", { style: {
|
|
49
|
+
marginTop: "-1rem",
|
|
50
|
+
} },
|
|
51
|
+
React.createElement(SideNavItems, null, Object.values(orgs ?? {})?.map(({ org, clusters }) => {
|
|
52
|
+
const isConsoleOrModeler = [APPS[0], APPS[1]].includes(appToNavigateTo);
|
|
53
|
+
const toOrg = getOrgLink(appToNavigateTo, org.uuid, domain ?? "");
|
|
54
|
+
if (isConsoleOrModeler) {
|
|
55
|
+
return (React.createElement(SideNavLink, { key: org?.uuid, href: toOrg }, org?.name));
|
|
56
|
+
}
|
|
57
|
+
const clustersToRender = getClustersToRender(clusters);
|
|
58
|
+
if (clustersToRender.length === 0) {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
return (React.createElement(SideNavMenu, { key: org?.uuid, title: org?.name }, clustersToRender?.map((cluster) => {
|
|
62
|
+
return (React.createElement(SideNavMenuItem, { key: cluster.uuid, href: cluster.endpoints[appToNavigateTo] }, cluster.name));
|
|
63
|
+
})));
|
|
64
|
+
}))))));
|
|
65
|
+
};
|
|
10
66
|
export const C3NavigationAppBar = ({ app: appProps, appBar, forwardRef, navbar, }) => {
|
|
11
67
|
const { currentApp, domain, analyticsTrack, currentClusterUuid } = useC3UserConfiguration();
|
|
12
|
-
const { clusters, activeOrg, isEnabled } = useC3Profile();
|
|
68
|
+
const { orgs, clusters, activeOrg, isEnabled, loadClustersById } = useC3Profile();
|
|
13
69
|
const [appBarOpen, setAppBarOpen] = useState(appBar.isOpen || false);
|
|
14
70
|
const { setPanelRef: panelRef, setIconRef: iconRef } = useOnClickOutside(() => setAppBarOpen(false));
|
|
15
71
|
const [appElements, setAppElements] = useState([]);
|
|
72
|
+
const [loadingStatus, setLoadingStatus] = useState("inactive");
|
|
73
|
+
const [clustersByOrgId, setClustersByOrgId] = useState(null);
|
|
74
|
+
const [appToNavigateTo, setAppToNavigateTo] = useState(APPS[0]);
|
|
16
75
|
if (!appBar.elements && !isEnabled)
|
|
17
76
|
console.warn("No app elements and user config provided. Please provide at least one of them.");
|
|
77
|
+
const isMemberOfTeam = useCallback((orgUuid) => orgs?.some(({ uuid }) => uuid === orgUuid) || false, [orgs]);
|
|
78
|
+
const getClustersForSuperOrg = useCallback(async () => {
|
|
79
|
+
const updatedClusterByOrgId = {};
|
|
80
|
+
await Promise.all(activeOrg?.childOrganizations
|
|
81
|
+
?.filter((team) => isMemberOfTeam(team.uuid))
|
|
82
|
+
.map((org) => loadClustersById(org.uuid)?.then(({ result }) => {
|
|
83
|
+
updatedClusterByOrgId[org.uuid] = {
|
|
84
|
+
org,
|
|
85
|
+
clusters: result ?? [],
|
|
86
|
+
};
|
|
87
|
+
})) ?? []);
|
|
88
|
+
setClustersByOrgId(updatedClusterByOrgId);
|
|
89
|
+
}, [activeOrg, isMemberOfTeam]);
|
|
90
|
+
const handleSuperOrg = useCallback(() => {
|
|
91
|
+
if (!activeOrg?.uuid) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
setIsOrgPickerModalOpen(true);
|
|
95
|
+
if (clustersByOrgId) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
setLoadingStatus("active");
|
|
99
|
+
getClustersForSuperOrg()
|
|
100
|
+
.catch((error) => console.error(error))
|
|
101
|
+
.finally(() => {
|
|
102
|
+
setLoadingStatus("finished");
|
|
103
|
+
});
|
|
104
|
+
}, [clustersByOrgId, activeOrg, getClustersForSuperOrg]);
|
|
105
|
+
useEffect(() => {
|
|
106
|
+
if (activeOrg?.isSuperOrg) {
|
|
107
|
+
getClustersForSuperOrg()
|
|
108
|
+
.catch((error) => console.error(error))
|
|
109
|
+
.finally(() => {
|
|
110
|
+
setLoadingStatus("finished");
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
}, [activeOrg, getClustersForSuperOrg]);
|
|
18
114
|
useEffect(() => {
|
|
19
115
|
if (appBar.elements)
|
|
20
116
|
return;
|
|
@@ -76,12 +172,24 @@ export const C3NavigationAppBar = ({ app: appProps, appBar, forwardRef, navbar,
|
|
|
76
172
|
}
|
|
77
173
|
}
|
|
78
174
|
}
|
|
79
|
-
if (element.href || element.routeProps || element.subElements)
|
|
175
|
+
if (element.href || element.routeProps || element.subElements) {
|
|
176
|
+
if (activeOrg.isSuperOrg) {
|
|
177
|
+
element.href = "#";
|
|
178
|
+
element.routeProps = null;
|
|
179
|
+
element.onClick = () => {
|
|
180
|
+
setAppToNavigateTo(app);
|
|
181
|
+
handleSuperOrg();
|
|
182
|
+
// probs want to track?
|
|
183
|
+
// analyticsTrack?.(`${app}:open`, { currentApp: app })
|
|
184
|
+
};
|
|
185
|
+
}
|
|
80
186
|
defaultElements.push(element);
|
|
187
|
+
}
|
|
81
188
|
}
|
|
82
189
|
setAppElements(defaultElements);
|
|
83
|
-
}, [
|
|
84
|
-
const appBarElements = appBar.elements || (clusters ? appElements : null);
|
|
190
|
+
}, [clusters, currentApp, domain, handleSuperOrg, getClustersForSuperOrg]);
|
|
191
|
+
const appBarElements = appBar.elements || (clusters || clustersByOrgId ? appElements : null);
|
|
192
|
+
const [isOrgPickerModalOpen, setIsOrgPickerModalOpen] = useState(false);
|
|
85
193
|
return (React.createElement(React.Fragment, null,
|
|
86
194
|
React.createElement(HeaderGlobalAction
|
|
87
195
|
// eslint-disable-next-line
|
|
@@ -95,6 +203,9 @@ export const C3NavigationAppBar = ({ app: appProps, appBar, forwardRef, navbar,
|
|
|
95
203
|
/* eslint-disable-next-line @typescript-eslint/ban-ts-comment */
|
|
96
204
|
/* @ts-ignore */
|
|
97
205
|
leaveDelayMs: 100 }, appBarOpen ? React.createElement(Close, { size: 20 }) : React.createElement(C3AppMenuIcon, { size: 20 })),
|
|
206
|
+
React.createElement(OrgPickerModal, { activeOrg: activeOrg, appToNavigateTo: appToNavigateTo, isOpen: isOrgPickerModalOpen, orgs: clustersByOrgId, onCancel: () => {
|
|
207
|
+
setIsOrgPickerModalOpen(false);
|
|
208
|
+
}, loadingStatus: loadingStatus, domain: domain }),
|
|
98
209
|
React.createElement(NavWrapper, null,
|
|
99
210
|
React.createElement(SideNav, { ref: panelRef, "aria-label": appBar.ariaLabel || "Side Navigation", expanded: appBarOpen, isPersistent: false },
|
|
100
211
|
React.createElement(SideNavItems, null,
|
package/lib/esm/src/components/c3-navigation/c3-navigation-sidebar/c3-navigation-sidebar-element.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Button } from "@carbon/react";
|
|
1
|
+
import { Button, SideNavLink } from "@carbon/react";
|
|
2
2
|
import React, { useEffect, useRef, useState } from "react";
|
|
3
3
|
import { SwitcherDivider } from "./components";
|
|
4
4
|
import styled from "styled-components";
|
|
@@ -7,6 +7,10 @@ const Row = styled.div `
|
|
|
7
7
|
justify-content: start;
|
|
8
8
|
align-items: center;
|
|
9
9
|
|
|
10
|
+
li {
|
|
11
|
+
list-style-type: none;
|
|
12
|
+
}
|
|
13
|
+
|
|
10
14
|
.cds--switcher__item:nth-child(1) {
|
|
11
15
|
margin-block-start: 0;
|
|
12
16
|
}
|
|
@@ -22,7 +26,8 @@ const Row = styled.div `
|
|
|
22
26
|
const C3NavigationSidebarElement = (props) => {
|
|
23
27
|
const sideBarElementRef = useRef(null);
|
|
24
28
|
const [isOverflown, setIsOverflown] = useState(false);
|
|
25
|
-
const { overflowMenu } = props.element;
|
|
29
|
+
const { overflowMenu, isActive, isNavLink } = props.element;
|
|
30
|
+
const Element = isNavLink ? SideNavLink : Button;
|
|
26
31
|
const handleSetIsOverflown = () => {
|
|
27
32
|
const element = sideBarElementRef.current;
|
|
28
33
|
setIsOverflown(element ? element.offsetWidth < element.scrollWidth : false);
|
|
@@ -35,7 +40,7 @@ const C3NavigationSidebarElement = (props) => {
|
|
|
35
40
|
return (React.createElement(React.Fragment, null,
|
|
36
41
|
props.element.preceedingDivider && React.createElement(SwitcherDivider, null),
|
|
37
42
|
React.createElement(Row, null,
|
|
38
|
-
React.createElement(
|
|
43
|
+
React.createElement(Element, { style: {
|
|
39
44
|
width: overflowMenu
|
|
40
45
|
? `calc(14rem - ${props.scrollBarWidth}px)`
|
|
41
46
|
: `calc(16rem - ${props.scrollBarWidth}px)`,
|
|
@@ -43,14 +48,14 @@ const C3NavigationSidebarElement = (props) => {
|
|
|
43
48
|
(!("elements" in props.sideBar) || !props.sideBar.elements)
|
|
44
49
|
? { marginTop: "1.5rem" }
|
|
45
50
|
: {}),
|
|
46
|
-
}, size: "sm", kind: props.element.kind ?? "ghost", className:
|
|
51
|
+
}, size: "sm", kind: props.element.kind ?? "ghost", className: `cds--switcher__item ${isActive ? "cds--side-nav__item--active" : ""}`, onClick: () => {
|
|
47
52
|
if (props.element.onClick) {
|
|
48
53
|
props.element.onClick();
|
|
49
54
|
}
|
|
50
55
|
if (props.sideBar.closeOnClick !== false) {
|
|
51
56
|
props.setSideBarOpen(false);
|
|
52
57
|
}
|
|
53
|
-
}, tabIndex: props.itemTabIndex, renderIcon: props.element.renderIcon },
|
|
58
|
+
}, tabIndex: props.itemTabIndex, renderIcon: props.element.renderIcon, isActive: isNavLink ? isActive : undefined },
|
|
54
59
|
React.createElement("span", { ref: sideBarElementRef, title: isOverflown ? props.element.label : "", style: {
|
|
55
60
|
overflow: "hidden",
|
|
56
61
|
textOverflow: "ellipsis",
|
package/lib/esm/src/components/c3-navigation/c3-navigation-sidebar/c3-navigation-sidebar.types.d.ts
CHANGED
|
@@ -18,6 +18,7 @@ export type CamundaOrg = {
|
|
|
18
18
|
label: string;
|
|
19
19
|
onClick: () => void;
|
|
20
20
|
};
|
|
21
|
+
teamsLabel?: string;
|
|
21
22
|
};
|
|
22
23
|
export type C3NavigationOrgSideBarProps = C3NavigationSideBarBaseProps & {
|
|
23
24
|
type: "org";
|
|
@@ -26,6 +27,8 @@ export type C3NavigationOrgSideBarProps = C3NavigationSideBarBaseProps & {
|
|
|
26
27
|
};
|
|
27
28
|
switchToOrg?: (orgId: string) => void;
|
|
28
29
|
manageOrg?: (orgId: string) => void;
|
|
30
|
+
manageSuperorg?: (orgId: string) => void;
|
|
31
|
+
manageTeam?: (orgId: string, teamId: string) => void;
|
|
29
32
|
};
|
|
30
33
|
export type C3NavigationInfoSideBarProps = C3NavigationSideBarBaseProps & {
|
|
31
34
|
type: "info";
|
package/lib/esm/src/components/c3-navigation/c3-navigation-sidebar/c3-notification-sidebar.js
CHANGED
|
@@ -43,7 +43,7 @@ const EmptyStateDescription = styled(NotificationDescription) `
|
|
|
43
43
|
`;
|
|
44
44
|
export const C3NotificationSidebar = ({ sideBar }) => {
|
|
45
45
|
const { onLinkClick } = sideBar;
|
|
46
|
-
const { notifications, markAsRead, dismiss, markAllAsRead, dismissAll, analytics, enabled, } = useContext(C3NotificationContext);
|
|
46
|
+
const { notifications, isFetching, markAsRead, dismiss, markAllAsRead, dismissAll, analytics, enabled, } = useContext(C3NotificationContext);
|
|
47
47
|
const [unreadNotifications, setUnreadNotifications] = useState([]);
|
|
48
48
|
const hasUnreadNotifications = notifications?.some(({ state }) => state === "new");
|
|
49
49
|
const { isOpen } = useNotificationSidebarState();
|
|
@@ -86,7 +86,7 @@ export const C3NotificationSidebar = ({ sideBar }) => {
|
|
|
86
86
|
...sideBar,
|
|
87
87
|
ariaLabel: sideBar.ariaLabel || "Notification Sidebar",
|
|
88
88
|
callbacks: { beforeOpening, afterClosing },
|
|
89
|
-
}, icon: hasUnreadNotifications ? (React.createElement(C3NotificationsUnreadIcon, { size: 20 })) : (React.createElement(NotificationIcon, { size: 20, "aria-label": "Notifications" })) },
|
|
89
|
+
}, icon: hasUnreadNotifications && !isFetching ? (React.createElement(C3NotificationsUnreadIcon, { size: 20 })) : (React.createElement(NotificationIcon, { size: 20, "aria-label": "Notifications" })) },
|
|
90
90
|
React.createElement(PanelHeader, null,
|
|
91
91
|
React.createElement(PanelTitle, null, "Notifications"),
|
|
92
92
|
notifications.length > 0 && (
|
|
@@ -17,6 +17,7 @@ import { useC3HelpCenter } from "../c3-help-center/c3-help-center-provider";
|
|
|
17
17
|
import { useC3Profile } from "../c3-user-configuration/c3-profile-provider/c3-profile-provider";
|
|
18
18
|
import { HelpCenterHint } from "../c3-help-center/help-center-hint";
|
|
19
19
|
import { useC3UserConfiguration } from "../c3-user-configuration/c3-user-configuration-provider";
|
|
20
|
+
import { OrgName } from "./c3-org-name";
|
|
20
21
|
import { Time, Warning } from "@carbon/react/icons";
|
|
21
22
|
/**
|
|
22
23
|
* UI SHELL
|
|
@@ -172,7 +173,7 @@ export const C3Navigation = ({ app, appBar, forwardRef, navbar, orgSideBar, info
|
|
|
172
173
|
marginTop: "0.75rem",
|
|
173
174
|
} },
|
|
174
175
|
React.createElement(Toggletip, null,
|
|
175
|
-
React.createElement(ToggletipButton, { label: buttonLabel },
|
|
176
|
+
React.createElement(ToggletipButton, { as: "span", label: buttonLabel },
|
|
176
177
|
React.createElement(Tag, { type: tag.color, renderIcon: tag.renderIcon, style: {
|
|
177
178
|
margin: "0",
|
|
178
179
|
cursor: "pointer",
|
|
@@ -192,14 +193,7 @@ export const C3Navigation = ({ app, appBar, forwardRef, navbar, orgSideBar, info
|
|
|
192
193
|
}),
|
|
193
194
|
(clusterUuid || currentClusterUuid) && (React.createElement(ClusterTagWrapper, null,
|
|
194
195
|
React.createElement(C3ClusterTag, { clusterUuid: clusterUuid || currentClusterUuid || "", conditionalRendering: options?.conditionalTagRendering }))),
|
|
195
|
-
orgName && (React.createElement(
|
|
196
|
-
fontSize: "14px",
|
|
197
|
-
lineHeight: "3rem",
|
|
198
|
-
textOverflow: "ellipsis",
|
|
199
|
-
whiteSpace: "nowrap",
|
|
200
|
-
overflow: "hidden",
|
|
201
|
-
maxWidth: "150px",
|
|
202
|
-
} }, orgName))),
|
|
196
|
+
orgName && (React.createElement(OrgName, { orgName: orgName, isSuperOrg: activeOrg?.isSuperOrg, activeSuperOrg: activeOrg?.superOrganization?.name }))),
|
|
203
197
|
actionButtons && React.createElement(C3ActionButtons, { elements: actionButtons }),
|
|
204
198
|
notificationSideBar && (React.createElement(C3NotificationSidebar, { sideBar: {
|
|
205
199
|
...notificationSideBar,
|
package/lib/esm/src/components/c3-navigation/c3-notification-provider/c3-notification-provider.d.ts
CHANGED
|
@@ -14,6 +14,7 @@ export type LinkProps = {
|
|
|
14
14
|
};
|
|
15
15
|
export type C3NotificationContextValue = {
|
|
16
16
|
enabled: boolean;
|
|
17
|
+
isFetching: boolean;
|
|
17
18
|
notifications: Notification[];
|
|
18
19
|
markAsRead: (notification: Notification) => void;
|
|
19
20
|
markAllAsRead: (notifications: Notification[]) => void;
|
package/lib/esm/src/components/c3-navigation/c3-notification-provider/c3-notification-provider.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import React, { useContext, useEffect, useState, } from "react";
|
|
1
|
+
import React, { useContext, useEffect, useRef, useState, } from "react";
|
|
2
2
|
import { NotificationService, } from "../../../api/notifications";
|
|
3
3
|
import { C3UserConfigurationContext } from "../../c3-user-configuration/c3-user-configuration-provider";
|
|
4
4
|
export const C3NotificationContext = React.createContext({
|
|
5
5
|
enabled: false,
|
|
6
|
+
isFetching: true,
|
|
6
7
|
notifications: [],
|
|
7
8
|
markAsRead: () => undefined,
|
|
8
9
|
markAllAsRead: () => undefined,
|
|
@@ -12,44 +13,46 @@ export const C3NotificationContext = React.createContext({
|
|
|
12
13
|
});
|
|
13
14
|
const C3NotificationProvider = ({ children, }) => {
|
|
14
15
|
const [notifications, setNotifications] = useState([]);
|
|
15
|
-
const [
|
|
16
|
-
const
|
|
17
|
-
const
|
|
16
|
+
const [isFetching, setIsFetching] = useState(true);
|
|
17
|
+
const isFetched = useRef(false);
|
|
18
|
+
const isEventStreamAvailable = useRef(false);
|
|
19
|
+
const activeOrganizationId = useRef("");
|
|
18
20
|
const config = useContext(C3UserConfigurationContext);
|
|
19
21
|
const enabled = !!config && !!config.activeOrganizationId && !!config.userToken;
|
|
20
22
|
// if the organization changes, we need to reset the state
|
|
21
|
-
if (enabled && config.activeOrganizationId !== activeOrganizationId) {
|
|
22
|
-
|
|
23
|
-
setFetched(false);
|
|
24
|
-
setNotifications([]);
|
|
23
|
+
if (enabled && config.activeOrganizationId !== activeOrganizationId.current) {
|
|
24
|
+
isFetched.current = false;
|
|
25
25
|
}
|
|
26
|
-
if (enabled &&
|
|
27
|
-
|
|
28
|
-
if (result)
|
|
29
|
-
setNotifications(result);
|
|
30
|
-
setFetched(true);
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
if (enabled && isFetched && !isEventStreamAvailable) {
|
|
34
|
-
setEventStreamAvailable(true);
|
|
26
|
+
if (enabled && isFetched.current && !isEventStreamAvailable.current) {
|
|
27
|
+
isEventStreamAvailable.current = true;
|
|
35
28
|
NotificationService.notificationsStream(config, (notification) => {
|
|
36
|
-
if (notification.orgId === activeOrganizationId) {
|
|
37
|
-
setNotifications(NotificationService.updateNotifications(
|
|
29
|
+
if (notification.orgId === config.activeOrganizationId) {
|
|
30
|
+
setNotifications((currentNotifications) => NotificationService.updateNotifications(currentNotifications, notification));
|
|
38
31
|
}
|
|
39
32
|
});
|
|
40
33
|
}
|
|
41
34
|
useEffect(() => {
|
|
42
|
-
|
|
43
|
-
|
|
35
|
+
const orgHasChanged = activeOrganizationId.current !== config.activeOrganizationId;
|
|
36
|
+
if (enabled && !isFetched.current && config.userToken && orgHasChanged) {
|
|
37
|
+
setIsFetching(true);
|
|
38
|
+
NotificationService.getNotifications(config)
|
|
39
|
+
.then(({ result }) => {
|
|
44
40
|
if (result)
|
|
45
41
|
setNotifications(result);
|
|
46
|
-
|
|
42
|
+
isFetched.current = true;
|
|
43
|
+
activeOrganizationId.current = config.activeOrganizationId;
|
|
44
|
+
})
|
|
45
|
+
.catch(() => {
|
|
46
|
+
// ignore
|
|
47
|
+
})
|
|
48
|
+
.finally(() => {
|
|
49
|
+
setIsFetching(false);
|
|
47
50
|
});
|
|
48
51
|
}
|
|
49
|
-
}, [config?.userToken]);
|
|
52
|
+
}, [config?.userToken, enabled, isFetched, config.activeOrganizationId]);
|
|
50
53
|
const changeState = (newState, { uuid, state }) => {
|
|
51
54
|
if (enabled && state !== newState) {
|
|
52
|
-
setNotifications(NotificationService.updateNotificationState(
|
|
55
|
+
setNotifications((currentNotifications) => NotificationService.updateNotificationState(currentNotifications, uuid, newState));
|
|
53
56
|
NotificationService.changeState(config, uuid, newState);
|
|
54
57
|
}
|
|
55
58
|
};
|
|
@@ -90,6 +93,7 @@ const C3NotificationProvider = ({ children, }) => {
|
|
|
90
93
|
dismiss,
|
|
91
94
|
dismissAll,
|
|
92
95
|
analytics,
|
|
96
|
+
isFetching,
|
|
93
97
|
} }, children));
|
|
94
98
|
};
|
|
95
99
|
export function withNotifications(Component) {
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import styled from "styled-components";
|
|
3
|
+
import { body01, label01 } from "../styles";
|
|
4
|
+
const OrgNameWrapper = styled.div `
|
|
5
|
+
display: flex;
|
|
6
|
+
padding-right: var(--12-px-075-rem-spacing-04, 12px);
|
|
7
|
+
align-items: center;
|
|
8
|
+
gap: var(--16-px-1-rem-spacing-05, 16px);
|
|
9
|
+
align-self: stretch;
|
|
10
|
+
`;
|
|
11
|
+
const SuperOrgWrapper = styled.div `
|
|
12
|
+
display: flex;
|
|
13
|
+
flex-direction: column;
|
|
14
|
+
justify-content: center;
|
|
15
|
+
align-items: flex-start;
|
|
16
|
+
gap: var(--0-px-0-rem-spacing-00, 0px);
|
|
17
|
+
`;
|
|
18
|
+
const SuperOrgName = styled.div `
|
|
19
|
+
color: var(--cds-text-secondary, #c6c6c6);
|
|
20
|
+
${label01}
|
|
21
|
+
`;
|
|
22
|
+
const TeamName = styled.div `
|
|
23
|
+
color: var(--cds-text-primary, #f4f4f4);
|
|
24
|
+
text-align: right;
|
|
25
|
+
${body01}
|
|
26
|
+
`;
|
|
27
|
+
const SingleOrgName = styled.div `
|
|
28
|
+
font-size: 14px;
|
|
29
|
+
line-height: 3rem;
|
|
30
|
+
text-overflow: ellipsis;
|
|
31
|
+
white-space: nowrap;
|
|
32
|
+
overflow: hidden;
|
|
33
|
+
max-width: 150px;
|
|
34
|
+
`;
|
|
35
|
+
export const OrgName = ({ orgName, isSuperOrg, activeSuperOrg }) => (React.createElement(OrgNameWrapper, null, activeSuperOrg && !isSuperOrg ? (React.createElement(SuperOrgWrapper, { title: orgName },
|
|
36
|
+
React.createElement(SuperOrgName, null, activeSuperOrg),
|
|
37
|
+
React.createElement(TeamName, null, orgName))) : (React.createElement(SingleOrgName, { className: "bodyText", title: orgName }, orgName))));
|
|
@@ -1,50 +1,85 @@
|
|
|
1
1
|
import React, { useState } from "react";
|
|
2
|
-
import { Button, FormLabel } from "@carbon/react";
|
|
2
|
+
import { Button, FormLabel, TreeNode, TreeView, } from "@carbon/react";
|
|
3
3
|
import { Enterprise } from "@carbon/react/icons";
|
|
4
4
|
import C3NavigationSideBar from "../c3-navigation-sidebar/c3-navigation-sidebar";
|
|
5
5
|
import { SwitcherDivider } from "../c3-navigation-sidebar/components";
|
|
6
6
|
import { useOrgSidebarState } from "../c3-navigation-sidebar/c3-sidebar-state-provider";
|
|
7
7
|
import { useC3Profile } from "../../c3-user-configuration/c3-profile-provider/c3-profile-provider";
|
|
8
8
|
import { useC3UserConfiguration } from "../../c3-user-configuration/c3-user-configuration-provider";
|
|
9
|
-
import { ActiveOrgName, ActiveOrgNameWrapper, ActiveOrgWrapper, ActiveOrgWrapperCustom, ConfirmLeaveModal, ManageOrgOverflowMenu, } from "./components";
|
|
9
|
+
import { ActiveOrgName, ActiveOrgNameWrapper, ActiveOrgWrapper, ActiveOrgWrapperCustom, ConfirmLeaveModal, ManageOrgOverflowMenu, OrgListWrapper, } from "./components";
|
|
10
10
|
import { leaveOrganization } from "../../../api/organizations";
|
|
11
|
+
function renderTree({ nodes, expanded, withIcons = false }) {
|
|
12
|
+
if (!nodes) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
return nodes.map(({ children, renderIcon, isExpanded, isDisabled, ...nodeProps }) => (React.createElement(TreeNode, { key: nodeProps.id, renderIcon: withIcons ? renderIcon : null, isExpanded: expanded ?? isExpanded, ...(isDisabled && {
|
|
16
|
+
style: { cursor: "not-allowed" },
|
|
17
|
+
"aria-disabled": "true",
|
|
18
|
+
}), ...nodeProps }, renderTree({ nodes: children, expanded, withIcons }))));
|
|
19
|
+
}
|
|
11
20
|
const C3OrgSidebar = ({ sideBar }) => {
|
|
12
|
-
const { customElements, switchToOrg, manageOrg,
|
|
21
|
+
const { customElements, switchToOrg, manageOrg, manageSuperorg, manageTeam, ...sideBarProps } = sideBar;
|
|
13
22
|
const { isOpen, setIsOpen, scrollBarWidth } = useOrgSidebarState();
|
|
14
23
|
const { userToken, decodedAudience, domain, analyticsTrack, setActiveOrgId } = useC3UserConfiguration();
|
|
15
24
|
const { activeOrg, orgs, removeOrg } = useC3Profile();
|
|
25
|
+
const superOrgs = orgs?.filter((org) => org.isSuperOrg) || [];
|
|
26
|
+
const hasSuperOrgs = superOrgs?.length;
|
|
27
|
+
const isSuperOrgAdmin = activeOrg?.isSuperOrg
|
|
28
|
+
? activeOrg?.permissions?.org?.settings?.read
|
|
29
|
+
: false;
|
|
30
|
+
const teams = activeOrg?.childOrganizations?.filter(({ uuid }) => activeOrg?.isSuperOrg &&
|
|
31
|
+
orgs?.some((org) => org.uuid === uuid) &&
|
|
32
|
+
activeOrg?.uuid !== uuid);
|
|
16
33
|
const isCustomized = Boolean(customElements?.activeOrganization);
|
|
17
34
|
const [isConfirmLeaveModalOpen, setIsConfirmLeaveModalOpen] = useState(false);
|
|
18
35
|
const [orgToLeave, setOrgToLeave] = useState(null);
|
|
19
36
|
const [loadingStatus, setLoadingStatus] = useState("inactive");
|
|
20
37
|
const isLastOrg = orgs?.length === 1;
|
|
21
|
-
const
|
|
38
|
+
const isMemberOfTeam = (orgUuid) => orgs?.some(({ uuid }) => uuid === orgUuid) || false;
|
|
39
|
+
const canUserLeaveOrg = (org) => (((isSuperOrgAdmin && isMemberOfTeam(org.uuid)) || !isSuperOrgAdmin) &&
|
|
40
|
+
!org.permissions?.org?.users?.owner?.update &&
|
|
22
41
|
orgs?.length &&
|
|
23
42
|
orgs.length > 1) ||
|
|
24
43
|
false;
|
|
25
|
-
const canUserManageOrg = (
|
|
44
|
+
const canUserManageOrg = (org) => (isSuperOrgAdmin && !org.permissions) ||
|
|
45
|
+
org.permissions?.org?.settings?.read ||
|
|
46
|
+
false;
|
|
26
47
|
const onManageCustomOrg = () => {
|
|
27
48
|
if (sideBar.closeOnClick !== false)
|
|
28
49
|
setIsOpen(false);
|
|
29
50
|
};
|
|
30
|
-
const
|
|
51
|
+
const canSwitchToOrg = (orgId) => orgs?.some((org) => org.uuid === orgId);
|
|
52
|
+
const onManageOrg = ({ uuid, isSuperOrg }) => {
|
|
31
53
|
setIsOpen(false);
|
|
32
54
|
analyticsTrack?.("navBar:orgSettings:click", { orgId: activeOrg?.uuid });
|
|
33
|
-
|
|
34
|
-
|
|
55
|
+
const isTeam = teams?.some((team) => team.uuid === uuid);
|
|
56
|
+
if (isSuperOrg && manageSuperorg) {
|
|
57
|
+
manageSuperorg(uuid);
|
|
58
|
+
}
|
|
59
|
+
else if (isSuperOrgAdmin && isTeam && manageTeam && activeOrg) {
|
|
60
|
+
manageTeam(activeOrg?.uuid, uuid);
|
|
61
|
+
}
|
|
62
|
+
else if (manageOrg) {
|
|
63
|
+
manageOrg(uuid);
|
|
35
64
|
}
|
|
36
65
|
else {
|
|
37
|
-
window.location.href =
|
|
66
|
+
window.location.href =
|
|
67
|
+
isSuperOrgAdmin &&
|
|
68
|
+
!isSuperOrg &&
|
|
69
|
+
teams?.some((team) => team.uuid === uuid)
|
|
70
|
+
? `https://console.${domain}/org/${activeOrg?.uuid}/team/${uuid}`
|
|
71
|
+
: `https://console.${domain}/org/${uuid}/${isSuperOrg ? "team" : "management"}`;
|
|
38
72
|
}
|
|
39
73
|
};
|
|
40
74
|
const activeOrganization = customElements?.activeOrganization || {
|
|
41
|
-
activeLabel: "Active organization",
|
|
75
|
+
activeLabel: hasSuperOrgs ? "Organization" : "Active organization",
|
|
42
76
|
orgName: activeOrg?.name || "",
|
|
43
77
|
action: {
|
|
44
78
|
onClick: onManageCustomOrg,
|
|
45
79
|
label: "Manage",
|
|
46
80
|
},
|
|
47
|
-
otherLabel: "
|
|
81
|
+
otherLabel: "",
|
|
82
|
+
teamsLabel: "Teams",
|
|
48
83
|
};
|
|
49
84
|
const switchOrg = (orgId, track) => {
|
|
50
85
|
setActiveOrgId(orgId);
|
|
@@ -53,17 +88,6 @@ const C3OrgSidebar = ({ sideBar }) => {
|
|
|
53
88
|
switchToOrg?.(orgId);
|
|
54
89
|
setIsOpen(false);
|
|
55
90
|
};
|
|
56
|
-
const orgElements = orgs
|
|
57
|
-
?.filter(({ uuid }) => uuid !== activeOrg?.uuid)
|
|
58
|
-
.map((org) => ({
|
|
59
|
-
key: org.uuid,
|
|
60
|
-
label: org.name,
|
|
61
|
-
onClick: () => switchOrg(org.uuid, true),
|
|
62
|
-
overflowMenu: (React.createElement(ManageOrgOverflowMenu, { canManage: canUserManageOrg(org.permissions), canLeave: canUserLeaveOrg(org.permissions), isLastOrg: isLastOrg, orgName: org.name, onManage: () => onManageOrg(org.uuid), onRequestToLeave: () => {
|
|
63
|
-
setOrgToLeave(org);
|
|
64
|
-
setIsConfirmLeaveModalOpen(true);
|
|
65
|
-
} })),
|
|
66
|
-
}));
|
|
67
91
|
const confirmLeaveOrg = async () => {
|
|
68
92
|
if (orgToLeave && decodedAudience && userToken) {
|
|
69
93
|
setLoadingStatus("active");
|
|
@@ -85,9 +109,27 @@ const C3OrgSidebar = ({ sideBar }) => {
|
|
|
85
109
|
}
|
|
86
110
|
};
|
|
87
111
|
const itemTabIndex = isOpen ? undefined : -1;
|
|
112
|
+
const groupedBySuperOrg = orgs?.reduce((acc, curr) => {
|
|
113
|
+
const superOrgUuid = curr?.superOrganization?.uuid;
|
|
114
|
+
if (!superOrgUuid) {
|
|
115
|
+
acc[curr.uuid] = acc[curr.uuid] ?? {
|
|
116
|
+
...curr,
|
|
117
|
+
childOrganizations: {},
|
|
118
|
+
};
|
|
119
|
+
return acc;
|
|
120
|
+
}
|
|
121
|
+
acc[superOrgUuid] = acc[superOrgUuid] ?? {
|
|
122
|
+
...curr?.superOrganization,
|
|
123
|
+
childOrganizations: {},
|
|
124
|
+
};
|
|
125
|
+
acc[superOrgUuid].childOrganizations[curr.uuid] = {
|
|
126
|
+
...curr,
|
|
127
|
+
childOrganizations: {},
|
|
128
|
+
};
|
|
129
|
+
return acc;
|
|
130
|
+
}, {}) ?? {};
|
|
88
131
|
return (React.createElement(C3NavigationSideBar, { sideBar: {
|
|
89
132
|
...sideBarProps,
|
|
90
|
-
elements: elements || orgElements,
|
|
91
133
|
ariaLabel: sideBarProps.ariaLabel || "Organization Sidebars",
|
|
92
134
|
}, icon: React.createElement(Enterprise, { size: 20 }) }, (customElements?.activeOrganization || activeOrg) && (React.createElement(React.Fragment, null,
|
|
93
135
|
isCustomized ? (React.createElement(ActiveOrgWrapperCustom, null,
|
|
@@ -105,7 +147,7 @@ const C3OrgSidebar = ({ sideBar }) => {
|
|
|
105
147
|
React.createElement(FormLabel, null, activeOrganization.activeLabel),
|
|
106
148
|
React.createElement(ActiveOrgNameWrapper, null,
|
|
107
149
|
React.createElement(ActiveOrgName, { className: "textPrimary", title: activeOrganization.orgName }, activeOrganization.orgName),
|
|
108
|
-
activeOrg?.uuid && domain && (React.createElement(ManageOrgOverflowMenu, { canManage: canUserManageOrg(activeOrg
|
|
150
|
+
activeOrg?.uuid && domain && (React.createElement(ManageOrgOverflowMenu, { canManage: canUserManageOrg(activeOrg), canLeave: canUserLeaveOrg(activeOrg), isLastOrg: isLastOrg, isOrgMember: isMemberOfTeam(activeOrg.uuid), orgName: activeOrg.name, onManage: () => onManageOrg(activeOrg), onRequestToLeave: () => {
|
|
109
151
|
setOrgToLeave(activeOrg);
|
|
110
152
|
setIsConfirmLeaveModalOpen(true);
|
|
111
153
|
} }))),
|
|
@@ -113,13 +155,41 @@ const C3OrgSidebar = ({ sideBar }) => {
|
|
|
113
155
|
setIsConfirmLeaveModalOpen(false);
|
|
114
156
|
setOrgToLeave(null);
|
|
115
157
|
}, loadingStatus: loadingStatus }))),
|
|
116
|
-
(
|
|
117
|
-
|
|
118
|
-
React.createElement(
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
158
|
+
React.createElement(SwitcherDivider, null),
|
|
159
|
+
React.createElement(OrgListWrapper, null,
|
|
160
|
+
React.createElement(TreeView, { label: "All organizations", ...(activeOrg?.uuid ? { selected: [activeOrg?.uuid] } : {}) }, renderTree({
|
|
161
|
+
nodes: Object.values(groupedBySuperOrg)?.map((element) => {
|
|
162
|
+
const userTeams = Object.values(element?.childOrganizations)?.filter((child) => isMemberOfTeam(child.uuid));
|
|
163
|
+
const defaultIsExpanded = (activeOrg?.uuid === element.uuid ||
|
|
164
|
+
!isMemberOfTeam(element.uuid) ||
|
|
165
|
+
userTeams?.some((team) => activeOrg?.uuid === team.uuid)) ??
|
|
166
|
+
false;
|
|
167
|
+
return {
|
|
168
|
+
children: userTeams?.length > 0
|
|
169
|
+
? userTeams.map((team) => {
|
|
170
|
+
return {
|
|
171
|
+
id: team.uuid,
|
|
172
|
+
label: team.name,
|
|
173
|
+
isExpanded: false,
|
|
174
|
+
value: team.uuid,
|
|
175
|
+
renderIcon: null,
|
|
176
|
+
onSelect: canSwitchToOrg(team.uuid)
|
|
177
|
+
? () => switchOrg(team.uuid, true)
|
|
178
|
+
: undefined,
|
|
179
|
+
};
|
|
180
|
+
})
|
|
181
|
+
: null,
|
|
182
|
+
id: element.uuid,
|
|
183
|
+
label: element.name,
|
|
184
|
+
value: element.uuid,
|
|
185
|
+
renderIcon: null,
|
|
186
|
+
isExpanded: defaultIsExpanded,
|
|
187
|
+
isDisabled: !isMemberOfTeam(element.uuid),
|
|
188
|
+
onSelect: canSwitchToOrg(element.uuid)
|
|
189
|
+
? () => switchOrg(element.uuid, true)
|
|
190
|
+
: undefined,
|
|
191
|
+
};
|
|
192
|
+
}),
|
|
193
|
+
})))))));
|
|
124
194
|
};
|
|
125
195
|
export default C3OrgSidebar;
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import React, { FC } from "react";
|
|
2
2
|
import { ModalProps } from "@carbon/react/es/components/Modal/Modal";
|
|
3
|
+
import { DropdownProps } from "@carbon/react/lib/components/Dropdown/Dropdown";
|
|
4
|
+
import { Organization } from "../../../utils/camunda.types";
|
|
3
5
|
export declare const ActiveOrgWrapperCustom: import("styled-components").IStyledComponent<"web", import("styled-components/dist/types").FastOmit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never>>;
|
|
4
6
|
export declare const ActiveOrgWrapper: import("styled-components").IStyledComponent<"web", import("styled-components/dist/types").Substitute<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, {
|
|
5
7
|
$scrollBarWidth: number;
|
|
6
8
|
}>>;
|
|
9
|
+
export declare const ActiveSuperorgWrapper: import("styled-components").IStyledComponent<"web", import("styled-components/dist/types").Substitute<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, {
|
|
10
|
+
$scrollBarWidth: number;
|
|
11
|
+
}>>;
|
|
7
12
|
export declare const ActiveOrgNameWrapper: import("styled-components").IStyledComponent<"web", import("styled-components/dist/types").FastOmit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never>>;
|
|
8
13
|
export declare const ActiveOrgName: import("styled-components").IStyledComponent<"web", import("styled-components/dist/types").Substitute<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, {
|
|
9
14
|
$isCustomized?: boolean | undefined;
|
|
@@ -19,7 +24,12 @@ export declare const ManageOrgOverflowMenu: FC<{
|
|
|
19
24
|
canManage: boolean;
|
|
20
25
|
canLeave: boolean;
|
|
21
26
|
isLastOrg?: boolean;
|
|
27
|
+
isOrgMember: boolean;
|
|
22
28
|
orgName: string;
|
|
23
29
|
onManage: () => void;
|
|
24
30
|
onRequestToLeave: () => void;
|
|
25
31
|
}>;
|
|
32
|
+
export declare const StyledDropdown: FC<DropdownProps<Organization> & {
|
|
33
|
+
$scrollBarWidth: number;
|
|
34
|
+
}>;
|
|
35
|
+
export declare const OrgListWrapper: import("styled-components").IStyledComponent<"web", import("styled-components/dist/types").FastOmit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never>>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import { Modal, OverflowMenu, OverflowMenuItem, Popover, PopoverContent, } from "@carbon/react";
|
|
2
|
+
import { Dropdown, Modal, OverflowMenu, OverflowMenuItem, Popover, PopoverContent, } from "@carbon/react";
|
|
3
3
|
import styled from "styled-components";
|
|
4
4
|
export const ActiveOrgWrapperCustom = styled.div `
|
|
5
5
|
padding: 1.5rem 1rem 15px;
|
|
@@ -11,6 +11,16 @@ export const ActiveOrgWrapper = styled.div `
|
|
|
11
11
|
padding: 15px 0 0.5rem 1rem;
|
|
12
12
|
width: ${({ $scrollBarWidth }) => `calc(16rem - ${$scrollBarWidth}px)`};
|
|
13
13
|
`;
|
|
14
|
+
export const ActiveSuperorgWrapper = styled.div `
|
|
15
|
+
padding: 24px 0 0.5rem 0;
|
|
16
|
+
width: ${({ $scrollBarWidth }) => `calc(16rem - ${$scrollBarWidth}px)`};
|
|
17
|
+
display: flex;
|
|
18
|
+
flex-direction: column;
|
|
19
|
+
|
|
20
|
+
label {
|
|
21
|
+
padding-left: 1rem;
|
|
22
|
+
}
|
|
23
|
+
`;
|
|
14
24
|
export const ActiveOrgNameWrapper = styled.div `
|
|
15
25
|
display: flex;
|
|
16
26
|
align-items: center;
|
|
@@ -47,18 +57,34 @@ const StyledOverflowMenu = styled(OverflowMenu) `
|
|
|
47
57
|
width: 165px;
|
|
48
58
|
}
|
|
49
59
|
`;
|
|
50
|
-
export const ManageOrgOverflowMenu = ({ canManage, canLeave, isLastOrg, onManage, onRequestToLeave, orgName, }) => {
|
|
60
|
+
export const ManageOrgOverflowMenu = ({ canManage, canLeave, isLastOrg, isOrgMember, onManage, onRequestToLeave, orgName, }) => {
|
|
51
61
|
const [isPopoverShown, setIsPopoverShown] = React.useState(false);
|
|
52
62
|
const leaveMenuItem = (React.createElement(OverflowMenuItem, { itemText: "Leave organization", disabled: !canLeave, onClick: onRequestToLeave, onMouseOver: () => setIsPopoverShown(true), onMouseLeave: () => setIsPopoverShown(false), "aria-label": `Leave organization - ${orgName}` }));
|
|
53
|
-
const popoverLabel =
|
|
54
|
-
? "You
|
|
55
|
-
:
|
|
63
|
+
const popoverLabel = !isOrgMember
|
|
64
|
+
? "You are not a member of this team. You can only leave teams you are a member of."
|
|
65
|
+
: isLastOrg
|
|
66
|
+
? "You need to be a member of at least one organization. Join a different organization first if you wish to leave."
|
|
67
|
+
: "Owners cannot leave an organization directly. Assign another user as an owner if you wish to leave.";
|
|
56
68
|
return (React.createElement(StyledOverflowMenu, { size: "sm", flipped: true, "aria-label": "Organization options menu",
|
|
57
69
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
58
|
-
// @ts-expect-error
|
|
59
70
|
align: "top-right", "data-floating-menu-container": "cds--header-panel" },
|
|
60
71
|
canManage && (React.createElement(OverflowMenuItem, { itemText: "Manage organization", onClick: onManage, "aria-label": `Manage organization - ${orgName}` })),
|
|
61
72
|
canLeave ? (leaveMenuItem) : (React.createElement(PopoverWrapper, { open: isPopoverShown, label: popoverLabel, align: "bottom-right" },
|
|
62
73
|
leaveMenuItem,
|
|
63
74
|
React.createElement(PopoverContent, null, popoverLabel)))));
|
|
64
75
|
};
|
|
76
|
+
export const StyledDropdown = styled(Dropdown) `
|
|
77
|
+
grid-gap: 0;
|
|
78
|
+
|
|
79
|
+
div {
|
|
80
|
+
max-width: calc(173px - ${({ $scrollBarWidth }) => $scrollBarWidth || 0}px);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.cds--list-box__menu {
|
|
84
|
+
max-inline-size: none;
|
|
85
|
+
min-inline-size: unset;
|
|
86
|
+
}
|
|
87
|
+
`;
|
|
88
|
+
export const OrgListWrapper = styled.div `
|
|
89
|
+
padding: 1rem 1rem 15px;
|
|
90
|
+
`;
|
package/lib/esm/src/components/c3-user-configuration/c3-profile-provider/c3-profile-provider.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React, { FC, PropsWithChildren } from "react";
|
|
2
2
|
import { ResolvedTheme } from "./carbon-theme-provider";
|
|
3
3
|
import { Cluster, Organization } from "../../../utils/camunda.types";
|
|
4
|
+
import { RequestResponse } from "../../../api/api";
|
|
4
5
|
export type Theme = "light" | "dark" | "system";
|
|
5
6
|
export declare const defaultTheme: "light";
|
|
6
7
|
export type C3ProfileContextValue = {
|
|
@@ -14,6 +15,7 @@ export type C3ProfileContextValue = {
|
|
|
14
15
|
reloadClusters: () => void;
|
|
15
16
|
resolvedTheme: ResolvedTheme;
|
|
16
17
|
onThemeChange: (newTheme: Theme) => void;
|
|
18
|
+
loadClustersById: (orgId: string) => Promise<RequestResponse<Cluster[] | null>> | undefined;
|
|
17
19
|
};
|
|
18
20
|
export declare const C3ProfileContext: React.Context<C3ProfileContextValue>;
|
|
19
21
|
export declare const C3ProfileProvider: FC<PropsWithChildren>;
|
package/lib/esm/src/components/c3-user-configuration/c3-profile-provider/c3-profile-provider.js
CHANGED
|
@@ -14,6 +14,7 @@ export const C3ProfileContext = createContext({
|
|
|
14
14
|
reloadClusters: () => undefined,
|
|
15
15
|
resolvedTheme: defaultTheme,
|
|
16
16
|
onThemeChange: () => undefined,
|
|
17
|
+
loadClustersById: () => undefined,
|
|
17
18
|
});
|
|
18
19
|
export const C3ProfileProvider = ({ children }) => {
|
|
19
20
|
const { decodedToken, decodedAudience, analyticsTrack, ...config } = useContext(C3UserConfigurationContext);
|
|
@@ -26,10 +27,15 @@ export const C3ProfileProvider = ({ children }) => {
|
|
|
26
27
|
const isConfigValid = !!(config.userToken &&
|
|
27
28
|
config.activeOrganizationId &&
|
|
28
29
|
decodedAudience);
|
|
30
|
+
const loadClustersById = (orgId) => {
|
|
31
|
+
if (!isConfigValid)
|
|
32
|
+
return;
|
|
33
|
+
return getClusters(decodedAudience, config.userToken, orgId);
|
|
34
|
+
};
|
|
29
35
|
const loadClusters = () => {
|
|
30
36
|
if (!isConfigValid)
|
|
31
37
|
return;
|
|
32
|
-
|
|
38
|
+
loadClustersById(config.activeOrganizationId)?.then(({ result }) => {
|
|
33
39
|
if (result)
|
|
34
40
|
setClusters(result);
|
|
35
41
|
});
|
|
@@ -39,7 +45,8 @@ export const C3ProfileProvider = ({ children }) => {
|
|
|
39
45
|
return;
|
|
40
46
|
getOrgs(decodedAudience, config.userToken).then(({ result: res }) => {
|
|
41
47
|
setOrgs(res?.sort(({ name: a }, { name: b }) => a.localeCompare(b)) || null);
|
|
42
|
-
|
|
48
|
+
const newActiveOrg = res?.find((org) => org.uuid === config.activeOrganizationId) || null;
|
|
49
|
+
setActiveOrg(newActiveOrg);
|
|
43
50
|
});
|
|
44
51
|
};
|
|
45
52
|
const removeOrg = (orgId) => {
|
|
@@ -58,11 +65,7 @@ export const C3ProfileProvider = ({ children }) => {
|
|
|
58
65
|
}
|
|
59
66
|
}
|
|
60
67
|
loadClusters();
|
|
61
|
-
}, [
|
|
62
|
-
config?.activeOrganizationId,
|
|
63
|
-
JSON.stringify(decodedToken),
|
|
64
|
-
decodedAudience,
|
|
65
|
-
]);
|
|
68
|
+
}, [config?.activeOrganizationId, decodedToken, decodedAudience]);
|
|
66
69
|
useEffect(() => {
|
|
67
70
|
const updateSystemTheme = ({ matches }) => {
|
|
68
71
|
if (themeRef.current === "system")
|
|
@@ -108,6 +111,7 @@ export const C3ProfileProvider = ({ children }) => {
|
|
|
108
111
|
reloadClusters: loadClusters,
|
|
109
112
|
removeOrg,
|
|
110
113
|
onThemeChange,
|
|
114
|
+
loadClustersById,
|
|
111
115
|
} }, content));
|
|
112
116
|
};
|
|
113
117
|
export const useC3Profile = () => useContext(C3ProfileContext);
|
|
@@ -9,5 +9,6 @@ export declare const body02: import("styled-components").RuleSet<object>;
|
|
|
9
9
|
export declare const Body02: import("styled-components").IStyledComponent<"web", import("styled-components/dist/types").FastOmit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>, never>>;
|
|
10
10
|
export declare const body01: import("styled-components").RuleSet<object>;
|
|
11
11
|
export declare const Body01: import("styled-components").IStyledComponent<"web", import("styled-components/dist/types").FastOmit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>, never>>;
|
|
12
|
+
export declare const label01: import("styled-components").RuleSet<object>;
|
|
12
13
|
export declare const fontMapping: import("styled-components").RuleSet<object>;
|
|
13
14
|
export declare const DefaultStyleWrapper: import("styled-components").IStyledComponent<"web", import("styled-components/dist/types").FastOmit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never>>;
|
|
@@ -53,6 +53,12 @@ export const body01 = css `
|
|
|
53
53
|
export const Body01 = styled.p `
|
|
54
54
|
${body01}
|
|
55
55
|
`;
|
|
56
|
+
export const label01 = css `
|
|
57
|
+
font-size: var(--cds-label-01-font-size);
|
|
58
|
+
font-weight: var(--cds-label-01-font-weight);
|
|
59
|
+
letter-spacing: var(--cds-label-01-letter-spacing);
|
|
60
|
+
line-height: var(--cds-label-01-line-height);
|
|
61
|
+
`;
|
|
56
62
|
export const fontMapping = css `
|
|
57
63
|
h1 {
|
|
58
64
|
${heading04}
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"type": "git",
|
|
5
5
|
"url": "git+https://github.com/camunda-cloud/camunda-composite-components.git"
|
|
6
6
|
},
|
|
7
|
-
"version": "0.
|
|
7
|
+
"version": "0.12.0",
|
|
8
8
|
"scripts": {
|
|
9
9
|
"clean": "rimraf lib/",
|
|
10
10
|
"build": "yarn clean && tsc",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"@babel/preset-env": "7.25.7",
|
|
35
35
|
"@babel/preset-react": "7.25.7",
|
|
36
36
|
"@babel/preset-typescript": "7.25.7",
|
|
37
|
-
"@carbon/react": "1.
|
|
37
|
+
"@carbon/react": "1.66.0",
|
|
38
38
|
"@mdx-js/react": "3.0.1",
|
|
39
39
|
"@playwright/test": "1.45.2",
|
|
40
40
|
"@semantic-release/changelog": "6.0.3",
|