@datalayer/core 1.0.20 → 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.
Files changed (138) hide show
  1. package/README.md +27 -0
  2. package/lib/api/DatalayerApi.d.ts +5 -1
  3. package/lib/api/DatalayerApi.js +55 -8
  4. package/lib/api/iam/oauth2.d.ts +3 -3
  5. package/lib/api/iam/oauth2.js +2 -2
  6. package/lib/api/iam/profile.d.ts +9 -1
  7. package/lib/api/iam/profile.js +15 -0
  8. package/lib/api/runtimes/checkpoints.js +1 -1
  9. package/lib/api/runtimes/snapshots.d.ts +7 -7
  10. package/lib/api/runtimes/snapshots.js +9 -9
  11. package/lib/client/auth/storage.js +103 -12
  12. package/lib/client/index.d.ts +7 -7
  13. package/lib/client/index.js +1 -1
  14. package/lib/client/mixins/RuntimesMixin.d.ts +6 -6
  15. package/lib/client/mixins/RuntimesMixin.js +5 -5
  16. package/lib/client/mixins/SpacerMixin.js +22 -1
  17. package/lib/collaboration/DatalayerCollaborationProvider.d.ts +4 -3
  18. package/lib/components/avatars/DLAvatar.d.ts +24 -0
  19. package/lib/components/avatars/DLAvatar.js +26 -0
  20. package/lib/components/avatars/UserProfileAvatar.js +4 -4
  21. package/lib/components/avatars/index.d.ts +1 -0
  22. package/lib/components/avatars/index.js +1 -0
  23. package/lib/components/billing/BillableAccountSelect.d.ts +35 -0
  24. package/lib/components/billing/BillableAccountSelect.js +340 -0
  25. package/lib/components/billing/index.d.ts +1 -0
  26. package/lib/components/billing/index.js +5 -0
  27. package/lib/components/checkout/StripeCheckout.d.ts +24 -3
  28. package/lib/components/checkout/StripeCheckout.js +1116 -55
  29. package/lib/components/display/DatalayerBox.js +6 -1
  30. package/lib/components/index.d.ts +2 -0
  31. package/lib/components/index.js +2 -0
  32. package/lib/components/principal/Principal.d.ts +37 -0
  33. package/lib/components/principal/Principal.js +54 -0
  34. package/lib/components/principal/PrincipalAvatar.d.ts +10 -0
  35. package/lib/components/principal/PrincipalAvatar.js +55 -0
  36. package/lib/components/principal/PrincipalBadge.d.ts +20 -0
  37. package/lib/components/principal/PrincipalBadge.js +208 -0
  38. package/lib/components/principal/PrincipalBanner.d.ts +7 -0
  39. package/lib/components/principal/PrincipalBanner.js +84 -0
  40. package/lib/components/principal/PrincipalDetailsOverlay.d.ts +23 -0
  41. package/lib/components/principal/PrincipalDetailsOverlay.js +86 -0
  42. package/lib/components/principal/PrincipalSwitcherMenu.d.ts +14 -0
  43. package/lib/components/principal/PrincipalSwitcherMenu.js +287 -0
  44. package/lib/components/principal/index.d.ts +7 -0
  45. package/lib/components/principal/index.js +10 -0
  46. package/lib/components/runtimes/RuntimeLauncherDialog.d.ts +2 -2
  47. package/lib/components/sharing/ShareAccessComponent.d.ts +18 -0
  48. package/lib/components/sharing/ShareAccessComponent.js +1368 -0
  49. package/lib/components/sharing/SharingEditor.d.ts +20 -0
  50. package/lib/components/sharing/SharingEditor.js +65 -0
  51. package/lib/components/sharing/index.d.ts +9 -0
  52. package/lib/components/sharing/index.js +12 -0
  53. package/lib/components/snapshots/{RuntimeSnapshotMenu.d.ts → SandboxSnapshotMenu.d.ts} +4 -4
  54. package/lib/components/snapshots/{RuntimeSnapshotMenu.js → SandboxSnapshotMenu.js} +33 -33
  55. package/lib/components/snapshots/index.d.ts +1 -1
  56. package/lib/components/snapshots/index.js +1 -1
  57. package/lib/components/storage/ContentsBrowser.js +2 -2
  58. package/lib/components/tokens/SpaceVariantToken.d.ts +1 -1
  59. package/lib/components/tokens/SpaceVariantToken.js +7 -4
  60. package/lib/components/users/PeerIndicator.js +5 -4
  61. package/lib/hooks/index.d.ts +5 -0
  62. package/lib/hooks/index.js +5 -0
  63. package/lib/hooks/useBillableAccountStore.d.ts +31 -0
  64. package/lib/hooks/useBillableAccountStore.js +53 -0
  65. package/lib/hooks/useCache.d.ts +125 -18
  66. package/lib/hooks/useCache.js +893 -120
  67. package/lib/hooks/useDatalayer.js +31 -3
  68. package/lib/hooks/useIAM.js +1 -8
  69. package/lib/hooks/usePrincipalStore.d.ts +41 -0
  70. package/lib/hooks/usePrincipalStore.js +101 -0
  71. package/lib/hooks/useProjects.d.ts +44 -0
  72. package/lib/hooks/useProjects.js +26 -2
  73. package/lib/hooks/useSelectedBillableAccount.d.ts +14 -0
  74. package/lib/hooks/useSelectedBillableAccount.js +36 -0
  75. package/lib/hooks/useSelectedPrincipal.d.ts +17 -0
  76. package/lib/hooks/useSelectedPrincipal.js +29 -0
  77. package/lib/hooks/useUsageRefreshStore.d.ts +8 -0
  78. package/lib/hooks/useUsageRefreshStore.js +16 -0
  79. package/lib/index.d.ts +2 -2
  80. package/lib/index.js +1 -1
  81. package/lib/mocks/components/FlashMock.js +8 -2
  82. package/lib/models/{RuntimeSnapshot.d.ts → CodeSandboxSnapshot.d.ts} +5 -5
  83. package/lib/models/{RuntimeSnapshot.js → CodeSandboxSnapshot.js} +1 -1
  84. package/lib/models/{RuntimeSnapshotDTO.d.ts → CodeSandboxSnapshotDTO.d.ts} +30 -30
  85. package/lib/models/{RuntimeSnapshotDTO.js → CodeSandboxSnapshotDTO.js} +6 -6
  86. package/lib/models/Environment.d.ts +4 -0
  87. package/lib/models/IAM.d.ts +66 -0
  88. package/lib/models/Organization.d.ts +1 -0
  89. package/lib/models/Organization.js +1 -0
  90. package/lib/models/Page.d.ts +2 -2
  91. package/lib/models/Page.js +2 -2
  92. package/lib/models/Profile.d.ts +1 -1
  93. package/lib/models/RuntimeDTO.d.ts +2 -2
  94. package/lib/models/Space.d.ts +1 -0
  95. package/lib/models/Space.js +29 -5
  96. package/lib/models/SpaceDTO.d.ts +4 -0
  97. package/lib/models/SpaceDTO.js +5 -0
  98. package/lib/models/Usage.d.ts +15 -0
  99. package/lib/models/Usage.js +9 -0
  100. package/lib/models/User.d.ts +21 -0
  101. package/lib/models/User.js +6 -2
  102. package/lib/models/index.d.ts +2 -2
  103. package/lib/models/index.js +2 -2
  104. package/lib/otel/auth.js +0 -4
  105. package/lib/services/index.d.ts +1 -1
  106. package/lib/services/index.js +1 -1
  107. package/lib/state/substates/IAMState.js +0 -7
  108. package/lib/state/substates/RuntimesState.d.ts +9 -9
  109. package/lib/state/substates/RuntimesState.js +3 -3
  110. package/lib/stateful/runtimes/actions.d.ts +17 -17
  111. package/lib/stateful/runtimes/actions.js +35 -55
  112. package/lib/stateful/runtimes/apis.d.ts +3 -3
  113. package/lib/stateful/runtimes/snapshots.d.ts +2 -2
  114. package/lib/stateful/runtimes/snapshots.js +7 -7
  115. package/lib/utils/Format.d.ts +6 -0
  116. package/lib/utils/Format.js +17 -0
  117. package/lib/utils/Handles.d.ts +3 -0
  118. package/lib/utils/Handles.js +53 -0
  119. package/lib/utils/Snapshot.d.ts +1 -1
  120. package/lib/utils/Snapshot.js +1 -1
  121. package/lib/utils/index.d.ts +1 -0
  122. package/lib/utils/index.js +1 -0
  123. package/lib/views/datasources/DatasourceDetail.d.ts +7 -1
  124. package/lib/views/datasources/DatasourceDetail.js +61 -23
  125. package/lib/views/datasources/DatasourceNew.d.ts +8 -1
  126. package/lib/views/datasources/DatasourceNew.js +23 -10
  127. package/lib/views/datasources/Datasources.d.ts +5 -1
  128. package/lib/views/datasources/Datasources.js +38 -30
  129. package/lib/views/iam-tokens/IAMTokenNew.d.ts +3 -1
  130. package/lib/views/iam-tokens/IAMTokenNew.js +2 -2
  131. package/lib/views/iam-tokens/IAMTokens.d.ts +3 -1
  132. package/lib/views/iam-tokens/IAMTokens.js +2 -2
  133. package/lib/views/otel/OtelHeader.d.ts +5 -0
  134. package/lib/views/otel/OtelHeader.js +3 -3
  135. package/lib/views/profile/UserBadge.d.ts +7 -2
  136. package/lib/views/profile/UserBadge.js +11 -2
  137. package/lib/views/secrets/Secrets.js +21 -21
  138. package/package.json +7 -8
