@camunda/camunda-composite-components 0.23.1 → 0.23.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 (28) hide show
  1. package/lib/esm/package.json +1 -1
  2. package/lib/esm/src/api/endpoints.const.js +2 -2
  3. package/lib/esm/src/components/c3-navigation/c3-navigation-appbar/c3-navigation-appbar.js +8 -4
  4. package/lib/esm/src/components/c3-navigation/c3-navigation-sidebar/c3-navigation-sidebar.js +8 -4
  5. package/lib/esm/src/components/c3-navigation/c3-notification-provider/c3-notification-container.d.ts +1 -1
  6. package/lib/esm/src/components/c3-navigation/c3-notification-provider/c3-notification-container.js +16 -11
  7. package/lib/esm/src/components/c3-navigation-v2/c3-breadcrumb-bar.js +10 -10
  8. package/lib/esm/src/components/c3-navigation-v2/c3-navigation-v2.js +10 -6
  9. package/lib/esm/src/components/c3-navigation-v2/c3-navigation-v2.types.d.ts +5 -1
  10. package/lib/esm/src/components/c3-navigation-v2/c3-sidebar.js +62 -25
  11. package/lib/esm/src/components/c3-navigation-v2/c3-tools-area.js +8 -2
  12. package/lib/esm/src/components/c3-navigation-v2/index.d.ts +2 -0
  13. package/lib/esm/src/components/c3-navigation-v2/index.js +1 -0
  14. package/lib/esm/src/components/c3-navigation-v2/stories/story-templates.d.ts +3 -0
  15. package/lib/esm/src/components/c3-navigation-v2/stories/story-templates.js +587 -2
  16. package/lib/esm/src/components/c3-navigation-v2/tools/c3-info-panel.js +2 -2
  17. package/lib/esm/src/components/c3-navigation-v2/tools/c3-notifications-panel.js +16 -11
  18. package/lib/esm/src/components/c3-navigation-v2/tools/c3-user-panel.js +12 -12
  19. package/lib/esm/src/components/c3-navigation-v2/tools/panel-primitives.js +1 -1
  20. package/lib/esm/src/components/c3-navigation-v2/use-camunda-tools.js +2 -2
  21. package/lib/esm/src/components/c3-navigation-v2/use-cluster-sidebar-entries.d.ts +58 -0
  22. package/lib/esm/src/components/c3-navigation-v2/use-cluster-sidebar-entries.js +84 -0
  23. package/lib/esm/src/components/c3-navigation-v2/use-cluster-webapp-breadcrumbs.js +7 -24
  24. package/lib/esm/src/index.d.ts +4 -2
  25. package/lib/esm/src/index.js +2 -1
  26. package/lib/esm/src/utils/camunda.d.ts +16 -0
  27. package/lib/esm/src/utils/camunda.js +29 -6
  28. package/package.json +2 -2
@@ -5,7 +5,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
5
5
  * You may not use this file except in compliance with the commercial license.
6
6
  */
7
7
  import { Button } from '@carbon/react';
8
- import { useContext, useEffect } from 'react';
8
+ import { useContext, useEffect, useState } from 'react';
9
9
  import styled from 'styled-components';
10
10
  import { C3BellIcon } from '../../../assets/c3-icons.js';
11
11
  import C3NotificationContainer, { NotificationDescription, NotificationTitle, } from '../../c3-navigation/c3-notification-provider/c3-notification-container.js';
@@ -18,26 +18,28 @@ const DismissAllButton = styled(Button) `
18
18
  letter-spacing: var(--cds-helper-text-01-letter-spacing);
19
19
  `;
20
20
  const EmptyState = styled.div `
21
- padding: 16px;
21
+ padding: var(--cds-spacing-05);
22
22
  `;
23
23
  const EmptyStateTitle = styled(NotificationTitle) `
24
- margin-top: 24px;
24
+ margin-top: var(--cds-spacing-06);
25
25
  `;
26
26
  const EmptyStateDescription = styled(NotificationDescription) `
27
27
  color: var(--cds-text-secondary);
28
- margin-top: 8px;
28
+ margin-top: var(--cds-spacing-03);
29
29
  `;
