@datalayer/core 1.0.1 → 1.0.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.
- package/README.md +1 -1
- package/lib/api/constants.d.ts +3 -0
- package/lib/api/constants.js +3 -0
- package/lib/api/index.d.ts +1 -0
- package/lib/api/index.js +1 -0
- package/lib/api/otel/index.d.ts +12 -0
- package/lib/api/otel/index.js +16 -0
- package/lib/api/otel/logs.d.ts +19 -0
- package/lib/api/otel/logs.js +43 -0
- package/lib/api/otel/metrics.d.ts +31 -0
- package/lib/api/otel/metrics.js +65 -0
- package/lib/api/otel/query.d.ts +16 -0
- package/lib/api/otel/query.js +37 -0
- package/lib/api/otel/services.d.ts +39 -0
- package/lib/api/otel/services.js +81 -0
- package/lib/api/otel/traces.d.ts +24 -0
- package/lib/api/otel/traces.js +53 -0
- package/lib/api/otel/types.d.ts +112 -0
- package/lib/api/otel/types.js +5 -0
- package/lib/api/spacer/index.d.ts +1 -2
- package/lib/api/spacer/index.js +1 -2
- package/lib/components/avatars/BoringAvatar.d.ts +3 -1
- package/lib/components/avatars/BoringAvatar.js +15 -14
- package/lib/components/avatars/BoringAvatar.stories.d.ts +2 -1
- package/lib/components/storage/ContentsBrowser.d.ts +6 -0
- package/lib/components/storage/ContentsBrowser.js +7 -8
- package/lib/config/Configuration.d.ts +4 -0
- package/lib/hooks/index.d.ts +2 -0
- package/lib/hooks/index.js +2 -0
- package/lib/hooks/useCache.d.ts +16 -40
- package/lib/hooks/useCache.js +28 -233
- package/lib/hooks/useProjectStore.d.ts +58 -0
- package/lib/hooks/useProjectStore.js +64 -0
- package/lib/hooks/useProjects.d.ts +590 -0
- package/lib/hooks/useProjects.js +166 -0
- package/lib/index.d.ts +2 -1
- package/lib/index.js +4 -2
- package/lib/models/Page.d.ts +2 -0
- package/lib/otel/OtelLive.d.ts +12 -0
- package/lib/otel/OtelLive.js +354 -0
- package/lib/otel/OtelLogsList.d.ts +11 -0
- package/lib/otel/OtelLogsList.js +137 -0
- package/lib/otel/OtelMetricsChart.d.ts +22 -0
- package/lib/otel/OtelMetricsChart.js +300 -0
- package/lib/otel/OtelMetricsList.d.ts +15 -0
- package/lib/otel/OtelMetricsList.js +213 -0
- package/lib/otel/OtelSearchBar.d.ts +11 -0
- package/lib/otel/OtelSearchBar.js +22 -0
- package/lib/otel/OtelSpanDetail.d.ts +11 -0
- package/lib/otel/OtelSpanDetail.js +172 -0
- package/lib/otel/OtelSpanTree.d.ts +11 -0
- package/lib/otel/OtelSpanTree.js +176 -0
- package/lib/otel/OtelSqlView.d.ts +16 -0
- package/lib/otel/OtelSqlView.js +239 -0
- package/lib/otel/OtelSystemView.d.ts +15 -0
- package/lib/otel/OtelSystemView.js +75 -0
- package/lib/otel/OtelTimeline.d.ts +11 -0
- package/lib/otel/OtelTimeline.js +101 -0
- package/lib/otel/OtelTimelineRangeSlider.d.ts +16 -0
- package/lib/otel/OtelTimelineRangeSlider.js +338 -0
- package/lib/otel/OtelTracesList.d.ts +13 -0
- package/lib/otel/OtelTracesList.js +199 -0
- package/lib/otel/hooks.d.ts +172 -0
- package/lib/otel/hooks.js +490 -0
- package/lib/otel/index.d.ts +25 -0
- package/lib/otel/index.js +19 -0
- package/lib/otel/types.d.ts +190 -0
- package/lib/otel/types.js +5 -0
- package/lib/otel/utils.d.ts +33 -0
- package/lib/otel/utils.js +181 -0
- package/lib/state/storage/IAMStorage.d.ts +2 -1
- package/lib/state/substates/CoreState.js +1 -0
- package/lib/utils/Jwt.d.ts +42 -0
- package/lib/utils/Jwt.js +44 -0
- package/lib/utils/index.d.ts +1 -0
- package/lib/utils/index.js +1 -0
- package/lib/views/iam/SignInSimple.d.ts +38 -0
- package/lib/views/iam/SignInSimple.js +80 -0
- package/lib/views/iam/index.d.ts +2 -0
- package/lib/views/iam/index.js +5 -0
- package/lib/views/iam-tokens/IAMTokenEdit.js +53 -4
- package/lib/views/iam-tokens/IAMTokens.js +65 -33
- package/lib/views/iam-tokens/Tokens.js +64 -32
- package/lib/views/index.d.ts +2 -1
- package/lib/views/index.js +2 -1
- package/lib/views/profile/UserBadge.d.ts +18 -0
- package/lib/views/profile/UserBadge.js +101 -0
- package/lib/views/profile/index.d.ts +2 -0
- package/lib/views/profile/index.js +5 -0
- package/lib/views/secrets/Secrets.js +1 -1
- package/package.json +27 -3
- package/lib/api/spacer/agentSpaces.d.ts +0 -193
- package/lib/api/spacer/agentSpaces.js +0 -127
- package/lib/theme/DatalayerTheme.d.ts +0 -52
- package/lib/theme/DatalayerTheme.js +0 -228
- package/lib/theme/DatalayerThemeProvider.d.ts +0 -29
- package/lib/theme/DatalayerThemeProvider.js +0 -54
- package/lib/theme/Palette.d.ts +0 -4
- package/lib/theme/Palette.js +0 -10
- package/lib/theme/index.d.ts +0 -4
- package/lib/theme/index.js +0 -8
- package/lib/theme/useSystemColorMode.d.ts +0 -9
- package/lib/theme/useSystemColorMode.js +0 -26
|
@@ -8,29 +8,33 @@ import { useParams } from 'react-router-dom';
|
|
|
8
8
|
import { PageHeader, Heading, Text, Button, TextInput, FormControl, Textarea, Label, } from '@primer/react';
|
|
9
9
|
import { Box } from '@datalayer/primer-addons';
|
|
10
10
|
import { BoringAvatar } from '../../components/avatars';
|
|
11
|
-
import { useCache, useToast } from '../../hooks';
|
|
11
|
+
import { useCache, useNavigate, useToast } from '../../hooks';
|
|
12
12
|
import { useRunStore } from '../../state';
|
|
13
13
|
export const IAMTokenEdit = () => {
|
|
14
14
|
const { tokenId } = useParams();
|
|
15
15
|
const runStore = useRunStore();
|
|
16
|
+
const navigate = useNavigate();
|
|
16
17
|
const { enqueueToast } = useToast();
|
|
17
|
-
const { useUpdateToken, useToken } = useCache();
|
|
18
|
+
const { useUpdateToken, useToken, useDeleteToken } = useCache();
|
|
18
19
|
const getTokenQuery = useToken(tokenId);
|
|
19
20
|
const updateTokenMutation = useUpdateToken();
|
|
21
|
+
const deleteTokenMutation = useDeleteToken();
|
|
20
22
|
const [token, setToken] = useState();
|
|
21
23
|
const [formValues, setFormValues] = useState({
|
|
22
24
|
name: token?.name,
|
|
25
|
+
nameConfirm: '',
|
|
23
26
|
description: token?.description,
|
|
24
27
|
});
|
|
25
28
|
const [validationResult, setValidationResult] = useState({
|
|
26
29
|
name: undefined,
|
|
30
|
+
nameConfirm: undefined,
|
|
27
31
|
description: undefined,
|
|
28
32
|
});
|
|
29
33
|
useEffect(() => {
|
|
30
34
|
if (getTokenQuery.data) {
|
|
31
35
|
const token = getTokenQuery.data;
|
|
32
36
|
setToken(token);
|
|
33
|
-
setFormValues({ ...token });
|
|
37
|
+
setFormValues({ ...token, nameConfirm: '' });
|
|
34
38
|
}
|
|
35
39
|
}, [getTokenQuery.data]);
|
|
36
40
|
const nameNameChange = (event) => {
|
|
@@ -39,6 +43,12 @@ export const IAMTokenEdit = () => {
|
|
|
39
43
|
name: event.target.value,
|
|
40
44
|
}));
|
|
41
45
|
};
|
|
46
|
+
const nameConfirmChange = (event) => {
|
|
47
|
+
setFormValues(prevFormValues => ({
|
|
48
|
+
...prevFormValues,
|
|
49
|
+
nameConfirm: event.target.value,
|
|
50
|
+
}));
|
|
51
|
+
};
|
|
42
52
|
const nameDescriptionChange = (event) => {
|
|
43
53
|
setFormValues(prevFormValues => ({
|
|
44
54
|
...prevFormValues,
|
|
@@ -53,6 +63,7 @@ export const IAMTokenEdit = () => {
|
|
|
53
63
|
: formValues.name.length > 2
|
|
54
64
|
? true
|
|
55
65
|
: false,
|
|
66
|
+
nameConfirm: formValues.nameConfirm === token?.name ? true : false,
|
|
56
67
|
description: formValues.description === undefined
|
|
57
68
|
? undefined
|
|
58
69
|
: formValues.description.length > 2
|
|
@@ -81,6 +92,44 @@ export const IAMTokenEdit = () => {
|
|
|
81
92
|
},
|
|
82
93
|
});
|
|
83
94
|
};
|
|
84
|
-
|
|
95
|
+
const handleDelete = async () => {
|
|
96
|
+
runStore.layout().showBackdrop('Deleting the token...');
|
|
97
|
+
deleteTokenMutation.mutate(token.id, {
|
|
98
|
+
onSuccess: (resp) => {
|
|
99
|
+
if (resp.success) {
|
|
100
|
+
enqueueToast('The token is successfully deleted.', {
|
|
101
|
+
variant: 'success',
|
|
102
|
+
});
|
|
103
|
+
navigate('/settings/iam/tokens');
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
enqueueToast(resp.message || 'Failed to delete token.', {
|
|
107
|
+
variant: 'error',
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
onError: () => {
|
|
112
|
+
enqueueToast('Failed to delete token.', { variant: 'error' });
|
|
113
|
+
},
|
|
114
|
+
onSettled: () => {
|
|
115
|
+
runStore.layout().hideBackdrop();
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
};
|
|
119
|
+
return (_jsxs(_Fragment, { children: [_jsx(PageHeader, { children: _jsx(Heading, { sx: { fontSize: 3 }, children: "IAM Token" }) }), _jsxs(Box, { display: "flex", children: [_jsxs(Box, { children: [_jsx(BoringAvatar, { displayName: token?.name, size: 100, style: { paddingRight: 10 } }), _jsx(Text, { as: "h2", sx: { paddingTop: 3 }, children: token?.name }), _jsx(Box, { mt: 3, children: _jsx(Label, { size: "large", children: token?.variant }) })] }), _jsxs(Box, { ml: 10, children: [_jsxs(Box, { sx: { label: { marginTop: 2 } }, children: [_jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Name" }), _jsx(TextInput, { block: true, value: formValues.name, onChange: nameNameChange }), validationResult.name === false && (_jsx(FormControl.Validation, { variant: "error", children: "Name must have more than 2 characters." }))] }), _jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Description" }), _jsx(Textarea, { block: true, value: formValues.description, onChange: nameDescriptionChange, rows: 5 }), validationResult.description === false && (_jsx(FormControl.Validation, { variant: "error", children: "Description must have more than 2 characters." }))] }), _jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Expiration date" }), _jsx(TextInput, { block: true, value: token?.expirationDate.toLocaleDateString(), onChange: nameNameChange, disabled: true })] }), _jsx(Button, { variant: "primary", disabled: !validationResult.name || !validationResult.description, sx: { marginTop: 3 }, onClick: nameSubmit, children: "Update token" })] }), _jsxs(Box, { sx: { marginTop: 3 }, children: [_jsx(Heading, { as: "h2", sx: {
|
|
120
|
+
fontSize: 4,
|
|
121
|
+
fontWeight: 'normal',
|
|
122
|
+
color: 'danger.fg',
|
|
123
|
+
mb: 2,
|
|
124
|
+
}, children: "Danger zone" }), _jsxs(Box, { sx: {
|
|
125
|
+
border: '1px solid',
|
|
126
|
+
borderColor: 'danger.emphasis',
|
|
127
|
+
borderRadius: 2,
|
|
128
|
+
p: 3,
|
|
129
|
+
display: 'flex',
|
|
130
|
+
alignItems: 'center',
|
|
131
|
+
justifyContent: 'space-between',
|
|
132
|
+
gap: 3,
|
|
133
|
+
}, children: [_jsxs(Box, { sx: { display: 'grid', gap: 1 }, children: [_jsx(Text, { sx: { fontSize: 1, fontWeight: 'bold', color: 'danger.fg' }, children: "Confirm the token name to delete" }), _jsx(FormControl, { children: _jsx(TextInput, { block: true, value: formValues.nameConfirm, onChange: nameConfirmChange }) }), _jsx(Text, { sx: { fontSize: 1, color: 'fg.muted' }, children: "This operation is not reversible." })] }), _jsx(Button, { variant: "danger", disabled: !validationResult.nameConfirm, onClick: handleDelete, children: "Delete token" })] })] })] })] })] }));
|
|
85
134
|
};
|
|
86
135
|
export default IAMTokenEdit;
|
|
@@ -1,52 +1,84 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
/*
|
|
3
3
|
* Copyright (c) 2023-2025 Datalayer, Inc.
|
|
4
4
|
* Distributed under the terms of the Modified BSD License.
|
|
5
5
|
*/
|
|
6
|
-
import { useState, useEffect } from 'react';
|
|
7
|
-
import { PageLayout, Button, IconButton, Text, Label, RelativeTime, } from '@primer/react';
|
|
6
|
+
import { useState, useEffect, useRef } from 'react';
|
|
7
|
+
import { PageLayout, Button, IconButton, Text, Label, RelativeTime, ConfirmationDialog, } from '@primer/react';
|
|
8
8
|
import { Blankslate, PageHeader, Table, DataTable, } from '@primer/react/experimental';
|
|
9
9
|
import { Box } from '@datalayer/primer-addons';
|
|
10
10
|
import { EditIcon } from '@datalayer/icons-react';
|
|
11
|
-
import {
|
|
11
|
+
import { TrashIcon } from '@primer/octicons-react';
|
|
12
|
+
import { useCache, useNavigate, useToast } from '../../hooks';
|
|
12
13
|
const TokensTable = ({ tokensListRoute }) => {
|
|
13
|
-
const { useTokens } = useCache();
|
|
14
|
+
const { useTokens, useDeleteToken } = useCache();
|
|
15
|
+
const { enqueueToast } = useToast();
|
|
14
16
|
const getTokensQuery = useTokens();
|
|
17
|
+
const deleteTokenMutation = useDeleteToken();
|
|
15
18
|
const navigate = useNavigate();
|
|
16
19
|
const [tokens, setTokens] = useState([]);
|
|
20
|
+
const [deletingToken, setDeletingToken] = useState(null);
|
|
21
|
+
const returnFocusRef = useRef(null);
|
|
17
22
|
useEffect(() => {
|
|
18
23
|
if (getTokensQuery.data) {
|
|
19
24
|
setTokens(getTokensQuery.data);
|
|
20
25
|
}
|
|
21
26
|
}, [getTokensQuery.data]);
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
{
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
{
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
:
|
|
48
|
-
|
|
49
|
-
|
|
27
|
+
const handleDeleteConfirm = () => {
|
|
28
|
+
if (!deletingToken)
|
|
29
|
+
return;
|
|
30
|
+
deleteTokenMutation.mutate(deletingToken.id, {
|
|
31
|
+
onSuccess: (resp) => {
|
|
32
|
+
if (resp.success) {
|
|
33
|
+
enqueueToast(`Token "${deletingToken.name}" deleted.`, {
|
|
34
|
+
variant: 'success',
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
enqueueToast(resp.message || 'Failed to delete token.', {
|
|
39
|
+
variant: 'error',
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
onError: () => {
|
|
44
|
+
enqueueToast('Failed to delete token.', { variant: 'error' });
|
|
45
|
+
},
|
|
46
|
+
onSettled: () => setDeletingToken(null),
|
|
47
|
+
});
|
|
48
|
+
};
|
|
49
|
+
return tokens.length === 0 ? (_jsxs(Blankslate, { border: true, spacious: true, children: [_jsx(Blankslate.Heading, { children: "IAM Tokens" }), _jsx(Blankslate.Description, { children: _jsx(Text, { sx: { textAlign: 'center' }, children: "No IAM Tokens found." }) })] })) : (_jsxs(_Fragment, { children: [_jsxs(Table.Container, { children: [_jsx(Table.Title, { as: "h2", id: "tokens", children: "IAM Tokens" }), _jsx(Table.Subtitle, { as: "p", id: "tokens-subtitle", children: "Your tokens." }), _jsx(DataTable, { "aria-labelledby": "teams", "aria-describedby": "teams-subtitle", data: tokens, columns: [
|
|
50
|
+
{
|
|
51
|
+
header: 'Type',
|
|
52
|
+
field: 'variant',
|
|
53
|
+
renderCell: token => _jsx(Label, { children: token.variant }),
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
header: 'Name',
|
|
57
|
+
field: 'name',
|
|
58
|
+
rowHeader: true,
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
header: 'Description',
|
|
62
|
+
field: 'description',
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
header: 'Expiration date',
|
|
66
|
+
field: 'expirationDate',
|
|
67
|
+
renderCell: token => (_jsx(RelativeTime, { date: new Date(token.expirationDate) })),
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
header: '',
|
|
71
|
+
field: 'id',
|
|
72
|
+
renderCell: token => (_jsxs(Box, { display: "flex", sx: { gap: 1 }, children: [_jsx(IconButton, { icon: EditIcon, "aria-label": "Edit", size: "small", variant: "invisible", onClick: e => navigate(tokensListRoute
|
|
73
|
+
? `${tokensListRoute}/${token.id}`
|
|
74
|
+
: `${token.id}`, e) }), _jsx(IconButton, { ref: returnFocusRef, icon: TrashIcon, "aria-label": "Delete", size: "small", variant: "invisible", sx: { color: 'danger.fg' }, onClick: () => setDeletingToken(token) })] })),
|
|
75
|
+
},
|
|
76
|
+
] })] }), deletingToken && (_jsxs(ConfirmationDialog, { title: "Delete token", onClose: gesture => {
|
|
77
|
+
if (gesture === 'confirm')
|
|
78
|
+
handleDeleteConfirm();
|
|
79
|
+
else
|
|
80
|
+
setDeletingToken(null);
|
|
81
|
+
}, confirmButtonContent: "Delete", confirmButtonType: "danger", children: ["Are you sure you want to delete the token", ' ', _jsx("strong", { children: deletingToken.name }), "? This action cannot be undone."] }))] }));
|
|
50
82
|
};
|
|
51
83
|
export const IAMTokens = ({ newTokenRoute = '/new/token', tokensListRoute, } = {}) => {
|
|
52
84
|
const navigate = useNavigate();
|
|
@@ -1,53 +1,85 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
/*
|
|
3
3
|
* Copyright (c) 2023-2025 Datalayer, Inc.
|
|
4
4
|
* Distributed under the terms of the Modified BSD License.
|
|
5
5
|
*/
|
|
6
|
-
import { useState, useEffect } from 'react';
|
|
7
|
-
import { PageLayout, Button, IconButton, Text, Label, RelativeTime, } from '@primer/react';
|
|
6
|
+
import { useState, useEffect, useRef } from 'react';
|
|
7
|
+
import { PageLayout, Button, IconButton, Text, Label, RelativeTime, ConfirmationDialog, } from '@primer/react';
|
|
8
8
|
import { Blankslate, PageHeader, Table, DataTable, } from '@primer/react/experimental';
|
|
9
9
|
import { Box } from '@datalayer/primer-addons';
|
|
10
10
|
import { EditIcon } from '@datalayer/icons-react';
|
|
11
|
-
import {
|
|
11
|
+
import { TrashIcon } from '@primer/octicons-react';
|
|
12
|
+
import { useCache, useNavigate, useToast } from '../../hooks';
|
|
12
13
|
const TokensTable = () => {
|
|
13
|
-
const { useTokens } = useCache();
|
|
14
|
+
const { useTokens, useDeleteToken } = useCache();
|
|
15
|
+
const { enqueueToast } = useToast();
|
|
14
16
|
const getTokensQuery = useTokens();
|
|
17
|
+
const deleteTokenMutation = useDeleteToken();
|
|
15
18
|
const navigate = useNavigate();
|
|
16
19
|
const [tokens, setTokens] = useState([]);
|
|
20
|
+
const [deletingToken, setDeletingToken] = useState(null);
|
|
21
|
+
const returnFocusRef = useRef(null);
|
|
17
22
|
useEffect(() => {
|
|
18
23
|
if (getTokensQuery.data) {
|
|
19
24
|
setTokens(getTokensQuery.data);
|
|
20
25
|
}
|
|
21
26
|
}, [getTokensQuery.data]);
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
{
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
{
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
27
|
+
const handleDeleteConfirm = () => {
|
|
28
|
+
if (!deletingToken)
|
|
29
|
+
return;
|
|
30
|
+
deleteTokenMutation.mutate(deletingToken.id, {
|
|
31
|
+
onSuccess: (resp) => {
|
|
32
|
+
if (resp.success) {
|
|
33
|
+
enqueueToast(`Token "${deletingToken.name}" deleted.`, {
|
|
34
|
+
variant: 'success',
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
enqueueToast(resp.message || 'Failed to delete token.', {
|
|
39
|
+
variant: 'error',
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
onError: () => {
|
|
44
|
+
enqueueToast('Failed to delete token.', { variant: 'error' });
|
|
45
|
+
},
|
|
46
|
+
onSettled: () => setDeletingToken(null),
|
|
47
|
+
});
|
|
48
|
+
};
|
|
49
|
+
return tokens.length === 0 ? (_jsxs(Blankslate, { border: true, spacious: true, children: [_jsx(Blankslate.Heading, { children: "Tokens" }), _jsx(Blankslate.Description, { children: _jsx(Text, { sx: { textAlign: 'center' }, children: "No Tokens found." }) })] })) : (_jsxs(_Fragment, { children: [_jsxs(Table.Container, { children: [_jsx(Table.Title, { as: "h2", id: "tokens", children: "Tokens" }), _jsx(Table.Subtitle, { as: "p", id: "tokens-subtitle", children: "Your tokens." }), _jsx(DataTable, { "aria-labelledby": "teams", "aria-describedby": "teams-subtitle", data: tokens, columns: [
|
|
50
|
+
{
|
|
51
|
+
header: 'Type',
|
|
52
|
+
field: 'variant',
|
|
53
|
+
renderCell: token => _jsx(Label, { children: token.variant }),
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
header: 'Name',
|
|
57
|
+
field: 'name',
|
|
58
|
+
rowHeader: true,
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
header: 'Description',
|
|
62
|
+
field: 'description',
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
header: 'Expiration date',
|
|
66
|
+
field: 'expirationDate',
|
|
67
|
+
renderCell: token => (_jsx(RelativeTime, { date: new Date(token.expirationDate) })),
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
header: '',
|
|
71
|
+
field: 'id',
|
|
72
|
+
renderCell: token => (_jsxs(Box, { display: "flex", sx: { gap: 1 }, children: [_jsx(IconButton, { icon: EditIcon, "aria-label": "Edit", size: "small", variant: "invisible", onClick: e => navigate(`${token.id}`, e) }), _jsx(IconButton, { ref: returnFocusRef, icon: TrashIcon, "aria-label": "Delete", size: "small", variant: "invisible", sx: { color: 'danger.fg' }, onClick: () => setDeletingToken(token) })] })),
|
|
73
|
+
},
|
|
74
|
+
] })] }), deletingToken && (_jsxs(ConfirmationDialog, { title: "Delete token", onClose: gesture => {
|
|
75
|
+
if (gesture === 'confirm')
|
|
76
|
+
handleDeleteConfirm();
|
|
77
|
+
else
|
|
78
|
+
setDeletingToken(null);
|
|
79
|
+
}, confirmButtonContent: "Delete", confirmButtonType: "danger", children: ["Are you sure you want to delete the token", ' ', _jsx("strong", { children: deletingToken.name }), "? This action cannot be undone."] }))] }));
|
|
48
80
|
};
|
|
49
81
|
export const Tokens = () => {
|
|
50
82
|
const navigate = useNavigate();
|
|
51
|
-
return (_jsxs(PageLayout, { containerWidth: "full", padding: "normal", style: { overflow: 'visible', minHeight: 'calc(100vh - 45px)' }, children: [_jsx(PageLayout.Header, { children: _jsxs(PageHeader, { children: [_jsx(PageHeader.TitleArea, { variant: "large", children: _jsx(PageHeader.Title, { children: "Tokens" }) }), _jsx(PageHeader.Actions, { children: _jsx(Button, { size: "small", variant: "primary", onClick: e => navigate('/new
|
|
83
|
+
return (_jsxs(PageLayout, { containerWidth: "full", padding: "normal", style: { overflow: 'visible', minHeight: 'calc(100vh - 45px)' }, children: [_jsx(PageLayout.Header, { children: _jsxs(PageHeader, { children: [_jsx(PageHeader.TitleArea, { variant: "large", children: _jsx(PageHeader.Title, { children: "Tokens" }) }), _jsx(PageHeader.Actions, { children: _jsx(Button, { size: "small", variant: "primary", onClick: e => navigate('/tokens/new', e), children: "New token" }) })] }) }), _jsx(PageLayout.Content, { children: _jsx(Box, { children: _jsx(TokensTable, {}) }) })] }));
|
|
52
84
|
};
|
|
53
85
|
export default Tokens;
|
package/lib/views/index.d.ts
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
export
|
|
1
|
+
export * from './iam';
|
|
2
|
+
export * from './profile';
|
package/lib/views/index.js
CHANGED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UserBadge – Shows the authenticated user's display name.
|
|
3
|
+
*
|
|
4
|
+
* On hover, reveals a JWT details popover with email, handle, UID,
|
|
5
|
+
* issued/expiry timestamps, roles, and the raw decoded claims.
|
|
6
|
+
*
|
|
7
|
+
* The trigger text and the popover share a single container so the
|
|
8
|
+
* popover stays open while the mouse travels from the label into it.
|
|
9
|
+
*
|
|
10
|
+
* @module views/profile
|
|
11
|
+
*/
|
|
12
|
+
import React from 'react';
|
|
13
|
+
export interface UserBadgeProps {
|
|
14
|
+
/** Raw JWT bearer token. */
|
|
15
|
+
token: string;
|
|
16
|
+
}
|
|
17
|
+
export declare const UserBadge: React.FC<UserBadgeProps>;
|
|
18
|
+
export default UserBadge;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, 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
|
+
* UserBadge – Shows the authenticated user's display name.
|
|
8
|
+
*
|
|
9
|
+
* On hover, reveals a JWT details popover with email, handle, UID,
|
|
10
|
+
* issued/expiry timestamps, roles, and the raw decoded claims.
|
|
11
|
+
*
|
|
12
|
+
* The trigger text and the popover share a single container so the
|
|
13
|
+
* popover stays open while the mouse travels from the label into it.
|
|
14
|
+
*
|
|
15
|
+
* @module views/profile
|
|
16
|
+
*/
|
|
17
|
+
import { useState, useRef, useCallback } from 'react';
|
|
18
|
+
import { Box, Text } from '@primer/react';
|
|
19
|
+
import { parseJwtPayload, getDatalayerJwtUser, getDatalayerDisplayName, } from '../../utils/Jwt';
|
|
20
|
+
// ── Component ─────────────────────────────────────────────────────
|
|
21
|
+
export const UserBadge = ({ token }) => {
|
|
22
|
+
const [open, setOpen] = useState(false);
|
|
23
|
+
const closeTimer = useRef(null);
|
|
24
|
+
const handleEnter = useCallback(() => {
|
|
25
|
+
if (closeTimer.current)
|
|
26
|
+
clearTimeout(closeTimer.current);
|
|
27
|
+
setOpen(true);
|
|
28
|
+
}, []);
|
|
29
|
+
const handleLeave = useCallback(() => {
|
|
30
|
+
// Small delay so the mouse can travel from the label into the popover
|
|
31
|
+
// without it closing.
|
|
32
|
+
closeTimer.current = setTimeout(() => setOpen(false), 120);
|
|
33
|
+
}, []);
|
|
34
|
+
const user = getDatalayerJwtUser(token);
|
|
35
|
+
const displayName = getDatalayerDisplayName(user, user?.handle ?? '');
|
|
36
|
+
const claims = parseJwtPayload(token);
|
|
37
|
+
return (_jsxs(Box, { sx: { position: 'relative' }, onMouseEnter: handleEnter, onMouseLeave: handleLeave, children: [_jsx(Text, { sx: {
|
|
38
|
+
fontSize: 1,
|
|
39
|
+
color: 'fg.muted',
|
|
40
|
+
cursor: 'default',
|
|
41
|
+
borderBottom: '1px dashed',
|
|
42
|
+
borderColor: 'border.muted',
|
|
43
|
+
pb: '1px',
|
|
44
|
+
userSelect: 'none',
|
|
45
|
+
}, children: displayName }), open && claims && (_jsxs(Box, { sx: {
|
|
46
|
+
position: 'absolute',
|
|
47
|
+
top: 'calc(100% + 4px)',
|
|
48
|
+
right: 0,
|
|
49
|
+
zIndex: 100,
|
|
50
|
+
width: '400px',
|
|
51
|
+
bg: 'canvas.overlay',
|
|
52
|
+
border: '1px solid',
|
|
53
|
+
borderColor: 'border.default',
|
|
54
|
+
borderRadius: 2,
|
|
55
|
+
boxShadow: 'shadow.large',
|
|
56
|
+
overflow: 'hidden',
|
|
57
|
+
}, children: [_jsx(Box, { sx: {
|
|
58
|
+
px: 3,
|
|
59
|
+
py: 2,
|
|
60
|
+
bg: 'canvas.subtle',
|
|
61
|
+
borderBottom: '1px solid',
|
|
62
|
+
borderColor: 'border.default',
|
|
63
|
+
}, children: _jsx(Text, { sx: { fontWeight: 'bold', fontSize: 1 }, children: "JWT Claims" }) }), claims.user && (_jsx(Box, { sx: {
|
|
64
|
+
px: 3,
|
|
65
|
+
py: 2,
|
|
66
|
+
borderBottom: '1px solid',
|
|
67
|
+
borderColor: 'border.muted',
|
|
68
|
+
}, children: _jsxs(Box, { sx: {
|
|
69
|
+
display: 'grid',
|
|
70
|
+
gridTemplateColumns: '90px 1fr',
|
|
71
|
+
gap: 1,
|
|
72
|
+
fontSize: 0,
|
|
73
|
+
}, children: [claims.user.email && (_jsxs(_Fragment, { children: [_jsx(Text, { sx: { color: 'fg.muted' }, children: "Email" }), _jsx(Text, { sx: { fontFamily: 'mono' }, children: claims.user.email })] })), _jsx(Text, { sx: { color: 'fg.muted' }, children: "Handle" }), _jsx(Text, { sx: { fontFamily: 'mono' }, children: claims.user.handle }), _jsx(Text, { sx: { color: 'fg.muted' }, children: "UID" }), _jsx(Text, { sx: { fontFamily: 'mono', wordBreak: 'break-all' }, children: claims.user.uid }), _jsx(Text, { sx: { color: 'fg.muted' }, children: "Issued" }), _jsx(Text, { sx: { fontFamily: 'mono' }, children: new Date(claims.iat * 1000).toISOString() }), _jsx(Text, { sx: { color: 'fg.muted' }, children: "Expires" }), _jsx(Text, { sx: { fontFamily: 'mono' }, children: new Date(claims.exp * 1000).toISOString() })] }) })), (claims.user?.roles ?? claims.roles ?? []).length > 0 && (_jsxs(Box, { sx: {
|
|
74
|
+
px: 3,
|
|
75
|
+
py: 2,
|
|
76
|
+
borderBottom: '1px solid',
|
|
77
|
+
borderColor: 'border.muted',
|
|
78
|
+
}, children: [_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted', display: 'block', mb: 1 }, children: "Roles" }), _jsx(Box, { sx: { display: 'flex', flexWrap: 'wrap', gap: 1 }, children: (claims.user?.roles ?? claims.roles ?? []).map(r => (_jsx(Box, { sx: {
|
|
79
|
+
fontSize: 0,
|
|
80
|
+
fontFamily: 'mono',
|
|
81
|
+
px: 1,
|
|
82
|
+
py: '1px',
|
|
83
|
+
bg: 'accent.subtle',
|
|
84
|
+
color: 'accent.fg',
|
|
85
|
+
borderRadius: 1,
|
|
86
|
+
border: '1px solid',
|
|
87
|
+
borderColor: 'accent.muted',
|
|
88
|
+
}, children: r }, r))) })] })), _jsx(Box, { as: "pre", sx: {
|
|
89
|
+
m: 0,
|
|
90
|
+
px: 3,
|
|
91
|
+
py: 2,
|
|
92
|
+
fontFamily: 'mono',
|
|
93
|
+
fontSize: 0,
|
|
94
|
+
color: 'fg.default',
|
|
95
|
+
bg: 'canvas.inset',
|
|
96
|
+
overflow: 'auto',
|
|
97
|
+
maxHeight: '220px',
|
|
98
|
+
whiteSpace: 'pre',
|
|
99
|
+
}, children: JSON.stringify(claims, null, 2) })] }))] }));
|
|
100
|
+
};
|
|
101
|
+
export default UserBadge;
|
|
@@ -43,6 +43,6 @@ const SecretsTable = () => {
|
|
|
43
43
|
};
|
|
44
44
|
export const Secrets = () => {
|
|
45
45
|
const navigate = useNavigate();
|
|
46
|
-
return (_jsxs(PageLayout, { containerWidth: "full", padding: "normal", style: { overflow: 'visible', minHeight: 'calc(100vh - 45px)' }, children: [_jsx(PageLayout.Header, { children: _jsxs(PageHeader, { children: [_jsx(PageHeader.TitleArea, { variant: "large", children: _jsx(PageHeader.Title, { children: "Secrets" }) }), _jsx(PageHeader.Actions, { children: _jsx(Button, { size: "small", variant: "primary", onClick: e => navigate('/new
|
|
46
|
+
return (_jsxs(PageLayout, { containerWidth: "full", padding: "normal", style: { overflow: 'visible', minHeight: 'calc(100vh - 45px)' }, children: [_jsx(PageLayout.Header, { children: _jsxs(PageHeader, { children: [_jsx(PageHeader.TitleArea, { variant: "large", children: _jsx(PageHeader.Title, { children: "Secrets" }) }), _jsx(PageHeader.Actions, { children: _jsx(Button, { size: "small", variant: "primary", onClick: e => navigate('/secrets/new', e), children: "New secret" }) })] }) }), _jsx(PageLayout.Content, { children: _jsx(Box, { children: _jsx(SecretsTable, {}) }) })] }));
|
|
47
47
|
};
|
|
48
48
|
export default Secrets;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@datalayer/core",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"workspaces": [
|
|
6
6
|
".",
|
|
@@ -28,6 +28,30 @@
|
|
|
28
28
|
],
|
|
29
29
|
"main": "lib/index.js",
|
|
30
30
|
"types": "lib/index.d.ts",
|
|
31
|
+
"exports": {
|
|
32
|
+
".": {
|
|
33
|
+
"types": "./lib/index.d.ts",
|
|
34
|
+
"default": "./lib/index.js"
|
|
35
|
+
},
|
|
36
|
+
"./otel": {
|
|
37
|
+
"types": "./lib/otel/index.d.ts",
|
|
38
|
+
"default": "./lib/otel/index.js"
|
|
39
|
+
},
|
|
40
|
+
"./views/iam": {
|
|
41
|
+
"types": "./lib/views/iam/index.d.ts",
|
|
42
|
+
"default": "./lib/views/iam/index.js"
|
|
43
|
+
},
|
|
44
|
+
"./views/profile": {
|
|
45
|
+
"types": "./lib/views/profile/index.d.ts",
|
|
46
|
+
"default": "./lib/views/profile/index.js"
|
|
47
|
+
},
|
|
48
|
+
"./utils/jwt": {
|
|
49
|
+
"types": "./lib/utils/Jwt.d.ts",
|
|
50
|
+
"default": "./lib/utils/Jwt.js"
|
|
51
|
+
},
|
|
52
|
+
"./style/*": "./style/*",
|
|
53
|
+
"./*": "./*"
|
|
54
|
+
},
|
|
31
55
|
"style": "style/index.css",
|
|
32
56
|
"repository": {
|
|
33
57
|
"type": "git",
|
|
@@ -54,7 +78,7 @@
|
|
|
54
78
|
"build": "npm run clean && gulp resources-to-lib && tsc -b && vite build",
|
|
55
79
|
"build:lib": "npm run clean:lib && gulp resources-to-lib && tsc -b && node scripts/fix-esm-imports.cjs",
|
|
56
80
|
"build:types": "npm run clean:lib && tsc -b",
|
|
57
|
-
"build:nextjs": "npm run build --workspace=
|
|
81
|
+
"build:nextjs": "npm run build --workspace=nextjj-example",
|
|
58
82
|
"build:examples": "npm run build:nextjs",
|
|
59
83
|
"build:all": "npm run build && npm run build:examples",
|
|
60
84
|
"lint": "eslint . --quiet",
|
|
@@ -90,7 +114,7 @@
|
|
|
90
114
|
"type-check:watch": "tsc -b -w --noEmit",
|
|
91
115
|
"examples": "run-p jupyter:start examples:vite",
|
|
92
116
|
"examples:vite": "VITE_DATALAYER_RUN_URL=http://localhost:8888 vite --config vite.examples.config.ts",
|
|
93
|
-
"examples:nextjs": "npm run dev --workspace=
|
|
117
|
+
"examples:nextjs": "npm run dev --workspace=nextjj-example",
|
|
94
118
|
"jupyter:start": "./dev/sh/start-jupyter-server.sh",
|
|
95
119
|
"prepare": "husky || true",
|
|
96
120
|
"kill": "./dev/sh/kill.sh || true",
|