@@ -1,27 +1,33 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } 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
6
  import { useState, useEffect } from 'react';
7
7
  import { useParams } from 'react-router-dom';
8
- import { PageHeader, Heading, Text, Button, TextInput, FormControl, Textarea, Label, } from '@primer/react';
8
+ import { PageLayout, Text, Button, TextInput, FormControl, Textarea, Label, Spinner, Heading, } from '@primer/react';
9
9
  import { Box } from '@datalayer/primer-addons';
10
10
  import { EyeIcon, EyeClosedIcon } from '@primer/octicons-react';
11
11
  import { BoringAvatar } from '../../components/avatars/index.js';
12
12
  import { useCache, useToast } from '../../hooks/index.js';
13
13
  import { useRunStore } from '../../state/index.js';
14
- export const DatasourceDetail = () => {
14
+ export const DatasourceDetail = ({ principalUid, principalKind, } = {}) => {
15
15
  const { datasourceId } = useParams();
16
16
  const runStore = useRunStore();
17
17
  const { enqueueToast } = useToast();
18
18
  const { useUpdateDatasource, useDatasource } = useCache();
19
- const updateDatasourceMutation = useUpdateDatasource();
20
- const datasourceQuery = useDatasource(datasourceId);
19
+ const updateDatasourceMutation = useUpdateDatasource({
20
+ principalUid,
21
+ principalKind,
22
+ });
23
+ const datasourceQuery = useDatasource(datasourceId ?? '', {
24
+ principalUid,
25
+ principalKind,
26
+ });
21
27
  const [datasource, setDatasource] = useState();
22
28
  const [formValues, setFormValues] = useState({
23
- name: datasource?.name,
24
- description: datasource?.description,
29
+ name: '',
30
+ description: '',
25
31
  });
26
32
  const [validationResult, setValidationResult] = useState({
27
33
  name: undefined,
@@ -32,7 +38,10 @@ export const DatasourceDetail = () => {
32
38
  if (datasourceQuery.data) {
33
39
  const datasource = datasourceQuery.data;
34
40
  setDatasource(datasource);
35
- setFormValues({ ...datasource });
41
+ setFormValues({
42
+ name: datasource.name || '',
43
+ description: datasource.description || '',
44
+ });
36
45
  }
37
46
  }, [datasourceQuery.data]);
38
47
  const nameNameChange = (event) => {
@@ -48,8 +57,8 @@ export const DatasourceDetail = () => {
48
57
  }));
49
58
  };
50
59
  useEffect(() => {
51
- setValidationResult({
52
- ...validationResult,
60
+ setValidationResult(prev => ({
61
+ ...prev,
53
62
  name: formValues.name === undefined
54
63
  ? undefined
55
64
  : formValues.name.length > 2
@@ -60,19 +69,28 @@ export const DatasourceDetail = () => {
60
69
  : formValues.description.length > 2
61
70
  ? true
62
71
  : false,
63
- });
72
+ }));
64
73
  }, [formValues]);
65
74
  const nameSubmit = async () => {
75
+ if (!datasource) {
76
+ return;
77
+ }
66
78
  runStore.layout().showBackdrop();
67
- datasource.name = formValues.name;
68
- datasource.description = formValues.description;
69
- updateDatasourceMutation.mutate(datasource, {
79
+ const updatedDatasource = {
80
+ ...datasource,
81
+ name: formValues.name,
82
+ description: formValues.description,
83
+ };
84
+ updateDatasourceMutation.mutate(updatedDatasource, {
70
85
  onSuccess: (resp) => {
71
- if (resp.success) {
86
+ if (typeof resp === 'object' &&
87
+ resp !== null &&
88
+ 'success' in resp &&
89
+ resp.success) {
72
90
  enqueueToast('The datasource is successfully updated.', {
73
91
  variant: 'success',
74
92
  });
75
- setDatasource(datasource);
93
+ setDatasource(updatedDatasource);
76
94
  }
77
95
  },
78
96
  onSettled: () => {
@@ -80,12 +98,32 @@ export const DatasourceDetail = () => {
80
98
  },
81
99
  });
82
100
  };
83
- return (_jsxs(_Fragment, { children: [_jsx(PageHeader, { children: _jsx(Heading, { sx: { fontSize: 3 }, children: "Datasource" }) }), _jsxs(Box, { display: "flex", children: [_jsxs(Box, { children: [_jsx(BoringAvatar, { displayName: datasource?.name, size: 100, style: { paddingRight: 10 } }), _jsx(Text, { as: "h2", sx: { paddingTop: 3 }, children: datasource?.name }), _jsx(Box, { mt: 3, children: _jsx(Label, { size: "large", children: datasource?.variant }) })] }), _jsx(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: "Database" }), _jsx(TextInput, { placeholder: "Database", monospace: true, contrast: true, size: "large", type: passwordVisibility ? 'text' : 'password', value: datasource?.database, trailingAction: _jsx(TextInput.Action, { onClick: () => {
84
- setPasswordVisibility(!passwordVisibility);
85
- }, icon: passwordVisibility ? EyeClosedIcon : EyeIcon, "aria-label": passwordVisibility ? 'Hide database' : 'Reveal database', sx: { color: 'var(--fgColor-muted)' } }), sx: { overflow: 'visible' } })] }), _jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Output bucket" }), _jsx(TextInput, { placeholder: "Output bucket", monospace: true, contrast: true, size: "large", type: passwordVisibility ? 'text' : 'password', value: datasource?.outputBucket, trailingAction: _jsx(TextInput.Action, { onClick: () => {
86
- setPasswordVisibility(!passwordVisibility);
87
- }, icon: passwordVisibility ? EyeClosedIcon : EyeIcon, "aria-label": passwordVisibility
88
- ? 'Hide output bucket'
89
- : 'Reveal output bucket', sx: { color: 'var(--fgColor-muted)' } }), sx: { overflow: 'visible' } })] }), _jsx(Button, { variant: "primary", disabled: !validationResult.name || !validationResult.description, sx: { marginTop: 3 }, onClick: nameSubmit, children: "Update datasource" })] }) })] })] }));
101
+ if (!datasourceId) {
102
+ return _jsx(_Fragment, {});
103
+ }
104
+ if (datasourceQuery.isLoading) {
105
+ return (_jsx(PageLayout, { containerWidth: "full", padding: "normal", style: { overflow: 'visible', minHeight: 'calc(100vh - 45px)' }, children: _jsx(PageLayout.Content, { children: _jsx(Box, { sx: { maxWidth: 960, mx: 'auto', width: '100%' }, children: _jsx(Box, { sx: {
106
+ display: 'flex',
107
+ justifyContent: 'center',
108
+ alignItems: 'center',
109
+ py: 6,
110
+ }, children: _jsx(Spinner, { size: "large" }) }) }) }) }));
111
+ }
112
+ if (datasourceQuery.isError || (!datasourceQuery.isLoading && !datasource)) {
113
+ return (_jsx(PageLayout, { containerWidth: "full", padding: "normal", style: { overflow: 'visible', minHeight: 'calc(100vh - 45px)' }, children: _jsx(PageLayout.Content, { children: _jsx(Box, { sx: { maxWidth: 960, mx: 'auto', width: '100%' }, children: _jsx(Box, { sx: { p: 4 }, children: _jsx(Text, { sx: { color: 'danger.fg' }, children: "Datasource not found or failed to load." }) }) }) }) }));
114
+ }
115
+ return (_jsx(PageLayout, { containerWidth: "full", padding: "normal", style: { overflow: 'visible', minHeight: 'calc(100vh - 45px)' }, children: _jsx(PageLayout.Content, { children: _jsxs(Box, { sx: { maxWidth: 960, mx: 'auto', width: '100%' }, children: [_jsxs(Box, { sx: { mb: 4 }, children: [_jsx(Heading, { as: "h2", sx: { fontSize: 3, mb: 1 }, children: "Datasource" }), _jsx(Text, { sx: { color: 'fg.muted', fontSize: 1 }, children: "Update datasource connection metadata and review configured credentials." })] }), _jsx(Box, { sx: {
116
+ border: '1px solid',
117
+ borderColor: 'border.default',
118
+ borderRadius: 2,
119
+ bg: 'canvas.default',
120
+ p: 3,
121
+ }, children: _jsxs(Box, { sx: { display: 'grid', gridTemplateColumns: ['1fr', '180px 1fr'], gap: 4 }, children: [_jsxs(Box, { children: [_jsx(BoringAvatar, { displayName: datasource?.name, size: 100, style: { paddingRight: 10 } }), _jsx(Box, { mt: 3, children: _jsx(Label, { size: "large", children: datasource?.variant }) })] }), _jsx(Box, { 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: "Database" }), _jsx(TextInput, { placeholder: "Database", monospace: true, size: "large", contrast: !passwordVisibility, disabled: !passwordVisibility, type: passwordVisibility ? 'text' : 'password', value: datasource?.database || '', readOnly: true, trailingAction: _jsx(TextInput.Action, { onClick: () => {
122
+ setPasswordVisibility(!passwordVisibility);
123
+ }, icon: passwordVisibility ? EyeClosedIcon : EyeIcon, "aria-label": passwordVisibility ? 'Hide database' : 'Reveal database', sx: { color: 'var(--fgColor-muted)' } }), sx: { overflow: 'visible' } })] }), _jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Output bucket" }), _jsx(TextInput, { placeholder: "Output bucket", monospace: true, size: "large", contrast: !passwordVisibility, disabled: !passwordVisibility, type: passwordVisibility ? 'text' : 'password', value: datasource?.outputBucket || '', readOnly: true, trailingAction: _jsx(TextInput.Action, { onClick: () => {
124
+ setPasswordVisibility(!passwordVisibility);
125
+ }, icon: passwordVisibility ? EyeClosedIcon : EyeIcon, "aria-label": passwordVisibility
126
+ ? 'Hide output bucket'
127
+ : 'Reveal output bucket', sx: { color: 'var(--fgColor-muted)' } }), sx: { overflow: 'visible' } })] }), _jsx(Box, { sx: { marginTop: 3 }, children: _jsx(Button, { variant: "primary", disabled: !validationResult.name || !validationResult.description, onClick: nameSubmit, children: "Update datasource" }) })] }) })] }) })] }) }) }));
90
128
  };