30
30
  const sortDescending = (a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime();
31
31
  export const C3NotificationsPanel = ({ onLinkClick, }) => {
32
- const { notifications, markAllAsRead, dismiss, dismissAll } = useContext(C3NotificationContext);
33
- // C3NotificationContainer ignores its onRead prop; read-state is driven by
34
- // panel open/close instead. Since the panel only mounts while its tool is
35
- // active, one mount-time pass is enough.
32
+ const { enabled, notifications, markAllAsRead, dismiss, dismissAll, analytics, } = useContext(C3NotificationContext);
33
+ // Snapshot uuids that were unread when the panel opened so "new" dots
34
+ // persist until the panel closes, even after mark-as-read runs.
35
+ const [unreadAtOpen] = useState(() => new Set(notifications.filter((n) => n.state === 'new').map((n) => n.uuid)));
36
36
  // biome-ignore lint/correctness/useExhaustiveDependencies: intentional mount-only
37
37
  useEffect(() => {
38
38
  const unread = notifications.filter((n) => n.state === 'new');
39
39
  if (unread.length)
40
40
  markAllAsRead(unread);
41
+ if (enabled)
42
+ analytics('notification-panel-opened');
41
43
  }, []);
42
44
  return (_jsxs(_Fragment, { children: [_jsxs(PanelHeader, { style: {
43
45
  width: '100%',
@@ -46,7 +48,10 @@ export const C3NotificationsPanel = ({ onLinkClick, }) => {
46
48
  flexDirection: 'row',
47
49
  justifyContent: 'space-between',
48
50
  alignItems: 'center',
49
- }, children: [_jsx(PanelTitle, { children: "Notifications" }), notifications.length > 0 && (_jsx(DismissAllButton, { kind: 'ghost', size: 'sm', onClick: () => dismissAll(notifications), children: "Dismiss all" }))] }), notifications.length > 0 ? ([...notifications]
50
- .sort(sortDescending)
51
- .map((notification) => (_jsx(C3NotificationContainer, { onRead: () => undefined, onDismiss: () => dismiss(notification), originalOnLinkClick: onLinkClick, onLinkClick: () => onLinkClick?.(notification.meta), unread: false, ...notification }, notification.uuid)))) : (_jsxs(EmptyState, { children: [_jsx(C3BellIcon, { size: 56 }), _jsx(EmptyStateTitle, { children: "No notifications" }), _jsx(EmptyStateDescription, { children: "New updates regarding your processes, clusters and more will appear here." })] }))] }));
51
+ }, children: [_jsx(PanelTitle, { children: "Notifications" }), notifications.length > 0 && (_jsx(DismissAllButton, { kind: 'ghost', size: 'sm', onClick: () => dismissAll(notifications), children: "Dismiss all" }))] }), notifications.length > 0 ? ([...notifications].sort(sortDescending).map((notification) => (_jsx(C3NotificationContainer, { onRead: () => undefined, onDismiss: () => dismiss(notification), originalOnLinkClick: onLinkClick, onLinkClick: () => {
52
+ if (enabled) {
53
+ analytics('notification-clicked-cta', notification.meta?.identifier);
54
+ }
55
+ onLinkClick?.(notification.meta);
56
+ }, unread: unreadAtOpen.has(notification.uuid), ...notification }, notification.uuid)))) : (_jsxs(EmptyState, { children: [_jsx(C3BellIcon, { size: 56 }), _jsx(EmptyStateTitle, { children: "No notifications" }), _jsx(EmptyStateDescription, { children: "New updates regarding your processes, clusters and more will appear here." })] }))] }));
52
57
  };
@@ -11,11 +11,11 @@ import { useC3Profile, } from '../../c3-user-configuration/c3-profile-provider/c
11
11
  import { useC3UserConfiguration } from '../../c3-user-configuration/c3-user-configuration-provider.js';
12
12
  import { PanelHeader, PanelTitle } from './panel-primitives.js';
13
13
  const ProfileSection = styled.div `
14
- padding: 1rem;
14
+ padding: var(--cds-spacing-05);
15
15
  display: flex;
16
16
  flex-direction: column;
17
- gap: 2px;
18
- border-bottom: 1px solid var(--cds-border-subtle);
17
+ gap: var(--cds-spacing-01);
18
+ border-bottom: 1px solid var(--cds-border-subtle-01);
19
19
  `;
20
20
  const ProfileName = styled.span `
21
21
  font-size: var(--cds-body-01-font-size);
@@ -27,21 +27,21 @@ const ProfileEmail = styled.span `
27
27
  color: var(--cds-text-secondary);
28
28
  `;
29
29
  const ThemeSection = styled.div `
30
- padding: 1rem;
31
- border-bottom: 1px solid var(--cds-border-subtle);
30
+ padding: var(--cds-spacing-05);
31
+ border-bottom: 1px solid var(--cds-border-subtle-01);
32
32
  `;
33
33
  const CustomSection = styled.div `
34
- border-bottom: 1px solid var(--cds-border-subtle);
34
+ border-bottom: 1px solid var(--cds-border-subtle-01);
35
35
  `;
36
36
  const ElementList = styled.ul `
