@datalayer/core 1.1.41 → 1.1.45
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/README.md +10 -121
- package/lib/client/auth/AuthenticationManager.d.ts +3 -3
- package/lib/client/auth/AuthenticationManager.js +13 -13
- package/lib/client/auth/strategies.d.ts +2 -2
- package/lib/client/auth/strategies.js +6 -6
- package/lib/client/base.d.ts +9 -9
- package/lib/client/base.js +17 -17
- package/lib/client/mixins/IAMMixin.js +26 -26
- package/lib/components/auth/Login.js +2 -2
- package/lib/components/billing/BillingEntitySelect.d.ts +35 -0
- package/lib/components/billing/{BillableAccountSelect.js → BillingEntitySelect.js} +39 -41
- package/lib/components/billing/index.d.ts +1 -1
- package/lib/components/billing/index.js +1 -1
- package/lib/components/checkout/StripeCheckout.js +5 -5
- package/lib/components/display/VisuallyHidden.d.ts +1 -1
- package/lib/components/display/index.d.ts +0 -1
- package/lib/components/display/index.js +0 -1
- package/lib/components/principal/Principal.js +4 -2
- package/lib/components/principal/PrincipalBadge.js +21 -0
- package/lib/components/principal/PrincipalDetailsOverlay.js +72 -37
- package/lib/components/principal/PrincipalSwitcherMenu.d.ts +3 -3
- package/lib/components/principal/PrincipalSwitcherMenu.js +133 -114
- package/lib/components/sharing/ShareAccessComponent.js +14 -14
- package/lib/config/Configuration.d.ts +13 -13
- package/lib/config/Configuration.js +5 -5
- package/lib/hooks/index.d.ts +2 -2
- package/lib/hooks/index.js +2 -2
- package/lib/hooks/useBillingEntityStore.d.ts +31 -0
- package/lib/hooks/useBillingEntityStore.js +53 -0
- package/lib/hooks/useCache.d.ts +34 -33
- package/lib/hooks/useCache.js +298 -282
- package/lib/hooks/useIAM.js +1 -1
- package/lib/hooks/useSelectedBillingEntity.d.ts +14 -0
- package/lib/hooks/{useSelectedBillableAccount.js → useSelectedBillingEntity.js} +14 -14
- package/lib/hooks/useSelectedPrincipal.d.ts +1 -1
- package/lib/hooks/useSelectedPrincipal.js +1 -1
- package/lib/models/UserSettings.d.ts +1 -1
- package/lib/models/UserSettings.js +1 -1
- package/lib/otel/client/OtelClient.d.ts +2 -2
- package/lib/otel/client/OtelClient.js +2 -2
- package/lib/otel/hooks/index.js +5 -5
- package/lib/otel/types.d.ts +1 -1
- package/lib/otel/views/OtelLive.js +1 -1
- package/lib/state/substates/CoreState.js +13 -13
- package/lib/state/substates/IAMState.d.ts +1 -1
- package/lib/state/substates/IAMState.js +14 -14
- package/lib/state/substates/SurveysState.d.ts +1 -1
- package/lib/state/substates/SurveysState.js +10 -10
- package/lib/views/iam/SignInSimple.js +6 -6
- package/package.json +4 -4
- package/lib/components/billing/BillableAccountSelect.d.ts +0 -35
- package/lib/components/display/LiveRelativeTime.d.ts +0 -1
- package/lib/components/display/LiveRelativeTime.js +0 -10
- package/lib/hooks/useBillableAccountStore.d.ts +0 -31
- package/lib/hooks/useBillableAccountStore.js +0 -53
- package/lib/hooks/useSelectedBillableAccount.d.ts +0 -14
|
@@ -4,14 +4,14 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
4
4
|
* Distributed under the terms of the Modified BSD License.
|
|
5
5
|
*/
|
|
6
6
|
import { useEffect, useMemo, useState } from 'react';
|
|
7
|
-
import { ActionList, ActionMenu, Box, Button, Label, Text } from '@primer/react';
|
|
7
|
+
import { ActionList, ActionMenu, Box, Button, Label, Text, ThemeProvider, } from '@primer/react';
|
|
8
8
|
import { OrganizationIcon, PeopleIcon, PersonIcon, TriangleDownIcon, } from '@primer/octicons-react';
|
|
9
9
|
import { useCache, useAuthorization } from '../../hooks/index.js';
|
|
10
10
|
import { useCoreStore } from '../../state/index.js';
|
|
11
11
|
import { useIAMStore } from '../../state/substates/index.js';
|
|
12
12
|
import { memberships as fetchMemberships } from '../../api/iam/profile.js';
|
|
13
13
|
import { usePrincipalStore } from '../../hooks/usePrincipalStore.js';
|
|
14
|
-
import {
|
|
14
|
+
import { useBillingEntityStore } from '../../hooks/useBillingEntityStore.js';
|
|
15
15
|
import { useSelectedPrincipal } from '../../hooks/useSelectedPrincipal.js';
|
|
16
16
|
import { DisplayHandle, displayHandleText } from '../display/DisplayHandle.js';
|
|
17
17
|
function truncatePrincipalLabel(label, maxChars) {
|
|
@@ -26,13 +26,13 @@ function truncatePrincipalLabel(label, maxChars) {
|
|
|
26
26
|
}
|
|
27
27
|
/**
|
|
28
28
|
* PrincipalSwitcherMenu — the *only* component allowed to write to the
|
|
29
|
-
* principal store and the
|
|
29
|
+
* principal store and the billing entity store. It keeps both stores in
|
|
30
30
|
* sync per the rule:
|
|
31
|
-
* - selecting a user/org principal →
|
|
32
|
-
* - selecting a team principal →
|
|
31
|
+
* - selecting a user/org principal → billing entity = same user/org
|
|
32
|
+
* - selecting a team principal → billing entity = the team's parent org
|
|
33
33
|
*/
|
|
34
34
|
export function PrincipalSwitcherMenu({ maxLabelChars = 48, fullWidth = true, showClosedBorder = true, }) {
|
|
35
|
-
const { user, token,
|
|
35
|
+
const { user, token, iamUrl } = useIAMStore();
|
|
36
36
|
const { configuration } = useCoreStore();
|
|
37
37
|
const { checkIsPlatformAdmin } = useAuthorization();
|
|
38
38
|
const { useUserOrganizations } = useCache();
|
|
@@ -45,17 +45,17 @@ export function PrincipalSwitcherMenu({ maxLabelChars = 48, fullWidth = true, sh
|
|
|
45
45
|
const selectPersonalPrincipal = usePrincipalStore(state => state.selectPersonalPrincipal);
|
|
46
46
|
const selectOrganizationPrincipal = usePrincipalStore(state => state.selectOrganizationPrincipal);
|
|
47
47
|
const selectTeamPrincipal = usePrincipalStore(state => state.selectTeamPrincipal);
|
|
48
|
-
const
|
|
48
|
+
const setBillingEntity = useBillingEntityStore(state => state.setBillingEntity);
|
|
49
49
|
const { selectedPrincipalKind, selectedPrincipalUid, selectedPrincipalHandle, selectedTeamParentOrganizationHandle, } = useSelectedPrincipal();
|
|
50
50
|
const personalUid = user?.uid || user?.id || '';
|
|
51
51
|
const personalHandle = user?.handle || '';
|
|
52
52
|
const selectUser = (uid, handle) => {
|
|
53
53
|
selectPersonalPrincipal(uid, handle);
|
|
54
|
-
|
|
54
|
+
setBillingEntity({ kind: 'user', uid, handle });
|
|
55
55
|
};
|
|
56
56
|
const selectOrganization = (uid, handle) => {
|
|
57
57
|
selectOrganizationPrincipal(uid, handle);
|
|
58
|
-
|
|
58
|
+
setBillingEntity({ kind: 'organization', uid, handle });
|
|
59
59
|
};
|
|
60
60
|
const selectTeam = (team, orgHandle) => {
|
|
61
61
|
if (!team.organizationUid) {
|
|
@@ -67,7 +67,7 @@ export function PrincipalSwitcherMenu({ maxLabelChars = 48, fullWidth = true, sh
|
|
|
67
67
|
organizationUid: team.organizationUid,
|
|
68
68
|
organizationHandle: orgHandle,
|
|
69
69
|
});
|
|
70
|
-
|
|
70
|
+
setBillingEntity({
|
|
71
71
|
kind: 'organization',
|
|
72
72
|
uid: team.organizationUid,
|
|
73
73
|
handle: orgHandle,
|
|
@@ -85,7 +85,7 @@ export function PrincipalSwitcherMenu({ maxLabelChars = 48, fullWidth = true, sh
|
|
|
85
85
|
}
|
|
86
86
|
setTeamsLoading(true);
|
|
87
87
|
try {
|
|
88
|
-
const baseUrl =
|
|
88
|
+
const baseUrl = iamUrl || configuration.iamUrl;
|
|
89
89
|
const response = await fetchMemberships(token, baseUrl);
|
|
90
90
|
const rawMemberships = Array.isArray(response?.memberships)
|
|
91
91
|
? response.memberships
|
|
@@ -95,6 +95,8 @@ export function PrincipalSwitcherMenu({ maxLabelChars = 48, fullWidth = true, sh
|
|
|
95
95
|
.map((membership) => ({
|
|
96
96
|
uid: String(membership?.uid || membership?.id || '').trim(),
|
|
97
97
|
handle: String(membership?.handle || '').trim(),
|
|
98
|
+
name: String(membership?.name || '').trim() || undefined,
|
|
99
|
+
displayName: String(membership?.display_name || membership?.displayName || '').trim() || undefined,
|
|
98
100
|
organizationUid: String(membership?.organization_uid || '').trim() || undefined,
|
|
99
101
|
organizationHandle: String(membership?.organization_handle ||
|
|
100
102
|
membership?.organization?.handle ||
|
|
@@ -120,7 +122,7 @@ export function PrincipalSwitcherMenu({ maxLabelChars = 48, fullWidth = true, sh
|
|
|
120
122
|
return () => {
|
|
121
123
|
cancelled = true;
|
|
122
124
|
};
|
|
123
|
-
}, [token,
|
|
125
|
+
}, [token, iamUrl, configuration.iamUrl]);
|
|
124
126
|
useEffect(() => {
|
|
125
127
|
if (!personalUid || !personalHandle) {
|
|
126
128
|
return;
|
|
@@ -132,7 +134,8 @@ export function PrincipalSwitcherMenu({ maxLabelChars = 48, fullWidth = true, sh
|
|
|
132
134
|
// Keep explicit organization/team selections stable across route transitions,
|
|
133
135
|
// even while memberships/organizations are still loading or temporarily
|
|
134
136
|
// unavailable for a given view.
|
|
135
|
-
if (selectedPrincipalKind === 'organization' ||
|
|
137
|
+
if (selectedPrincipalKind === 'organization' ||
|
|
138
|
+
selectedPrincipalKind === 'team') {
|
|
136
139
|
return;
|
|
137
140
|
}
|
|
138
141
|
if (selectedPrincipalKind === 'personal' &&
|
|
@@ -203,122 +206,138 @@ export function PrincipalSwitcherMenu({ maxLabelChars = 48, fullWidth = true, sh
|
|
|
203
206
|
textTransform: 'lowercase',
|
|
204
207
|
lineHeight: 1.2,
|
|
205
208
|
};
|
|
206
|
-
return (_jsxs(ActionMenu, { children: [_jsx(ActionMenu.Anchor, { children: showClosedBorder ? (_jsxs(Box, { as: "button", type: "button", "aria-label": "Switch principal", sx: {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
alignItems: 'center',
|
|
214
|
-
justifyContent: 'space-between',
|
|
215
|
-
gap: 2,
|
|
216
|
-
bg: 'canvas.subtle',
|
|
217
|
-
border: '1px solid',
|
|
218
|
-
borderColor: 'border.default',
|
|
219
|
-
borderRadius: 2,
|
|
220
|
-
cursor: 'pointer',
|
|
221
|
-
textAlign: 'left',
|
|
222
|
-
transition: 'background-color 120ms ease',
|
|
223
|
-
':hover': {
|
|
224
|
-
bg: 'canvas.subtle',
|
|
225
|
-
},
|
|
226
|
-
':focus-visible': {
|
|
227
|
-
bg: 'canvas.subtle',
|
|
228
|
-
outline: '2px solid',
|
|
229
|
-
outlineColor: 'accent.emphasis',
|
|
230
|
-
outlineOffset: '2px',
|
|
231
|
-
},
|
|
232
|
-
}, children: [_jsx(Box, { sx: {
|
|
233
|
-
display: 'inline-flex',
|
|
234
|
-
alignItems: 'center',
|
|
235
|
-
color: 'fg.muted',
|
|
236
|
-
flexShrink: 0,
|
|
237
|
-
}, children: selectedPrincipalKind === 'organization' ? (_jsx(OrganizationIcon, { size: 16 })) : selectedPrincipalKind === 'team' ? (_jsx(PeopleIcon, { size: 16 })) : (_jsx(PersonIcon, { size: 16 })) }), _jsx(Box, { sx: { minWidth: 0, flex: 1 }, children: _jsx(Text, { sx: {
|
|
238
|
-
display: 'block',
|
|
239
|
-
color: 'accent.fg',
|
|
240
|
-
fontWeight: 'semibold',
|
|
241
|
-
fontSize: 'inherit',
|
|
242
|
-
whiteSpace: 'nowrap',
|
|
243
|
-
overflow: 'hidden',
|
|
244
|
-
textOverflow: 'ellipsis',
|
|
245
|
-
maxWidth: '100%',
|
|
246
|
-
}, children: selectedPrincipalLabelClosed }) }), isPlatformAdmin && isCurrentUserPrincipal ? (_jsx(Box, { sx: {
|
|
247
|
-
flexShrink: 0,
|
|
248
|
-
display: 'inline-flex',
|
|
249
|
-
alignItems: 'center',
|
|
250
|
-
}, children: _jsx(Label, { variant: "secondary", size: "small", sx: adminBadgeSx, children: "admin" }) })) : null, _jsx(Box, { sx: {
|
|
251
|
-
display: 'inline-flex',
|
|
252
|
-
alignItems: 'center',
|
|
253
|
-
color: 'fg.muted',
|
|
254
|
-
flexShrink: 0,
|
|
255
|
-
ml: isPlatformAdmin && isCurrentUserPrincipal ? 1 : 0,
|
|
256
|
-
}, children: _jsx(TriangleDownIcon, { size: 12 }) })] })) : (_jsx(Button, { variant: "invisible", size: "small", "aria-label": "Switch principal", trailingVisual: TriangleDownIcon, sx: {
|
|
257
|
-
maxWidth: fullWidth ? '100%' : ['200px', '260px', '360px'],
|
|
258
|
-
width: fullWidth ? '100%' : 'auto',
|
|
259
|
-
}, children: _jsxs(Box, { sx: {
|
|
260
|
-
display: 'inline-flex',
|
|
209
|
+
return (_jsx(ThemeProvider, { children: _jsxs(ActionMenu, { children: [_jsx(ActionMenu.Anchor, { children: showClosedBorder ? (_jsxs(Box, { as: "button", type: "button", "aria-label": "Switch principal", sx: {
|
|
210
|
+
width: fullWidth ? '100%' : 'auto',
|
|
211
|
+
boxSizing: 'border-box',
|
|
212
|
+
px: 2,
|
|
213
|
+
py: 2,
|
|
214
|
+
fontSize: 0,
|
|
215
|
+
display: 'flex',
|
|
261
216
|
alignItems: 'center',
|
|
262
|
-
|
|
263
|
-
|
|
217
|
+
justifyContent: 'space-between',
|
|
218
|
+
gap: 2,
|
|
219
|
+
bg: 'canvas.subtle',
|
|
220
|
+
border: '1px solid',
|
|
221
|
+
borderColor: 'border.default',
|
|
222
|
+
borderRadius: 2,
|
|
223
|
+
cursor: 'pointer',
|
|
224
|
+
textAlign: 'left',
|
|
225
|
+
transition: 'background-color 120ms ease',
|
|
226
|
+
':hover': {
|
|
227
|
+
bg: 'canvas.subtle',
|
|
228
|
+
},
|
|
229
|
+
':focus-visible': {
|
|
230
|
+
bg: 'canvas.subtle',
|
|
231
|
+
outline: '2px solid',
|
|
232
|
+
outlineColor: 'accent.emphasis',
|
|
233
|
+
outlineOffset: '2px',
|
|
234
|
+
},
|
|
264
235
|
}, children: [_jsx(Box, { sx: {
|
|
265
236
|
display: 'inline-flex',
|
|
266
237
|
alignItems: 'center',
|
|
267
238
|
color: 'fg.muted',
|
|
268
239
|
flexShrink: 0,
|
|
269
|
-
}, children: selectedPrincipalKind === 'organization' ? (_jsx(
|
|
270
|
-
|
|
271
|
-
overflow: 'hidden',
|
|
272
|
-
textOverflow: 'ellipsis',
|
|
273
|
-
whiteSpace: 'nowrap',
|
|
274
|
-
}, children: _jsx(Text, { sx: {
|
|
240
|
+
}, children: selectedPrincipalKind === 'organization' ? (_jsx(PeopleIcon, { size: 16 })) : selectedPrincipalKind === 'team' ? (_jsx(OrganizationIcon, { size: 16 })) : (_jsx(PersonIcon, { size: 16 })) }), _jsx(Box, { sx: { minWidth: 0, flex: 1 }, children: _jsx(Text, { sx: {
|
|
241
|
+
display: 'block',
|
|
275
242
|
color: 'accent.fg',
|
|
276
243
|
fontWeight: 'semibold',
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
244
|
+
fontSize: 'inherit',
|
|
245
|
+
whiteSpace: 'nowrap',
|
|
246
|
+
overflow: 'hidden',
|
|
247
|
+
textOverflow: 'ellipsis',
|
|
248
|
+
maxWidth: '100%',
|
|
249
|
+
}, children: selectedPrincipalLabelClosed }) }), isPlatformAdmin && isCurrentUserPrincipal ? (_jsx(Box, { sx: {
|
|
250
|
+
flexShrink: 0,
|
|
251
|
+
display: 'inline-flex',
|
|
252
|
+
alignItems: 'center',
|
|
253
|
+
}, children: _jsx(Label, { variant: "secondary", size: "small", sx: adminBadgeSx, children: "admin" }) })) : null, _jsx(Box, { sx: {
|
|
254
|
+
display: 'inline-flex',
|
|
255
|
+
alignItems: 'center',
|
|
256
|
+
color: 'fg.muted',
|
|
257
|
+
flexShrink: 0,
|
|
258
|
+
ml: isPlatformAdmin && isCurrentUserPrincipal ? 1 : 0,
|
|
259
|
+
}, children: _jsx(TriangleDownIcon, { size: 12 }) })] })) : (_jsx(Button, { variant: "invisible", size: "small", "aria-label": "Switch principal", trailingVisual: TriangleDownIcon, sx: {
|
|
260
|
+
maxWidth: fullWidth ? '100%' : ['200px', '260px', '360px'],
|
|
261
|
+
width: fullWidth ? '100%' : 'auto',
|
|
262
|
+
}, children: _jsxs(Box, { sx: {
|
|
263
|
+
display: 'inline-flex',
|
|
264
|
+
alignItems: 'center',
|
|
265
|
+
gap: 1,
|
|
266
|
+
minWidth: 0,
|
|
267
|
+
}, children: [_jsx(Box, { sx: {
|
|
268
|
+
display: 'inline-flex',
|
|
269
|
+
alignItems: 'center',
|
|
270
|
+
color: 'fg.muted',
|
|
271
|
+
flexShrink: 0,
|
|
272
|
+
}, children: selectedPrincipalKind === 'organization' ? (_jsx(PeopleIcon, { size: 16 })) : selectedPrincipalKind === 'team' ? (_jsx(OrganizationIcon, { size: 16 })) : (_jsx(PersonIcon, { size: 16 })) }), _jsx(Box, { sx: {
|
|
273
|
+
minWidth: 0,
|
|
274
|
+
overflow: 'hidden',
|
|
275
|
+
textOverflow: 'ellipsis',
|
|
276
|
+
whiteSpace: 'nowrap',
|
|
277
|
+
}, children: _jsx(Text, { sx: {
|
|
278
|
+
color: 'accent.fg',
|
|
279
|
+
fontWeight: 'semibold',
|
|
280
|
+
}, children: selectedPrincipalLabelClosed }) }), isPlatformAdmin && isCurrentUserPrincipal ? (_jsx(Label, { variant: "secondary", size: "small", sx: adminBadgeSx, children: "admin" })) : null] }) })) }), _jsx(ActionMenu.Overlay, { width: "large", children: _jsxs(ActionList, { children: [_jsxs(ActionList.Group, { children: [_jsx(ActionList.GroupHeading, { children: "Personal" }), _jsxs(ActionList.Item, { disabled: isCurrentUserPrincipal, selected: isCurrentUserPrincipal, sx: isCurrentUserPrincipal ? selectedItemSx : undefined, onSelect: () => {
|
|
281
|
+
if (isCurrentUserPrincipal) {
|
|
292
282
|
return;
|
|
293
283
|
}
|
|
294
|
-
if (
|
|
295
|
-
|
|
296
|
-
}
|
|
297
|
-
}, children: [_jsx(ActionList.LeadingVisual, { children: _jsx(OrganizationIcon, {}) }), _jsx(Box, { as: "span", title: displayHandleText(organization.handle), sx: {
|
|
298
|
-
display: 'block',
|
|
299
|
-
minWidth: 0,
|
|
300
|
-
overflow: 'hidden',
|
|
301
|
-
textOverflow: 'ellipsis',
|
|
302
|
-
whiteSpace: 'nowrap',
|
|
303
|
-
}, children: _jsx(DisplayHandle, { handle: organization.handle }) })] }, organizationUid));
|
|
304
|
-
}))] }), _jsxs(ActionList.Group, { children: [_jsx(ActionList.GroupHeading, { children: "Teams" }), teams.length === 0 ? (_jsx(ActionList.Item, { disabled: true, children: "No teams" })) : (teams.map(team => {
|
|
305
|
-
const isCurrentTeamPrincipal = selectedPrincipalKind === 'team' &&
|
|
306
|
-
selectedPrincipalUid === team.uid;
|
|
307
|
-
const orgHandle = resolveTeamOrganizationHandle(team) ||
|
|
308
|
-
personalHandle ||
|
|
309
|
-
'organization';
|
|
310
|
-
return (_jsxs(ActionList.Item, { disabled: isCurrentTeamPrincipal, selected: isCurrentTeamPrincipal, sx: isCurrentTeamPrincipal ? selectedItemSx : undefined, onSelect: () => {
|
|
311
|
-
if (isCurrentTeamPrincipal) {
|
|
312
|
-
return;
|
|
284
|
+
if (personalUid && personalHandle) {
|
|
285
|
+
selectUser(personalUid, personalHandle);
|
|
313
286
|
}
|
|
314
|
-
|
|
315
|
-
}, children: [_jsx(ActionList.LeadingVisual, { children: _jsx(PeopleIcon, {}) }), _jsxs(Box, { as: "span", title: `${displayHandleText(orgHandle)}/${displayHandleText(team.handle, { withAt: false })}`, sx: {
|
|
287
|
+
}, children: [_jsx(ActionList.LeadingVisual, { children: _jsx(PersonIcon, {}) }), _jsxs(Box, { as: "span", sx: {
|
|
316
288
|
display: 'block',
|
|
317
289
|
minWidth: 0,
|
|
318
290
|
overflow: 'hidden',
|
|
319
291
|
textOverflow: 'ellipsis',
|
|
320
292
|
whiteSpace: 'nowrap',
|
|
321
|
-
}, children: [
|
|
322
|
-
|
|
293
|
+
}, children: [String(user?.friendlyName ||
|
|
294
|
+
user?.displayName ||
|
|
295
|
+
personalHandle ||
|
|
296
|
+
'Me').trim(), _jsxs(Box, { as: "span", sx: { color: 'fg.muted' }, children: [' ', "\u00B7 ", _jsx(DisplayHandle, { handle: personalHandle || 'me' })] })] }), isPlatformAdmin ? (_jsx(ActionList.TrailingVisual, { children: _jsx(Label, { variant: "secondary", size: "small", sx: adminBadgeSx, children: "admin" }) })) : null] })] }), _jsxs(ActionList.Group, { children: [_jsx(ActionList.GroupHeading, { children: "Organizations" }), organizations.length === 0 ? (_jsx(ActionList.Item, { disabled: true, children: "No organizations" })) : (organizations.map((organization) => {
|
|
297
|
+
const organizationUid = getOrganizationUid(organization);
|
|
298
|
+
const isCurrentOrganizationPrincipal = selectedPrincipalKind === 'organization' &&
|
|
299
|
+
selectedPrincipalUid === organizationUid;
|
|
300
|
+
return (_jsxs(ActionList.Item, { disabled: isCurrentOrganizationPrincipal, selected: isCurrentOrganizationPrincipal, sx: isCurrentOrganizationPrincipal
|
|
301
|
+
? selectedItemSx
|
|
302
|
+
: undefined, onSelect: () => {
|
|
303
|
+
if (isCurrentOrganizationPrincipal) {
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
if (organizationUid && organization.handle) {
|
|
307
|
+
selectOrganization(organizationUid, organization.handle);
|
|
308
|
+
}
|
|
309
|
+
}, children: [_jsx(ActionList.LeadingVisual, { children: _jsx(PeopleIcon, {}) }), _jsxs(Box, { as: "span", title: displayHandleText(organization.handle), sx: {
|
|
310
|
+
display: 'block',
|
|
311
|
+
minWidth: 0,
|
|
312
|
+
overflow: 'hidden',
|
|
313
|
+
textOverflow: 'ellipsis',
|
|
314
|
+
whiteSpace: 'nowrap',
|
|
315
|
+
}, children: [String(organization?.display_name ||
|
|
316
|
+
organization?.displayName ||
|
|
317
|
+
organization?.name ||
|
|
318
|
+
organization?.handle ||
|
|
319
|
+
'Organization').trim(), _jsxs(Box, { as: "span", sx: { color: 'fg.muted' }, children: [' ', "\u00B7 ", _jsx(DisplayHandle, { handle: organization.handle })] })] })] }, organizationUid));
|
|
320
|
+
}))] }), _jsxs(ActionList.Group, { children: [_jsx(ActionList.GroupHeading, { children: "Teams" }), teams.length === 0 ? (_jsx(ActionList.Item, { disabled: true, children: "No teams" })) : (teams.map(team => {
|
|
321
|
+
const isCurrentTeamPrincipal = selectedPrincipalKind === 'team' &&
|
|
322
|
+
selectedPrincipalUid === team.uid;
|
|
323
|
+
const orgHandle = resolveTeamOrganizationHandle(team) ||
|
|
324
|
+
personalHandle ||
|
|
325
|
+
'organization';
|
|
326
|
+
return (_jsxs(ActionList.Item, { disabled: isCurrentTeamPrincipal, selected: isCurrentTeamPrincipal, sx: isCurrentTeamPrincipal ? selectedItemSx : undefined, onSelect: () => {
|
|
327
|
+
if (isCurrentTeamPrincipal) {
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
selectTeam(team, orgHandle);
|
|
331
|
+
}, children: [_jsx(ActionList.LeadingVisual, { children: _jsx(OrganizationIcon, {}) }), _jsxs(Box, { as: "span", title: `${displayHandleText(orgHandle)}/${displayHandleText(team.handle, { withAt: false })}`, sx: {
|
|
332
|
+
display: 'block',
|
|
333
|
+
minWidth: 0,
|
|
334
|
+
overflow: 'hidden',
|
|
335
|
+
textOverflow: 'ellipsis',
|
|
336
|
+
whiteSpace: 'nowrap',
|
|
337
|
+
}, children: [String(team.displayName ||
|
|
338
|
+
team.name ||
|
|
339
|
+
team.handle ||
|
|
340
|
+
'Team').trim(), _jsxs(Box, { as: "span", sx: { color: 'fg.muted' }, children: [' ', "\u00B7 ", _jsx(DisplayHandle, { handle: orgHandle }), "/", _jsx(DisplayHandle, { handle: team.handle, withAt: false })] })] })] }, team.uid));
|
|
341
|
+
}))] })] }) })] }) }));
|
|
323
342
|
}
|
|
324
343
|
export default PrincipalSwitcherMenu;
|
|
@@ -433,8 +433,8 @@ export function ShareAccessComponent({ isOpen, requestUrl, resourceLabel, resour
|
|
|
433
433
|
]);
|
|
434
434
|
// ----- Derived -----
|
|
435
435
|
const canRequest = Boolean(requestUrl && token);
|
|
436
|
-
const canSearchPrincipals = Boolean(configuration?.
|
|
437
|
-
const
|
|
436
|
+
const canSearchPrincipals = Boolean(configuration?.iamUrl && token);
|
|
437
|
+
const iamUrl = configuration?.iamUrl;
|
|
438
438
|
const principalKindsSet = useMemo(() => new Set(principalKinds), [principalKinds]);
|
|
439
439
|
const principalKindsKey = useMemo(() => [...principalKinds].sort().join('|'), [principalKinds]);
|
|
440
440
|
const aclEntries = useMemo(() => buildAclEntries(access, principalKinds), [access, principalKinds]);
|
|
@@ -592,14 +592,14 @@ export function ShareAccessComponent({ isOpen, requestUrl, resourceLabel, resour
|
|
|
592
592
|
]);
|
|
593
593
|
// ----- Fetch shareable principals on open -----
|
|
594
594
|
useEffect(() => {
|
|
595
|
-
if (!isOpen || !canSearchPrincipals || !
|
|
595
|
+
if (!isOpen || !canSearchPrincipals || !iamUrl || !token) {
|
|
596
596
|
return;
|
|
597
597
|
}
|
|
598
598
|
let cancelled = false;
|
|
599
599
|
const run = async () => {
|
|
600
600
|
setIsLoadingShareable(true);
|
|
601
601
|
try {
|
|
602
|
-
const response = await fetch(`${
|
|
602
|
+
const response = await fetch(`${iamUrl}/api/iam/v1/principals/shareable`, {
|
|
603
603
|
method: 'GET',
|
|
604
604
|
headers: {
|
|
605
605
|
'Content-Type': 'application/json',
|
|
@@ -671,7 +671,7 @@ export function ShareAccessComponent({ isOpen, requestUrl, resourceLabel, resour
|
|
|
671
671
|
return () => {
|
|
672
672
|
cancelled = true;
|
|
673
673
|
};
|
|
674
|
-
}, [isOpen, canSearchPrincipals,
|
|
674
|
+
}, [isOpen, canSearchPrincipals, iamUrl, token, mergePrincipalCacheEntry]);
|
|
675
675
|
// ----- Debounce search query -----
|
|
676
676
|
useEffect(() => {
|
|
677
677
|
const timeout = window.setTimeout(() => {
|
|
@@ -683,7 +683,7 @@ export function ShareAccessComponent({ isOpen, requestUrl, resourceLabel, resour
|
|
|
683
683
|
}, [searchQuery]);
|
|
684
684
|
// ----- Run search against /principals/search -----
|
|
685
685
|
useEffect(() => {
|
|
686
|
-
if (!isOpen || !canSearchPrincipals || !
|
|
686
|
+
if (!isOpen || !canSearchPrincipals || !iamUrl || !token) {
|
|
687
687
|
setSearchResults([]);
|
|
688
688
|
return;
|
|
689
689
|
}
|
|
@@ -704,7 +704,7 @@ export function ShareAccessComponent({ isOpen, requestUrl, resourceLabel, resour
|
|
|
704
704
|
let response;
|
|
705
705
|
let payload;
|
|
706
706
|
try {
|
|
707
|
-
response = await fetch(`${
|
|
707
|
+
response = await fetch(`${iamUrl}/api/iam/v1/principals/search`, {
|
|
708
708
|
method: 'POST',
|
|
709
709
|
headers: {
|
|
710
710
|
'Content-Type': 'application/json',
|
|
@@ -837,7 +837,7 @@ export function ShareAccessComponent({ isOpen, requestUrl, resourceLabel, resour
|
|
|
837
837
|
}, [
|
|
838
838
|
isOpen,
|
|
839
839
|
canSearchPrincipals,
|
|
840
|
-
|
|
840
|
+
iamUrl,
|
|
841
841
|
token,
|
|
842
842
|
normalizedDebouncedSearch,
|
|
843
843
|
principalKindsKey,
|
|
@@ -847,7 +847,7 @@ export function ShareAccessComponent({ isOpen, requestUrl, resourceLabel, resour
|
|
|
847
847
|
]);
|
|
848
848
|
// ----- Hydrate ACL user uids in bulk -----
|
|
849
849
|
useEffect(() => {
|
|
850
|
-
if (!isOpen || !canSearchPrincipals || !
|
|
850
|
+
if (!isOpen || !canSearchPrincipals || !iamUrl || !token) {
|
|
851
851
|
return;
|
|
852
852
|
}
|
|
853
853
|
const userUids = Array.from(new Set([
|
|
@@ -881,7 +881,7 @@ export function ShareAccessComponent({ isOpen, requestUrl, resourceLabel, resour
|
|
|
881
881
|
return acc;
|
|
882
882
|
}, {}));
|
|
883
883
|
try {
|
|
884
|
-
const response = await fetch(`${
|
|
884
|
+
const response = await fetch(`${iamUrl}/api/iam/v1/users/bulk`, {
|
|
885
885
|
method: 'POST',
|
|
886
886
|
headers: {
|
|
887
887
|
'Content-Type': 'application/json',
|
|
@@ -942,7 +942,7 @@ export function ShareAccessComponent({ isOpen, requestUrl, resourceLabel, resour
|
|
|
942
942
|
}, [
|
|
943
943
|
isOpen,
|
|
944
944
|
canSearchPrincipals,
|
|
945
|
-
|
|
945
|
+
iamUrl,
|
|
946
946
|
token,
|
|
947
947
|
aclEntries,
|
|
948
948
|
ownerPrincipals,
|
|
@@ -951,7 +951,7 @@ export function ShareAccessComponent({ isOpen, requestUrl, resourceLabel, resour
|
|
|
951
951
|
]);
|
|
952
952
|
// ----- Hydrate ACL team uids individually -----
|
|
953
953
|
useEffect(() => {
|
|
954
|
-
if (!isOpen || !canSearchPrincipals || !
|
|
954
|
+
if (!isOpen || !canSearchPrincipals || !iamUrl || !token) {
|
|
955
955
|
return;
|
|
956
956
|
}
|
|
957
957
|
const unknownTeams = aclEntries.filter(entry => {
|
|
@@ -967,7 +967,7 @@ export function ShareAccessComponent({ isOpen, requestUrl, resourceLabel, resour
|
|
|
967
967
|
let cancelled = false;
|
|
968
968
|
void Promise.all(unknownTeams.map(async (entry) => {
|
|
969
969
|
try {
|
|
970
|
-
const response = await fetch(`${
|
|
970
|
+
const response = await fetch(`${iamUrl}/api/iam/v1/teams/${encodeURIComponent(entry.uid)}`, {
|
|
971
971
|
method: 'GET',
|
|
972
972
|
headers: {
|
|
973
973
|
'Content-Type': 'application/json',
|
|
@@ -1001,7 +1001,7 @@ export function ShareAccessComponent({ isOpen, requestUrl, resourceLabel, resour
|
|
|
1001
1001
|
}, [
|
|
1002
1002
|
isOpen,
|
|
1003
1003
|
canSearchPrincipals,
|
|
1004
|
-
|
|
1004
|
+
iamUrl,
|
|
1005
1005
|
token,
|
|
1006
1006
|
aclEntries,
|
|
1007
1007
|
principalCache,
|
|
@@ -12,7 +12,7 @@ export type IDatalayerCoreConfig = {
|
|
|
12
12
|
* The base URL for the Datalayer platform API.
|
|
13
13
|
* @example "https://prod1.datalayer.run"
|
|
14
14
|
*/
|
|
15
|
-
|
|
15
|
+
datalayerUrl: string;
|
|
16
16
|
/**
|
|
17
17
|
* Datalayer API authentication token.
|
|
18
18
|
* Used for authenticating requests to the Datalayer platform.
|
|
@@ -46,51 +46,51 @@ export type IDatalayerCoreConfig = {
|
|
|
46
46
|
/**
|
|
47
47
|
* IAM API URL.
|
|
48
48
|
*/
|
|
49
|
-
|
|
49
|
+
iamUrl: string;
|
|
50
50
|
/**
|
|
51
51
|
* Runtimes API URL.
|
|
52
52
|
*/
|
|
53
|
-
|
|
53
|
+
runtimesUrl: string;
|
|
54
54
|
/**
|
|
55
55
|
* Spacer API URL.
|
|
56
56
|
*/
|
|
57
|
-
|
|
57
|
+
spacerUrl: string;
|
|
58
58
|
/**
|
|
59
59
|
* Library API URL.
|
|
60
60
|
*/
|
|
61
|
-
|
|
61
|
+
libraryUrl: string;
|
|
62
62
|
/**
|
|
63
63
|
* AI Agents API URL.
|
|
64
64
|
*/
|
|
65
|
-
|
|
65
|
+
aiAgentsUrl: string;
|
|
66
66
|
/**
|
|
67
67
|
* AI Inference API URL.
|
|
68
68
|
*/
|
|
69
|
-
|
|
69
|
+
aiInferenceUrl: string;
|
|
70
70
|
/**
|
|
71
71
|
* MCP Servers API URL.
|
|
72
72
|
*/
|
|
73
|
-
|
|
73
|
+
mcpServersUrl: string;
|
|
74
74
|
/**
|
|
75
75
|
* OTEL (OpenTelemetry) API URL.
|
|
76
76
|
*/
|
|
77
|
-
|
|
77
|
+
otelUrl: string;
|
|
78
78
|
/**
|
|
79
79
|
* Growth API URL.
|
|
80
80
|
*/
|
|
81
|
-
|
|
81
|
+
growthUrl: string;
|
|
82
82
|
/**
|
|
83
83
|
* Inbounds API URL.
|
|
84
84
|
*/
|
|
85
|
-
|
|
85
|
+
inboundsUrl: string;
|
|
86
86
|
/**
|
|
87
87
|
* Success API URL.
|
|
88
88
|
*/
|
|
89
|
-
|
|
89
|
+
successUrl: string;
|
|
90
90
|
/**
|
|
91
91
|
* Support API URL.
|
|
92
92
|
*/
|
|
93
|
-
|
|
93
|
+
supportUrl: string;
|
|
94
94
|
/**
|
|
95
95
|
* Load configuration from server.
|
|
96
96
|
*/
|
|
@@ -26,7 +26,7 @@ export class DatalayerConfiguration {
|
|
|
26
26
|
* Default configuration values for Datalayer
|
|
27
27
|
*/
|
|
28
28
|
export const DEFAULT_DATALAYER_CONFIG = {
|
|
29
|
-
|
|
29
|
+
datalayerUrl: 'https://oss.datalayer.run',
|
|
30
30
|
credits: 100,
|
|
31
31
|
cpuEnvironment: 'ai-agents-env',
|
|
32
32
|
gpuEnvironment: 'ai-env',
|
|
@@ -37,7 +37,7 @@ export const DEFAULT_DATALAYER_CONFIG = {
|
|
|
37
37
|
export function isDatalayerConfig(config) {
|
|
38
38
|
return (config &&
|
|
39
39
|
typeof config === 'object' &&
|
|
40
|
-
typeof config.
|
|
40
|
+
typeof config.datalayerUrl === 'string' &&
|
|
41
41
|
typeof config.token === 'string' &&
|
|
42
42
|
typeof config.credits === 'number' &&
|
|
43
43
|
typeof config.cpuEnvironment === 'string' &&
|
|
@@ -51,10 +51,10 @@ export function isDatalayerConfig(config) {
|
|
|
51
51
|
export function mergeConfigWithDefaults(config) {
|
|
52
52
|
if (!config)
|
|
53
53
|
return undefined;
|
|
54
|
-
// If we have required fields (token and
|
|
55
|
-
if (config.token && config.
|
|
54
|
+
// If we have required fields (token and datalayerUrl), merge with defaults for optional fields
|
|
55
|
+
if (config.token && config.datalayerUrl) {
|
|
56
56
|
return {
|
|
57
|
-
|
|
57
|
+
datalayerUrl: config.datalayerUrl,
|
|
58
58
|
token: config.token,
|
|
59
59
|
credits: config.credits ?? DEFAULT_DATALAYER_CONFIG.credits,
|
|
60
60
|
cpuEnvironment: config.cpuEnvironment ?? DEFAULT_DATALAYER_CONFIG.cpuEnvironment,
|
package/lib/hooks/index.d.ts
CHANGED
|
@@ -28,8 +28,8 @@ export * from './useProjectStore';
|
|
|
28
28
|
export * from './useMobile';
|
|
29
29
|
export * from './useUsageRefreshStore';
|
|
30
30
|
export * from './usePrincipalStore';
|
|
31
|
-
export * from './
|
|
31
|
+
export * from './useBillingEntityStore';
|
|
32
32
|
export * from './useSelectedPrincipal';
|
|
33
|
-
export * from './
|
|
33
|
+
export * from './useSelectedBillingEntity';
|
|
34
34
|
export * from './useVisibilityObserver';
|
|
35
35
|
export * from './useWindowSize';
|
package/lib/hooks/index.js
CHANGED
|
@@ -33,8 +33,8 @@ export * from './useProjectStore.js';
|
|
|
33
33
|
export * from './useMobile.js';
|
|
34
34
|
export * from './useUsageRefreshStore.js';
|
|
35
35
|
export * from './usePrincipalStore.js';
|
|
36
|
-
export * from './
|
|
36
|
+
export * from './useBillingEntityStore.js';
|
|
37
37
|
export * from './useSelectedPrincipal.js';
|
|
38
|
-
export * from './
|
|
38
|
+
export * from './useSelectedBillingEntity.js';
|
|
39
39
|
export * from './useVisibilityObserver.js';
|
|
40
40
|
export * from './useWindowSize.js';
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The kind of billing entity currently in scope.
|
|
3
|
+
*
|
|
4
|
+
* An account is the *billing* principal — only users and organizations
|
|
5
|
+
* are accounts. Teams are NOT accounts; when a team is the selected
|
|
6
|
+
* principal, the billing entity is the team's parent organization.
|
|
7
|
+
*/
|
|
8
|
+
export type BillingEntityKind = 'user' | 'organization';
|
|
9
|
+
export type BillingEntityState = {
|
|
10
|
+
billingEntityKind: BillingEntityKind;
|
|
11
|
+
billingEntityUid?: string;
|
|
12
|
+
billingEntityHandle?: string;
|
|
13
|
+
setBillingEntity: (args: {
|
|
14
|
+
kind: BillingEntityKind;
|
|
15
|
+
uid: string;
|
|
16
|
+
handle: string;
|
|
17
|
+
}) => void;
|
|
18
|
+
resetBillingEntity: () => void;
|
|
19
|
+
};
|
|
20
|
+
export declare const useBillingEntityStore: import("zustand").UseBoundStore<Omit<import("zustand").StoreApi<BillingEntityState>, "persist"> & {
|
|
21
|
+
persist: {
|
|
22
|
+
setOptions: (options: Partial<import("zustand/middleware").PersistOptions<BillingEntityState, unknown>>) => void;
|
|
23
|
+
clearStorage: () => void;
|
|
24
|
+
rehydrate: () => Promise<void> | void;
|
|
25
|
+
hasHydrated: () => boolean;
|
|
26
|
+
onHydrate: (fn: (state: BillingEntityState) => void) => () => void;
|
|
27
|
+
onFinishHydration: (fn: (state: BillingEntityState) => void) => () => void;
|
|
28
|
+
getOptions: () => Partial<import("zustand/middleware").PersistOptions<BillingEntityState, unknown>>;
|
|
29
|
+
};
|
|
30
|
+
}>;
|
|
31
|
+
export default useBillingEntityStore;
|