91
129
  export default DatasourceDetail;
@@ -1,8 +1,15 @@
1
+ import { ReactNode } from 'react';
1
2
  export type DatasourceNewProps = {
2
3
  /** Route to navigate after creating a datasource. Defaults to '/settings/integrations/datasources'. */
3
4
  datasourcesListRoute?: string;
4
5
  /** Route to navigate to the secrets page. Defaults to '/settings/iam/secrets'. */
5
6
  secretsRoute?: string;
7
+ /** Optional principal uid used to scope datasource creation. */
8
+ principalUid?: string;
9
+ /** Optional principal kind used to scope datasource creation. */
10
+ principalKind?: 'user' | 'organization' | 'team';
11
+ /** Optional contextual principal summary rendered below the page intro. */
12
+ accountPrincipal?: ReactNode;
6
13
  };
7
- export declare const DatasourceNew: ({ datasourcesListRoute, secretsRoute, }?: DatasourceNewProps) => import("react/jsx-runtime").JSX.Element;
14
+ export declare const DatasourceNew: ({ datasourcesListRoute, secretsRoute, principalUid, principalKind, accountPrincipal, }?: DatasourceNewProps) => import("react/jsx-runtime").JSX.Element;
8
15
  export default DatasourceNew;
@@ -4,14 +4,17 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
4
4
  * Distributed under the terms of the Modified BSD License.
5
5
  */
6
6
  import { useEffect, useState } from 'react';
7
- import { PageHeader, FormControl, Button, TextInput, Text, Textarea, Select, Flash, Link, } from '@primer/react';
7
+ import { PageLayout, FormControl, Button, TextInput, Text, Heading, Textarea, Select, Flash, Link, } from '@primer/react';
8
8
  import { Box } from '@datalayer/primer-addons';
9
9
  import { useCache, useNavigate, useToast } from '../../hooks/index.js';
10
10
  import { useRunStore } from '../../state/index.js';