37
37
  list-style: none;
38
38
  margin: 0;
39
- padding: 0.5rem 0;
39
+ padding: var(--cds-spacing-03) 0;
40
40
  `;
41
41
  const ElementItem = styled.li ``;
42
42
  const ElementButton = styled.button `
43
43
  width: 100%;
44
- padding: 0.625rem 1rem;
44
+ padding: 0.625rem var(--cds-spacing-05);
45
45
  background: transparent;
46
46
  border: none;
47
47
  cursor: pointer;
@@ -68,7 +68,7 @@ const BottomPinned = styled.div `
68
68
  flex-direction: column;
69
69
  `;
70
70
  const BottomActions = styled.div `
71
- border-top: 1px solid var(--cds-border-subtle);
71
+ border-top: 1px solid var(--cds-border-subtle-01);
72
72
  display: flex;
73
73
  flex-direction: column;
74
74
 
@@ -79,7 +79,7 @@ const BottomActions = styled.div `
79
79
  }
80
80
  `;
81
81
  const VersionWrapper = styled.div `
82
- padding: 0.5rem 1rem;
82
+ padding: var(--cds-spacing-03) var(--cds-spacing-05);
83
83
  `;
84
84
  const VersionText = styled.p `
85
85
  color: var(--cds-text-primary);
