@datalayer/core 1.0.21 → 1.0.22
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/components/billing/BillableAccountSelect.d.ts +35 -0
- package/lib/components/billing/BillableAccountSelect.js +340 -0
- package/lib/components/billing/index.d.ts +1 -0
- package/lib/components/billing/index.js +5 -0
- package/lib/components/index.d.ts +2 -0
- package/lib/components/index.js +2 -0
- package/lib/components/principal/Principal.d.ts +37 -0
- package/lib/components/principal/Principal.js +54 -0
- package/lib/components/principal/PrincipalAvatar.d.ts +10 -0
- package/lib/components/principal/PrincipalAvatar.js +55 -0
- package/lib/components/principal/PrincipalBadge.d.ts +20 -0
- package/lib/components/principal/PrincipalBadge.js +208 -0
- package/lib/components/principal/PrincipalBanner.d.ts +7 -0
- package/lib/components/principal/PrincipalBanner.js +84 -0
- package/lib/components/principal/PrincipalDetailsOverlay.d.ts +23 -0
- package/lib/components/principal/PrincipalDetailsOverlay.js +86 -0
- package/lib/components/principal/PrincipalSwitcherMenu.d.ts +14 -0
- package/lib/components/principal/PrincipalSwitcherMenu.js +287 -0
- package/lib/components/principal/index.d.ts +7 -0
- package/lib/components/principal/index.js +10 -0
- package/lib/components/sharing/ShareAccessComponent.d.ts +18 -0
- package/lib/components/sharing/ShareAccessComponent.js +1368 -0
- package/lib/components/sharing/SharingEditor.d.ts +20 -0
- package/lib/components/sharing/SharingEditor.js +65 -0
- package/lib/components/sharing/index.d.ts +9 -0
- package/lib/components/sharing/index.js +12 -0
- package/lib/hooks/index.d.ts +5 -0
- package/lib/hooks/index.js +5 -0
- package/lib/hooks/useBillableAccountStore.d.ts +31 -0
- package/lib/hooks/useBillableAccountStore.js +53 -0
- package/lib/hooks/usePrincipalStore.d.ts +41 -0
- package/lib/hooks/usePrincipalStore.js +101 -0
- package/lib/hooks/useSelectedBillableAccount.d.ts +14 -0
- package/lib/hooks/useSelectedBillableAccount.js +36 -0
- package/lib/hooks/useSelectedPrincipal.d.ts +17 -0
- package/lib/hooks/useSelectedPrincipal.js +29 -0
- package/lib/hooks/useUsageRefreshStore.d.ts +8 -0
- package/lib/hooks/useUsageRefreshStore.js +16 -0
- package/lib/utils/Handles.d.ts +3 -0
- package/lib/utils/Handles.js +53 -0
- package/lib/utils/index.d.ts +1 -0
- package/lib/utils/index.js +1 -0
- package/package.json +1 -3
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/*
|
|
3
|
+
* Copyright (c) 2023-2025 Datalayer, Inc.
|
|
4
|
+
* Distributed under the terms of the Modified BSD License.
|
|
5
|
+
*/
|
|
6
|
+
import { useMemo } from 'react';
|
|
7
|
+
import { Box, Label, Text } from '@primer/react';
|
|
8
|
+
import { useCache } from '../../hooks/index.js';
|
|
9
|
+
import { useIAMStore } from '../../state/substates/index.js';
|
|
10
|
+
import { useSelectedPrincipal } from '../../hooks/useSelectedPrincipal.js';
|
|
11
|
+
import { formatFriendlyHandle } from '../../utils/Handles.js';
|
|
12
|
+
import { Principal } from './Principal.js';
|
|
13
|
+
function normalizeUserOrigin(originRaw) {
|
|
14
|
+
const value = (originRaw || '').trim();
|
|
15
|
+
if (!value) {
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
18
|
+
const lower = value.toLowerCase();
|
|
19
|
+
if (lower === 'github') {
|
|
20
|
+
return 'GitHub';
|
|
21
|
+
}
|
|
22
|
+
if (lower === 'google') {
|
|
23
|
+
return 'Google';
|
|
24
|
+
}
|
|
25
|
+
if (lower === 'linkedin') {
|
|
26
|
+
return 'LinkedIn';
|
|
27
|
+
}
|
|
28
|
+
if (lower === 'microsoft') {
|
|
29
|
+
return 'Microsoft';
|
|
30
|
+
}
|
|
31
|
+
if (lower === 'datalayer') {
|
|
32
|
+
return 'Datalayer';
|
|
33
|
+
}
|
|
34
|
+
return value;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* PrincipalBadge — small inline pill that displays a resolved principal
|
|
38
|
+
* (user / organization / team). Falls back to the currently selected
|
|
39
|
+
* principal when no explicit `principal` prop is supplied.
|
|
40
|
+
*/
|
|
41
|
+
export const PrincipalBadge = ({ principal: providedPrincipal, showPrincipalLabel = true, showApplyingToText = true, showOriginLabel = true, principalLabel = 'Principal', isAdmin = false, sx, } = {}) => {
|
|
42
|
+
const { user } = useIAMStore();
|
|
43
|
+
const { selectedPrincipalKind, selectedPrincipalUid, selectedPrincipalHandle, selectedTeamParentOrganizationHandle, } = useSelectedPrincipal();
|
|
44
|
+
const { useUser, useOrganization } = useCache();
|
|
45
|
+
const basePrincipal = useMemo(() => {
|
|
46
|
+
if (providedPrincipal) {
|
|
47
|
+
return {
|
|
48
|
+
...providedPrincipal,
|
|
49
|
+
displayName: providedPrincipal.displayName ||
|
|
50
|
+
providedPrincipal.handle ||
|
|
51
|
+
providedPrincipal.uid ||
|
|
52
|
+
'Principal',
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
if (selectedPrincipalKind === 'organization') {
|
|
56
|
+
return {
|
|
57
|
+
kind: 'organization',
|
|
58
|
+
uid: selectedPrincipalUid,
|
|
59
|
+
handle: selectedPrincipalHandle,
|
|
60
|
+
accountHandle: selectedPrincipalHandle,
|
|
61
|
+
displayName: selectedPrincipalHandle
|
|
62
|
+
? `@${formatFriendlyHandle(selectedPrincipalHandle)}`
|
|
63
|
+
: 'Organization',
|
|
64
|
+
origin: 'Datalayer',
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
if (selectedPrincipalKind === 'team') {
|
|
68
|
+
const teamHandle = selectedPrincipalHandle || 'team';
|
|
69
|
+
const orgHandle = selectedTeamParentOrganizationHandle || 'organization';
|
|
70
|
+
return {
|
|
71
|
+
kind: 'team',
|
|
72
|
+
uid: selectedPrincipalUid,
|
|
73
|
+
handle: `${orgHandle}/${teamHandle}`,
|
|
74
|
+
accountHandle: teamHandle,
|
|
75
|
+
displayName: `@${formatFriendlyHandle(orgHandle)}/${formatFriendlyHandle(teamHandle)}`,
|
|
76
|
+
origin: 'Datalayer',
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
const fullName = [user?.firstName, user?.lastName]
|
|
80
|
+
.filter(Boolean)
|
|
81
|
+
.join(' ')
|
|
82
|
+
.trim();
|
|
83
|
+
const resolvedHandle = user?.handle || selectedPrincipalHandle;
|
|
84
|
+
const fallbackHandle = resolvedHandle
|
|
85
|
+
? `@${formatFriendlyHandle(resolvedHandle)}`
|
|
86
|
+
: '@me';
|
|
87
|
+
return {
|
|
88
|
+
kind: 'user',
|
|
89
|
+
uid: user?.id || selectedPrincipalUid,
|
|
90
|
+
displayName: fullName || fallbackHandle,
|
|
91
|
+
handle: resolvedHandle,
|
|
92
|
+
accountHandle: resolvedHandle,
|
|
93
|
+
firstName: user?.firstName,
|
|
94
|
+
lastName: user?.lastName,
|
|
95
|
+
email: user?.email,
|
|
96
|
+
avatarUrl: user?.avatarUrl,
|
|
97
|
+
origin: normalizeUserOrigin(user?.origin),
|
|
98
|
+
};
|
|
99
|
+
}, [
|
|
100
|
+
providedPrincipal,
|
|
101
|
+
selectedPrincipalKind,
|
|
102
|
+
selectedPrincipalUid,
|
|
103
|
+
selectedPrincipalHandle,
|
|
104
|
+
selectedTeamParentOrganizationHandle,
|
|
105
|
+
user?.id,
|
|
106
|
+
user?.origin,
|
|
107
|
+
user?.handle,
|
|
108
|
+
user?.firstName,
|
|
109
|
+
user?.lastName,
|
|
110
|
+
user?.email,
|
|
111
|
+
user?.avatarUrl,
|
|
112
|
+
]);
|
|
113
|
+
const userLookupUid = basePrincipal.kind === 'user' ? String(basePrincipal.uid || '') : '';
|
|
114
|
+
const organizationLookupUid = basePrincipal.kind === 'organization'
|
|
115
|
+
? String(basePrincipal.uid || '')
|
|
116
|
+
: '';
|
|
117
|
+
const { data: resolvedUser } = useUser(userLookupUid);
|
|
118
|
+
const { data: resolvedOrganization } = useOrganization(organizationLookupUid);
|
|
119
|
+
const principal = useMemo(() => {
|
|
120
|
+
if (basePrincipal.kind === 'organization') {
|
|
121
|
+
const resolvedHandle = resolvedOrganization?.handle ||
|
|
122
|
+
basePrincipal.handle ||
|
|
123
|
+
basePrincipal.accountHandle;
|
|
124
|
+
const normalizedHandle = resolvedHandle
|
|
125
|
+
? formatFriendlyHandle(resolvedHandle)
|
|
126
|
+
: 'organization';
|
|
127
|
+
return {
|
|
128
|
+
kind: 'organization',
|
|
129
|
+
uid: basePrincipal.uid,
|
|
130
|
+
displayName: resolvedOrganization?.name ||
|
|
131
|
+
basePrincipal.displayName ||
|
|
132
|
+
`@${normalizedHandle}`,
|
|
133
|
+
handle: resolvedHandle,
|
|
134
|
+
accountHandle: resolvedHandle,
|
|
135
|
+
origin: basePrincipal.origin || 'Datalayer',
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
if (basePrincipal.kind === 'team') {
|
|
139
|
+
return {
|
|
140
|
+
kind: 'team',
|
|
141
|
+
uid: basePrincipal.uid,
|
|
142
|
+
displayName: basePrincipal.displayName ||
|
|
143
|
+
basePrincipal.handle ||
|
|
144
|
+
basePrincipal.uid ||
|
|
145
|
+
'Team',
|
|
146
|
+
handle: basePrincipal.handle,
|
|
147
|
+
accountHandle: basePrincipal.accountHandle,
|
|
148
|
+
avatarUrl: basePrincipal.avatarUrl,
|
|
149
|
+
origin: basePrincipal.origin,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
const fullName = [
|
|
153
|
+
resolvedUser?.firstName || basePrincipal.firstName,
|
|
154
|
+
resolvedUser?.lastName || basePrincipal.lastName,
|
|
155
|
+
]
|
|
156
|
+
.filter(Boolean)
|
|
157
|
+
.join(' ')
|
|
158
|
+
.trim();
|
|
159
|
+
const resolvedHandle = resolvedUser?.handle ||
|
|
160
|
+
basePrincipal.handle ||
|
|
161
|
+
basePrincipal.accountHandle;
|
|
162
|
+
const fallbackHandle = resolvedHandle
|
|
163
|
+
? `@${formatFriendlyHandle(resolvedHandle)}`
|
|
164
|
+
: '@me';
|
|
165
|
+
const resolvedDisplayName = resolvedUser?.displayName ||
|
|
166
|
+
basePrincipal.displayName ||
|
|
167
|
+
fullName ||
|
|
168
|
+
fallbackHandle;
|
|
169
|
+
const origin = normalizeUserOrigin(resolvedUser?.origin || basePrincipal.origin);
|
|
170
|
+
return {
|
|
171
|
+
kind: 'user',
|
|
172
|
+
uid: resolvedUser?.uid || basePrincipal.uid,
|
|
173
|
+
displayName: resolvedDisplayName,
|
|
174
|
+
handle: resolvedHandle,
|
|
175
|
+
accountHandle: resolvedHandle,
|
|
176
|
+
firstName: resolvedUser?.firstName || basePrincipal.firstName,
|
|
177
|
+
lastName: resolvedUser?.lastName || basePrincipal.lastName,
|
|
178
|
+
email: resolvedUser?.email || basePrincipal.email,
|
|
179
|
+
avatarUrl: resolvedUser?.avatarUrl || basePrincipal.avatarUrl,
|
|
180
|
+
origin,
|
|
181
|
+
};
|
|
182
|
+
}, [
|
|
183
|
+
basePrincipal,
|
|
184
|
+
resolvedOrganization?.name,
|
|
185
|
+
resolvedOrganization?.handle,
|
|
186
|
+
resolvedUser?.uid,
|
|
187
|
+
resolvedUser?.displayName,
|
|
188
|
+
resolvedUser?.handle,
|
|
189
|
+
resolvedUser?.firstName,
|
|
190
|
+
resolvedUser?.lastName,
|
|
191
|
+
resolvedUser?.email,
|
|
192
|
+
resolvedUser?.avatarUrl,
|
|
193
|
+
resolvedUser?.origin,
|
|
194
|
+
]);
|
|
195
|
+
return (_jsxs(Box, { sx: {
|
|
196
|
+
display: 'inline-flex',
|
|
197
|
+
alignItems: 'center',
|
|
198
|
+
gap: 2,
|
|
199
|
+
px: 2,
|
|
200
|
+
py: 1,
|
|
201
|
+
border: '1px solid',
|
|
202
|
+
borderColor: 'border.default',
|
|
203
|
+
borderRadius: 2,
|
|
204
|
+
bg: 'canvas.subtle',
|
|
205
|
+
...sx,
|
|
206
|
+
}, children: [showPrincipalLabel && (_jsx(Label, { size: "small", variant: "accent", children: principalLabel })), showApplyingToText && (_jsx(Text, { sx: { fontSize: 1, color: 'fg.muted' }, children: "Applying to" })), _jsx(Principal, { principal: principal, isAdmin: isAdmin }), showOriginLabel && principal.origin && (_jsx(Label, { size: "small", variant: "secondary", children: principal.origin }))] }));
|
|
207
|
+
};
|
|
208
|
+
export default PrincipalBadge;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
export type PrincipalBannerProps = {
|
|
3
|
+
caption?: string;
|
|
4
|
+
rightContent?: ReactNode;
|
|
5
|
+
};
|
|
6
|
+
export declare const PrincipalBanner: ({ caption, rightContent, }: PrincipalBannerProps) => import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export default PrincipalBanner;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/*
|
|
3
|
+
* Copyright (c) 2023-2025 Datalayer, Inc.
|
|
4
|
+
* Distributed under the terms of the Modified BSD License.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* PrincipalBanner — displays the currently selected principal
|
|
8
|
+
* (user, organization, or team) with a colored visual so a user
|
|
9
|
+
* can immediately see which principal a settings page applies to.
|
|
10
|
+
*/
|
|
11
|
+
import { Box, Label, Text } from '@primer/react';
|
|
12
|
+
import { OrganizationIcon, PeopleIcon, PersonIcon, } from '@primer/octicons-react';
|
|
13
|
+
import { useSelectedPrincipal } from '../../hooks/useSelectedPrincipal.js';
|
|
14
|
+
import { useIAMStore } from '../../state/substates/index.js';
|
|
15
|
+
export const PrincipalBanner = ({ caption, rightContent, }) => {
|
|
16
|
+
const { user } = useIAMStore();
|
|
17
|
+
const { selectedPrincipalKind, selectedPrincipalHandle, selectedTeamParentOrganizationHandle, } = useSelectedPrincipal();
|
|
18
|
+
const isOrganization = selectedPrincipalKind === 'organization';
|
|
19
|
+
const isTeam = selectedPrincipalKind === 'team';
|
|
20
|
+
const handle = isOrganization
|
|
21
|
+
? selectedPrincipalHandle || ''
|
|
22
|
+
: isTeam
|
|
23
|
+
? `${selectedTeamParentOrganizationHandle || 'organization'}/${selectedPrincipalHandle || 'team'}`
|
|
24
|
+
: user?.handle || selectedPrincipalHandle || '';
|
|
25
|
+
const Icon = isOrganization
|
|
26
|
+
? OrganizationIcon
|
|
27
|
+
: isTeam
|
|
28
|
+
? PeopleIcon
|
|
29
|
+
: PersonIcon;
|
|
30
|
+
const accent = isOrganization ? 'done' : isTeam ? 'attention' : 'accent';
|
|
31
|
+
const bg = isOrganization
|
|
32
|
+
? 'done.subtle'
|
|
33
|
+
: isTeam
|
|
34
|
+
? 'attention.subtle'
|
|
35
|
+
: 'accent.subtle';
|
|
36
|
+
const borderColor = isOrganization
|
|
37
|
+
? 'done.muted'
|
|
38
|
+
: isTeam
|
|
39
|
+
? 'attention.muted'
|
|
40
|
+
: 'accent.muted';
|
|
41
|
+
const fg = isOrganization ? 'done.fg' : isTeam ? 'attention.fg' : 'accent.fg';
|
|
42
|
+
return (_jsxs(Box, { sx: {
|
|
43
|
+
display: 'flex',
|
|
44
|
+
alignItems: 'center',
|
|
45
|
+
gap: 3,
|
|
46
|
+
p: 3,
|
|
47
|
+
border: '1px solid',
|
|
48
|
+
borderColor,
|
|
49
|
+
borderRadius: 2,
|
|
50
|
+
bg,
|
|
51
|
+
}, children: [_jsx(Box, { sx: {
|
|
52
|
+
width: 40,
|
|
53
|
+
height: 40,
|
|
54
|
+
display: 'flex',
|
|
55
|
+
alignItems: 'center',
|
|
56
|
+
justifyContent: 'center',
|
|
57
|
+
borderRadius: '50%',
|
|
58
|
+
bg: 'canvas.default',
|
|
59
|
+
border: '1px solid',
|
|
60
|
+
borderColor,
|
|
61
|
+
color: fg,
|
|
62
|
+
flex: '0 0 auto',
|
|
63
|
+
}, children: _jsx(Icon, { size: 20 }) }), _jsxs(Box, { sx: { minWidth: 0, flex: 1 }, children: [_jsxs(Box, { sx: {
|
|
64
|
+
display: 'flex',
|
|
65
|
+
alignItems: 'center',
|
|
66
|
+
gap: 2,
|
|
67
|
+
flexWrap: 'wrap',
|
|
68
|
+
}, children: [_jsx(Text, { sx: {
|
|
69
|
+
color: 'fg.muted',
|
|
70
|
+
fontSize: 0,
|
|
71
|
+
textTransform: 'uppercase',
|
|
72
|
+
letterSpacing: '0.04em',
|
|
73
|
+
}, children: "Principal" }), _jsx(Label, { variant: accent, children: isOrganization ? 'Organization' : isTeam ? 'Team' : 'User' })] }), _jsx(Text, { as: "p", sx: { m: 0, mt: 1, fontWeight: 600, color: 'fg.default' }, children: handle
|
|
74
|
+
? `@${handle}`
|
|
75
|
+
: isOrganization
|
|
76
|
+
? 'Organization'
|
|
77
|
+
: isTeam
|
|
78
|
+
? 'Team'
|
|
79
|
+
: 'User' }), caption && (_jsx(Text, { as: "p", sx: { m: 0, mt: 1, color: 'fg.muted', fontSize: 1 }, children: caption }))] }), rightContent ? (_jsx(Box, { sx: {
|
|
80
|
+
flex: ['1 1 100%', '0 0 auto'],
|
|
81
|
+
width: ['100%', 'min(640px, 58%)'],
|
|
82
|
+
}, children: rightContent })) : null] }));
|
|
83
|
+
};
|
|
84
|
+
export default PrincipalBanner;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export type PrincipalKind = 'user' | 'team' | 'organization';
|
|
2
|
+
export type PrincipalDetailsOverlayProps = {
|
|
3
|
+
kind: PrincipalKind;
|
|
4
|
+
uid?: string;
|
|
5
|
+
displayName: string;
|
|
6
|
+
handle?: string;
|
|
7
|
+
accountHandle?: string;
|
|
8
|
+
firstName?: string;
|
|
9
|
+
lastName?: string;
|
|
10
|
+
email?: string;
|
|
11
|
+
origin?: string;
|
|
12
|
+
avatarUrl?: string;
|
|
13
|
+
isAdmin?: boolean;
|
|
14
|
+
};
|
|
15
|
+
export declare function buildPrincipalProfilePath({ kind, uid, handle, accountHandle, isAdmin, }: {
|
|
16
|
+
kind: PrincipalKind;
|
|
17
|
+
uid?: string;
|
|
18
|
+
handle?: string;
|
|
19
|
+
accountHandle?: string;
|
|
20
|
+
isAdmin?: boolean;
|
|
21
|
+
}): string | null;
|
|
22
|
+
export declare function PrincipalDetailsOverlay({ kind, uid, displayName, handle, accountHandle, firstName, lastName, email, origin, avatarUrl, isAdmin, }: PrincipalDetailsOverlayProps): JSX.Element;
|
|
23
|
+
export default PrincipalDetailsOverlay;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
/*
|
|
3
|
+
* Copyright (c) 2023-2025 Datalayer, Inc.
|
|
4
|
+
* Distributed under the terms of the Modified BSD License.
|
|
5
|
+
*/
|
|
6
|
+
import { ActionMenu, Box, Button, Text } from '@primer/react';
|
|
7
|
+
import { useNavigate } from '../../hooks/index.js';
|
|
8
|
+
import { PrincipalAvatar } from './PrincipalAvatar.js';
|
|
9
|
+
function normalize(value) {
|
|
10
|
+
return (value || '').trim();
|
|
11
|
+
}
|
|
12
|
+
export function buildPrincipalProfilePath({ kind, uid, handle, accountHandle, isAdmin, }) {
|
|
13
|
+
const normalizedUid = normalize(uid);
|
|
14
|
+
const normalizedHandle = normalize(handle);
|
|
15
|
+
const normalizedAccountHandle = normalize(accountHandle);
|
|
16
|
+
const safeHandle = normalizedHandle && normalizedHandle !== normalizedUid
|
|
17
|
+
? normalizedHandle
|
|
18
|
+
: '';
|
|
19
|
+
if (kind === 'user') {
|
|
20
|
+
if (isAdmin && normalizedUid) {
|
|
21
|
+
return `/admin/management/iam/users/${normalizedUid}`;
|
|
22
|
+
}
|
|
23
|
+
if (safeHandle) {
|
|
24
|
+
return `/${safeHandle}`;
|
|
25
|
+
}
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
if (kind === 'team') {
|
|
29
|
+
if (normalizedAccountHandle && safeHandle) {
|
|
30
|
+
return `/${normalizedAccountHandle}/team/${safeHandle}`;
|
|
31
|
+
}
|
|
32
|
+
if (safeHandle.includes('/')) {
|
|
33
|
+
const [orgHandle, teamHandle] = safeHandle.split('/', 2);
|
|
34
|
+
if (orgHandle && teamHandle) {
|
|
35
|
+
return `/${orgHandle}/team/${teamHandle}`;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
if (safeHandle) {
|
|
39
|
+
return `/datalayer/team/${safeHandle}`;
|
|
40
|
+
}
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
if (safeHandle) {
|
|
44
|
+
return `/${safeHandle}`;
|
|
45
|
+
}
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
export function PrincipalDetailsOverlay({ kind, uid, displayName, handle, accountHandle, firstName, lastName, email, origin, avatarUrl, isAdmin = false, }) {
|
|
49
|
+
const navigate = useNavigate();
|
|
50
|
+
const normalizedDisplayName = normalize(displayName) ||
|
|
51
|
+
normalize(handle) ||
|
|
52
|
+
normalize(uid) ||
|
|
53
|
+
'Principal';
|
|
54
|
+
const normalizedHandle = normalize(handle);
|
|
55
|
+
const normalizedUid = normalize(uid);
|
|
56
|
+
const targetPath = buildPrincipalProfilePath({
|
|
57
|
+
kind,
|
|
58
|
+
uid: normalizedUid,
|
|
59
|
+
handle: normalizedHandle,
|
|
60
|
+
accountHandle,
|
|
61
|
+
isAdmin,
|
|
62
|
+
});
|
|
63
|
+
return (_jsxs(ActionMenu, { children: [_jsx(ActionMenu.Anchor, { children: _jsx(Box, { as: "button", type: "button", sx: {
|
|
64
|
+
fontWeight: 'semibold',
|
|
65
|
+
color: 'accent.fg',
|
|
66
|
+
textDecoration: 'underline',
|
|
67
|
+
background: 'transparent',
|
|
68
|
+
border: 0,
|
|
69
|
+
padding: 0,
|
|
70
|
+
margin: 0,
|
|
71
|
+
cursor: 'pointer',
|
|
72
|
+
':hover': {
|
|
73
|
+
textDecoration: 'underline',
|
|
74
|
+
},
|
|
75
|
+
}, children: normalizedDisplayName }) }), _jsx(ActionMenu.Overlay, { width: "large", children: _jsxs(Box, { sx: { display: 'grid', gap: 3, p: 4, minWidth: 420 }, children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(PrincipalAvatar, { kind: kind, avatarUrl: avatarUrl, alt: normalizedDisplayName, size: 40 }), _jsxs(Box, { sx: { display: 'grid', gap: 0.5 }, children: [_jsx(Text, { sx: { fontWeight: 'semibold' }, children: normalizedDisplayName }), normalizedHandle ? (_jsxs(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: ["@", normalizedHandle] })) : null] })] }), _jsxs(Box, { sx: {
|
|
76
|
+
display: 'grid',
|
|
77
|
+
gridTemplateColumns: '110px 1fr',
|
|
78
|
+
gap: 1,
|
|
79
|
+
alignItems: 'baseline',
|
|
80
|
+
}, children: [_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "Type" }), _jsx(Text, { sx: { fontSize: 1 }, children: kind }), normalizedHandle ? (_jsxs(_Fragment, { children: [_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "Handle" }), _jsxs(Text, { sx: { fontSize: 1 }, children: ["@", normalizedHandle] })] })) : null, normalizedUid ? (_jsxs(_Fragment, { children: [_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "UID" }), _jsx(Text, { sx: { fontSize: 1 }, children: normalizedUid })] })) : null, kind === 'user' ? (_jsxs(_Fragment, { children: [_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "First name" }), _jsx(Text, { sx: { fontSize: 1 }, children: firstName || 'N/A' }), _jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "Last name" }), _jsx(Text, { sx: { fontSize: 1 }, children: lastName || 'N/A' }), _jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "Origin" }), _jsx(Text, { sx: { fontSize: 1 }, children: origin || 'Datalayer' }), email ? (_jsxs(_Fragment, { children: [_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "Email" }), _jsx(Text, { sx: { fontSize: 1 }, children: email })] })) : null] })) : (_jsxs(_Fragment, { children: [_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "Origin" }), _jsx(Text, { sx: { fontSize: 1 }, children: origin || 'Datalayer' })] }))] }), _jsx(Box, { sx: { display: 'flex', justifyContent: 'flex-end' }, children: _jsx(Button, { size: "small", onClick: () => {
|
|
81
|
+
if (targetPath) {
|
|
82
|
+
navigate(targetPath);
|
|
83
|
+
}
|
|
84
|
+
}, disabled: !targetPath, children: "View profile" }) })] }) })] }));
|
|
85
|
+
}
|
|
86
|
+
export default PrincipalDetailsOverlay;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type PrincipalSwitcherMenuProps = {
|
|
2
|
+
maxLabelChars?: number;
|
|
3
|
+
fullWidth?: boolean;
|
|
4
|
+
showClosedBorder?: boolean;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* PrincipalSwitcherMenu — the *only* component allowed to write to the
|
|
8
|
+
* principal store and the billable account store. It keeps both stores in
|
|
9
|
+
* sync per the rule:
|
|
10
|
+
* - selecting a user/org principal → billable account = same user/org
|
|
11
|
+
* - selecting a team principal → billable account = the team's parent org
|
|
12
|
+
*/
|
|
13
|
+
export declare function PrincipalSwitcherMenu({ maxLabelChars, fullWidth, showClosedBorder, }: PrincipalSwitcherMenuProps): JSX.Element;
|
|
14
|
+
export default PrincipalSwitcherMenu;
|