11
- export const DatasourceNew = ({ datasourcesListRoute = '/settings/integrations/datasources', secretsRoute = '/settings/iam/secrets', } = {}) => {
11
+ export const DatasourceNew = ({ datasourcesListRoute = '/settings/integrations/datasources', secretsRoute = '/settings/iam/secrets', principalUid, principalKind, accountPrincipal, } = {}) => {
12
12
  const runStore = useRunStore();
13
13
  const { useCreateDatasource } = useCache();
14
- const createDatasourceMutation = useCreateDatasource();
14
+ const createDatasourceMutation = useCreateDatasource({
15
+ principalUid,
16
+ principalKind,
17
+ });
15
18
  const navigate = useNavigate();
16
19
  const { enqueueToast } = useToast();
17
20
  const [formValues, setFormValues] = useState({
@@ -59,8 +62,8 @@ export const DatasourceNew = ({ datasourcesListRoute = '/settings/integrations/d
59
62
  }));
60
63
  };
61
64
  useEffect(() => {
62
- setValidationResult({
63
- ...validationResult,
65
+ setValidationResult(prev => ({
66
+ ...prev,
64
67
  name: formValues.name === undefined
65
68
  ? undefined
66
69
  : formValues.name.length > 2
@@ -85,9 +88,12 @@ export const DatasourceNew = ({ datasourcesListRoute = '/settings/integrations/d
85
88
  : formValues.outputBucket.length > 0
86
89
  ? true
87
90
  : false,
88
- });
91
+ }));
89
92
  }, [formValues]);
90
93
  const submitCreate = () => {
94
+ if (!formValues.name || !formValues.description) {
95
+ return;
96
+ }
91
97
  runStore.layout().showBackdrop('Creating an datasource...');
92
98
  createDatasourceMutation.mutate({
93
99
  name: formValues.name,
@@ -97,8 +103,15 @@ export const DatasourceNew = ({ datasourcesListRoute = '/settings/integrations/d
97
103
  description: formValues.description,
98
104
  }, {
99
105
  onSuccess: (resp) => {
100
- if (resp.success) {
101
- enqueueToast(resp.message, { variant: 'success' });
106
+ if (typeof resp === 'object' &&
107
+ resp !== null &&
108
+ 'success' in resp &&
109
+ resp.success) {
110
+ const message = 'message' in resp &&
111
+ typeof resp.message === 'string'
112
+ ? resp.message
113
+ : 'Datasource created.';
114
+ enqueueToast(message, { variant: 'success' });
102
115
  navigate(datasourcesListRoute);
103
116
  }
104
117
  },
@@ -107,12 +120,12 @@ export const DatasourceNew = ({ datasourcesListRoute = '/settings/integrations/d
107
120
  },
108
121
  });
109
122
  };
110
- return (_jsxs(Box, { children: [_jsx(PageHeader, { children: _jsx(PageHeader.TitleArea, { variant: "large", children: _jsx(PageHeader.Title, { children: "New Datasource" }) }) }), _jsxs(Flash, { variant: "warning", children: [formValues.variant === 'athena' && (_jsxs(Text, { children: ["For ", _jsx(Link, { href: "https://aws.amazon.com/athena", children: "Amazon Athena" }), ", ensure the following", ' ', _jsx(Link, { href: "javascript: return false;", onClick: e => navigate(secretsRoute, e), children: "Secrets" }), ' ', "are available:", ' ', _jsx(Text, { as: "code", children: "AWS_SECRET_ACCESS_KEY" }), ' ', _jsx(Text, { as: "code", children: "AWS_ACCESS_KEY_ID" }), ' ', _jsx(Text, { as: "code", children: "AWS_DEFAULT_REGION" })] })), formValues.variant === 'bigquery' && (_jsxs(Text, { children: ["For", ' ', _jsx(Link, { href: "https://cloud.google.com/bigquery", children: "Google Big Query" }), ", ensure the following", ' ', _jsx(Link, { href: "javascript: return false;", onClick: e => navigate(secretsRoute, e), children: "Secret" }), ' ', "is available:", ' ', _jsx(Text, { as: "code", children: "GOOGLE_APPLICATION_CREDENTIALS" })] })), formValues.variant === 'mssentinel' && (_jsxs(Text, { children: ["For", ' ', _jsx(Link, { href: "https://learn.microsoft.com/en-us/azure/sentinel/overview?tabs=defender-portaly", children: "Microsoft Sentinel" }), ", ensure the following", ' ', _jsx(Link, { href: "javascript: return false;", onClick: e => navigate(secretsRoute, e), children: "Secret" }), ' ', "is available:", ' ', _jsx(Text, { as: "code", children: "AZURE_TENANT_ID" }), ` `, _jsx(Text, { as: "code", children: "AZURE_CLIENT_ID" }), ` `, _jsx(Text, { as: "code", children: "AZURE_CLIENT_SECRET" }), ` `, _jsx(Text, { as: "code", children: "AZURE_SUBSCRIPTION_ID" }), ` `, _jsx(Text, { as: "code", children: "AZURE_RESOURCE_GROUP" }), ` `, _jsx(Text, { as: "code", children: "MSSENTINEL_WORKSPACE_ID" }), ` `, _jsx(Text, { as: "code", children: "MSSENTINEL_WORKSPACE_NAME" })] })), formValues.variant === 'splunk' && (_jsxs(Text, { children: ["For ", _jsx(Link, { href: "https://www.splunk.com/", children: "Splunk" }), ", ensure the following", ' ', _jsx(Link, { href: "javascript: return false;", onClick: e => navigate(secretsRoute, e), children: "Secret" }), ' ', "is available:", ' ', _jsx(Text, { as: "code", children: "SPLUNK_HOST" }), ` `, _jsx(Text, { as: "code", children: "SPLUNK_PORT" }), ` `, _jsx(Text, { as: "code", children: "SPLUNK_USERNAME" }), ` `, _jsx(Text, { as: "code", children: "SPLUNK_PASSWORD" })] }))] }), _jsx(Box, { display: "grid", gridTemplateColumns: "1fr 1fr", sx: { gap: 3 }, children: _jsx(Box, { children: _jsxs(Box, { sx: { label: { marginTop: 2 } }, children: [_jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Datasource type" }), _jsxs(Select, { name: "type", value: formValues.variant, onChange: valueVariantChange, children: [_jsx(Select.Option, { value: "athena", children: "Amazon Athena" }), _jsx(Select.Option, { value: "bigquery", children: "Google BigQuery" }), _jsx(Select.Option, { value: "mssentinel", children: "Microsoft Sentinel" }), _jsx(Select.Option, { value: "splunk", children: "Splunk" })] }), _jsx(FormControl.Caption, { children: "Pick the most appropriate datasource type." })] }), _jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Name" }), _jsx(TextInput, { block: true, value: formValues.name, onChange: valueNameChange, autoFocus: true }), _jsx(FormControl.Caption, { children: "Hint: The datasource name is a short name that identifies in a unique way your datasource." }), validationResult.name === false && (_jsx(FormControl.Validation, { variant: "error", children: "Name length must be between 2 and 32 characters." }))] }), formValues.variant === 'athena' && (_jsxs(_Fragment, { children: [_jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Database" }), _jsx(TextInput, { block: true, value: formValues.database, onChange: valueDatabaseChange }), validationResult.database === false && (_jsx(FormControl.Validation, { variant: "error", children: "Database must have more than 1." }))] }), _jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Output Bucket" }), _jsx(TextInput, { block: true, value: formValues.outputBucket, onChange: valueOutputBucketChange }), validationResult.database === false && (_jsx(FormControl.Validation, { variant: "error", children: "Output bucket must have more than 1." }))] })] })), _jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Description" }), _jsx(Textarea, { block: true, value: formValues.description, onChange: valueDescriptionChange }), validationResult.description === false && (_jsx(FormControl.Validation, { variant: "error", children: "Description must have more than 2 characters." }))] }), _jsx(Button, { variant: "primary", disabled: !validationResult.database ||
123
+ return (_jsx(PageLayout, { containerWidth: "full", padding: "normal", style: { overflow: 'visible', minHeight: 'calc(100vh - 45px)' }, children: _jsx(PageLayout.Content, { children: _jsxs(Box, { sx: { maxWidth: 960, mx: 'auto', width: '100%' }, children: [_jsxs(Box, { sx: { mb: 4 }, children: [_jsx(Heading, { as: "h2", sx: { fontSize: 3, mb: 1 }, children: "New Datasource" }), _jsx(Text, { sx: { color: 'fg.muted', fontSize: 1 }, children: "Create a datasource and configure required secrets for the selected provider." }), accountPrincipal && _jsx(Box, { sx: { mt: 2 }, children: accountPrincipal })] }), _jsxs(Flash, { variant: "warning", sx: { mb: 3 }, children: [formValues.variant === 'athena' && (_jsxs(Text, { children: ["For", ' ', _jsx(Link, { href: "https://aws.amazon.com/athena", children: "Amazon Athena" }), ",", ' ', "ensure the following", ' ', _jsx(Link, { href: "javascript: return false;", onClick: e => navigate(secretsRoute, e), children: "Secrets" }), ' ', "are available:", ' ', _jsx(Text, { as: "code", children: "AWS_SECRET_ACCESS_KEY" }), ' ', _jsx(Text, { as: "code", children: "AWS_ACCESS_KEY_ID" }), ' ', _jsx(Text, { as: "code", children: "AWS_DEFAULT_REGION" })] })), formValues.variant === 'bigquery' && (_jsxs(Text, { children: ["For", ' ', _jsx(Link, { href: "https://cloud.google.com/bigquery", children: "Google Big Query" }), ", ensure the following", ' ', _jsx(Link, { href: "javascript: return false;", onClick: e => navigate(secretsRoute, e), children: "Secret" }), ' ', "is available:", ' ', _jsx(Text, { as: "code", children: "GOOGLE_APPLICATION_CREDENTIALS" })] })), formValues.variant === 'mssentinel' && (_jsxs(Text, { children: ["For", ' ', _jsx(Link, { href: "https://learn.microsoft.com/en-us/azure/sentinel/overview?tabs=defender-portaly", children: "Microsoft Sentinel" }), ", ensure the following", ' ', _jsx(Link, { href: "javascript: return false;", onClick: e => navigate(secretsRoute, e), children: "Secret" }), ' ', "is available:", ' ', _jsx(Text, { as: "code", children: "AZURE_TENANT_ID" }), ` `, _jsx(Text, { as: "code", children: "AZURE_CLIENT_ID" }), ` `, _jsx(Text, { as: "code", children: "AZURE_CLIENT_SECRET" }), ` `, _jsx(Text, { as: "code", children: "AZURE_SUBSCRIPTION_ID" }), ` `, _jsx(Text, { as: "code", children: "AZURE_RESOURCE_GROUP" }), ` `, _jsx(Text, { as: "code", children: "MSSENTINEL_WORKSPACE_ID" }), ` `, _jsx(Text, { as: "code", children: "MSSENTINEL_WORKSPACE_NAME" })] })), formValues.variant === 'splunk' && (_jsxs(Text, { children: ["For ", _jsx(Link, { href: "https://www.splunk.com/", children: "Splunk" }), ", ensure the following", ' ', _jsx(Link, { href: "javascript: return false;", onClick: e => navigate(secretsRoute, e), children: "Secret" }), ' ', "is available:", ' ', _jsx(Text, { as: "code", children: "SPLUNK_HOST" }), ` `, _jsx(Text, { as: "code", children: "SPLUNK_PORT" }), ` `, _jsx(Text, { as: "code", children: "SPLUNK_USERNAME" }), ` `, _jsx(Text, { as: "code", children: "SPLUNK_PASSWORD" })] }))] }), _jsxs(Box, { sx: { label: { marginTop: 2 } }, children: [_jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Datasource type" }), _jsxs(Select, { name: "type", value: formValues.variant, onChange: valueVariantChange, children: [_jsx(Select.Option, { value: "athena", children: "Amazon Athena" }), _jsx(Select.Option, { value: "bigquery", children: "Google BigQuery" }), _jsx(Select.Option, { value: "mssentinel", children: "Microsoft Sentinel" }), _jsx(Select.Option, { value: "splunk", children: "Splunk" })] }), _jsx(FormControl.Caption, { children: "Pick the most appropriate datasource type." })] }), _jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Name" }), _jsx(TextInput, { block: true, value: formValues.name, onChange: valueNameChange, autoFocus: true }), _jsx(FormControl.Caption, { children: "Hint: The datasource name is a short name that identifies in a unique way your datasource." }), validationResult.name === false && (_jsx(FormControl.Validation, { variant: "error", children: "Name length must be between 2 and 32 characters." }))] }), formValues.variant === 'athena' && (_jsxs(_Fragment, { children: [_jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Database" }), _jsx(TextInput, { block: true, value: formValues.database, onChange: valueDatabaseChange }), validationResult.database === false && (_jsx(FormControl.Validation, { variant: "error", children: "Database must have more than 1." }))] }), _jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Output Bucket" }), _jsx(TextInput, { block: true, value: formValues.outputBucket, onChange: valueOutputBucketChange }), validationResult.database === false && (_jsx(FormControl.Validation, { variant: "error", children: "Output bucket must have more than 1." }))] })] })), _jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Description" }), _jsx(Textarea, { block: true, value: formValues.description, onChange: valueDescriptionChange }), validationResult.description === false && (_jsx(FormControl.Validation, { variant: "error", children: "Description must have more than 2 characters." }))] }), _jsx(Button, { variant: "primary", disabled: !validationResult.database ||
111
124
  !validationResult.outputBucket ||
112
125
  !validationResult.name ||
113
126
  !validationResult.description, sx: { marginTop: 2 }, onClick: e => {
114
127
  e.preventDefault();
115
128
  submitCreate();
116
- }, children: "Create a datasource" })] }) }) })] }));
129
+ }, children: "Create a datasource" })] })] }) }) }));
117
130
  };
