@datalayer/core 1.1.43 → 1.1.46
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/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 +130 -111
- package/lib/config/Configuration.d.ts +18 -0
- 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 +32 -16
- 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/otel/client/OtelClient.js +2 -3
- package/lib/otel/hooks/index.js +6 -6
- package/lib/otel/views/OtelLive.js +2 -2
- package/lib/state/substates/CoreState.d.ts +11 -0
- package/lib/state/substates/CoreState.js +18 -0
- 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,10 +26,10 @@ 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
35
|
const { user, token, iamUrl } = useIAMStore();
|
|
@@ -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,
|
|
@@ -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 ||
|
|
@@ -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;
|
|
@@ -73,8 +73,26 @@ export type IDatalayerCoreConfig = {
|
|
|
73
73
|
mcpServersUrl: string;
|
|
74
74
|
/**
|
|
75
75
|
* OTEL (OpenTelemetry) API URL.
|
|
76
|
+
*
|
|
77
|
+
* This is the endpoint runtimes/agents export telemetry to. In the browser
|
|
78
|
+
* it is also the default endpoint used to *consume* (query) telemetry, unless
|
|
79
|
+
* {@link otelInUrl} is provided.
|
|
76
80
|
*/
|
|
77
81
|
otelUrl: string;
|
|
82
|
+
/**
|
|
83
|
+
* OTEL (OpenTelemetry) *consume* (read/query) API URL override.
|
|
84
|
+
*
|
|
85
|
+
* When set, the UI reads telemetry (metrics, traces, logs) from this URL
|
|
86
|
+
* instead of {@link otelUrl}. When empty/undefined, {@link otelUrl} is used.
|
|
87
|
+
*
|
|
88
|
+
* Primary use case: local development. When runtimes run in the cloud and
|
|
89
|
+
* export their telemetry to the production OTEL service, a local UI must fetch
|
|
90
|
+
* telemetry from production rather than from the local OTEL endpoint. Set this
|
|
91
|
+
* to e.g. `https://prod1.datalayer.run` while `otelUrl` stays local.
|
|
92
|
+
*
|
|
93
|
+
* Fed by the `DATALAYER_OTEL_IN_URL` environment variable at build/deploy time.
|
|
94
|
+
*/
|
|
95
|
+
otelInUrl?: string;
|
|
78
96
|
/**
|
|
79
97
|
* Growth API URL.
|
|
80
98
|
*/
|
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;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2023-2025 Datalayer, Inc.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
import { create } from 'zustand';
|
|
6
|
+
import { createJSONStorage, persist } from 'zustand/middleware';
|
|
7
|
+
import { useUsageRefreshStore } from './useUsageRefreshStore.js';
|
|
8
|
+
export const useBillingEntityStore = create()(persist(set => ({
|
|
9
|
+
billingEntityKind: 'user',
|
|
10
|
+
billingEntityUid: undefined,
|
|
11
|
+
billingEntityHandle: undefined,
|
|
12
|
+
setBillingEntity: ({ kind, uid, handle }) => set(state => {
|
|
13
|
+
const unchanged = state.billingEntityKind === kind &&
|
|
14
|
+
state.billingEntityUid === uid &&
|
|
15
|
+
state.billingEntityHandle === handle;
|
|
16
|
+
if (unchanged) {
|
|
17
|
+
return state;
|
|
18
|
+
}
|
|
19
|
+
useUsageRefreshStore
|
|
20
|
+
.getState()
|
|
21
|
+
.requestUsageRefresh('billing-entity-changed');
|
|
22
|
+
return {
|
|
23
|
+
billingEntityKind: kind,
|
|
24
|
+
billingEntityUid: uid,
|
|
25
|
+
billingEntityHandle: handle,
|
|
26
|
+
};
|
|
27
|
+
}),
|
|
28
|
+
resetBillingEntity: () => set(state => {
|
|
29
|
+
const unchanged = state.billingEntityKind === 'user' &&
|
|
30
|
+
state.billingEntityUid === undefined &&
|
|
31
|
+
state.billingEntityHandle === undefined;
|
|
32
|
+
if (unchanged) {
|
|
33
|
+
return state;
|
|
34
|
+
}
|
|
35
|
+
useUsageRefreshStore
|
|
36
|
+
.getState()
|
|
37
|
+
.requestUsageRefresh('billing-entity-reset');
|
|
38
|
+
return {
|
|
39
|
+
billingEntityKind: 'user',
|
|
40
|
+
billingEntityUid: undefined,
|
|
41
|
+
billingEntityHandle: undefined,
|
|
42
|
+
};
|
|
43
|
+
}),
|
|
44
|
+
}), {
|
|
45
|
+
name: 'datalayer-billing-entity',
|
|
46
|
+
storage: createJSONStorage(() => localStorage),
|
|
47
|
+
partialize: state => ({
|
|
48
|
+
billingEntityKind: state.billingEntityKind,
|
|
49
|
+
billingEntityUid: state.billingEntityUid,
|
|
50
|
+
billingEntityHandle: state.billingEntityHandle,
|
|
51
|
+
}),
|
|
52
|
+
}));
|
|
53
|
+
export default useBillingEntityStore;
|