@datalayer/core 1.2.18 → 1.2.19

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 (107) hide show
  1. package/lib/cli-login.js +5 -51
  2. package/lib/components/anonymous/AnonymousKeyExpired.js +1 -15
  3. package/lib/components/anonymous/AnonymousKeyTimer.js +5 -15
  4. package/lib/components/auth/SignIn.js +2 -2
  5. package/lib/components/avatars/UserAvatar.js +3 -27
  6. package/lib/components/billing/BillingEntitySelect.js +6 -48
  7. package/lib/components/checkout/StripeCheckout.js +33 -217
  8. package/lib/components/contents/ContentSourceCard.js +2 -11
  9. package/lib/components/contents/HomeFolderObjectsBrowser.d.ts +1 -1
  10. package/lib/components/contents/HomeFolderObjectsBrowser.js +11 -35
  11. package/lib/components/display/CenteredSpinner.js +1 -6
  12. package/lib/components/display/DatalayerBox.js +2 -14
  13. package/lib/components/flashes/FlashClosable.js +2 -17
  14. package/lib/components/mcp/ClientBadge.js +1 -1
  15. package/lib/components/mcp/McpErrorBlankslate.js +1 -1
  16. package/lib/components/mcp/ScopeList.js +1 -1
  17. package/lib/components/notebooks/JupyterNotebook.js +1 -2
  18. package/lib/components/principal/Principal.js +2 -10
  19. package/lib/components/principal/PrincipalAppearance.js +19 -63
  20. package/lib/components/principal/PrincipalAvatar.js +5 -25
  21. package/lib/components/principal/PrincipalBadge.js +3 -13
  22. package/lib/components/principal/PrincipalBanner.js +4 -32
  23. package/lib/components/principal/PrincipalDetailsOverlay.js +7 -23
  24. package/lib/components/principal/PrincipalHoverCard.js +15 -18
  25. package/lib/components/principal/PrincipalSwitcherMenu.js +15 -83
  26. package/lib/components/progress/ConsumptionBar.js +1 -1
  27. package/lib/components/sharing/ShareAccessComponent.js +21 -229
  28. package/lib/components/sharing/SharingEditor.js +1 -1
  29. package/lib/components/spaces/SpaceDetailsOverlay.js +4 -35
  30. package/lib/components/spaces/SpaceDisplay.js +1 -8
  31. package/lib/components/storage/ContentsBrowser.js +3 -26
  32. package/lib/components/storage/ContentsTable.js +4 -23
  33. package/lib/components/students/StudentResultCircle.js +7 -29
  34. package/lib/components/timeline/Timeline.js +4 -36
  35. package/lib/hooks/layouts/LayoutBackdrop.js +2 -1
  36. package/lib/hooks/layouts/LayoutScreencapture.js +1 -1
  37. package/lib/hooks/libraryApp.d.ts +6 -1
  38. package/lib/hooks/libraryApp.js +10 -1
  39. package/lib/hooks/useAuthorization.js +6 -1
  40. package/lib/mocks/components/FlashMock.js +3 -8
  41. package/lib/mocks/views/DashboardMock.js +6 -72
  42. package/lib/mocks/views/FormMock.js +3 -2
  43. package/lib/mocks/views/TableMock.js +3 -2
  44. package/lib/mocks/views/WipMock.js +2 -1
  45. package/lib/models/Person.d.ts +9 -0
  46. package/lib/models/Person.js +23 -0
  47. package/lib/otel/views/OtelLive.js +4 -62
  48. package/lib/otel/views/OtelLiveSpans.js +4 -29
  49. package/lib/otel/views/OtelLogsList.js +13 -64
  50. package/lib/otel/views/OtelMetricsChart.js +4 -19
  51. package/lib/otel/views/OtelMetricsList.js +8 -77
  52. package/lib/otel/views/OtelSearchBar.js +3 -12
  53. package/lib/otel/views/OtelSpanDetail.js +9 -79
  54. package/lib/otel/views/OtelSpanTree.js +7 -57
  55. package/lib/otel/views/OtelSqlView.js +5 -99
  56. package/lib/otel/views/OtelSystemView.js +4 -34
  57. package/lib/otel/views/OtelTimeline.js +7 -37
  58. package/lib/otel/views/OtelTimelineRangeSlider.js +7 -140
  59. package/lib/otel/views/OtelTracesList.js +12 -76
  60. package/lib/portable/index.d.ts +8 -0
  61. package/lib/portable/index.js +12 -0
  62. package/lib/state/substates/NavigationState.d.ts +6 -4
  63. package/lib/state/substates/NavigationState.js +6 -5
  64. package/lib/views/api-keys/APIKeyEdit.js +2 -11
  65. package/lib/views/api-keys/APIKeyNew.js +3 -19
  66. package/lib/views/api-keys/APIKeys.js +6 -11
  67. package/lib/views/api-keys/APIKeysStandalone.js +1 -1
  68. package/lib/views/datasources/DatasourceDetail.d.ts +1 -1
  69. package/lib/views/datasources/DatasourceDetail.js +24 -17
  70. package/lib/views/datasources/DatasourceNew.js +2 -6
  71. package/lib/views/datasources/Datasources.js +2 -14
  72. package/lib/views/iam/SignInSimple.js +8 -64
  73. package/lib/views/mcp/AdmittedClients.js +3 -16
  74. package/lib/views/mcp/AlertDestinations.js +2 -10
  75. package/lib/views/mcp/AlertRules.js +4 -9
  76. package/lib/views/mcp/ApprovalQueue.js +4 -21
  77. package/lib/views/mcp/AuditLog.js +7 -44
  78. package/lib/views/mcp/ConnectedAgents.js +1 -6
  79. package/lib/views/mcp/EnterpriseConsole.js +10 -23
  80. package/lib/views/mcp/IdentityProviders.js +7 -31
  81. package/lib/views/mcp/McpDashboard.js +14 -30
  82. package/lib/views/mcp/McpHome.js +7 -54
  83. package/lib/views/mcp/McpObservability.js +13 -85
  84. package/lib/views/mcp/NotebookRuns.js +5 -12
  85. package/lib/views/mcp/OrganizationPolicy.js +3 -9
  86. package/lib/views/mcp/OrganizationUsage.js +4 -11
  87. package/lib/views/mcp/PersonalPolicy.js +3 -3
  88. package/lib/views/mcp/Policies.js +5 -46
  89. package/lib/views/mcp/PolicyForm.js +1 -9
  90. package/lib/views/mcp/PolicyHistory.js +2 -8
  91. package/lib/views/mcp/RunDetail.js +3 -25
  92. package/lib/views/mcp/ScimProvisioning.js +3 -15
  93. package/lib/views/mcp/ServiceAgents.js +4 -17
  94. package/lib/views/mcp/TeamPolicies.js +7 -19
  95. package/lib/views/mcp/ToolAccess.js +15 -22
  96. package/lib/views/mcp/TraceTimeline.js +8 -23
  97. package/lib/views/otel/DashboardView.js +2 -2
  98. package/lib/views/otel/LogsView.js +2 -2
  99. package/lib/views/otel/MetricsView.js +2 -2
  100. package/lib/views/otel/OtelHeader.js +6 -40
  101. package/lib/views/otel/SqlView.js +2 -2
  102. package/lib/views/otel/SystemView.js +2 -2
  103. package/lib/views/otel/TracesView.js +2 -2
  104. package/lib/views/profile/UserBadge.js +6 -98
  105. package/lib/views/secrets/SecretEdit.js +2 -11
  106. package/lib/views/secrets/SecretNew.js +1 -1
  107. package/package.json +1 -1
@@ -7,11 +7,13 @@
7
7
  * shows. `home` is the whole platform, shown as *Power*, at `/power`: the key
8
8
  * is kept so a cookie written before the Studio existed still names the
9
9
  * shell it meant. `agentify` is the business cases; `admin` is the
10
- * platform's administration. `loop` is LOOP: AI applications built by spec,
11
- * by drag and drop or in Python — the Studio's app pages under a sidebar of
12
- * their own, and the LOOP workspace at `/loop`.
10
+ * platform's administration. `apps` is the applications: AI applications
11
+ * built by spec, by drag and drop or in Python — the Studio's app pages under
12
+ * a sidebar of their own, every address under `/apps`, and the workspace at
13
+ * `/apps` itself. It was `loop`, at `/loop`: a cookie still saying `loop`
14
+ * names no shell and is ignored, the reader opening on the Studio.
13
15
  */
14
- export type NavigationView = 'studio' | 'home' | 'agentify' | 'admin' | 'loop';
16
+ export type NavigationView = 'studio' | 'home' | 'agentify' | 'admin' | 'apps';
15
17
  /** The shells, and where each one opens when it has not been visited. */
16
18
  export declare const NAVIGATION_VIEW_HOMES: Record<NavigationView, string>;
17
19
  /** Well-known view identifiers for {@link NavigationState.tabsByView}. */
@@ -39,8 +39,8 @@ export const NAVIGATION_VIEW_HOMES = {
39
39
  home: '/power',
40
40
  agentify: '/agentify',
41
41
  admin: '/admin',
42
- // The applications: what a builder opens LOOP for.
43
- loop: '/loop/apps',
42
+ // The applications: what a builder opens the Apps shell for.
43
+ apps: '/apps/apps',
44
44
  };
45
45
  /** Well-known view identifiers for {@link NavigationState.tabsByView}. */
46
46
  export const LIBRARY_VIEW = 'library';
@@ -59,7 +59,7 @@ const isView = (value) => value === 'studio' ||
59
59
  value === 'home' ||
60
60
  value === 'agentify' ||
61
61
  value === 'admin' ||