118
131
  export default DatasourceNew;
@@ -3,6 +3,10 @@ export type DatasourcesProps = {
3
3
  newDatasourceRoute?: string;
4
4
  /** Base route for the datasources list (used for edit navigation). Defaults to current relative path. */
5
5
  datasourcesListRoute?: string;
6
+ /** Optional principal uid used to scope datasource reads. */
7
+ principalUid?: string;
8
+ /** Optional principal kind used to scope datasource reads. */
9
+ principalKind?: 'user' | 'organization' | 'team';
6
10
  };
7
- export declare const Datasources: ({ newDatasourceRoute, datasourcesListRoute, }?: DatasourcesProps) => import("react/jsx-runtime").JSX.Element;
11
+ export declare const Datasources: ({ newDatasourceRoute, datasourcesListRoute, principalUid, principalKind, }?: DatasourcesProps) => import("react/jsx-runtime").JSX.Element;
8
12
  export default Datasources;
@@ -4,14 +4,15 @@ 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 { useState, useEffect } from 'react';
7
- import { PageLayout, Button, IconButton, Text, Label } from '@primer/react';
8
- import { Blankslate, PageHeader, Table, DataTable, } from '@primer/react/experimental';
7
+ import { PageLayout, Button, IconButton, Text, Label, Heading, } from '@primer/react';
8
+ import { Blankslate, Table, DataTable } from '@primer/react/experimental';
9
+ import { DatabaseIcon } from '@primer/octicons-react';
9
10
  import { Box } from '@datalayer/primer-addons';
10
11
  import { EditIcon } from '@datalayer/icons-react';
11
12
  import { useCache, useNavigate } from '../../hooks/index.js';