@@ -119,7 +119,7 @@ export const C3UserPanel = ({ name, email, version, onLogout, stageToggle, custo
119
119
  const defaults = LEGAL_LINKS.filter((l) => !consumerKeys.has(l.key));
120
120
  const allElements = [...(elements ?? []), ...defaults];
121
121
  return (_jsxs(_Fragment, { children: [_jsx(PanelHeader, { children: _jsx(PanelTitle, { children: "Account" }) }), (name || email) && (_jsxs(ProfileSection, { children: [name && _jsx(ProfileName, { children: name }), email && _jsx(ProfileEmail, { children: email })] })), customSection && _jsx(CustomSection, { children: customSection }), handleTheme && (_jsx(ThemeSection, { children: _jsxs(Stack, { gap: 3, children: [_jsx(FormLabel, { children: "Theme" }), _jsxs(RadioButtonGroup, { name: 'theme', valueSelected: theme, onChange: (value) => onThemeChange(value), orientation: 'vertical', children: [_jsx(RadioButton, { value: 'light', labelText: 'Light', id: 'theme-light' }), _jsx(RadioButton, { value: 'dark', labelText: 'Dark', id: 'theme-dark' }), _jsx(RadioButton, { value: 'system', labelText: 'System', id: 'theme-system' })] })] }) })), stageToggle && (_jsx("div", { style: {
122
- padding: '1rem',
123
- borderBottom: '1px solid var(--cds-border-subtle)',
122
+ padding: 'var(--cds-spacing-05)',
123
+ borderBottom: '1px solid var(--cds-border-subtle-01)',
124
124
  }, children: _jsx(Toggle, { id: 'stage-toggle', labelText: 'Production features', toggled: stageToggle.prodFeaturesEnabled, onToggle: stageToggle.toggle, size: 'sm' }) })), allElements.length > 0 && (_jsx(ElementList, { children: allElements.map((element) => (_jsx(ElementItem, { children: _jsx(ElementButton, { type: 'button', onClick: element.onClick, children: element.label }) }, element.key))) })), _jsxs(BottomPinned, { children: [version && (_jsxs(VersionWrapper, { children: [_jsx(VersionText, { children: version }), _jsxs(Copyright, { children: ["\u00A9 Camunda Services GmbH ", new Date().getFullYear()] })] })), onLogout && (_jsx(BottomActions, { children: _jsx(Button, { kind: 'ghost', size: 'lg', onClick: onLogout, renderIcon: ArrowRight, children: "Log out" }) }))] })] }));
125
125
  };
@@ -2,7 +2,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  const headerStyle = {
3
3
  background: 'var(--cds-layer-01)',
4
4
  boxShadow: 'inset 0 -1px 0 var(--cds-border-subtle-01)',
5
- padding: '24px 16px 13px',
5
+ padding: 'var(--cds-spacing-06) var(--cds-spacing-05) 13px',
6
6
  flexShrink: 0,
7
7
  };
8
8
  const titleStyle = {
@@ -20,8 +20,8 @@ import { C3UserPanel } from './tools/c3-user-panel.js';
20
20
  * Must be called within a C3UserConfigurationProvider tree (SaaS).
21
21
  */
22
22
  export const useCamundaTools = (options) => {
23
- const { notifications: notifCtx } = useContext(C3NotificationContext);
24
- const hasUnread = notifCtx?.some((n) => n.state === 'new');
23
+ const { notifications, isFetching } = useContext(C3NotificationContext);
24
+ const hasUnread = !isFetching && notifications.some((n) => n.state === 'new');
25
25
  return useMemo(() => {
26
26
  const tools = [];
27
27
  if (options.notifications !== undefined) {
@@ -0,0 +1,58 @@
1
+ import { type ReactNode } from 'react';
2
+ import type { CamundaApp, Cluster } from '../../utils/camunda.types';
3
+ import type { LinkProps } from './c3-navigation-v2.types';
4
+ import type { GroupDescriptor, GroupItemDescriptor } from './use-c3-navigation-v2';
5
+ type ActiveMatcher = boolean | string | ((activeItemKey: string) => boolean);
6
+ export interface UseClusterSidebarEntriesOptions {
7
+ /**
8
+ * Hide apps the current user can't see. Receives the canonical app key
9
+ * from the iteration list (e.g. `'admin'`), never the resolved `identity`
10
+ * alias, so consumers don't have to branch on version.
11
+ */
12
+ isAppVisible?: (app: CamundaApp, cluster: Cluster) => boolean;
13
+ /**
14
+ * Resolves the parent cluster's click target. When it returns link props,
15
+ * the entry renders as a `group-item` (clickable + expandable). When
16
+ * omitted or returning `undefined`, it renders as a pure `group`.
17
+ */
18
+ resolveClusterLinkProps?: (cluster: Cluster) => LinkProps | undefined;
19
+ /**
20
+ * Active-state matcher for the cluster row. Mirrors the sidebar descriptor
21
+ * `isActive` shape (boolean, string, or `(activeItemKey) => boolean`).
22
+ * Ignored when the cluster renders as a pure group (no clickable target).
23
+ */
24
+ isClusterActive?: (cluster: Cluster) => ActiveMatcher | undefined;
25
+ /**
26
+ * Fallback link props for paused or unhealthy clusters, per app. Keyable by
27
+ * either the canonical iteration value (`'admin'`) or the resolved alias
28
+ * (`'identity'`); both are checked, alias first.
29
+ */
30
+ appTeaserRoutes?: Partial<Record<CamundaApp, LinkProps>>;
31
+ /**
32
+ * Consumer-provided labels, keyed by the display alias
33
+ * (`'admin'` or `'identity'` after version resolution). Fallback:
34
+ * `getReadableAppName(resolvedApp)`.
35
+ */
36
+ appLabels?: Partial<Record<CamundaApp, string>>;
37
+ /** Per-cluster trailing badge (stage tag, etc.). */
38
+ renderTrailingElement?: (cluster: Cluster) => ReactNode;
39
+ /** Override the app list. Default: operate, tasklist, optimize, admin. */
40
+ apps?: CamundaApp[];
41
+ }
42
+ /**
43
+ * Pure builder used by `useClusterSidebarEntries`. Kept as a separate
44
+ * function so stories and tests can drive it with synthetic data without
45
+ * mocking `useC3Profile`. Not exported from the package surface; consumers
46
+ * use the hook.
47
+ */
48
+ declare function buildClusterSidebarEntries(clusters: Cluster[], { isAppVisible, resolveClusterLinkProps, isClusterActive, appTeaserRoutes, appLabels, renderTrailingElement, apps, }: UseClusterSidebarEntriesOptions): Array<GroupDescriptor | GroupItemDescriptor>;
49
+ export { buildClusterSidebarEntries };
50
+ /**
51
+ * Builds cluster sidebar entries for consumers that list clusters in their
52
+ * sidebar (web modeler, console). Reads clusters from `useC3Profile`, iterates
53
+ * each cluster's available webapps, respects the identity/admin alias per
54
+ * cluster version, filters invisible or missing entries, and returns the
55
+ * sibling nodes ready to drop into a `SidebarSection`. Returns `[]` when
56
+ * nothing is renderable.
57
+ */
58
+ export declare function useClusterSidebarEntries(options?: UseClusterSidebarEntriesOptions): Array<GroupDescriptor | GroupItemDescriptor>;
@@ -0,0 +1,84 @@
1
+ /*
2
+ * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH
3
+ * under one or more contributor license agreements. Licensed under a commercial license.
4
+ * You may not use this file except in compliance with the commercial license.
5
+ */
6
+ import { CloudApp } from '@carbon/react/icons/index.esm.js';
7
+ import { useMemo } from 'react';
8
+ import { APP_ICONS, getEndpointForApp, getReadableAppName, isAdminApp, resolveAdminAppName, } from '../../utils/camunda.js';
9
+ import { useC3Profile } from '../c3-user-configuration/c3-profile-provider/c3-profile-provider.js';
10
+ const DEFAULT_APPS = ['operate', 'tasklist', 'optimize', 'admin'];
11
+ /**
12
+ * Pure builder used by `useClusterSidebarEntries`. Kept as a separate
13
+ * function so stories and tests can drive it with synthetic data without
14
+ * mocking `useC3Profile`. Not exported from the package surface; consumers
15
+ * use the hook.
16
+ */
17
+ function buildClusterSidebarEntries(clusters, { isAppVisible, resolveClusterLinkProps, isClusterActive, appTeaserRoutes, appLabels, renderTrailingElement, apps = DEFAULT_APPS, }) {
18
+ return clusters.flatMap((cluster) => {
19
+ const children = apps.flatMap((app) => {
20
+ if (isAppVisible && !isAppVisible(app, cluster))
21
+ return [];
22
+ const resolvedApp = isAdminApp(app) ? resolveAdminAppName(cluster) : app;
23
+ const endpoint = getEndpointForApp(app, cluster.endpoints);
24
+ if (!endpoint)
25
+ return [];
26
+ const teaser = appTeaserRoutes?.[resolvedApp] ?? appTeaserRoutes?.[app];
27
+ // Admin has no dedicated status on the cluster DTO and doesn't depend
28
+ // on zeebe being up (it manages the cluster itself). Always link to
29
+ // the endpoint when present; consumers can still override via
30
+ // `appTeaserRoutes.admin` / `.identity` if they want a teaser page.
31
+ const linkProps = isAdminApp(app)
32
+ ? (teaser ?? { href: endpoint })
33
+ : cluster.status?.[resolvedApp] === 'Healthy'
34
+ ? { href: endpoint }
35
+ : teaser;
36
+ if (!linkProps)
37
+ return [];
38
+ return [
39
+ {
40
+ type: 'item',
41
+ key: `${cluster.uuid}-${resolvedApp}`,
42
+ label: appLabels?.[resolvedApp] ?? getReadableAppName(resolvedApp),
43
+ icon: APP_ICONS[resolvedApp],
44
+ linkProps,
45
+ },
46
+ ];
47
+ });
48
+ if (children.length === 0)
49
+ return [];
50
+ const clusterLinkProps = resolveClusterLinkProps?.(cluster);
51
+ const base = {
52
+ key: `cluster-${cluster.uuid}`,
53
+ label: cluster.name,
54
+ icon: CloudApp,
55
+ trailingElement: renderTrailingElement?.(cluster),
56
+ children,
57
+ };
58
+ if (!clusterLinkProps) {
59
+ const group = { type: 'group', ...base };
60
+ return [group];
61
+ }
62
+ const activeMatcher = isClusterActive?.(cluster);
63
+ const groupItem = {
64
+ type: 'group-item',
65
+ ...base,
66
+ linkProps: clusterLinkProps,
67
+ ...(activeMatcher !== undefined ? { isActive: activeMatcher } : {}),
68
+ };
69
+ return [groupItem];
70
+ });
71
+ }
72
+ export { buildClusterSidebarEntries };
73
+ /**
74
+ * Builds cluster sidebar entries for consumers that list clusters in their
75
+ * sidebar (web modeler, console). Reads clusters from `useC3Profile`, iterates
76
+ * each cluster's available webapps, respects the identity/admin alias per
77
+ * cluster version, filters invisible or missing entries, and returns the
78
+ * sibling nodes ready to drop into a `SidebarSection`. Returns `[]` when
79
+ * nothing is renderable.
80
+ */
81
+ export function useClusterSidebarEntries(options = {}) {
82
+ const { clusters } = useC3Profile();
83
+ return useMemo(() => buildClusterSidebarEntries(clusters ?? [], options), [clusters, options]);
84
+ }
@@ -3,28 +3,11 @@
3
3
  * under one or more contributor license agreements. Licensed under a commercial license.
4
4
  * You may not use this file except in compliance with the commercial license.
5
5
  */
6
- import { Analytics, Application, Building, CloudApp, Diagram, Play, Settings, Task, } from '@carbon/react/icons/index.esm.js';
6
+ import { Building, CloudApp } from '@carbon/react/icons/index.esm.js';
7
7
  import { useMemo } from 'react';
8
+ import { APP_ICONS, getReadableAppName } from '../../utils/camunda.js';
8
9
  import { useC3Profile } from '../c3-user-configuration/c3-profile-provider/c3-profile-provider.js';
9
10
  import { useC3UserConfiguration } from '../c3-user-configuration/c3-user-configuration-provider.js';
10
- const APP_ICONS = {
11
- operate: Play,
12
- tasklist: Task,
13
- optimize: Analytics,
14
- admin: Settings,
15
- identity: Settings,
16
- modeler: Diagram,
17
- console: Application,
18
- };
19
- const APP_LABELS = {
20
- operate: 'Operate',
21
- tasklist: 'Tasklist',
22
- optimize: 'Optimize',
23
- admin: 'Admin',
24
- identity: 'Admin',
25
- modeler: 'Modeler',
26
- console: 'Console',
27
- };
28
11
  /**
29
12
  * Apps shown in the app-switcher dropdown. Order determines display order.
30
13
  * `identity` is excluded: it was deprecated in favor of `admin` (8.9+).
@@ -89,21 +72,21 @@ export function useClusterWebappBreadcrumbs(options) {
89
72
  });
90
73
  }
91
74
  // ── App crumb ──────────────────────────────────────────────────────────
92
- const appLabel = APP_LABELS[currentApp] ?? currentApp;
93
- const appIcon = APP_ICONS[currentApp] ?? Application;
75
+ const appLabel = getReadableAppName(currentApp);
76
+ const appIcon = APP_ICONS[currentApp];
94
77
  const siblingApps = SWITCHABLE_APPS.filter((app) => app !== currentApp && currentCluster?.endpoints?.[app]);
95
78
  const dropdownItems = siblingApps.length > 0
96
79
  ? [
97
80
  {
98
81
  key: currentApp,
99
82
  label: appLabel,
100
- icon: APP_ICONS[currentApp] ?? Application,
83
+ icon: appIcon,
101
84
  isSelected: true,
102
85
  },
103
86
  ...siblingApps.map((app) => ({
104
87
  key: app,
105
- label: APP_LABELS[app] ?? app,
106
- icon: APP_ICONS[app] ?? Application,
88
+ label: getReadableAppName(app),
89
+ icon: APP_ICONS[app],
107
90
  isSelected: false,
108
91
  linkProps: resolveLinkProps(currentCluster?.endpoints[app] ?? '', app),
109
92
  })),
@@ -15,8 +15,8 @@ export { C3LicenseTag, type C3LicenseTagProps, } from './components/c3-license-t
15
15
  export { default as C3Navigation } from './components/c3-navigation';
16
16
  export { C3NavigationAppProps, C3NavigationElementProps, C3NavigationNavBarElement, C3NavigationNavBarProps, C3NavigationNavBarSubElement, C3NavigationProps, } from './components/c3-navigation/c3-navigation.types';
17
17
  export { C3NavigationSideBarBaseProps } from './components/c3-navigation/c3-navigation-sidebar/c3-navigation-sidebar.types';
18
- export type { AppProps as C3NavV2AppProps, BreadcrumbDescriptor, BreadcrumbDropdownItem, BreadcrumbSegment, C3NavigationV2Props, C3NotificationsPanelProps, C3UserPanelProps, GlobalActionButton, GroupDescriptor, GroupItemDescriptor, HelpToolOptions, ItemDescriptor, LinkComponent, LinkProps, NotificationsToolOptions, SectionDescriptor, SidebarGroup, SidebarGroupItem, SidebarItem, SidebarNode, SidebarNodeDescriptor, SidebarProps, SidebarSection, ToolDescriptor, UseC3NavigationV2Options, UseC3NavigationV2Return, UseCamundaToolsOptions, UserToolOptions, } from './components/c3-navigation-v2';
19
- export { C3BreadcrumbBar as preview_C3BreadcrumbBar, C3NavigationV2 as preview_C3NavigationV2, C3NotificationsPanel as preview_C3NotificationsPanel, C3Sidebar as preview_C3Sidebar, C3ToolsArea as preview_C3ToolsArea, C3UserPanel as preview_C3UserPanel, useC3NavigationV2 as preview_useC3NavigationV2, useCamundaTools as preview_useCamundaTools, useClusterWebappBreadcrumbs as preview_useClusterWebappBreadcrumbs, } from './components/c3-navigation-v2';
18
+ export type { AppProps as C3NavV2AppProps, BreadcrumbDescriptor, BreadcrumbDropdownItem, BreadcrumbSegment, C3NavigationV2Props, C3NotificationsPanelProps, C3UserPanelProps, GlobalActionButton, GroupDescriptor, GroupItemDescriptor, HelpToolOptions, ItemDescriptor, LinkComponent, LinkProps, NotificationsToolOptions, SectionDescriptor, SidebarGroup, SidebarGroupItem, SidebarItem, SidebarNode, SidebarNodeDescriptor, SidebarProps, SidebarSection, ToolDescriptor, UseC3NavigationV2Options, UseC3NavigationV2Return, UseCamundaToolsOptions, UseClusterSidebarEntriesOptions, UserToolOptions, } from './components/c3-navigation-v2';
19
+ export { C3BreadcrumbBar as preview_C3BreadcrumbBar, C3NavigationV2 as preview_C3NavigationV2, C3NotificationsPanel as preview_C3NotificationsPanel, C3Sidebar as preview_C3Sidebar, C3ToolsArea as preview_C3ToolsArea, C3UserPanel as preview_C3UserPanel, useC3NavigationV2 as preview_useC3NavigationV2, useCamundaTools as preview_useCamundaTools, useClusterSidebarEntries as preview_useClusterSidebarEntries, useClusterWebappBreadcrumbs as preview_useClusterWebappBreadcrumbs, } from './components/c3-navigation-v2';
20
20
  export { C3BreadcrumbProps } from './components/c3-page/c3-breadcrumb/c3-breadcrumb.types';
21
21
  export { C3Page } from './components/c3-page/c3-page';
22
22
  export { C3PageProps } from './components/c3-page/c3-page.types';
@@ -26,3 +26,5 @@ export { useC3Profile } from './components/c3-user-configuration/c3-profile-prov
26
26
  export { C3UserConfiguration, default as C3UserConfigurationProvider, } from './components/c3-user-configuration/c3-user-configuration-provider';
27
27
  export { C3ClusterUpdateManager } from './contexts/c3-cluster-update-manager';
28
28
  export { useApi } from './hooks/useApi';
29
+ export { APP_ICONS, APPS, getReadableAppName, isAdminApp, } from './utils/camunda';
30
+ export type { CamundaApp, CamundaAppWithZeebe } from './utils/camunda.types';
@@ -13,10 +13,11 @@ export { C3HelpCenter } from './components/c3-help-center/c3-help-center.js';
13
13
  export { useC3HelpCenter } from './components/c3-help-center/c3-help-center-provider.js';
14
14
  export { C3LicenseTag, } from './components/c3-license-tag/index.js';
15
15
  export { default as C3Navigation } from './components/c3-navigation/index.js';
16
- export { C3BreadcrumbBar as preview_C3BreadcrumbBar, C3NavigationV2 as preview_C3NavigationV2, C3NotificationsPanel as preview_C3NotificationsPanel, C3Sidebar as preview_C3Sidebar, C3ToolsArea as preview_C3ToolsArea, C3UserPanel as preview_C3UserPanel, useC3NavigationV2 as preview_useC3NavigationV2, useCamundaTools as preview_useCamundaTools, useClusterWebappBreadcrumbs as preview_useClusterWebappBreadcrumbs, } from './components/c3-navigation-v2/index.js';
16
+ export { C3BreadcrumbBar as preview_C3BreadcrumbBar, C3NavigationV2 as preview_C3NavigationV2, C3NotificationsPanel as preview_C3NotificationsPanel, C3Sidebar as preview_C3Sidebar, C3ToolsArea as preview_C3ToolsArea, C3UserPanel as preview_C3UserPanel, useC3NavigationV2 as preview_useC3NavigationV2, useCamundaTools as preview_useCamundaTools, useClusterSidebarEntries as preview_useClusterSidebarEntries, useClusterWebappBreadcrumbs as preview_useClusterWebappBreadcrumbs, } from './components/c3-navigation-v2/index.js';
17
17
  export { C3Page } from './components/c3-page/c3-page.js';
18
18
  export { C3ResponsiveStack } from './components/c3-responsive-stack/c3-responsive-stack.js';
19
19
  export { useC3Profile } from './components/c3-user-configuration/c3-profile-provider/c3-profile-provider.js';
20
20
  export { default as C3UserConfigurationProvider, } from './components/c3-user-configuration/c3-user-configuration-provider.js';
21
21
  export { C3ClusterUpdateManager } from './contexts/c3-cluster-update-manager.js';
22
22
  export { useApi } from './hooks/useApi.js';
23
+ export { APP_ICONS, APPS, getReadableAppName, isAdminApp, } from './utils/camunda.js';
@@ -5,7 +5,22 @@ import type { CamundaApp, Cluster, Organization } from './camunda.types';
5
5
  * The CamundaApp type still accepts 'identity' for backward compatibility.
6
6
  */
7
7
  export declare const APPS: CamundaApp[];
8
+ type AppIconComponent = React.ComponentType<{
9
+ size?: number;
10
+ style?: React.CSSProperties;
11
+ }>;
12
+ /**
13
+ * Per-app iconography used across nav surfaces (cluster-webapp breadcrumbs,
14
+ * cluster sidebar entries, app switchers). `identity` and `admin` map to the
15
+ * same icon so consumers don't have to unify the alias themselves.
16
+ */
17
+ export declare const APP_ICONS: Record<CamundaApp, AppIconComponent>;
8
18
  export declare const isAdminApp: (app: CamundaApp | undefined) => boolean;
19
+ /**
20
+ * Picks the display alias (`admin` vs `identity`) for a cluster by zeebe version.
21
+ * Renamed in 8.9.0-alpha5. URL and permission still live on `identity`.
22
+ */
23
+ export declare const resolveAdminAppName: (cluster: Cluster) => "admin" | "identity";
9
24
  export declare const getReadableAppName: (appName: CamundaApp) => string;
10
25
  /**
11
26
  * Gets the endpoint URL for an app, handling identity/admin aliasing.
@@ -19,3 +34,4 @@ export declare const isTrialExpired: (org: Organization) => boolean;
19
34
  export declare const getOrgSalesPlan: (org: Organization) => string | undefined;
20
35
  export declare const canUpgradePlan: (org: Organization) => boolean;
21
36
  export declare const canCreateCluster: (org: Organization) => boolean;
37
+ export {};
@@ -3,6 +3,8 @@
3
3
  * under one or more contributor license agreements. Licensed under a commercial license.
4
4
  * You may not use this file except in compliance with the commercial license.
5
5
  */
6
+ import { Analytics, Application, Diagram, Play, Settings, Task, } from '@carbon/react/icons/index.esm.js';
7
+ import { checkVersion, VersionCheckOperator } from './versionCheck.utils.js';
6
8
  /**
7
9
  * List of Camunda apps for navigation.
8
10
  * Note: Only 'admin' is included (not 'identity') to avoid duplicate menu entries.
@@ -16,15 +18,36 @@ export const APPS = [
16
18
  'optimize',
17
19
  'admin',
18
20
  ];
21
+ /**
22
+ * Per-app iconography used across nav surfaces (cluster-webapp breadcrumbs,
23
+ * cluster sidebar entries, app switchers). `identity` and `admin` map to the
24
+ * same icon so consumers don't have to unify the alias themselves.
25
+ */
26
+ export const APP_ICONS = {
27
+ operate: Play,
28
+ tasklist: Task,
29
+ optimize: Analytics,
30
+ admin: Settings,
31
+ identity: Settings,
32
+ modeler: Diagram,
33
+ console: Application,
34
+ };
19
35
  export const isAdminApp = (app) => {
20
36
  return app === 'identity' || app === 'admin';
21
37
  };
22
- export const getReadableAppName = (appName) => {
23
- if (isAdminApp(appName)) {
24
- return 'Admin';
25
- }
26
- return appName.charAt(0).toUpperCase() + appName.slice(1);
27
- };
38
+ /**
39
+ * Picks the display alias (`admin` vs `identity`) for a cluster by zeebe version.
40
+ * Renamed in 8.9.0-alpha5. URL and permission still live on `identity`.
41
+ */
42
+ export const resolveAdminAppName = (cluster) => checkVersion({
43
+ possiblyVersionOrDockerPath: cluster.generation?.versions?.zeebe,
44
+ trueForSnapshot: true,
45
+ versionToCompareTo: '8.9.0-alpha5',
46
+ operator: VersionCheckOperator.GreaterThanOrEqual,
47
+ })
48
+ ? 'admin'
49
+ : 'identity';
50
+ export const getReadableAppName = (appName) => appName.charAt(0).toUpperCase() + appName.slice(1);
28
51
  /**
29
52
  * Gets the endpoint URL for an app, handling identity/admin aliasing.
30
53
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camunda/camunda-composite-components",
3
- "version": "0.23.1",
3
+ "version": "0.23.3",
4
4
  "description": "Camunda Composite Components",
5
5
  "bugs": {
6
6
  "url": "https://github.com/camunda/camunda-cloud-management-apps/issues"
@@ -60,7 +60,7 @@
60
60
  "styled-components": "6.4.0",
61
61
  "typescript-eslint": "8.58.1",
62
62
  "wait-on": "9.0.5",
63
- "@camunda/ccma-shared-types": "0.0.4"
63
+ "@camunda/ccma-shared-types": "0.0.6"
64
64
  },
65
65
  "peerDependencies": {
66
66
  "@carbon/react": "1.x",