62
- value === 'loop';
62
+ value === 'apps';
63
63
  function readCookie(name) {
64
64
  if (typeof document === 'undefined') {
65
65
  return undefined;
@@ -247,8 +247,9 @@ export function viewForRoute(route, current) {
247
247
  if (path === '/admin' || path.startsWith('/admin/')) {
248
248
  return 'admin';
249
249
  }
250
- if (path === '/loop' || path.startsWith('/loop/')) {
251
- return 'loop';
250
+ // The Apps shell, and the hosted applications at `/apps/<slug>` with it.
251
+ if (path === '/apps' || path.startsWith('/apps/')) {
252
+ return 'apps';
252
253
  }
253
254
  return 'home';
254
255
  }
@@ -116,20 +116,11 @@ export const APIKeyEdit = ({ apiKeysListRoute = '/settings/iam/api-keys', showTi
116
116
  },
117
117
  });
118
118
  };
119
- return (_jsxs(_Fragment, { children: [showTitle && (_jsx(PageHeader, { children: _jsx(Heading, { sx: { fontSize: 3 }, children: "API Key" }) })), _jsxs(Box, { display: "flex", children: [_jsxs(Box, { children: [_jsx(BoringAvatar, { displayName: apiKey?.name, size: 100, style: { paddingRight: 10 } }), _jsx(Text, { as: "h2", sx: { paddingTop: 3 }, children: apiKey?.name }), _jsx(Box, { mt: 3, children: _jsx(Label, { size: "large", children: apiKey?.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: apiKey?.expirationDate.toLocaleDateString(), onChange: nameNameChange, disabled: true })] }), _jsx(Button, { variant: "primary", disabled: !validationResult.name || !validationResult.description, sx: { marginTop: 3 }, onClick: submitAPIKey, children: "Update API key" })] }), _jsxs(Box, { sx: { marginTop: 3 }, children: [_jsx(Heading, { as: "h2", sx: {
119
+ return (_jsxs(_Fragment, { children: [showTitle && (_jsx(PageHeader, { children: _jsx(Heading, { sx: { fontSize: 3 }, children: "API Key" }) })), _jsxs(Box, { display: "flex", children: [_jsxs(Box, { children: [_jsx(BoringAvatar, { displayName: apiKey?.name, size: 100, style: { paddingRight: 10 } }), _jsx(Text, { as: "h2", sx: { paddingTop: 3 }, children: apiKey?.name }), _jsx(Box, { mt: 3, children: _jsx(Label, { size: "large", children: apiKey?.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: apiKey?.expirationDate.toLocaleDateString(), onChange: nameNameChange, disabled: true })] }), _jsx(Button, { variant: "primary", disabled: !validationResult.name || !validationResult.description, sx: { marginTop: 3 }, onClick: submitAPIKey, children: "Update API key" })] }), _jsxs(Box, { marginTop: 3, children: [_jsx(Heading, { as: "h2", sx: {
120
120
  fontSize: 4,
121
121
  fontWeight: 'normal',
122
122
  color: 'danger.fg',
123
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 API key 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: deleteAPIKey, children: "Delete API key" })] })] })] })] })] }));
124
+ }, children: "Danger zone" }), _jsxs(Box, { border: "1px solid", borderColor: "danger.emphasis", borderRadius: 2, p: 3, display: "flex", alignItems: "center", justifyContent: "space-between", gap: 3, children: [_jsxs(Box, { display: "grid", gap: 1, children: [_jsx(Text, { sx: { fontSize: 1, fontWeight: 'bold', color: 'danger.fg' }, children: "Confirm the API key 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: deleteAPIKey, children: "Delete API key" })] })] })] })] })] }));
134
125
  };
135
126
  export default APIKeyEdit;
@@ -95,25 +95,9 @@ export const APIKeyNew = ({ apiKeysListRoute = '/settings/iam/api-keys', showTit
95
95
  },
96
96
  });
97
97
  };
98
- return (_jsx(Box, { children: showAPIKey ? (_jsxs(_Fragment, { children: [_jsx(PageHeader, { children: _jsx(PageHeader.TitleArea, { variant: "large", children: _jsx(PageHeader.Title, { children: "Your API Key Is Created" }) }) }), _jsxs(Box, { sx: {
99
- border: '1px solid',
100
- borderColor: 'accent.muted',
101
- borderRadius: 2,
102
- p: 3,
103
- bg: 'canvas.subtle',
104
- mb: 3,
105
- }, children: [_jsx(Text, { sx: { fontWeight: 600, color: 'accent.fg' }, children: "Important" }), _jsx(Text, { as: "p", sx: { mt: 1, color: 'fg.muted' }, children: "Save this API key now. You will not be able to see the full value again after leaving this page." })] }), _jsxs(Box, { sx: {
106
- border: '1px solid',
107
- borderColor: 'border.default',
108
- borderRadius: 2,
109
- p: 3,
110
- bg: 'canvas.default',
111
- }, children: [_jsxs(Box, { display: "grid", gridTemplateColumns: "minmax(120px, 180px) 1fr", sx: { rowGap: 2, columnGap: 3, mb: 3 }, children: [_jsx(Text, { sx: { color: 'fg.muted' }, children: "Name" }), _jsx(Text, { sx: { fontWeight: 600 }, children: apiKey?.name || '-' }), _jsx(Text, { sx: { color: 'fg.muted' }, children: "Description" }), _jsx(Text, { children: apiKey?.description || '-' }), _jsx(Text, { sx: { color: 'fg.muted' }, children: "Expiration date" }), _jsx(Text, { children: apiKey?.expirationDate
98
+ return (_jsx(Box, { children: showAPIKey ? (_jsxs(_Fragment, { children: [_jsx(PageHeader, { children: _jsx(PageHeader.TitleArea, { variant: "large", children: _jsx(PageHeader.Title, { children: "Your API Key Is Created" }) }) }), _jsxs(Box, { border: "1px solid", borderColor: "accent.muted", borderRadius: 2, p: 3, bg: "canvas.subtle", mb: 3, children: [_jsx(Text, { sx: { fontWeight: 600, color: 'accent.fg' }, children: "Important" }), _jsx(Text, { as: "p", sx: { mt: 1, color: 'fg.muted' }, children: "Save this API key now. You will not be able to see the full value again after leaving this page." })] }), _jsxs(Box, { border: "1px solid", borderColor: "border.default", borderRadius: 2, p: 3, bg: "canvas.default", children: [_jsxs(Box, { display: "grid", gridTemplateColumns: "minmax(120px, 180px) 1fr", rowGap: 2, columnGap: 3, mb: 3, children: [_jsx(Text, { sx: { color: 'fg.muted' }, children: "Name" }), _jsx(Text, { sx: { fontWeight: 600 }, children: apiKey?.name || '-' }), _jsx(Text, { sx: { color: 'fg.muted' }, children: "Description" }), _jsx(Text, { children: apiKey?.description || '-' }), _jsx(Text, { sx: { color: 'fg.muted' }, children: "Expiration date" }), _jsx(Text, { children: apiKey?.expirationDate
112
99
  ? `${apiKey.expirationDate.toLocaleString()} (${apiKey.expirationDate.toISOString()})`
113
- : '-' })] }), _jsx(Text, { sx: { color: 'fg.muted', mb: 2 }, children: "API key value" }), _jsxs(Box, { display: "flex", sx: {
114
- alignItems: 'flex-start',
115
- gap: 2,
116
- }, children: [_jsx(Text, { as: "code", sx: {
100
+ : '-' })] }), _jsx(Text, { sx: { color: 'fg.muted', mb: 2 }, children: "API key value" }), _jsxs(Box, { display: "flex", alignItems: "flex-start", gap: 2, children: [_jsx(Text, { as: "code", sx: {
117
101
  color: 'fg.default',
118
102
  bg: 'canvas.inset',
119
103
  border: '1px solid',
@@ -133,7 +117,7 @@ export const APIKeyNew = ({ apiKeysListRoute = '/settings/iam/api-keys', showTit
133
117
  variant: 'success',
134
118
  });
135
119
  }
136
- } })] })] }), _jsx(Box, { mt: 3, children: _jsx(Button, { onClick: e => navigate(apiKeysListRoute, 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: "Type" }), _jsxs(Select, { name: "type", value: formValues.variant, onChange: valueVariantChange, children: [_jsx(Select.Option, { value: "secret", children: "Secret" }), _jsx(Select.Option, { value: "publishable", disabled: true, children: "Publishable (coming soon)" }), _jsx(Select.Option, { value: "restricted", disabled: true, children: "Restricted (coming soon)" }), _jsx(Select.Option, { value: "temporary", disabled: true, children: "Temporary (coming soon)" })] }), _jsx(FormControl.Caption, { children: "Secret is currently available. Additional API key types are coming soon." })] }), _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 ||
120
+ } })] })] }), _jsx(Box, { mt: 3, children: _jsx(Button, { onClick: e => navigate(apiKeysListRoute, 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", gap: 3, children: _jsx(Box, { children: _jsxs(Box, { sx: { label: { marginTop: 2 } }, children: [_jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Type" }), _jsxs(Select, { name: "type", value: formValues.variant, onChange: valueVariantChange, children: [_jsx(Select.Option, { value: "secret", children: "Secret" }), _jsx(Select.Option, { value: "publishable", disabled: true, children: "Publishable (coming soon)" }), _jsx(Select.Option, { value: "restricted", disabled: true, children: "Restricted (coming soon)" }), _jsx(Select.Option, { value: "temporary", disabled: true, children: "Temporary (coming soon)" })] }), _jsx(FormControl.Caption, { children: "Secret is currently available. Additional API key types are coming soon." })] }), _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 ||
137
121
  !validationResult.description ||
138
122
  !validationResult.expirationDate, sx: { marginTop: 2 }, onClick: e => {
139
123
  e.preventDefault();
@@ -20,10 +20,10 @@ const APIKeysTable = ({ apiKeysListRoute, showTitle = true, }) => {
20
20
  const [deletingAPIKey, setDeletingAPIKey] = useState(null);
21
21
  const [deleteNameConfirm, setDeleteNameConfirm] = useState('');
22
22
  const returnFocusRef = useRef(null);
23
- const showInitialSpinner = apiKeys.length === 0
24
- && (getAPIKeysQuery.isLoading
25
- || getAPIKeysQuery.isFetching
26
- || !Array.isArray(getAPIKeysQuery.data));
23
+ const showInitialSpinner = apiKeys.length === 0 &&
24
+ (getAPIKeysQuery.isLoading ||
25
+ getAPIKeysQuery.isFetching ||
26
+ !Array.isArray(getAPIKeysQuery.data));
27
27
  useEffect(() => {
28
28
  if (getAPIKeysQuery.data) {
29
29
  const normalized = getAPIKeysQuery.data.filter((apiKey) => Boolean(apiKey));
@@ -85,12 +85,7 @@ const APIKeysTable = ({ apiKeysListRoute, showTitle = true, }) => {
85
85
  navigate(routeOf(id));
86
86
  }
87
87
  };
88
- return apiKeys.length === 0 ? (_jsx(Blankslate, { border: true, spacious: true, children: showInitialSpinner ? (_jsx(Box, { sx: {
89
- display: 'flex',
90
- alignItems: 'center',
91
- justifyContent: 'center',
92
- minHeight: '40px',
93
- }, children: _jsx(Spinner, {}) })) : (_jsxs(_Fragment, { children: [showTitle && _jsx(Blankslate.Heading, { children: "API Keys" }), _jsx(Blankslate.Description, { children: _jsx(Text, { sx: { textAlign: 'center' }, children: "No API Keys found." }) })] })) })) : (_jsxs(_Fragment, { children: [_jsxs(Table.Container, { onClick: openClickedRow, sx: {
88
+ return apiKeys.length === 0 ? (_jsx(Blankslate, { border: true, spacious: true, children: showInitialSpinner ? (_jsx(Box, { display: "flex", alignItems: "center", justifyContent: "center", minHeight: "40px", children: _jsx(Spinner, {}) })) : (_jsxs(_Fragment, { children: [showTitle && _jsx(Blankslate.Heading, { children: "API Keys" }), _jsx(Blankslate.Description, { children: _jsx(Text, { sx: { textAlign: 'center' }, children: "No API Keys found." }) })] })) })) : (_jsxs(_Fragment, { children: [_jsxs(Table.Container, { onClick: openClickedRow, sx: {
94
89
  '& tbody tr': { cursor: 'pointer' },
95
90
  '& tbody tr:hover': { bg: 'canvas.subtle' },
96
91
  }, children: [showTitle && (_jsxs(_Fragment, { children: [_jsx(Table.Title, { as: "h2", id: "api-keys", children: "API Keys" }), _jsx(Table.Subtitle, { as: "p", id: "api-keys-subtitle", children: "Your API keys." })] })), _jsx(DataTable, { "aria-labelledby": "api-keys", "aria-describedby": "api-keys-subtitle", data: apiKeys, columns: [
@@ -117,7 +112,7 @@ const APIKeysTable = ({ apiKeysListRoute, showTitle = true, }) => {
117
112
  {
118
113
  header: '',
119
114
  field: 'id',
120
- renderCell: apiKey => (_jsxs(Box, { display: "flex", sx: { gap: 1 }, children: [_jsx(IconButton, { icon: EditIcon, "aria-label": "Edit", size: "small", variant: "invisible", onClick: e => navigate(routeOf(apiKey.id), e) }), _jsx(IconButton, { ref: returnFocusRef, icon: TrashIcon, "aria-label": "Delete", size: "small", variant: "invisible", sx: { color: 'danger.fg' }, onClick: () => {
115
+ renderCell: apiKey => (_jsxs(Box, { display: "flex", gap: 1, children: [_jsx(IconButton, { icon: EditIcon, "aria-label": "Edit", size: "small", variant: "invisible", onClick: e => navigate(routeOf(apiKey.id), e) }), _jsx(IconButton, { ref: returnFocusRef, icon: TrashIcon, "aria-label": "Delete", size: "small", variant: "invisible", sx: { color: 'danger.fg' }, onClick: () => {
121
116
  setDeletingAPIKey(apiKey);
122
117
  setDeleteNameConfirm('');
123
118
  } })] })),
@@ -80,7 +80,7 @@ const APIKeysTable = () => {
80
80
  {
81
81
  header: '',
82
82
  field: 'id',
83
- renderCell: apiKey => (_jsxs(Box, { display: "flex", sx: { gap: 1 }, children: [_jsx(IconButton, { icon: EditIcon, "aria-label": "Edit", size: "small", variant: "invisible", onClick: e => navigate(`${apiKey.id}`, e) }), _jsx(IconButton, { ref: returnFocusRef, icon: TrashIcon, "aria-label": "Delete", size: "small", variant: "invisible", sx: { color: 'danger.fg' }, onClick: () => {
83
+ renderCell: apiKey => (_jsxs(Box, { display: "flex", gap: 1, children: [_jsx(IconButton, { icon: EditIcon, "aria-label": "Edit", size: "small", variant: "invisible", onClick: e => navigate(`${apiKey.id}`, e) }), _jsx(IconButton, { ref: returnFocusRef, icon: TrashIcon, "aria-label": "Delete", size: "small", variant: "invisible", sx: { color: 'danger.fg' }, onClick: () => {
84
84
  setDeletingAPIKey(apiKey);
85
85
  setDeleteNameConfirm('');
86
86
  } })] })),
@@ -11,5 +11,5 @@ export type DatasourceDetailProps = {
11
11
  * two people editing the same record do not overwrite each other. Running
12
12
  * queries is the Contents detail's job, not this page's.
13
13
  */
14
- export declare const DatasourceDetail: ({ sourceUid: givenUid }?: DatasourceDetailProps) => import("react").JSX.Element;
14
+ export declare const DatasourceDetail: ({ sourceUid: givenUid, }?: DatasourceDetailProps) => import("react").JSX.Element;
15
15
  export default DatasourceDetail;
@@ -14,10 +14,10 @@ import { Box } from '@datalayer/primer-addons';
14
14
  import { DATASOURCE_CONNECTOR_LABELS, } from '../../api/contents/index.js';
15
15
  import { BoringAvatar } from '../../components/avatars/index.js';
16
16
  import { useContentSource, useCredentialDiagnostics, useTestDatasource, useToast, useUpdateContentSource, } from '../../hooks/index.js';
17
- function Row({ label, children }) {
18
- return (_jsxs(Box, { sx: { display: 'grid', gridTemplateColumns: ['1fr', '200px 1fr'], gap: 1 }, children: [_jsx(Text, { sx: { color: 'fg.muted', fontSize: 1 }, children: label }), _jsx(Box, { sx: { fontSize: 1 }, children: children })] }));
17
+ function Row({ label, children, }) {
18
+ return (_jsxs(Box, { display: "grid", gridTemplateColumns: ['1fr', '200px 1fr'], gap: 1, children: [_jsx(Text, { sx: { color: 'fg.muted', fontSize: 1 }, children: label }), _jsx(Box, { fontSize: 1, children: children })] }));
19
19
  }
20
- const frame = (children) => (_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: children }) }) }));
20
+ const frame = (children) => (_jsx(PageLayout, { containerWidth: "full", padding: "normal", style: { overflow: 'visible', minHeight: 'calc(100vh - 45px)' }, children: _jsx(PageLayout.Content, { children: _jsx(Box, { maxWidth: 960, mx: "auto", width: "100%", children: children }) }) }));
21
21
  /**
22
22
  * A Datasource's settings: what it connects to, and the name it goes by.
23
23
  *
@@ -27,7 +27,7 @@ const frame = (children) => (_jsx(PageLayout, { containerWidth: "full", padding:
27
27
  * two people editing the same record do not overwrite each other. Running
28
28
  * queries is the Contents detail's job, not this page's.
29
29
  */
30
- export const DatasourceDetail = ({ sourceUid: givenUid } = {}) => {
30
+ export const DatasourceDetail = ({ sourceUid: givenUid, } = {}) => {
31
31
  const params = useParams();
32
32
  const sourceUid = givenUid ?? params.sourceUid ?? params.datasourceId;
33
33
  const { enqueueToast } = useToast();
@@ -45,7 +45,9 @@ export const DatasourceDetail = ({ sourceUid: givenUid } = {}) => {
45
45
  if (source && configuration) {
46
46
  setName(source.name);
47
47
  setDescription(source.description ?? '');
48
- setRowLimit(configuration.defaultRowLimit ? String(configuration.defaultRowLimit) : '');
48
+ setRowLimit(configuration.defaultRowLimit
49
+ ? String(configuration.defaultRowLimit)
50
+ : '');
49
51
  setMaxSeconds(configuration.maxSeconds ? String(configuration.maxSeconds) : '');
50
52
  }
51
53
  }, [source, configuration]);
@@ -53,10 +55,13 @@ export const DatasourceDetail = ({ sourceUid: givenUid } = {}) => {
53
55
  return _jsx(_Fragment, {});
54
56
  }
55
57
  if (sourceQuery.isPending) {
56
- return frame(_jsx(Box, { sx: { display: 'flex', justifyContent: 'center', alignItems: 'center', py: 6 }, children: _jsx(Spinner, { size: "large" }) }));
58
+ return frame(_jsx(Box, { display: "flex", justifyContent: "center", alignItems: "center", py: 6, children: _jsx(Spinner, { size: "large" }) }));
57
59
  }
58
- if (sourceQuery.isError || !source || !configuration || source.kind !== 'datasource') {
59
- return frame(_jsx(Box, { sx: { p: 4 }, children: _jsx(Text, { sx: { color: 'danger.fg' }, children: sourceQuery.isError
60
+ if (sourceQuery.isError ||
61
+ !source ||
62
+ !configuration ||
63
+ source.kind !== 'datasource') {
64
+ return frame(_jsx(Box, { p: 4, children: _jsx(Text, { sx: { color: 'danger.fg' }, children: sourceQuery.isError
60
65
  ? sourceQuery.error.message
61
66
  : 'Datasource not found, or this content is not a Datasource.' }) }));
62
67
  }
@@ -86,14 +91,16 @@ export const DatasourceDetail = ({ sourceUid: givenUid } = {}) => {
86
91
  onSuccess: () => enqueueToast('The Datasource is updated.', { variant: 'success' }),
87
92
  });
88
93
  };
89
- const connector = DATASOURCE_CONNECTOR_LABELS[configuration.connectorType]
90
- ?? configuration.connectorType;
91
- return frame(_jsxs(_Fragment, { 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: "The connection as Contents holds it. The credential is never shown; the page says whether one is attached and resolvable." })] }), _jsx(Box, { sx: {
92
- border: '1px solid',
93
- borderColor: 'border.default',
94
- borderRadius: 2,
95
- bg: 'canvas.default',
96
- p: 3,
97
- }, children: _jsxs(Box, { sx: { display: 'grid', gridTemplateColumns: ['1fr', '180px 1fr'], gap: 4 }, children: [_jsxs(Box, { children: [_jsx(BoringAvatar, { displayName: source.name, size: 100, style: { paddingRight: 10 } }), _jsx(Box, { mt: 3, children: _jsx(Label, { size: "large", children: connector }) })] }), _jsxs(Box, { sx: { display: 'grid', gap: 3 }, children: [_jsxs(Box, { sx: { display: 'grid', gap: 2 }, children: [_jsx(Row, { label: "Connector", children: _jsx(Text, { children: connector }) }), _jsxs(Row, { label: "Route", children: [_jsx(Label, { variant: configuration.networkRoute === 'dataserver' ? 'accent' : 'secondary', children: configuration.networkRoute === 'dataserver' ? 'Through a Dataserver' : 'Direct' }), configuration.dataServerUid && (_jsx(Text, { sx: { fontFamily: 'mono', fontSize: 0, ml: 2 }, children: configuration.dataServerUid }))] }), configuration.endpoint && (_jsx(Row, { label: "Endpoint", children: _jsx(Text, { sx: { fontFamily: 'mono', fontSize: 0 }, children: configuration.endpoint }) })), configuration.databaseOrProject && (_jsx(Row, { label: configuration.connectorType === 'bigquery' ? 'Project' : 'Database', children: _jsx(Text, { sx: { fontFamily: 'mono', fontSize: 0 }, children: configuration.databaseOrProject }) })), _jsx(Row, { label: "Allowed operations", children: _jsx(Text, { children: (configuration.allowedOperations ?? []).join(', ') || '—' }) }), _jsx(Row, { label: "Credential", children: configuration.networkRoute === 'dataserver' ? (_jsx(Text, { sx: { color: 'fg.muted' }, children: "Held by the Dataserver, in your network." })) : configuration.credentialUid ? (_jsxs(Box, { sx: { display: 'flex', gap: 2, alignItems: 'center', flexWrap: 'wrap' }, children: [diagnostics.data ? (_jsx(Label, { variant: diagnostics.data.resolvable ? 'success' : 'danger', children: diagnostics.data.resolvable ? 'Resolvable' : 'Not resolvable' })) : (_jsx(Label, { variant: "success", children: "Attached" })), _jsx(Text, { sx: { color: 'fg.muted', fontSize: 0 }, children: diagnostics.data?.credentialName ?? 'Held in Vault and resolved server-side.' })] })) : (_jsx(Label, { variant: "attention", children: "None" })) }), _jsx(Row, { label: "Connection", children: _jsxs(Box, { sx: { display: 'flex', gap: 2, alignItems: 'center', flexWrap: 'wrap' }, children: [_jsx(Button, { size: "small", disabled: test.isPending || !permissions.execute, onClick: () => test.mutate(source.uid), children: test.isPending ? 'Testing…' : 'Test connection' }), test.data && (_jsxs(_Fragment, { children: [_jsx(Label, { variant: test.data.ok ? 'success' : 'danger', children: test.data.ok ? 'Reachable' : 'Not reachable' }), _jsx(Text, { sx: { color: 'fg.muted', fontSize: 0 }, children: test.data.detail })] })), test.isError && (_jsx(Text, { sx: { color: 'danger.fg', fontSize: 0 }, children: test.error.message }))] }) })] }), _jsxs(Box, { sx: { label: { marginTop: 2 } }, children: [_jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Name" }), _jsx(TextInput, { block: true, value: name, disabled: !permissions.update, onChange: event => setName(event.target.value) }), !nameValid && (_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, rows: 3, value: description, disabled: !permissions.update, onChange: event => setDescription(event.target.value) })] }), _jsxs(Box, { sx: { display: 'grid', gridTemplateColumns: ['1fr', '1fr 1fr'], gap: 3 }, children: [_jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Default row limit" }), _jsx(TextInput, { block: true, type: "number", min: 1, value: rowLimit, placeholder: "Service default", disabled: !permissions.update, onChange: event => setRowLimit(event.target.value) })] }), _jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Max seconds" }), _jsx(TextInput, { block: true, type: "number", min: 1, value: maxSeconds, placeholder: "Service default", disabled: !permissions.update, onChange: event => setMaxSeconds(event.target.value) })] })] }), update.isError && (_jsx(Text, { as: "p", sx: { color: 'danger.fg' }, children: update.error.message })), permissions.update && (_jsx(Box, { sx: { marginTop: 3 }, children: _jsx(Button, { variant: "primary", disabled: !nameValid || update.isPending, onClick: save, children: update.isPending ? 'Saving…' : 'Update Datasource' }) }))] })] })] }) })] }));
94
+ const connector = DATASOURCE_CONNECTOR_LABELS[configuration.connectorType] ?? configuration.connectorType;
95
+ return frame(_jsxs(_Fragment, { children: [_jsxs(Box, { mb: 4, children: [_jsx(Heading, { as: "h2", sx: { fontSize: 3, mb: 1 }, children: "Datasource" }), _jsx(Text, { sx: { color: 'fg.muted', fontSize: 1 }, children: "The connection as Contents holds it. The credential is never shown; the page says whether one is attached and resolvable." })] }), _jsx(Box, { border: "1px solid", borderColor: "border.default", borderRadius: 2, bg: "canvas.default", p: 3, children: _jsxs(Box, { display: "grid", gridTemplateColumns: ['1fr', '180px 1fr'], gap: 4, children: [_jsxs(Box, { children: [_jsx(BoringAvatar, { displayName: source.name, size: 100, style: { paddingRight: 10 } }), _jsx(Box, { mt: 3, children: _jsx(Label, { size: "large", children: connector }) })] }), _jsxs(Box, { display: "grid", gap: 3, children: [_jsxs(Box, { display: "grid", gap: 2, children: [_jsx(Row, { label: "Connector", children: _jsx(Text, { children: connector }) }), _jsxs(Row, { label: "Route", children: [_jsx(Label, { variant: configuration.networkRoute === 'dataserver'
96
+ ? 'accent'
97
+ : 'secondary', children: configuration.networkRoute === 'dataserver'
98
+ ? 'Through a Dataserver'
99
+ : 'Direct' }), configuration.dataServerUid && (_jsx(Text, { sx: { fontFamily: 'mono', fontSize: 0, ml: 2 }, children: configuration.dataServerUid }))] }), configuration.endpoint && (_jsx(Row, { label: "Endpoint", children: _jsx(Text, { sx: { fontFamily: 'mono', fontSize: 0 }, children: configuration.endpoint }) })), configuration.databaseOrProject && (_jsx(Row, { label: configuration.connectorType === 'bigquery'
100
+ ? 'Project'
101
+ : 'Database', children: _jsx(Text, { sx: { fontFamily: 'mono', fontSize: 0 }, children: configuration.databaseOrProject }) })), _jsx(Row, { label: "Allowed operations", children: _jsx(Text, { children: (configuration.allowedOperations ?? []).join(', ') || '—' }) }), _jsx(Row, { label: "Credential", children: configuration.networkRoute === 'dataserver' ? (_jsx(Text, { sx: { color: 'fg.muted' }, children: "Held by the Dataserver, in your network." })) : configuration.credentialUid ? (_jsxs(Box, { display: "flex", gap: 2, alignItems: "center", flexWrap: "wrap", children: [diagnostics.data ? (_jsx(Label, { variant: diagnostics.data.resolvable ? 'success' : 'danger', children: diagnostics.data.resolvable
102
+ ? 'Resolvable'
103
+ : 'Not resolvable' })) : (_jsx(Label, { variant: "success", children: "Attached" })), _jsx(Text, { sx: { color: 'fg.muted', fontSize: 0 }, children: diagnostics.data?.credentialName ??
104
+ 'Held in Vault and resolved server-side.' })] })) : (_jsx(Label, { variant: "attention", children: "None" })) }), _jsx(Row, { label: "Connection", children: _jsxs(Box, { display: "flex", gap: 2, alignItems: "center", flexWrap: "wrap", children: [_jsx(Button, { size: "small", disabled: test.isPending || !permissions.execute, onClick: () => test.mutate(source.uid), children: test.isPending ? 'Testing…' : 'Test connection' }), test.data && (_jsxs(_Fragment, { children: [_jsx(Label, { variant: test.data.ok ? 'success' : 'danger', children: test.data.ok ? 'Reachable' : 'Not reachable' }), _jsx(Text, { sx: { color: 'fg.muted', fontSize: 0 }, children: test.data.detail })] })), test.isError && (_jsx(Text, { sx: { color: 'danger.fg', fontSize: 0 }, children: test.error.message }))] }) })] }), _jsxs(Box, { sx: { label: { marginTop: 2 } }, children: [_jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Name" }), _jsx(TextInput, { block: true, value: name, disabled: !permissions.update, onChange: event => setName(event.target.value) }), !nameValid && (_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, rows: 3, value: description, disabled: !permissions.update, onChange: event => setDescription(event.target.value) })] }), _jsxs(Box, { display: "grid", gridTemplateColumns: ['1fr', '1fr 1fr'], gap: 3, children: [_jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Default row limit" }), _jsx(TextInput, { block: true, type: "number", min: 1, value: rowLimit, placeholder: "Service default", disabled: !permissions.update, onChange: event => setRowLimit(event.target.value) })] }), _jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Max seconds" }), _jsx(TextInput, { block: true, type: "number", min: 1, value: maxSeconds, placeholder: "Service default", disabled: !permissions.update, onChange: event => setMaxSeconds(event.target.value) })] })] }), update.isError && (_jsx(Text, { as: "p", sx: { color: 'danger.fg' }, children: update.error.message })), permissions.update && (_jsx(Box, { marginTop: 3, children: _jsx(Button, { variant: "primary", disabled: !nameValid || update.isPending, onClick: save, children: update.isPending ? 'Saving…' : 'Update Datasource' }) }))] })] })] }) })] }));
98
105
  };
99
106
  export default DatasourceDetail;
@@ -128,15 +128,11 @@ export const DatasourceNew = ({ datasourcesListRoute = '/datasources', secretsRo
128
128
  // The error is shown under the form; the idempotency key makes a retry safe.
129
129
  }
130
130
  };
131
- 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: "Connect a Datasource" }), _jsx(Text, { sx: { color: 'fg.muted', fontSize: 1 }, children: "A database, warehouse or query service your notebooks and agents query through Datalayer. The credential stays in Vault; a query receives a short-lived, scoped connection." }), accountPrincipal && _jsx(Box, { sx: { mt: 2 }, children: accountPrincipal })] }), _jsxs(Box, { as: "form", onSubmit: submit, sx: { display: 'grid', gap: 3 }, children: [_jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Connector" }), _jsx(Select, { value: connectorType, onChange: event => setConnectorType(event.target.value), children: CONNECTORS.map(connector => (_jsx(Select.Option, { value: connector, children: DATASOURCE_CONNECTOR_LABELS[connector] }, connector))) }), _jsx(FormControl.Caption, { children: labels.hint })] }), _jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Name" }), _jsx(TextInput, { block: true, value: name, onChange: event => setName(event.target.value), autoFocus: true }), name.length > 0 && !nameValid && (_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: description, onChange: event => setDescription(event.target.value) })] }), _jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: labels.endpoint }), _jsx(TextInput, { block: true, monospace: true, value: endpoint, placeholder: connectorType === 'sql' ? 'warehouse.internal:5432' : '', onChange: event => setEndpoint(event.target.value) })] }), _jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: labels.target }), _jsx(TextInput, { block: true, monospace: true, value: target, onChange: event => setTarget(event.target.value) })] }), _jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Network route" }), _jsxs(Select, { value: route, onChange: event => setRoute(event.target.value), children: [_jsx(Select.Option, { value: "direct", children: "Direct \u2014 Datalayer reaches the endpoint" }), _jsx(Select.Option, { value: "dataserver", children: "Through a Dataserver in your network" })] }), _jsx(FormControl.Caption, { children: "A private endpoint is reached through a Dataserver, which holds the credential in the network the database lives in." })] }), route === 'direct' ? (_jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Credential" }), _jsxs(Select, { value: credentialUid, onChange: event => setCredentialUid(event.target.value), children: [_jsx(Select.Option, { value: "", children: "Choose a Secret\u2026" }), (secrets.data ?? []).map(secret => (_jsx(Select.Option, { value: secret.id, children: secret.name }, secret.id)))] }), _jsxs(FormControl.Caption, { children: ["Held in Vault and resolved server-side for each query; never handed to notebook code. Add one under", ' ', _jsx(Link, { href: secretsRoute, onClick: event => {
131
+ return (_jsx(PageLayout, { containerWidth: "full", padding: "normal", style: { overflow: 'visible', minHeight: 'calc(100vh - 45px)' }, children: _jsx(PageLayout.Content, { children: _jsxs(Box, { maxWidth: 960, mx: "auto", width: "100%", children: [_jsxs(Box, { mb: 4, children: [_jsx(Heading, { as: "h2", sx: { fontSize: 3, mb: 1 }, children: "Connect a Datasource" }), _jsx(Text, { sx: { color: 'fg.muted', fontSize: 1 }, children: "A database, warehouse or query service your notebooks and agents query through Datalayer. The credential stays in Vault; a query receives a short-lived, scoped connection." }), accountPrincipal && _jsx(Box, { mt: 2, children: accountPrincipal })] }), _jsxs(Box, { as: "form", onSubmit: submit, display: "grid", gap: 3, children: [_jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Connector" }), _jsx(Select, { value: connectorType, onChange: event => setConnectorType(event.target.value), children: CONNECTORS.map(connector => (_jsx(Select.Option, { value: connector, children: DATASOURCE_CONNECTOR_LABELS[connector] }, connector))) }), _jsx(FormControl.Caption, { children: labels.hint })] }), _jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Name" }), _jsx(TextInput, { block: true, value: name, onChange: event => setName(event.target.value), autoFocus: true }), name.length > 0 && !nameValid && (_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: description, onChange: event => setDescription(event.target.value) })] }), _jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: labels.endpoint }), _jsx(TextInput, { block: true, monospace: true, value: endpoint, placeholder: connectorType === 'sql' ? 'warehouse.internal:5432' : '', onChange: event => setEndpoint(event.target.value) })] }), _jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: labels.target }), _jsx(TextInput, { block: true, monospace: true, value: target, onChange: event => setTarget(event.target.value) })] }), _jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Network route" }), _jsxs(Select, { value: route, onChange: event => setRoute(event.target.value), children: [_jsx(Select.Option, { value: "direct", children: "Direct \u2014 Datalayer reaches the endpoint" }), _jsx(Select.Option, { value: "dataserver", children: "Through a Dataserver in your network" })] }), _jsx(FormControl.Caption, { children: "A private endpoint is reached through a Dataserver, which holds the credential in the network the database lives in." })] }), route === 'direct' ? (_jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Credential" }), _jsxs(Select, { value: credentialUid, onChange: event => setCredentialUid(event.target.value), children: [_jsx(Select.Option, { value: "", children: "Choose a Secret\u2026" }), (secrets.data ?? []).map(secret => (_jsx(Select.Option, { value: secret.id, children: secret.name }, secret.id)))] }), _jsxs(FormControl.Caption, { children: ["Held in Vault and resolved server-side for each query; never handed to notebook code. Add one under", ' ', _jsx(Link, { href: secretsRoute, onClick: event => {
132
132
  event.preventDefault();
133
133
  navigate(secretsRoute, event);
134
134
  }, children: "Secrets" }), "."] })] })) : (_jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Dataserver" }), _jsxs(Select, { value: dataServerUid, onChange: event => setDataServerUid(event.target.value), children: [_jsx(Select.Option, { value: "", children: "Choose a Dataserver\u2026" }), dataserverOptions.map(option => (_jsxs(Select.Option, { value: option.uid, children: [option.name, " (", option.state, ")"] }, option.uid)))] }), _jsx(FormControl.Caption, { children: dataserverOptions.length === 0
135
135
  ? 'No Dataserver is registered yet; register one under Dataservers first.'
136
- : 'The gateway that reaches the endpoint. Contents routes to it only while it is online.' })] })), _jsxs(CheckboxGroup, { children: [_jsx(CheckboxGroup.Label, { children: "Allowed operations" }), DATASOURCE_OPERATIONS.map(operation => (_jsxs(FormControl, { children: [_jsx(Checkbox, { value: operation, checked: operations.includes(operation), onChange: event => toggleOperation(operation, event.target.checked) }), _jsx(FormControl.Label, { sx: { textTransform: 'capitalize' }, children: operation })] }, operation))), _jsx(CheckboxGroup.Caption, { children: "The service refuses any statement outside this list before a query exists. Writes are never allowed through a Datasource." })] }), _jsxs(Box, { sx: {
137
- display: 'grid',
138
- gridTemplateColumns: ['1fr', 'repeat(3, minmax(0, 1fr))'],
139
- gap: 3,
140
- }, children: [_jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Default row limit" }), _jsx(TextInput, { block: true, type: "number", min: 1, value: rowLimit, onChange: event => setRowLimit(event.target.value) })] }), _jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Max bytes" }), _jsx(TextInput, { block: true, type: "number", min: 1, value: maxBytes, placeholder: "Unlimited", onChange: event => setMaxBytes(event.target.value) })] }), _jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Max seconds" }), _jsx(TextInput, { block: true, type: "number", min: 1, value: maxSeconds, onChange: event => setMaxSeconds(event.target.value) })] })] }), createSource.isError && (_jsx(Flash, { variant: "danger", children: createSource.error.message })), _jsxs(Box, { sx: { display: 'flex', gap: 2 }, children: [_jsx(Button, { type: "button", onClick: event => navigate(datasourcesListRoute, event), children: "Cancel" }), _jsx(Button, { type: "submit", variant: "primary", disabled: !canSubmit, children: createSource.isPending ? 'Connecting…' : 'Connect Datasource' })] })] })] }) }) }));
136
+ : 'The gateway that reaches the endpoint. Contents routes to it only while it is online.' })] })), _jsxs(CheckboxGroup, { children: [_jsx(CheckboxGroup.Label, { children: "Allowed operations" }), DATASOURCE_OPERATIONS.map(operation => (_jsxs(FormControl, { children: [_jsx(Checkbox, { value: operation, checked: operations.includes(operation), onChange: event => toggleOperation(operation, event.target.checked) }), _jsx(FormControl.Label, { sx: { textTransform: 'capitalize' }, children: operation })] }, operation))), _jsx(CheckboxGroup.Caption, { children: "The service refuses any statement outside this list before a query exists. Writes are never allowed through a Datasource." })] }), _jsxs(Box, { display: "grid", gridTemplateColumns: ['1fr', 'repeat(3, minmax(0, 1fr))'], gap: 3, children: [_jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Default row limit" }), _jsx(TextInput, { block: true, type: "number", min: 1, value: rowLimit, onChange: event => setRowLimit(event.target.value) })] }), _jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Max bytes" }), _jsx(TextInput, { block: true, type: "number", min: 1, value: maxBytes, placeholder: "Unlimited", onChange: event => setMaxBytes(event.target.value) })] }), _jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Max seconds" }), _jsx(TextInput, { block: true, type: "number", min: 1, value: maxSeconds, onChange: event => setMaxSeconds(event.target.value) })] })] }), createSource.isError && (_jsx(Flash, { variant: "danger", children: createSource.error.message })), _jsxs(Box, { display: "flex", gap: 2, children: [_jsx(Button, { type: "button", onClick: event => navigate(datasourcesListRoute, event), children: "Cancel" }), _jsx(Button, { type: "submit", variant: "primary", disabled: !canSubmit, children: createSource.isPending ? 'Connecting…' : 'Connect Datasource' })] })] })] }) }) }));
141
137
  };
142
138
  export default DatasourceNew;
@@ -88,12 +88,7 @@ const LiveDatasourcesTable = ({ datasourcesListRoute, spaceUid, showInlineLoadin
88
88
  if (datasourcesQuery.isError && datasources.length === 0) {
89
89
  return (_jsxs(Blankslate, { border: true, spacious: true, children: [_jsx(Blankslate.Visual, { children: _jsx(DatabaseIcon, { size: 24 }) }), _jsx(Blankslate.Heading, { children: "Datasources could not be listed" }), _jsx(Blankslate.Description, { children: _jsx(Text, { sx: { textAlign: 'center' }, children: datasourcesQuery.error.message }) }), _jsx(Blankslate.PrimaryAction, { onClick: () => datasourcesQuery.refetch(), children: "Try again" })] }));
90
90
  }
91
- return showInitialSpinner ? (_jsx(Blankslate, { border: true, spacious: true, children: showInlineLoadingIndicator ? (_jsx(Box, { sx: {
92
- display: 'flex',
93
- alignItems: 'center',
94
- justifyContent: 'center',
95
- minHeight: '40px',
96
- }, children: _jsx(Spinner, {}) })) : (_jsxs(_Fragment, { children: [_jsx(Blankslate.Heading, { children: "Datasources" }), _jsx(Blankslate.Description, { children: _jsx(Text, { sx: { textAlign: 'center' }, children: "Loading datasources..." }) })] })) })) : (_jsx(DatasourcesTable, { datasources: datasources, datasourcesListRoute: datasourcesListRoute }));
91
+ return showInitialSpinner ? (_jsx(Blankslate, { border: true, spacious: true, children: showInlineLoadingIndicator ? (_jsx(Box, { display: "flex", alignItems: "center", justifyContent: "center", minHeight: "40px", children: _jsx(Spinner, {}) })) : (_jsxs(_Fragment, { children: [_jsx(Blankslate.Heading, { children: "Datasources" }), _jsx(Blankslate.Description, { children: _jsx(Text, { sx: { textAlign: 'center' }, children: "Loading datasources..." }) })] })) })) : (_jsx(DatasourcesTable, { datasources: datasources, datasourcesListRoute: datasourcesListRoute }));
97
92
  };
98
93
  /**
99
94
  * The Datasources of the Contents catalog.
@@ -104,14 +99,7 @@ const LiveDatasourcesTable = ({ datasourcesListRoute, spaceUid, showInlineLoadin
104
99
  */
105
100
  export const Datasources = ({ newDatasourceRoute = '/datasources/new', datasourcesListRoute, spaceUid, showInlineLoadingIndicator = true, mock = false, embedded = false, } = {}) => {
106
101
  const navigate = useNavigate();
107
- const body = (_jsxs(Box, { sx: { maxWidth: embedded ? undefined : 960, mx: 'auto', width: '100%' }, children: [_jsxs(Box, { sx: {
108
- display: 'flex',
109
- alignItems: 'flex-start',
110
- justifyContent: 'space-between',
111
- gap: 3,
112
- flexWrap: 'wrap',
113
- mb: embedded ? 2 : 4,
114
- }, children: [_jsx(Box, { children: !embedded && (_jsxs(_Fragment, { children: [_jsx(Heading, { as: "h2", sx: { fontSize: 3, mb: 1 }, children: "Datasources" }), _jsx(Text, { sx: { color: 'fg.muted', fontSize: 1 }, children: "Databases, warehouses and query services your notebooks and agents query through Datalayer, without holding the credential." })] })) }), _jsx(Button, { size: "small", variant: "primary", leadingVisual: DatabaseIcon, disabled: mock, onClick: e => navigate(newDatasourceRoute, e), children: "Connect Datasource" })] }), mock ? (_jsx(DatasourcesTable, { datasources: DATASOURCES_MOCK.map(toDatasourceRow), mock: true })) : (_jsx(LiveDatasourcesTable, { datasourcesListRoute: datasourcesListRoute, spaceUid: spaceUid, showInlineLoadingIndicator: showInlineLoadingIndicator }))] }));
102
+ const body = (_jsxs(Box, { maxWidth: embedded ? undefined : 960, mx: "auto", width: "100%", children: [_jsxs(Box, { display: "flex", alignItems: "flex-start", justifyContent: "space-between", gap: 3, flexWrap: "wrap", mb: embedded ? 2 : 4, children: [_jsx(Box, { children: !embedded && (_jsxs(_Fragment, { children: [_jsx(Heading, { as: "h2", sx: { fontSize: 3, mb: 1 }, children: "Datasources" }), _jsx(Text, { sx: { color: 'fg.muted', fontSize: 1 }, children: "Databases, warehouses and query services your notebooks and agents query through Datalayer, without holding the credential." })] })) }), _jsx(Button, { size: "small", variant: "primary", leadingVisual: DatabaseIcon, disabled: mock, onClick: e => navigate(newDatasourceRoute, e), children: "Connect Datasource" })] }), mock ? (_jsx(DatasourcesTable, { datasources: DATASOURCES_MOCK.map(toDatasourceRow), mock: true })) : (_jsx(LiveDatasourcesTable, { datasourcesListRoute: datasourcesListRoute, spaceUid: spaceUid, showInlineLoadingIndicator: showInlineLoadingIndicator }))] }));
115
103
  if (embedded) {
116
104
  return body;
117
105
  }
@@ -15,7 +15,8 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
15
15
  import { useState, useCallback, useRef, useMemo, useEffect, } from 'react';
16
16
  import { PageConfig, URLExt } from '@jupyterlab/coreutils';
17
17
  import { createGlobalStyle } from 'styled-components';
18
- import { Box, Button, FormControl, Heading, Link, Spinner, Text, Textarea, TextInput, } from '@primer/react';
18
+ import { Box } from '@datalayer/primer-addons';
19
+ import { Button, FormControl, Heading, Link, Spinner, Text, Textarea, TextInput, } from '@primer/react';
19
20
  import { EyeIcon, EyeClosedIcon, KeyIcon, MailIcon, SignInIcon, TelescopeIcon, } from '@primer/octicons-react';
20
21
  import { GithubMarkIcon, GoogleIcon, LinkedInGreyIcon, } from '@datalayer/icons-react';
21
22
  import { useToast } from '../../hooks/index.js';
@@ -318,30 +319,12 @@ export const SignInSimple = ({ onSignIn, onApiKeySignIn, loginUrl: loginUrlProp,
318
319
  }
319
320
  }, [mfaUserUid, submit, submitMfa]);
320
321
  const socialDisabled = socialLoading || loading || asDoc;
321
- return (_jsxs(Box, { className: "signin-input-theme-scope", sx: {
322
- display: 'flex',
323
- alignItems: hideHero || compactDocMode || !fillHeight ? 'flex-start' : 'center',
324
- justifyContent: 'center',
325
- minHeight: hideHero || compactDocMode || !fillHeight ? 'auto' : '100vh',
326
- bg: hideHero ? 'transparent' : 'canvas.default',
327
- color: 'fg.default',
328
- py: hideHero ? 0 : compactDocMode ? 2 : 4,
329
- }, children: [_jsx(SignInInputGlobalStyle, {}), _jsxs(Box, { sx: {
330
- width: '100%',
331
- maxWidth: 440,
332
- p: 0,
333
- }, children: [!hideHero && (_jsxs(_Fragment, { children: [_jsxs(Box, { sx: {
334
- display: 'flex',
335
- alignItems: 'center',
336
- gap: 2,
337
- mb: compactDocMode ? 2 : 3,
338
- justifyContent: 'center',
339
- }, children: [headingIcon, _jsx(Heading, { sx: { fontSize: 3 }, children: headingText })] }), _jsx(Text, { as: "p", sx: {
322
+ return (_jsxs(Box, { className: "signin-input-theme-scope", display: "flex", alignItems: hideHero || compactDocMode || !fillHeight ? 'flex-start' : 'center', justifyContent: "center", minHeight: hideHero || compactDocMode || !fillHeight ? 'auto' : '100vh', bg: hideHero ? 'transparent' : 'canvas.default', color: "fg.default", py: hideHero ? 0 : compactDocMode ? 2 : 4, children: [_jsx(SignInInputGlobalStyle, {}), _jsxs(Box, { width: "100%", maxWidth: 440, p: 0, children: [!hideHero && (_jsxs(_Fragment, { children: [_jsxs(Box, { display: "flex", alignItems: "center", gap: 2, mb: compactDocMode ? 2 : 3, justifyContent: "center", children: [headingIcon, _jsx(Heading, { sx: { fontSize: 3 }, children: headingText })] }), _jsx(Text, { as: "p", sx: {
340
323
  fontSize: 1,
341
324
  color: 'fg.muted',
342
325
  mb: compactDocMode ? 2 : 3,
343
326
  textAlign: 'center',
344
- }, children: description })] })), (calloutTitle || calloutDescription) && (_jsxs(Box, { sx: { mb: 3, textAlign: 'center' }, children: [calloutTitle && (_jsx(Heading, { as: "h3", sx: { fontSize: 2, mb: 1 }, children: calloutTitle })), calloutDescription && (_jsx(Text, { sx: { color: 'fg.muted', fontSize: 1 }, children: calloutDescription }))] })), !mfaUserUid && (github || google || linkedin) && (_jsxs(_Fragment, { children: [github && (_jsx(Button, { block: true, size: "large", leadingVisual: pendingSocialProvider === 'github'
327
+ }, children: description })] })), (calloutTitle || calloutDescription) && (_jsxs(Box, { mb: 3, textAlign: "center", children: [calloutTitle && (_jsx(Heading, { as: "h3", sx: { fontSize: 2, mb: 1 }, children: calloutTitle })), calloutDescription && (_jsx(Text, { sx: { color: 'fg.muted', fontSize: 1 }, children: calloutDescription }))] })), !mfaUserUid && (github || google || linkedin) && (_jsxs(_Fragment, { children: [github && (_jsx(Button, { block: true, size: "large", leadingVisual: pendingSocialProvider === 'github'
345
328
  ? () => _jsx(Spinner, { size: "small" })
346
329
  : GithubMarkIcon, disabled: socialDisabled, onClick: () => startOAuthSignIn(OAUTH2_PROVIDERS.github), sx: {
347
330
  mt: socialButtonMarginTop,
@@ -359,12 +342,7 @@ export const SignInSimple = ({ onSignIn, onApiKeySignIn, loginUrl: loginUrlProp,
359
342
  mt: socialButtonMarginTop,
360
343
  maxWidth: actionButtonMaxWidth,
361
344
  mx: 'auto',
362
- }, children: "Sign in with LinkedIn" }))] })), !mfaUserUid && !showPasswordForm && (_jsx(Box, { sx: { mt: 3, display: 'flex', justifyContent: 'center' }, children: _jsx(Button, { leadingVisual: KeyIcon, size: "large", block: true, disabled: asDoc || loading || socialLoading, onClick: () => setShowPasswordForm(true), sx: { maxWidth: actionButtonMaxWidth, mx: 'auto' }, children: passwordToggleLabel }) })), !mfaUserUid && showPasswordForm && (_jsxs(Box, { sx: { mt: 4 }, children: [_jsxs(FormControl, { required: true, sx: { mb: 3 }, children: [_jsx(FormControl.Label, { children: "Username" }), _jsx(TextInput, { autoFocus: true, block: true, placeholder: "Your username", value: handle, disabled: loading || asDoc, onChange: e => setHandle(e.target.value), onKeyDown: handleKeyDown })] }), _jsxs(FormControl, { required: true, sx: { mb: 3 }, children: [_jsx(FormControl.Label, { children: "Password" }), _jsx(TextInput, { block: true, placeholder: "Your password", type: showPassword ? 'text' : 'password', value: password, disabled: loading || asDoc, onChange: e => setPassword(e.target.value), onKeyDown: handleKeyDown, trailingAction: _jsx(TextInput.Action, { onClick: () => setShowPassword(!showPassword), disabled: loading || asDoc, icon: showPassword ? EyeClosedIcon : EyeIcon, "aria-label": showPassword ? 'Hide password' : 'Show password', sx: { color: 'var(--fgColor-muted)' } }) })] }), _jsxs(Box, { sx: {
363
- display: 'flex',
364
- alignItems: 'center',
365
- justifyContent: 'space-between',
366
- mt: 3,
367
- }, children: [_jsx(Button, { variant: "primary", disabled: loading || asDoc || !handle || !password, onClick: submit, children: loading ? 'Signing in…' : 'Sign In' }), showForgotPassword && (_jsx(Link, { as: "button", sx: {
345
+ }, children: "Sign in with LinkedIn" }))] })), !mfaUserUid && !showPasswordForm && (_jsx(Box, { mt: 3, display: "flex", justifyContent: "center", children: _jsx(Button, { leadingVisual: KeyIcon, size: "large", block: true, disabled: asDoc || loading || socialLoading, onClick: () => setShowPasswordForm(true), sx: { maxWidth: actionButtonMaxWidth, mx: 'auto' }, children: passwordToggleLabel }) })), !mfaUserUid && showPasswordForm && (_jsxs(Box, { mt: 4, children: [_jsxs(FormControl, { required: true, sx: { mb: 3 }, children: [_jsx(FormControl.Label, { children: "Username" }), _jsx(TextInput, { autoFocus: true, block: true, placeholder: "Your username", value: handle, disabled: loading || asDoc, onChange: e => setHandle(e.target.value), onKeyDown: handleKeyDown })] }), _jsxs(FormControl, { required: true, sx: { mb: 3 }, children: [_jsx(FormControl.Label, { children: "Password" }), _jsx(TextInput, { block: true, placeholder: "Your password", type: showPassword ? 'text' : 'password', value: password, disabled: loading || asDoc, onChange: e => setPassword(e.target.value), onKeyDown: handleKeyDown, trailingAction: _jsx(TextInput.Action, { onClick: () => setShowPassword(!showPassword), disabled: loading || asDoc, icon: showPassword ? EyeClosedIcon : EyeIcon, "aria-label": showPassword ? 'Hide password' : 'Show password', sx: { color: 'var(--fgColor-muted)' } }) })] }), _jsxs(Box, { display: "flex", alignItems: "center", justifyContent: "space-between", mt: 3, children: [_jsx(Button, { variant: "primary", disabled: loading || asDoc || !handle || !password, onClick: submit, children: loading ? 'Signing in…' : 'Sign In' }), showForgotPassword && (_jsx(Link, { as: "button", sx: {
368
346
  fontSize: 1,
369
347
  cursor: asDoc ? 'not-allowed' : 'pointer',
370
348
  background: 'none',
@@ -375,47 +353,13 @@ export const SignInSimple = ({ onSignIn, onApiKeySignIn, loginUrl: loginUrlProp,
375
353
  return;
376
354
  }
377
355
  handleForgotPassword();
378
- }, children: forgotPasswordLabel }))] })] })), mfaUserUid && (_jsxs(_Fragment, { children: [_jsxs(FormControl, { required: true, sx: { mb: 3, mt: 2 }, children: [_jsx(FormControl.Label, { children: "MFA Code" }), _jsx(TextInput, { autoFocus: true, block: true, placeholder: "Your MFA code", value: mfaCode, disabled: loading || asDoc, onChange: e => setMfaCode(e.target.value), onKeyDown: handleKeyDown })] }), _jsx(Button, { variant: "primary", block: true, disabled: loading || asDoc || !mfaCode.trim(), onClick: submitMfa, sx: { maxWidth: actionButtonMaxWidth, mx: 'auto' }, children: loading ? 'Validating…' : 'Validate MFA Code' })] })), error && (_jsx(Text, { sx: { color: 'danger.fg', fontSize: 1, mt: 3, display: 'block' }, children: error })), signUp && (_jsxs(Box, { sx: {
379
- mt: 4,
380
- pt: 4,
381
- textAlign: 'center',
382
- borderTop: '1px solid',
383
- borderColor: 'border.muted',
384
- }, children: [_jsx(Heading, { as: "h3", sx: { fontSize: 2, mb: 2 }, children: signUpTitle }), _jsx(Text, { as: "p", sx: { color: 'fg.muted', fontSize: 1, mb: 3 }, children: signUpDescription }), _jsx(Box, { sx: { display: 'flex', justifyContent: 'center' }, children: _jsx(Button, { variant: "primary", size: "large", leadingVisual: MailIcon, disabled: asDoc || loading, onClick: handleSignUp, children: signUpLabel }) })] })), apiKey && onApiKeySignIn && (_jsxs(_Fragment, { children: [_jsxs(Box, { sx: {
385
- display: 'flex',
386
- alignItems: 'center',
387
- gap: 2,
388
- my: 3,
389
- }, children: [_jsx(Box, { sx: { flex: 1, height: '1px', bg: 'border.default' } }), _jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "or" }), _jsx(Box, { sx: { flex: 1, height: '1px', bg: 'border.default' } })] }), _jsx(Button, { block: true, leadingVisual: KeyIcon, disabled: asDoc || loading || socialLoading, onClick: () => setShowApiKeyDialog(true), sx: { maxWidth: actionButtonMaxWidth, mx: 'auto' }, children: "Sign in with an API Key" }), showApiKeyDialog && (_jsx(Box, { role: "dialog", "aria-modal": "true", "aria-labelledby": "signin-api-key-title", onKeyDown: event => {
356
+ }, children: forgotPasswordLabel }))] })] })), mfaUserUid && (_jsxs(_Fragment, { children: [_jsxs(FormControl, { required: true, sx: { mb: 3, mt: 2 }, children: [_jsx(FormControl.Label, { children: "MFA Code" }), _jsx(TextInput, { autoFocus: true, block: true, placeholder: "Your MFA code", value: mfaCode, disabled: loading || asDoc, onChange: e => setMfaCode(e.target.value), onKeyDown: handleKeyDown })] }), _jsx(Button, { variant: "primary", block: true, disabled: loading || asDoc || !mfaCode.trim(), onClick: submitMfa, sx: { maxWidth: actionButtonMaxWidth, mx: 'auto' }, children: loading ? 'Validating…' : 'Validate MFA Code' })] })), error && (_jsx(Text, { sx: { color: 'danger.fg', fontSize: 1, mt: 3, display: 'block' }, children: error })), signUp && (_jsxs(Box, { mt: 4, pt: 4, textAlign: "center", borderTop: "1px solid", borderColor: "border.muted", children: [_jsx(Heading, { as: "h3", sx: { fontSize: 2, mb: 2 }, children: signUpTitle }), _jsx(Text, { as: "p", sx: { color: 'fg.muted', fontSize: 1, mb: 3 }, children: signUpDescription }), _jsx(Box, { display: "flex", justifyContent: "center", children: _jsx(Button, { variant: "primary", size: "large", leadingVisual: MailIcon, disabled: asDoc || loading, onClick: handleSignUp, children: signUpLabel }) })] })), apiKey && onApiKeySignIn && (_jsxs(_Fragment, { children: [_jsxs(Box, { display: "flex", alignItems: "center", gap: 2, my: 3, children: [_jsx(Box, { flex: 1, height: "1px", bg: "border.default" }), _jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "or" }), _jsx(Box, { flex: 1, height: "1px", bg: "border.default" })] }), _jsx(Button, { block: true, leadingVisual: KeyIcon, disabled: asDoc || loading || socialLoading, onClick: () => setShowApiKeyDialog(true), sx: { maxWidth: actionButtonMaxWidth, mx: 'auto' }, children: "Sign in with an API Key" }), showApiKeyDialog && (_jsx(Box, { role: "dialog", "aria-modal": "true", "aria-labelledby": "signin-api-key-title", onKeyDown: event => {
390
357
  if (event.key === 'Escape') {
391
358
  closeApiKeyDialog();
392
359
  }
393
- }, onClick: closeApiKeyDialog, sx: {
394
- position: 'fixed',
395
- inset: 0,
396
- zIndex: 200,
397
- p: 3,
398
- bg: 'canvas.backdrop',
399
- display: 'flex',
400
- alignItems: 'center',
401
- justifyContent: 'center',
402
- }, children: _jsxs(Box, { as: "form", onClick: event => event.stopPropagation(), onSubmit: event => {
360
+ }, onClick: closeApiKeyDialog, position: "fixed", inset: 0, zIndex: 200, p: 3, bg: "canvas.backdrop", display: "flex", alignItems: "center", justifyContent: "center", children: _jsxs(Box, { as: "form", onClick: event => event.stopPropagation(), onSubmit: event => {
403
361
  event.preventDefault();
404
362
  handleApiKeyAuthenticate();
405
- }, sx: {
406
- width: 'min(560px, 100%)',
407
- p: 3,
408
- borderRadius: 2,
409
- border: '1px solid',
410
- borderColor: 'border.default',
411
- bg: 'canvas.overlay',
412
- color: 'fg.default',
413
- boxShadow: 'shadow.large',
414
- }, children: [_jsx(Heading, { id: "signin-api-key-title", sx: { fontSize: 2, mb: 2 }, children: "Enter your API Key" }), _jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "API Key" }), _jsx(Textarea, { block: true, required: true, autoFocus: true, placeholder: "Paste your API key here", value: apiKeyValue, onInput: e => setApiKeyValue(e.currentTarget.value), ref: apiKeyRef })] }), _jsxs(Box, { sx: {
415
- mt: 3,
416
- display: 'flex',
417
- justifyContent: 'flex-end',
418
- gap: 2,
419
- }, children: [_jsx(Button, { type: "button", onClick: closeApiKeyDialog, children: "Cancel" }), _jsx(Button, { type: "submit", variant: "primary", disabled: !apiKeyValue.trim(), children: "Authenticate" })] })] }) }))] }))] })] }));
363
+ }, width: "min(560px, 100%)", p: 3, borderRadius: 2, border: "1px solid", borderColor: "border.default", bg: "canvas.overlay", color: "fg.default", boxShadow: "shadow.large", children: [_jsx(Heading, { id: "signin-api-key-title", sx: { fontSize: 2, mb: 2 }, children: "Enter your API Key" }), _jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "API Key" }), _jsx(Textarea, { block: true, required: true, autoFocus: true, placeholder: "Paste your API key here", value: apiKeyValue, onInput: e => setApiKeyValue(e.currentTarget.value), ref: apiKeyRef })] }), _jsxs(Box, { mt: 3, display: "flex", justifyContent: "flex-end", gap: 2, children: [_jsx(Button, { type: "button", onClick: closeApiKeyDialog, children: "Cancel" }), _jsx(Button, { type: "submit", variant: "primary", disabled: !apiKeyValue.trim(), children: "Authenticate" })] })] }) }))] }))] })] }));
420
364
  };
421
365
  export default SignInSimple;
@@ -8,20 +8,7 @@ const Row = ({ client }) => {
8
8
  const unusable = client.kind === 'unusable';
9
9
  const icon = unusable ? (_jsx(QuestionIcon, {})) : unreadable ? (_jsx(AlertIcon, {})) : client.kind === 'hostname' ? (_jsx(GlobeIcon, {})) : (_jsx(CheckCircleIcon, {}));
10
10
  const tone = unusable || unreadable ? 'danger.fg' : 'fg.muted';
11
- return (_jsxs(Box, { sx: {
12
- display: 'grid',
13
- gridTemplateColumns: 'auto 1fr',
14
- gap: 2,
15
- alignItems: 'start',
16
- py: 2,
17
- borderTop: '1px solid',
18
- borderColor: 'border.muted',
19
- }, children: [_jsx(Box, { sx: { color: tone, pt: '2px' }, children: icon }), _jsxs(Box, { sx: { display: 'grid', gap: 1, minWidth: 0 }, children: [_jsxs(Box, { sx: {
20
- display: 'flex',
21
- gap: 2,
22
- alignItems: 'center',
23
- flexWrap: 'wrap',
24
- }, children: [client.clientName && (_jsx(Text, { sx: { fontWeight: 'semibold' }, children: client.clientName })), client.kind === 'hostname' && (_jsx(Label, { variant: "accent", size: "small", children: "Whole host" })), client.localhostOnly && (
11
+ return (_jsxs(Box, { display: "grid", gridTemplateColumns: "auto 1fr", gap: 2, alignItems: "start", py: 2, borderTop: "1px solid", borderColor: "border.muted", children: [_jsx(Box, { color: tone, pt: "2px", children: icon }), _jsxs(Box, { display: "grid", gap: 1, minWidth: 0, children: [_jsxs(Box, { display: "flex", gap: 2, alignItems: "center", flexWrap: "wrap", children: [client.clientName && (_jsx(Text, { sx: { fontWeight: 'semibold' }, children: client.clientName })), client.kind === 'hostname' && (_jsx(Label, { variant: "accent", size: "small", children: "Whole host" })), client.localhostOnly && (
25
12
  // Worth a label of its own: every redirect goes to the reader's
26
13
  // own machine, and a document cannot prove which local program
27
14
  // is listening on the port.
@@ -35,7 +22,7 @@ const Row = ({ client }) => {
35
22
  export const AdmittedClients = ({ scope, subjectUid, edited = false, }) => {
36
23
  const described = useAdmittedClients(scope, subjectUid);
37
24
  if (described.isPending && !described.data) {
38
- return (_jsx(Box, { sx: { display: 'flex', justifyContent: 'center', py: 3 }, children: _jsx(Spinner, { size: "small" }) }));
25
+ return (_jsx(Box, { display: "flex", justifyContent: "center", py: 3, children: _jsx(Spinner, { size: "small" }) }));
39
26
  }
40
27
  // An error here is not worth a blankslate: the field above still works and
41
28
  // the policy is unaffected. Saying nothing would be worse than saying it
@@ -54,6 +41,6 @@ export const AdmittedClients = ({ scope, subjectUid, edited = false, }) => {
54
41
  return (_jsx(Text, { as: "p", sx: { fontSize: 0, color: 'fg.muted', m: 0 }, children: answer.detail ??
55
42
  'This layer names no client allowlist, so every client is admitted by it.' }));
56
43
  }
57
- return (_jsxs(Box, { sx: { display: 'grid', gap: 1 }, children: [edited && (_jsxs(Text, { as: "p", sx: { fontSize: 0, color: 'attention.fg', m: 0 }, children: ["Describing the ", _jsx("strong", { children: "saved" }), " list. Save to check what you have just typed."] })), answer.clients.map(client => (_jsx(Row, { client: client }, client.entry)))] }));
44
+ return (_jsxs(Box, { display: "grid", gap: 1, children: [edited && (_jsxs(Text, { as: "p", sx: { fontSize: 0, color: 'attention.fg', m: 0 }, children: ["Describing the ", _jsx("strong", { children: "saved" }), " list. Save to check what you have just typed."] })), answer.clients.map(client => (_jsx(Row, { client: client }, client.entry)))] }));
58
45
  };
59
46
  export default AdmittedClients;
@@ -60,16 +60,8 @@ export const AlertDestinations = ({ errorState, orgUid, readOnly = false, }) =>
60
60
  return (_jsx(McpErrorBlankslate, { state: errorState(settings.error, 'Alert destinations'), onRetry: () => settings.refetch() }));
61
61
  }
62
62
  if (settings.isPending && settings.data === undefined) {
63
- return (_jsx(Box, { sx: { display: 'flex', justifyContent: 'center', py: 4 }, children: _jsx(Spinner, {}) }));
63
+ return (_jsx(Box, { display: "flex", justifyContent: "center", py: 4, children: _jsx(Spinner, {}) }));
64
64
  }
65
- return (_jsxs(Box, { sx: {
66
- display: 'grid',
67
- gap: 3,
68
- maxWidth: '42rem',
69
- p: 3,
70
- border: '1px solid',
71
- borderColor: 'border.default',
72
- borderRadius: 2,
73
- }, children: [_jsxs(Box, { children: [_jsx(Heading, { as: "h3", sx: { fontSize: 2, mb: 1 }, children: "Where alerts go" }), _jsx(Text, { as: "p", sx: { color: 'fg.muted', fontSize: 1, m: 0 }, children: "Set once for the organization, not per rule \u2014 a URL repeated on twenty rules is nineteen places to forget when it rotates. Every firing also appears in the app, and that copy is sent first so it is not lost when one of these is down." })] }), refusal && (_jsx(Flash, { variant: "danger", children: _jsx(Text, { sx: { fontSize: 1 }, children: refusal }) })), _jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Email" }), _jsx(TextInput, { block: true, disabled: readOnly, value: draft.alertEmails, onChange: event => set('alertEmails', event.target.value), placeholder: "ops@example.co, oncall@example.co" }), _jsx(FormControl.Caption, { children: "Comma-separated, up to twenty. An alert list is the people who want to know, not a mailing list \u2014 a firing that emails two hundred people is a firing that gets the rule switched off." })] }), _jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Slack" }), _jsx(TextInput, { block: true, disabled: readOnly, value: draft.alertSlackWebhookUrl, onChange: event => set('alertSlackWebhookUrl', event.target.value), placeholder: "https://hooks.slack.com/services/\u2026" }), _jsx(FormControl.Caption, { children: "A Slack incoming webhook, and only Slack\u2019s \u2014 anything else belongs in the field below. Through this one Slack gets a coloured attachment with the reading, the severity and the scope as separate fields; through the generic webhook it shows one grey line." })] }), _jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Webhook" }), _jsx(TextInput, { block: true, disabled: readOnly, value: draft.alertWebhookUrl, onChange: event => set('alertWebhookUrl', event.target.value), placeholder: "https://events.pagerduty.com/\u2026" }), _jsxs(FormControl.Caption, { children: ["Anything that takes a JSON POST: PagerDuty, a chat relay, your own endpoint. ", _jsx("strong", { children: "https only" }), " \u2014 an alert names your organization and what its agents did. It carries no credential."] })] }), !readOnly && (_jsxs(Box, { sx: { display: 'flex', gap: 2 }, children: [_jsx(Button, { variant: "primary", onClick: apply, disabled: !changed || save.isPending, children: "Save destinations" }), changed && (_jsx(Button, { variant: "invisible", onClick: () => setDraft(stored), children: "Discard changes" }))] })), _jsxs(Text, { sx: { fontSize: 0, color: 'fg.subtle' }, children: ["Each copy is attempted on its own: if the webhook is down you still get the email. A copy that fails is recorded and", ' ', _jsx("strong", { children: "not retried" }), " \u2014 retrying a notice against an endpoint that is down turns its outage into a loop against it, and the alert is already in the app."] })] }));
65
+ return (_jsxs(Box, { display: "grid", gap: 3, maxWidth: "42rem", p: 3, border: "1px solid", borderColor: "border.default", borderRadius: 2, children: [_jsxs(Box, { children: [_jsx(Heading, { as: "h3", sx: { fontSize: 2, mb: 1 }, children: "Where alerts go" }), _jsx(Text, { as: "p", sx: { color: 'fg.muted', fontSize: 1, m: 0 }, children: "Set once for the organization, not per rule \u2014 a URL repeated on twenty rules is nineteen places to forget when it rotates. Every firing also appears in the app, and that copy is sent first so it is not lost when one of these is down." })] }), refusal && (_jsx(Flash, { variant: "danger", children: _jsx(Text, { sx: { fontSize: 1 }, children: refusal }) })), _jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Email" }), _jsx(TextInput, { block: true, disabled: readOnly, value: draft.alertEmails, onChange: event => set('alertEmails', event.target.value), placeholder: "ops@example.co, oncall@example.co" }), _jsx(FormControl.Caption, { children: "Comma-separated, up to twenty. An alert list is the people who want to know, not a mailing list \u2014 a firing that emails two hundred people is a firing that gets the rule switched off." })] }), _jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Slack" }), _jsx(TextInput, { block: true, disabled: readOnly, value: draft.alertSlackWebhookUrl, onChange: event => set('alertSlackWebhookUrl', event.target.value), placeholder: "https://hooks.slack.com/services/\u2026" }), _jsx(FormControl.Caption, { children: "A Slack incoming webhook, and only Slack\u2019s \u2014 anything else belongs in the field below. Through this one Slack gets a coloured attachment with the reading, the severity and the scope as separate fields; through the generic webhook it shows one grey line." })] }), _jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Webhook" }), _jsx(TextInput, { block: true, disabled: readOnly, value: draft.alertWebhookUrl, onChange: event => set('alertWebhookUrl', event.target.value), placeholder: "https://events.pagerduty.com/\u2026" }), _jsxs(FormControl.Caption, { children: ["Anything that takes a JSON POST: PagerDuty, a chat relay, your own endpoint. ", _jsx("strong", { children: "https only" }), " \u2014 an alert names your organization and what its agents did. It carries no credential."] })] }), !readOnly && (_jsxs(Box, { display: "flex", gap: 2, children: [_jsx(Button, { variant: "primary", onClick: apply, disabled: !changed || save.isPending, children: "Save destinations" }), changed && (_jsx(Button, { variant: "invisible", onClick: () => setDraft(stored), children: "Discard changes" }))] })), _jsxs(Text, { sx: { fontSize: 0, color: 'fg.subtle' }, children: ["Each copy is attempted on its own: if the webhook is down you still get the email. A copy that fails is recorded and", ' ', _jsx("strong", { children: "not retried" }), " \u2014 retrying a notice against an endpoint that is down turns its outage into a loop against it, and the alert is already in the app."] })] }));
74
66
  };
75
67
  export default AlertDestinations;