12
- const DatasourcesTable = ({ datasourcesListRoute, }) => {
13
+ const DatasourcesTable = ({ datasourcesListRoute, principalUid, principalKind, }) => {
13
14
  const { useDatasources } = useCache();
14
- const datasourcesQuery = useDatasources();
15
+ const datasourcesQuery = useDatasources({ principalUid, principalKind });
15
16
  const navigate = useNavigate();
16
17
  const [datasources, setDatasources] = useState([]);
17
18
  useEffect(() => {
@@ -19,33 +20,40 @@ const DatasourcesTable = ({ datasourcesListRoute, }) => {
19
20
  setDatasources(datasourcesQuery.data || []);
20
21
  }
21
22
  }, [datasourcesQuery.data]);
22
- return datasources.length === 0 ? (_jsxs(Blankslate, { border: true, spacious: true, children: [_jsx(Blankslate.Heading, { children: "Datasources" }), _jsx(Blankslate.Description, { children: _jsx(Text, { sx: { textAlign: 'center' }, children: "No Datasources found." }) })] })) : (_jsxs(Table.Container, { children: [_jsx(Table.Title, { as: "h2", id: "datasources", children: "Datasources" }), _jsx(Table.Subtitle, { as: "p", id: "datasources-subtitle", children: "Your datasources." }), _jsx(DataTable, { "aria-labelledby": "teams", "aria-describedby": "teams-subtitle", data: datasources, columns: [
23
- // @ts-ignore
24
- {
25
- header: 'Type',
26
- field: 'variant',
27
- renderCell: datasource => _jsx(Label, { children: datasource.variant }),
28
- },
29
- {
30
- header: 'Name',
31
- field: 'name',
32
- rowHeader: true,
33
- },
34
- {
35
- header: 'Description',
36
- field: 'description',
37
- },
38
- {
39
- header: '',
40
- field: 'id',
41
- renderCell: datasource => (_jsx(IconButton, { icon: EditIcon, "aria-label": "Edit", size: "small", variant: "invisible", onClick: e => navigate(datasourcesListRoute
42
- ? `${datasourcesListRoute}/${datasource.id}`
43
- : `${datasource.id}`, e) })),
44
- },
45
- ] })] }));
23
+ return datasources.length === 0 ? (_jsxs(Blankslate, { border: true, spacious: true, children: [_jsx(Blankslate.Heading, { children: "Datasources" }), _jsx(Blankslate.Description, { children: _jsx(Text, { sx: { textAlign: 'center' }, children: "No Datasources found." }) })] })) : (_jsx(Table.Container, { children: _jsx(DataTable, { "aria-labelledby": "teams", "aria-describedby": "teams-subtitle", data: datasources, columns: [
24
+ // @ts-ignore
25
+ {
26
+ header: 'Type',
27
+ field: 'variant',
28
+ renderCell: datasource => _jsx(Label, { children: datasource.variant }),
29
+ },
30
+ {
31
+ header: 'Name',
32
+ field: 'name',
33
+ rowHeader: true,
34
+ },
35
+ {
36
+ header: 'Description',
37
+ field: 'description',
38
+ },
39
+ {
40
+ header: '',
41
+ field: 'id',
42
+ renderCell: datasource => (_jsx(IconButton, { icon: EditIcon, "aria-label": "Edit", size: "small", variant: "invisible", onClick: e => navigate(datasourcesListRoute
43
+ ? `${datasourcesListRoute}/${datasource.id}`
44
+ : `${datasource.id}`, e) })),
45
+ },
46
+ ] }) }));
46
47
  };
47
- export const Datasources = ({ newDatasourceRoute = '/new/datasource', datasourcesListRoute, } = {}) => {
48
+ export const Datasources = ({ newDatasourceRoute = '/new/datasource', datasourcesListRoute, principalUid, principalKind, } = {}) => {
48
49
  const navigate = useNavigate();
49
- 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: "Datasources" }) }), _jsx(PageHeader.Actions, { children: _jsx(Button, { size: "small", variant: "primary", onClick: e => navigate(newDatasourceRoute, e), children: "New datasource" }) })] }) }), _jsx(PageLayout.Content, { children: _jsx(Box, { children: _jsx(DatasourcesTable, { datasourcesListRoute: datasourcesListRoute }) }) })] }));
50
+ return (_jsx(PageLayout, { containerWidth: "full", padding: "normal", style: { overflow: 'visible', minHeight: 'calc(100vh - 45px)' }, children: _jsx(PageLayout.Content, { children: _jsxs(Box, { sx: { maxWidth: 960, mx: 'auto', width: '100%' }, children: [_jsxs(Box, { sx: {
51
+ display: 'flex',
52
+ alignItems: 'flex-start',
53
+ justifyContent: 'space-between',
54
+ gap: 3,
55
+ flexWrap: 'wrap',
56
+ mb: 4,
57
+ }, children: [_jsxs(Box, { children: [_jsx(Heading, { as: "h2", sx: { fontSize: 3, mb: 1 }, children: "Datasources" }), _jsx(Text, { sx: { color: 'fg.muted', fontSize: 1 }, children: "Configure external data providers available to your workspaces and agents." })] }), _jsx(Button, { size: "small", variant: "primary", leadingVisual: DatabaseIcon, onClick: e => navigate(newDatasourceRoute, e), children: "New datasource" })] }), _jsx(DatasourcesTable, { datasourcesListRoute: datasourcesListRoute, principalUid: principalUid, principalKind: principalKind })] }) }) }));
50
58
  };
51
59
  export default Datasources;
@@ -1,6 +1,8 @@
1
1
  export type IAMTokenNewProps = {
2
2
  /** Route to navigate when clicking "List my Tokens". Defaults to '/settings/iam/tokens'. */
3
3
  tokensListRoute?: string;
4
+ /** Whether to render the "New API Key" title header in create mode. Defaults to true. */
5
+ showTitle?: boolean;
4
6
  };
5
- export declare const IAMTokenNew: ({ tokensListRoute, }?: IAMTokenNewProps) => import("react/jsx-runtime").JSX.Element;
7
+ export declare const IAMTokenNew: ({ tokensListRoute, showTitle, }?: IAMTokenNewProps) => import("react/jsx-runtime").JSX.Element;
6
8
  export default IAMTokenNew;
@@ -10,7 +10,7 @@ import { CopyIcon } from '@primer/octicons-react';
10
10
  import { Calendar, defaultCalendarStrings } from '@fluentui/react';
11
11
  import { useCache, useNavigate, useToast } from '../../hooks/index.js';
12
12
  import { useRunStore } from '../../state/index.js';
13
- export const IAMTokenNew = ({ tokensListRoute = '/settings/iam/tokens', } = {}) => {
13
+ export const IAMTokenNew = ({ tokensListRoute = '/settings/iam/tokens', showTitle = true, } = {}) => {
14
14
  const runStore = useRunStore();
15
15
  const { useCreateToken } = useCache();
16
16
  const createTokenMutation = useCreateToken();
@@ -108,7 +108,7 @@ export const IAMTokenNew = ({ tokensListRoute = '/settings/iam/tokens', } = {})
108
108
  variant: 'success',
109
109
  });
110
110
  }
111
- } })] })] }), _jsx(Box, { mt: 3, children: _jsx(Button, { onClick: e => navigate(tokensListRoute, e), children: "List my API Keys" }) })] })) : (_jsxs(_Fragment, { children: [_jsx(PageHeader, { children: _jsx(PageHeader.TitleArea, { variant: "large", children: _jsx(PageHeader.Title, { children: "New API Key" }) }) }), _jsx(Box, { display: "grid", gridTemplateColumns: "1fr 1fr", sx: { gap: 3 }, children: _jsx(Box, { children: _jsxs(Box, { sx: { label: { marginTop: 2 } }, children: [_jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Token type" }), _jsx(Select, { name: "type", value: formValues.variant, onChange: valueVariantChange, children: _jsx(Select.Option, { value: "user_token", children: "User Token" }) }), _jsx(FormControl.Caption, { children: "Pick the most appropriate token type." })] }), _jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Name" }), _jsx(TextInput, { block: true, value: formValues.name, onChange: valueNameChange, autoFocus: true }), _jsx(FormControl.Caption, { children: "Hint: The token name is a short name that identifies in a unique way your token." }), validationResult.name === false && (_jsx(FormControl.Validation, { variant: "error", children: "Name length must be between 2 and 32 characters." }))] }), _jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Expiration day" }), _jsx(Calendar, { showGoToToday: true, onSelectDate: expirationDateChange, value: formValues.expirationDate, strings: defaultCalendarStrings }), validationResult.expirationDate !== true ? (_jsx(FormControl.Validation, { variant: "error", children: "Pick an expiration date in the future." })) : (_jsxs(FormControl.Validation, { variant: "success", children: ["Expiration date:", ' ', formValues.expirationDate?.toLocaleDateString(), "."] }))] }), _jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Description" }), _jsx(Textarea, { block: true, value: formValues.description, onChange: valueDescriptionChange }), validationResult.description === false && (_jsx(FormControl.Validation, { variant: "error", children: "Description must have more than 2 characters." }))] }), _jsx(Button, { variant: "primary", disabled: !validationResult.name ||
111
+ } })] })] }), _jsx(Box, { mt: 3, children: _jsx(Button, { onClick: e => navigate(tokensListRoute, e), children: "List my API Keys" }) })] })) : (_jsxs(_Fragment, { children: [showTitle ? (_jsx(PageHeader, { children: _jsx(PageHeader.TitleArea, { variant: "large", children: _jsx(PageHeader.Title, { children: "New API Key" }) }) })) : null, _jsx(Box, { display: "grid", gridTemplateColumns: "1fr 1fr", sx: { gap: 3 }, children: _jsx(Box, { children: _jsxs(Box, { sx: { label: { marginTop: 2 } }, children: [_jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Token type" }), _jsx(Select, { name: "type", value: formValues.variant, onChange: valueVariantChange, children: _jsx(Select.Option, { value: "user_token", children: "User Token" }) }), _jsx(FormControl.Caption, { children: "Pick the most appropriate token type." })] }), _jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Name" }), _jsx(TextInput, { block: true, value: formValues.name, onChange: valueNameChange, autoFocus: true }), _jsx(FormControl.Caption, { children: "Hint: The token name is a short name that identifies in a unique way your token." }), validationResult.name === false && (_jsx(FormControl.Validation, { variant: "error", children: "Name length must be between 2 and 32 characters." }))] }), _jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Expiration day" }), _jsx(Calendar, { showGoToToday: true, onSelectDate: expirationDateChange, value: formValues.expirationDate, strings: defaultCalendarStrings }), validationResult.expirationDate !== true ? (_jsx(FormControl.Validation, { variant: "error", children: "Pick an expiration date in the future." })) : (_jsxs(FormControl.Validation, { variant: "success", children: ["Expiration date:", ' ', formValues.expirationDate?.toLocaleDateString(), "."] }))] }), _jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Description" }), _jsx(Textarea, { block: true, value: formValues.description, onChange: valueDescriptionChange }), validationResult.description === false && (_jsx(FormControl.Validation, { variant: "error", children: "Description must have more than 2 characters." }))] }), _jsx(Button, { variant: "primary", disabled: !validationResult.name ||
112
112
  !validationResult.description ||
113
113
  !validationResult.expirationDate, sx: { marginTop: 2 }, onClick: e => {
114
114
  e.preventDefault();
@@ -5,6 +5,8 @@ export type IAMTokensProps = {
5
5
  tokensListRoute?: string;
6
6
  /** Whether to display view titles/headings. Defaults to true. */
7
7
  showTitle?: boolean;
8
+ /** Whether to render the "New API Key" button in this component header. Defaults to true. */
9
+ showNewButton?: boolean;
8
10
  };
9
- export declare const IAMTokens: ({ newTokenRoute, tokensListRoute, showTitle, }?: IAMTokensProps) => import("react/jsx-runtime").JSX.Element;
11
+ export declare const IAMTokens: ({ newTokenRoute, tokensListRoute, showTitle, showNewButton, }?: IAMTokensProps) => import("react/jsx-runtime").JSX.Element;
10
12
  export default IAMTokens;
@@ -80,8 +80,8 @@ const TokensTable = ({ tokensListRoute, showTitle = true, }) => {
80
80
  setDeletingToken(null);
81
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."] }))] }));
82
82
  };
83
- export const IAMTokens = ({ newTokenRoute = '/new/token', tokensListRoute, showTitle = true, } = {}) => {
83
+ export const IAMTokens = ({ newTokenRoute = '/new/token', tokensListRoute, showTitle = true, showNewButton = true, } = {}) => {
84
84
  const navigate = useNavigate();
85
- return (_jsxs(PageLayout, { containerWidth: "full", padding: "normal", style: { overflow: 'visible', minHeight: 'calc(100vh - 45px)' }, children: [_jsx(PageLayout.Header, { children: _jsxs(PageHeader, { children: [showTitle && (_jsx(PageHeader.TitleArea, { variant: "large", children: _jsx(PageHeader.Title, { children: "API Keys" }) })), _jsx(PageHeader.Actions, { children: _jsx(Button, { size: "small", variant: "primary", onClick: e => navigate(newTokenRoute, e), children: "New API Key" }) })] }) }), _jsx(PageLayout.Content, { children: _jsx(Box, { children: _jsx(TokensTable, { tokensListRoute: tokensListRoute, showTitle: showTitle }) }) })] }));
85
+ return (_jsxs(PageLayout, { containerWidth: "full", padding: "normal", style: { overflow: 'visible', minHeight: 'calc(100vh - 45px)' }, children: [showTitle || showNewButton ? (_jsx(PageLayout.Header, { children: _jsxs(PageHeader, { children: [showTitle && (_jsx(PageHeader.TitleArea, { variant: "large", children: _jsx(PageHeader.Title, { children: "API Keys" }) })), showNewButton && (_jsx(PageHeader.Actions, { children: _jsx(Button, { size: "small", variant: "primary", onClick: e => navigate(newTokenRoute, e), children: "New API Key" }) }))] }) })) : null, _jsx(PageLayout.Content, { children: _jsx(Box, { children: _jsx(TokensTable, { tokensListRoute: tokensListRoute, showTitle: showTitle }) }) })] }));
86
86
  };
87
87
  export default IAMTokens;
@@ -29,5 +29,10 @@ export interface OtelHeaderProps {
29
29
  * where data is generated by agents, not manually).
30
30
  */
31
31
  showGenerateButtons?: boolean;
32
+ /**
33
+ * Whether to show account controls (token badge + sign out).
34
+ * Defaults to `true`.
35
+ */
36
+ showAccountControls?: boolean;
32
37
  }
33
38
  export declare const OtelHeader: React.FC<OtelHeaderProps>;
@@ -14,7 +14,7 @@ import { useState, useCallback, useRef } from 'react';
14
14
  import { Box, Text, Button, Dialog } from '@primer/react';
15
15
  import { TelescopeIcon, SignOutIcon } from '@primer/octicons-react';
16
16
  import { UserBadge } from '../profile/index.js';
17
- export const OtelHeader = ({ baseUrl = '', token, trailing, onNavigate, onSignOut, showGenerateButtons = true, }) => {
17
+ export const OtelHeader = ({ baseUrl = '', token, trailing, onNavigate, onSignOut, showGenerateButtons = true, showAccountControls = true, }) => {
18
18
  const [dialogOpen, setDialogOpen] = useState(false);
19
19
  const [dialogTitle, setDialogTitle] = useState('');
20
20
  const [dialogRequest, setDialogRequest] = useState('');
@@ -33,7 +33,7 @@ export const OtelHeader = ({ baseUrl = '', token, trailing, onNavigate, onSignOu
33
33
  onNavigate(signalMap[kind]);
34
34
  }
35
35
  setGenerating(true);
36
- const url = `${baseUrl}/api/generate/${kind}?count=${count}`;
36
+ const url = `${baseUrl}/api/otel/v1/generate/${kind}?count=${count}`;
37
37
  setDialogRequest(`POST ${url}`);
38
38
  try {
39
39
  const headers = {};
@@ -70,7 +70,7 @@ export const OtelHeader = ({ baseUrl = '', token, trailing, onNavigate, onSignOu
70
70
  flexShrink: 0,
71
71
  // Allow overflow visible so the JWT popover can extend below the header.
72
72
  overflow: 'visible',
73
- }, children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(TelescopeIcon, { size: 20 }), _jsx(Text, { sx: { fontWeight: 'bold', fontSize: 2 }, children: "Datalayer OTEL" })] }), _jsx(Box, { sx: { flex: 1 } }), showGenerateButtons && (_jsxs(_Fragment, { children: [_jsx(Button, { ref: returnFocusRef, size: "small", variant: "primary", disabled: generating, onClick: () => generate('traces', 3), children: "+ Traces" }), _jsx(Button, { size: "small", variant: "primary", disabled: generating, onClick: () => generate('ai-traces', 3), children: "+ AI Traces" }), _jsx(Button, { size: "small", variant: "primary", disabled: generating, onClick: () => generate('logs', 10), children: "+ Logs" }), _jsx(Button, { size: "small", variant: "primary", disabled: generating, onClick: () => generate('metrics', 5), children: "+ Metrics" })] })), trailing, token && _jsx(UserBadge, { token: token }), onSignOut && (_jsx(Button, { size: "small", variant: "invisible", onClick: onSignOut, sx: { color: 'fg.muted' }, leadingVisual: SignOutIcon, children: "Sign out" }))] }), dialogOpen && (_jsxs(Dialog, { title: dialogTitle, onClose: () => setDialogOpen(false), footerButtons: [
73
+ }, children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(TelescopeIcon, { size: 20 }), _jsx(Text, { sx: { fontWeight: 'bold', fontSize: 2 }, children: "Datalayer OTEL" })] }), _jsx(Box, { sx: { flex: 1 } }), showGenerateButtons && (_jsxs(_Fragment, { children: [_jsx(Button, { ref: returnFocusRef, size: "small", variant: "primary", disabled: generating, onClick: () => generate('traces', 3), children: "+ Traces" }), _jsx(Button, { size: "small", variant: "primary", disabled: generating, onClick: () => generate('ai-traces', 3), children: "+ AI Traces" }), _jsx(Button, { size: "small", variant: "primary", disabled: generating, onClick: () => generate('logs', 10), children: "+ Logs" }), _jsx(Button, { size: "small", variant: "primary", disabled: generating, onClick: () => generate('metrics', 5), children: "+ Metrics" })] })), trailing, showAccountControls && token && _jsx(UserBadge, { token: token }), showAccountControls && onSignOut && (_jsx(Button, { size: "small", variant: "invisible", onClick: onSignOut, sx: { color: 'fg.muted' }, leadingVisual: SignOutIcon, children: "Sign out" }))] }), dialogOpen && (_jsxs(Dialog, { title: dialogTitle, onClose: () => setDialogOpen(false), footerButtons: [
74
74
  {
75
75
  content: 'Close',
76
76
  onClick: () => setDialogOpen(false),
@@ -27,10 +27,15 @@ export interface UserBadgeProps {
27
27
  * Threshold (in milliseconds) below which the expiration timestamp is
28
28
  * rendered in ``attention.fg`` (yellow) to warn that the token is about
29
29
  * to expire. Tokens already past ``exp`` render in ``danger.fg`` (red);
30
- * tokens further out than this threshold render in ``success.fg``
31
- * (green). Defaults to ``3_600_000`` (1 hour).
30
+ * tokens further out than this threshold render in ``success.fg`` (green).
31
+ * Defaults to ``10_800_000`` (3 hours).
32
32
  */
33
33
  expirationWarningMs?: number;
34
+ /**
35
+ * Optional callback fired when the claims popover is open and the token is
36
+ * already expired.
37
+ */
38
+ onTokenExpired?: () => void;
34
39
  }
35
40
  export declare const UserBadge: React.FC<UserBadgeProps>;
36
41
  export default UserBadge;
@@ -14,12 +14,12 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
14
14
  *
15
15
  * @module views/profile
16
16
  */
17
- import { useState, useRef, useCallback } from 'react';
17
+ import { useState, useRef, useCallback, useEffect } from 'react';
18
18
  import { Box, Text } from '@primer/react';
19
19
  import { parseJwtPayload, getDatalayerJwtUser, getDatalayerDisplayName, } from '../../utils/Jwt.js';
20
20
  import { getRelativeTime } from '../../utils/Date.js';
21
21
  // ── Component ─────────────────────────────────────────────────────
22
- export const UserBadge = ({ token, variant = 'full', showExpandToggle = true, expirationWarningMs = 60 * 60 * 1000, }) => {
22
+ export const UserBadge = ({ token, variant = 'full', showExpandToggle = true, expirationWarningMs = 3 * 60 * 60 * 1000, onTokenExpired, }) => {
23
23
  const [open, setOpen] = useState(false);
24
24
  const [expanded, setExpanded] = useState(false);
25
25
  const closeTimer = useRef(null);
@@ -52,6 +52,15 @@ export const UserBadge = ({ token, variant = 'full', showExpandToggle = true, ex
52
52
  : expiringSoon
53
53
  ? 'attention.fg'
54
54
  : 'fg.muted';
55
+ const expiredNotifiedForTokenRef = useRef(null);
56
+ useEffect(() => {
57
+ if (!onTokenExpired || !open || !expired)
58
+ return;
59
+ if (expiredNotifiedForTokenRef.current === token)
60
+ return;
61
+ expiredNotifiedForTokenRef.current = token;
62
+ onTokenExpired();
63
+ }, [expired, onTokenExpired, open, token]);
55
64
  return (_jsxs(Box, { sx: { position: 'relative' }, onMouseEnter: handleEnter, onMouseLeave: handleLeave, children: [_jsx(Text, { sx: {
56
65
  fontSize: 1,
57
66
  color: triggerColor,
@@ -19,27 +19,27 @@ const SecretsTable = () => {
19
19
  setSecrets(secretsQuery.data || []);
20
20
  }
21
21
  }, [secretsQuery.data]);
22
- return secrets.length === 0 ? (_jsxs(Blankslate, { border: true, spacious: true, children: [_jsx(Blankslate.Heading, { children: "Secrets" }), _jsx(Blankslate.Description, { children: _jsx(Text, { sx: { textAlign: 'center' }, children: "No Secrets found." }) })] })) : (_jsxs(Table.Container, { children: [_jsx(Table.Title, { as: "h2", id: "secrets", children: "Secrets" }), _jsx(DataTable, { "aria-labelledby": "secrets", "aria-describedby": "secrets-subtitle", data: secrets, columns: [
23
- {
24
- header: 'Type',
25
- field: 'variant',
26
- renderCell: secret => _jsx(Label, { children: secret.variant }),
27
- },
28
- {
29
- header: 'Name',
30
- field: 'name',
31
- rowHeader: true,
32
- },
33
- {
34
- header: 'Description',
35
- field: 'description',
36
- },
37
- {
38
- header: '',
39
- field: 'id',
40
- renderCell: secret => (_jsx(IconButton, { icon: EditIcon, "aria-label": "Edit", size: "small", variant: "invisible", onClick: e => navigate(`${secret.id}`, e) })),
41
- },
42
- ] })] }));
22
+ return secrets.length === 0 ? (_jsxs(Blankslate, { border: true, spacious: true, children: [_jsx(Blankslate.Heading, { children: "Secrets" }), _jsx(Blankslate.Description, { children: _jsx(Text, { sx: { textAlign: 'center' }, children: "No Secrets found." }) })] })) : (_jsx(Table.Container, { children: _jsx(DataTable, { "aria-labelledby": "secrets", "aria-describedby": "secrets-subtitle", data: secrets, columns: [
23
+ {
24
+ header: 'Type',
25
+ field: 'variant',
26
+ renderCell: secret => _jsx(Label, { children: secret.variant }),
27
+ },
28
+ {
29
+ header: 'Name',
30
+ field: 'name',
31
+ rowHeader: true,
32
+ },
33
+ {
34
+ header: 'Description',
35
+ field: 'description',
36
+ },
37
+ {
38
+ header: '',
39
+ field: 'id',
40
+ renderCell: secret => (_jsx(IconButton, { icon: EditIcon, "aria-label": "Edit", size: "small", variant: "invisible", onClick: e => navigate(`${secret.id}`, e) })),
41
+ },
42
+ ] }) }));
43
43
  };
44
44
  export const Secrets = () => {
45
45
  const navigate = useNavigate();