@gntik-ai/templates 0.0.0-stage → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +2420 -0
- package/dist/index.js +14520 -0
- package/dist/index.js.map +1 -0
- package/package.json +64 -3
- package/src/accept-invitation/Page.test.tsx +40 -0
- package/src/accept-invitation/Page.tsx +153 -0
- package/src/accept-invitation/data.ts +22 -0
- package/src/accept-invitation/template.meta.ts +12 -0
- package/src/assistant-chat/Page.test.tsx +51 -0
- package/src/assistant-chat/Page.tsx +273 -0
- package/src/assistant-chat/data.ts +71 -0
- package/src/assistant-chat/template.meta.ts +12 -0
- package/src/audit-log/Page.test.tsx +58 -0
- package/src/audit-log/Page.tsx +284 -0
- package/src/audit-log/data.ts +133 -0
- package/src/audit-log/template.meta.ts +12 -0
- package/src/board/Lane.tsx +117 -0
- package/src/board/Page.test.tsx +68 -0
- package/src/board/Page.tsx +178 -0
- package/src/board/data.ts +127 -0
- package/src/board/template.meta.ts +12 -0
- package/src/board/useBoard.ts +146 -0
- package/src/changelog/Page.test.tsx +31 -0
- package/src/changelog/Page.tsx +174 -0
- package/src/changelog/data.ts +105 -0
- package/src/changelog/template.meta.ts +12 -0
- package/src/code-editor/Page.test.tsx +83 -0
- package/src/code-editor/Page.tsx +207 -0
- package/src/code-editor/data.ts +130 -0
- package/src/code-editor/template.meta.ts +12 -0
- package/src/contact/Page.test.tsx +33 -0
- package/src/contact/Page.tsx +272 -0
- package/src/contact/PublicFrame.tsx +84 -0
- package/src/contact/data.ts +129 -0
- package/src/contact/template.meta.ts +12 -0
- package/src/create-wizard/Overlay.test.tsx +68 -0
- package/src/create-wizard/Page.test.tsx +57 -0
- package/src/create-wizard/Page.tsx +246 -0
- package/src/create-wizard/data.ts +139 -0
- package/src/create-wizard/examples/Overlay.tsx +60 -0
- package/src/create-wizard/template.meta.ts +12 -0
- package/src/create-workspace/Page.test.tsx +41 -0
- package/src/create-workspace/Page.tsx +227 -0
- package/src/create-workspace/data.ts +61 -0
- package/src/create-workspace/template.meta.ts +12 -0
- package/src/data-explorer/Page.test.tsx +45 -0
- package/src/data-explorer/Page.tsx +247 -0
- package/src/data-explorer/data.ts +94 -0
- package/src/data-explorer/template.meta.ts +12 -0
- package/src/docs-article/ArticleFeedback.tsx +75 -0
- package/src/docs-article/Page.test.tsx +34 -0
- package/src/docs-article/Page.tsx +200 -0
- package/src/docs-article/data.ts +65 -0
- package/src/docs-article/template.meta.ts +12 -0
- package/src/evaluations/Page.test.tsx +40 -0
- package/src/evaluations/Page.tsx +238 -0
- package/src/evaluations/data.ts +194 -0
- package/src/evaluations/template.meta.ts +12 -0
- package/src/forgot-password/Page.test.tsx +199 -0
- package/src/forgot-password/Page.tsx +66 -0
- package/src/forgot-password/data.ts +5 -0
- package/src/forgot-password/examples/UsernameOrEmail.tsx +25 -0
- package/src/forgot-password/template.meta.ts +12 -0
- package/src/getting-started/Page.test.tsx +27 -0
- package/src/getting-started/Page.tsx +125 -0
- package/src/getting-started/data.ts +67 -0
- package/src/getting-started/template.meta.ts +12 -0
- package/src/help-home/Page.test.tsx +33 -0
- package/src/help-home/Page.tsx +159 -0
- package/src/help-home/data.ts +151 -0
- package/src/help-home/template.meta.ts +12 -0
- package/src/home-dashboard/Page.test.tsx +57 -0
- package/src/home-dashboard/Page.tsx +152 -0
- package/src/home-dashboard/data.ts +66 -0
- package/src/home-dashboard/template.meta.ts +12 -0
- package/src/incident-board/Page.test.tsx +77 -0
- package/src/incident-board/Page.tsx +263 -0
- package/src/incident-board/data.ts +220 -0
- package/src/incident-board/template.meta.ts +12 -0
- package/src/incident-console/Page.test.tsx +49 -0
- package/src/incident-console/Page.tsx +274 -0
- package/src/incident-console/data.ts +176 -0
- package/src/incident-console/template.meta.ts +12 -0
- package/src/index.ts +250 -0
- package/src/keyboard-shortcuts/Page.test.tsx +45 -0
- package/src/keyboard-shortcuts/Page.tsx +94 -0
- package/src/keyboard-shortcuts/ShortcutSheet.tsx +80 -0
- package/src/keyboard-shortcuts/data.ts +84 -0
- package/src/keyboard-shortcuts/template.meta.ts +12 -0
- package/src/landing/Page.test.tsx +27 -0
- package/src/landing/Page.tsx +110 -0
- package/src/landing/data.ts +17 -0
- package/src/landing/template.meta.ts +11 -0
- package/src/live-event-stream/Page.test.tsx +251 -0
- package/src/live-event-stream/Page.tsx +307 -0
- package/src/live-event-stream/data.ts +44 -0
- package/src/live-event-stream/examples/Connecting.tsx +5 -0
- package/src/live-event-stream/examples/Error.tsx +5 -0
- package/src/live-event-stream/examples/LiveEmpty.tsx +5 -0
- package/src/live-event-stream/examples/LiveWithEvents.tsx +6 -0
- package/src/live-event-stream/examples/Stopped.tsx +6 -0
- package/src/live-event-stream/template.meta.ts +74 -0
- package/src/messaging/Page.test.tsx +34 -0
- package/src/messaging/Page.tsx +236 -0
- package/src/messaging/data.ts +118 -0
- package/src/messaging/template.meta.ts +12 -0
- package/src/meta.ts +24 -0
- package/src/mfa/Page.test.tsx +22 -0
- package/src/mfa/Page.tsx +45 -0
- package/src/mfa/data.ts +6 -0
- package/src/mfa/template.meta.ts +12 -0
- package/src/model-providers/Page.test.tsx +58 -0
- package/src/model-providers/Page.tsx +198 -0
- package/src/model-providers/data.ts +101 -0
- package/src/model-providers/template.meta.ts +12 -0
- package/src/notification-inbox/Page.test.tsx +49 -0
- package/src/notification-inbox/Page.tsx +237 -0
- package/src/notification-inbox/data.ts +129 -0
- package/src/notification-inbox/template.meta.ts +12 -0
- package/src/pending-activation/Page.test.tsx +390 -0
- package/src/pending-activation/Page.tsx +145 -0
- package/src/pending-activation/data.ts +25 -0
- package/src/pending-activation/template.meta.ts +12 -0
- package/src/pricing/ComparisonTable.tsx +75 -0
- package/src/pricing/Page.test.tsx +29 -0
- package/src/pricing/Page.tsx +83 -0
- package/src/pricing/PublicFrame.tsx +84 -0
- package/src/pricing/data.ts +104 -0
- package/src/pricing/template.meta.ts +12 -0
- package/src/prompt-playground/Page.test.tsx +72 -0
- package/src/prompt-playground/Page.tsx +254 -0
- package/src/prompt-playground/data.ts +68 -0
- package/src/prompt-playground/template.meta.ts +12 -0
- package/src/public-hub/Page.test.tsx +176 -0
- package/src/public-hub/Page.tsx +125 -0
- package/src/public-hub/Source.test.ts +54 -0
- package/src/public-hub/data.ts +24 -0
- package/src/public-hub/examples/Dark.tsx +11 -0
- package/src/public-hub/examples/Full.tsx +6 -0
- package/src/public-hub/examples/HighContrast.tsx +11 -0
- package/src/public-hub/examples/Light.tsx +11 -0
- package/src/public-hub/examples/Loading.tsx +6 -0
- package/src/public-hub/examples/Minimal.tsx +5 -0
- package/src/public-hub/examples/MinimalLoading.tsx +5 -0
- package/src/public-hub/template.meta.ts +60 -0
- package/src/reset-password/Page.test.tsx +27 -0
- package/src/reset-password/Page.tsx +46 -0
- package/src/reset-password/data.ts +5 -0
- package/src/reset-password/template.meta.ts +12 -0
- package/src/resource-detail/Page.test.tsx +260 -0
- package/src/resource-detail/Page.tsx +262 -0
- package/src/resource-detail/data.ts +55 -0
- package/src/resource-detail/examples/postgres-table-detail.tsx +28 -0
- package/src/resource-detail/examples/realtime-configuration.tsx +26 -0
- package/src/resource-detail/examples/runtime-with-transition.tsx +30 -0
- package/src/resource-detail/examples/runtime-without-transition.tsx +33 -0
- package/src/resource-detail/examples/shell.ts +12 -0
- package/src/resource-detail/template.meta.ts +21 -0
- package/src/resource-form/Page.test.tsx +40 -0
- package/src/resource-form/Page.tsx +186 -0
- package/src/resource-form/data.ts +63 -0
- package/src/resource-form/template.meta.ts +12 -0
- package/src/resource-gallery/Page.test.tsx +42 -0
- package/src/resource-gallery/Page.tsx +125 -0
- package/src/resource-gallery/data.ts +44 -0
- package/src/resource-gallery/template.meta.ts +20 -0
- package/src/resource-index/Page.test.tsx +120 -0
- package/src/resource-index/Page.tsx +216 -0
- package/src/resource-index/data.ts +84 -0
- package/src/resource-index/template.meta.ts +21 -0
- package/src/resource-tree/Page.test.tsx +31 -0
- package/src/resource-tree/Page.tsx +232 -0
- package/src/resource-tree/data.ts +180 -0
- package/src/resource-tree/template.meta.ts +12 -0
- package/src/run-trace/Page.test.tsx +56 -0
- package/src/run-trace/Page.tsx +242 -0
- package/src/run-trace/data.ts +171 -0
- package/src/run-trace/template.meta.ts +12 -0
- package/src/scorecard/Page.test.tsx +38 -0
- package/src/scorecard/Page.tsx +160 -0
- package/src/scorecard/TargetsTable.tsx +92 -0
- package/src/scorecard/data.ts +118 -0
- package/src/scorecard/template.meta.ts +12 -0
- package/src/search-results/Page.test.tsx +43 -0
- package/src/search-results/Page.tsx +243 -0
- package/src/search-results/data.ts +130 -0
- package/src/search-results/template.meta.ts +12 -0
- package/src/settings-api-keys/Page.test.tsx +39 -0
- package/src/settings-api-keys/Page.tsx +171 -0
- package/src/settings-api-keys/data.ts +58 -0
- package/src/settings-api-keys/template.meta.ts +12 -0
- package/src/settings-appearance/Page.test.tsx +44 -0
- package/src/settings-appearance/Page.tsx +92 -0
- package/src/settings-appearance/data.ts +21 -0
- package/src/settings-appearance/template.meta.ts +11 -0
- package/src/settings-billing/Page.test.tsx +31 -0
- package/src/settings-billing/Page.tsx +71 -0
- package/src/settings-billing/data.ts +15 -0
- package/src/settings-billing/template.meta.ts +19 -0
- package/src/settings-integrations/Page.test.tsx +31 -0
- package/src/settings-integrations/Page.tsx +185 -0
- package/src/settings-integrations/data.ts +116 -0
- package/src/settings-integrations/template.meta.ts +11 -0
- package/src/settings-members/Page.test.tsx +32 -0
- package/src/settings-members/Page.tsx +140 -0
- package/src/settings-members/data.ts +28 -0
- package/src/settings-members/template.meta.ts +12 -0
- package/src/settings-notifications/Page.test.tsx +31 -0
- package/src/settings-notifications/Page.tsx +71 -0
- package/src/settings-notifications/data.ts +12 -0
- package/src/settings-notifications/template.meta.ts +11 -0
- package/src/settings-profile/Baseline.test.tsx +9 -0
- package/src/settings-profile/Editable.test.tsx +21 -0
- package/src/settings-profile/Page.test.tsx +41 -0
- package/src/settings-profile/Page.tsx +133 -0
- package/src/settings-profile/ReadOnly.test.tsx +143 -0
- package/src/settings-profile/ReadOnlyProfile.tsx +85 -0
- package/src/settings-profile/SettingsFrame.test.tsx +55 -0
- package/src/settings-profile/SettingsFrame.tsx +88 -0
- package/src/settings-profile/__tests__/dom.ts +8 -0
- package/src/settings-profile/data.ts +41 -0
- package/src/settings-profile/examples/ReadOnly.tsx +31 -0
- package/src/settings-profile/fixtures/editable.snap +1 -0
- package/src/settings-profile/settings-nav.ts +98 -0
- package/src/settings-profile/template.meta.ts +21 -0
- package/src/settings-security/MfaSetupDialog.tsx +96 -0
- package/src/settings-security/Page.test.tsx +44 -0
- package/src/settings-security/Page.tsx +158 -0
- package/src/settings-security/data.ts +11 -0
- package/src/settings-security/template.meta.ts +11 -0
- package/src/settings-workspace/Page.test.tsx +44 -0
- package/src/settings-workspace/Page.tsx +120 -0
- package/src/settings-workspace/data.ts +44 -0
- package/src/settings-workspace/template.meta.ts +19 -0
- package/src/shared/ConsoleShell.test.tsx +81 -0
- package/src/shared/ConsoleShell.tsx +98 -0
- package/src/shell-sidebar/Page.test.tsx +21 -0
- package/src/shell-sidebar/Page.tsx +85 -0
- package/src/shell-sidebar/data.ts +24 -0
- package/src/shell-sidebar/template.meta.ts +12 -0
- package/src/shell-topnav/Page.test.tsx +25 -0
- package/src/shell-topnav/Page.tsx +94 -0
- package/src/shell-topnav/data.ts +25 -0
- package/src/shell-topnav/template.meta.ts +12 -0
- package/src/sign-in/BrandPanel.tsx +34 -0
- package/src/sign-in/Page.test.tsx +41 -0
- package/src/sign-in/Page.tsx +73 -0
- package/src/sign-in/data.ts +11 -0
- package/src/sign-in/template.meta.ts +12 -0
- package/src/sign-in-card/Page.test.tsx +182 -0
- package/src/sign-in-card/Page.tsx +82 -0
- package/src/sign-in-card/data.ts +8 -0
- package/src/sign-in-card/examples/Username.tsx +28 -0
- package/src/sign-in-card/template.meta.ts +12 -0
- package/src/sign-up/Page.test.tsx +33 -0
- package/src/sign-up/Page.tsx +70 -0
- package/src/sign-up/data.ts +15 -0
- package/src/sign-up/template.meta.ts +12 -0
- package/src/sso-login/Page.test.tsx +41 -0
- package/src/sso-login/Page.tsx +151 -0
- package/src/sso-login/data.ts +10 -0
- package/src/sso-login/template.meta.ts +12 -0
- package/src/status-403/Page.test.tsx +23 -0
- package/src/status-403/Page.tsx +92 -0
- package/src/status-403/data.ts +13 -0
- package/src/status-403/template.meta.ts +12 -0
- package/src/status-404/Page.test.tsx +23 -0
- package/src/status-404/Page.tsx +60 -0
- package/src/status-404/PageHeading.test.tsx +52 -0
- package/src/status-404/data.ts +12 -0
- package/src/status-404/examples/PageHeader.tsx +28 -0
- package/src/status-404/template.meta.ts +12 -0
- package/src/status-500/Page.test.tsx +24 -0
- package/src/status-500/Page.tsx +70 -0
- package/src/status-500/data.ts +20 -0
- package/src/status-500/template.meta.ts +12 -0
- package/src/status-maintenance/Page.test.tsx +24 -0
- package/src/status-maintenance/Page.tsx +84 -0
- package/src/status-maintenance/data.ts +14 -0
- package/src/status-maintenance/template.meta.ts +12 -0
- package/src/styles.css +11 -0
- package/src/test/a11y.ts +18 -0
- package/src/test/setup.ts +36 -0
- package/src/upgrade-required/Page.test.tsx +28 -0
- package/src/upgrade-required/Page.tsx +79 -0
- package/src/upgrade-required/data.ts +19 -0
- package/src/upgrade-required/template.meta.ts +12 -0
- package/src/usage-analytics/Page.test.tsx +83 -0
- package/src/usage-analytics/Page.tsx +184 -0
- package/src/usage-analytics/data.ts +161 -0
- package/src/usage-analytics/template.meta.ts +21 -0
- package/src/verify-email/Page.test.tsx +32 -0
- package/src/verify-email/Page.tsx +118 -0
- package/src/verify-email/data.ts +7 -0
- package/src/verify-email/template.meta.ts +12 -0
- package/src/workflow-builder/Page.test.tsx +97 -0
- package/src/workflow-builder/Page.tsx +163 -0
- package/src/workflow-builder/data.ts +13 -0
- package/src/workflow-builder/template.meta.ts +12 -0
- package/README.md +0 -3
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ActivityFeed,
|
|
3
|
+
DangerZone,
|
|
4
|
+
DescriptionListCard,
|
|
5
|
+
FormSection,
|
|
6
|
+
PageHeader,
|
|
7
|
+
StatusTimeline,
|
|
8
|
+
type ActivityItem,
|
|
9
|
+
type DangerZoneAction,
|
|
10
|
+
type DescriptionItem,
|
|
11
|
+
type PageChromeAction,
|
|
12
|
+
type PageHeaderMetaItem,
|
|
13
|
+
type StatusEvent,
|
|
14
|
+
} from '@gntik-ai/blocks';
|
|
15
|
+
import { FileText, RotateCw } from '@gntik-ai/icons';
|
|
16
|
+
import {
|
|
17
|
+
Button,
|
|
18
|
+
Field,
|
|
19
|
+
FieldDescription,
|
|
20
|
+
FieldLabel,
|
|
21
|
+
Grid,
|
|
22
|
+
Input,
|
|
23
|
+
Page,
|
|
24
|
+
Section,
|
|
25
|
+
Stack,
|
|
26
|
+
Tabs,
|
|
27
|
+
TabsList,
|
|
28
|
+
TabsPanel,
|
|
29
|
+
TabsTab,
|
|
30
|
+
Textarea,
|
|
31
|
+
type BreadcrumbItem,
|
|
32
|
+
} from '@gntik-ai/ui';
|
|
33
|
+
import { useState } from 'react';
|
|
34
|
+
import { ConsoleShell, type ConsoleShellProps } from '../shared/ConsoleShell';
|
|
35
|
+
import {
|
|
36
|
+
detailActivity,
|
|
37
|
+
detailBreadcrumbs,
|
|
38
|
+
detailDangerActions,
|
|
39
|
+
detailEvents,
|
|
40
|
+
detailItems,
|
|
41
|
+
detailMeta,
|
|
42
|
+
detailResource,
|
|
43
|
+
type ResourceSettings,
|
|
44
|
+
} from './data';
|
|
45
|
+
|
|
46
|
+
export type ResourceDetailTab = 'overview' | 'activity' | 'settings';
|
|
47
|
+
|
|
48
|
+
export interface ResourceDetailProps {
|
|
49
|
+
/**
|
|
50
|
+
* Optional boolean, default false. Shows only the keyboard-accessible Overview tab in
|
|
51
|
+
* the "Resource sections" tablist, using explicit resource props without sample defaults.
|
|
52
|
+
* Ignores activity, events, dangerActions, defaultTab, onSaveSettings and onDangerAction.
|
|
53
|
+
* Explicit actions and onEditDetails are still honored. ConsoleShell defaults are unchanged.
|
|
54
|
+
*/
|
|
55
|
+
readOnly?: boolean;
|
|
56
|
+
/**
|
|
57
|
+
* Optional readonly StatusEvent[], default undefined; used only when readOnly is true.
|
|
58
|
+
* Omitted: no history card. Empty: named StatusTimeline empty state. Populated: exactly
|
|
59
|
+
* the supplied events, without fabricated actors. Editable mode continues to use events.
|
|
60
|
+
*/
|
|
61
|
+
history?: readonly StatusEvent[];
|
|
62
|
+
name: string;
|
|
63
|
+
description: string;
|
|
64
|
+
/** StatusTag key next to the title. */
|
|
65
|
+
status: string;
|
|
66
|
+
breadcrumbs: BreadcrumbItem[];
|
|
67
|
+
meta: PageHeaderMetaItem[];
|
|
68
|
+
actions: PageChromeAction[];
|
|
69
|
+
details: readonly DescriptionItem[];
|
|
70
|
+
/** Title of the Overview details card (default "Configuration"). */
|
|
71
|
+
overviewTitle: string;
|
|
72
|
+
/** Description of the Overview details card. */
|
|
73
|
+
overviewDescription: string;
|
|
74
|
+
/** Title of the Overview status-history card (default "Status history"). */
|
|
75
|
+
historyTitle: string;
|
|
76
|
+
events: readonly StatusEvent[];
|
|
77
|
+
activity: readonly ActivityItem[];
|
|
78
|
+
dangerActions: DangerZoneAction[];
|
|
79
|
+
defaultTab: ResourceDetailTab;
|
|
80
|
+
onTabChange: (tab: ResourceDetailTab) => void;
|
|
81
|
+
onEditDetails: () => void;
|
|
82
|
+
onSaveSettings: (settings: ResourceSettings) => void;
|
|
83
|
+
/** Confirmed Danger zone action id (e.g. `pause`, `delete`). */
|
|
84
|
+
onDangerAction: (actionId: string) => void | Promise<void>;
|
|
85
|
+
shell: Omit<ConsoleShellProps, 'children'>;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const TABS: Array<{ value: ResourceDetailTab; label: string }> = [
|
|
89
|
+
{ value: 'overview', label: 'Overview' },
|
|
90
|
+
{ value: 'activity', label: 'Activity' },
|
|
91
|
+
{ value: 'settings', label: 'Settings' },
|
|
92
|
+
];
|
|
93
|
+
|
|
94
|
+
/** Resource detail: editable tabs by default, or explicit-only Overview when readOnly is true. */
|
|
95
|
+
export default function ResourceDetailPage(props: Partial<ResourceDetailProps>) {
|
|
96
|
+
const readOnly = props.readOnly ?? false;
|
|
97
|
+
const {
|
|
98
|
+
name = readOnly ? '' : detailResource.name,
|
|
99
|
+
description = readOnly ? '' : detailResource.description,
|
|
100
|
+
status = readOnly ? '' : detailResource.status,
|
|
101
|
+
breadcrumbs = readOnly ? [] : detailBreadcrumbs,
|
|
102
|
+
meta: metaProp,
|
|
103
|
+
actions: actionsProp,
|
|
104
|
+
details: detailsProp,
|
|
105
|
+
overviewTitle = 'Configuration',
|
|
106
|
+
overviewDescription = readOnly ? '' : 'Settings of this resource.',
|
|
107
|
+
historyTitle = 'Status history',
|
|
108
|
+
events = readOnly ? [] : detailEvents,
|
|
109
|
+
activity = readOnly ? [] : detailActivity,
|
|
110
|
+
dangerActions = readOnly ? [] : detailDangerActions,
|
|
111
|
+
defaultTab = 'overview',
|
|
112
|
+
onTabChange,
|
|
113
|
+
onEditDetails,
|
|
114
|
+
onSaveSettings,
|
|
115
|
+
onDangerAction,
|
|
116
|
+
shell,
|
|
117
|
+
} = props;
|
|
118
|
+
const meta = metaProp ?? (readOnly ? [] : detailMeta);
|
|
119
|
+
const actions: PageChromeAction[] =
|
|
120
|
+
actionsProp ??
|
|
121
|
+
(readOnly
|
|
122
|
+
? []
|
|
123
|
+
: [
|
|
124
|
+
{ label: 'View logs', icon: FileText, variant: 'secondary' },
|
|
125
|
+
{ label: 'Redeploy', icon: RotateCw, variant: 'primary' },
|
|
126
|
+
]);
|
|
127
|
+
const details = detailsProp ?? (readOnly ? [] : detailItems);
|
|
128
|
+
const historyEvents = readOnly ? props.history : events;
|
|
129
|
+
const [tab, setTab] = useState<ResourceDetailTab>(defaultTab);
|
|
130
|
+
const [settings, setSettings] = useState<ResourceSettings>({ name, description });
|
|
131
|
+
const [saved, setSaved] = useState<ResourceSettings>({ name, description });
|
|
132
|
+
const dirty = settings.name !== saved.name || settings.description !== saved.description;
|
|
133
|
+
const tabs = readOnly ? TABS.filter((t) => t.value === 'overview') : TABS;
|
|
134
|
+
|
|
135
|
+
return (
|
|
136
|
+
<ConsoleShell currentHref="/deployments" breadcrumbs={breadcrumbs} {...shell}>
|
|
137
|
+
<Page
|
|
138
|
+
width="wide"
|
|
139
|
+
header={
|
|
140
|
+
<PageHeader
|
|
141
|
+
breadcrumbs={null}
|
|
142
|
+
title={name}
|
|
143
|
+
description={description}
|
|
144
|
+
status={status}
|
|
145
|
+
meta={meta}
|
|
146
|
+
actions={actions}
|
|
147
|
+
tabs={null}
|
|
148
|
+
/>
|
|
149
|
+
}
|
|
150
|
+
>
|
|
151
|
+
<Tabs
|
|
152
|
+
value={readOnly ? 'overview' : tab}
|
|
153
|
+
onValueChange={(v) => {
|
|
154
|
+
const next = tabs.find((t) => t.value === v)?.value ?? 'overview';
|
|
155
|
+
setTab(next);
|
|
156
|
+
onTabChange?.(next);
|
|
157
|
+
}}
|
|
158
|
+
>
|
|
159
|
+
<TabsList aria-label="Resource sections">
|
|
160
|
+
{tabs.map((t) => (
|
|
161
|
+
<TabsTab key={t.value} value={t.value}>
|
|
162
|
+
{t.label}
|
|
163
|
+
</TabsTab>
|
|
164
|
+
))}
|
|
165
|
+
</TabsList>
|
|
166
|
+
|
|
167
|
+
<TabsPanel value="overview">
|
|
168
|
+
<Grid cols={historyEvents === undefined ? 1 : { base: 1, lg: 5 }} gap={6} align="start">
|
|
169
|
+
<DescriptionListCard
|
|
170
|
+
className={historyEvents === undefined ? undefined : 'lg:col-span-3'}
|
|
171
|
+
titleAs="h2"
|
|
172
|
+
title={overviewTitle}
|
|
173
|
+
description={overviewDescription}
|
|
174
|
+
items={details}
|
|
175
|
+
onEdit={onEditDetails}
|
|
176
|
+
/>
|
|
177
|
+
{historyEvents !== undefined && (
|
|
178
|
+
<Section
|
|
179
|
+
role={readOnly ? 'group' : undefined}
|
|
180
|
+
variant="card"
|
|
181
|
+
padding="md"
|
|
182
|
+
title={historyTitle}
|
|
183
|
+
className="lg:col-span-2"
|
|
184
|
+
>
|
|
185
|
+
<StatusTimeline events={historyEvents} maxVisible={readOnly ? undefined : 4} />
|
|
186
|
+
</Section>
|
|
187
|
+
)}
|
|
188
|
+
</Grid>
|
|
189
|
+
</TabsPanel>
|
|
190
|
+
|
|
191
|
+
{!readOnly && (
|
|
192
|
+
<TabsPanel value="activity">
|
|
193
|
+
<Section
|
|
194
|
+
variant="card"
|
|
195
|
+
padding="md"
|
|
196
|
+
title="Activity"
|
|
197
|
+
description="Changes made to this resource by people and automations."
|
|
198
|
+
>
|
|
199
|
+
<ActivityFeed items={activity} label="Activity, newest first" />
|
|
200
|
+
</Section>
|
|
201
|
+
</TabsPanel>
|
|
202
|
+
)}
|
|
203
|
+
|
|
204
|
+
{!readOnly && (
|
|
205
|
+
<TabsPanel value="settings">
|
|
206
|
+
<Stack gap={8}>
|
|
207
|
+
<form
|
|
208
|
+
noValidate
|
|
209
|
+
onSubmit={(e) => {
|
|
210
|
+
e.preventDefault();
|
|
211
|
+
setSaved(settings);
|
|
212
|
+
onSaveSettings?.(settings);
|
|
213
|
+
}}
|
|
214
|
+
>
|
|
215
|
+
<FormSection
|
|
216
|
+
title="General"
|
|
217
|
+
description="Name and description shown across the console."
|
|
218
|
+
actions={
|
|
219
|
+
<>
|
|
220
|
+
<Button variant="ghost" disabled={!dirty} onClick={() => setSettings(saved)}>
|
|
221
|
+
Discard
|
|
222
|
+
</Button>
|
|
223
|
+
<Button type="submit" disabled={!dirty}>
|
|
224
|
+
Save changes
|
|
225
|
+
</Button>
|
|
226
|
+
</>
|
|
227
|
+
}
|
|
228
|
+
>
|
|
229
|
+
<Stack gap={4}>
|
|
230
|
+
<Field>
|
|
231
|
+
<FieldLabel required>Name</FieldLabel>
|
|
232
|
+
<Input
|
|
233
|
+
required
|
|
234
|
+
value={settings.name}
|
|
235
|
+
onValueChange={(v) => setSettings((s) => ({ ...s, name: v }))}
|
|
236
|
+
/>
|
|
237
|
+
<FieldDescription>Lowercase letters, numbers and dashes.</FieldDescription>
|
|
238
|
+
</Field>
|
|
239
|
+
<Field>
|
|
240
|
+
<FieldLabel>Description</FieldLabel>
|
|
241
|
+
<Textarea
|
|
242
|
+
rows={3}
|
|
243
|
+
value={settings.description}
|
|
244
|
+
onValueChange={(v) => setSettings((s) => ({ ...s, description: v }))}
|
|
245
|
+
/>
|
|
246
|
+
</Field>
|
|
247
|
+
</Stack>
|
|
248
|
+
</FormSection>
|
|
249
|
+
</form>
|
|
250
|
+
<DangerZone
|
|
251
|
+
description="These actions affect everyone who uses this resource."
|
|
252
|
+
actions={dangerActions}
|
|
253
|
+
onConfirm={onDangerAction}
|
|
254
|
+
/>
|
|
255
|
+
</Stack>
|
|
256
|
+
</TabsPanel>
|
|
257
|
+
)}
|
|
258
|
+
</Tabs>
|
|
259
|
+
</Page>
|
|
260
|
+
</ConsoleShell>
|
|
261
|
+
);
|
|
262
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DEPLOYMENT_DETAILS,
|
|
3
|
+
DEPLOYMENT_EVENTS,
|
|
4
|
+
PROJECT_ACTIVITY,
|
|
5
|
+
type ActivityItem,
|
|
6
|
+
type DangerZoneAction,
|
|
7
|
+
type DescriptionItem,
|
|
8
|
+
type PageHeaderMetaItem,
|
|
9
|
+
type StatusEvent,
|
|
10
|
+
} from '@gntik-ai/blocks';
|
|
11
|
+
import { Clock, GitBranch, Globe } from '@gntik-ai/icons';
|
|
12
|
+
import type { BreadcrumbItem } from '@gntik-ai/ui';
|
|
13
|
+
|
|
14
|
+
/** The editable part of the resource (Settings tab). */
|
|
15
|
+
export interface ResourceSettings {
|
|
16
|
+
name: string;
|
|
17
|
+
description: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const detailResource = {
|
|
21
|
+
name: 'orders-api',
|
|
22
|
+
status: 'running',
|
|
23
|
+
description: 'Public REST API for orders, carts and checkout.',
|
|
24
|
+
} as const;
|
|
25
|
+
|
|
26
|
+
export const detailBreadcrumbs: BreadcrumbItem[] = [
|
|
27
|
+
{ label: 'Deployments', href: '/deployments' },
|
|
28
|
+
{ label: detailResource.name, mono: true },
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
export const detailMeta: PageHeaderMetaItem[] = [
|
|
32
|
+
{ label: 'Region', value: 'eu-west-1', icon: Globe, mono: true },
|
|
33
|
+
{ label: 'Branch', value: 'main', icon: GitBranch, mono: true },
|
|
34
|
+
{ label: 'Last deploy', value: '12 min ago', icon: Clock },
|
|
35
|
+
];
|
|
36
|
+
export const detailItems: DescriptionItem[] = DEPLOYMENT_DETAILS;
|
|
37
|
+
export const detailEvents: StatusEvent[] = DEPLOYMENT_EVENTS;
|
|
38
|
+
export const detailActivity: ActivityItem[] = PROJECT_ACTIVITY;
|
|
39
|
+
|
|
40
|
+
export const detailDangerActions: DangerZoneAction[] = [
|
|
41
|
+
{
|
|
42
|
+
id: 'pause',
|
|
43
|
+
title: 'Pause deployment',
|
|
44
|
+
description: 'Stops serving traffic until you resume it. Configuration is kept.',
|
|
45
|
+
actionLabel: 'Pause',
|
|
46
|
+
destructive: false,
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
id: 'delete',
|
|
50
|
+
title: 'Delete deployment',
|
|
51
|
+
description: `Permanently deletes ${detailResource.name}, its secrets and history. This cannot be undone.`,
|
|
52
|
+
actionLabel: 'Delete deployment',
|
|
53
|
+
confirmText: detailResource.name,
|
|
54
|
+
},
|
|
55
|
+
];
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import ResourceDetailPage from '../Page';
|
|
2
|
+
import { shell } from './shell';
|
|
3
|
+
|
|
4
|
+
export default function TableDetail() {
|
|
5
|
+
const table = {
|
|
6
|
+
name: 'records',
|
|
7
|
+
columns: ['id', 'title', 'created_at'],
|
|
8
|
+
indexes: ['records_pkey'],
|
|
9
|
+
policies: ['Members can read'],
|
|
10
|
+
security: 'Row-level security enabled',
|
|
11
|
+
};
|
|
12
|
+
// Schema information has no history field; no timeline or empty history card is appropriate.
|
|
13
|
+
return (
|
|
14
|
+
<ResourceDetailPage
|
|
15
|
+
readOnly
|
|
16
|
+
name="Table detail"
|
|
17
|
+
meta={[{ label: 'Table', value: table.name, mono: true }]}
|
|
18
|
+
overviewTitle="Schema details"
|
|
19
|
+
details={[
|
|
20
|
+
{ id: 'columns', label: 'Columns', value: table.columns.join(', '), mono: true },
|
|
21
|
+
{ id: 'indexes', label: 'Indexes', value: table.indexes.join(', '), mono: true },
|
|
22
|
+
{ id: 'policies', label: 'Policies', value: table.policies.join(', ') },
|
|
23
|
+
{ id: 'security', label: 'Security', value: table.security },
|
|
24
|
+
]}
|
|
25
|
+
shell={shell}
|
|
26
|
+
/>
|
|
27
|
+
);
|
|
28
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import ResourceDetailPage from '../Page';
|
|
2
|
+
import { shell } from './shell';
|
|
3
|
+
|
|
4
|
+
export default function RealtimeConfiguration() {
|
|
5
|
+
const configuration = {
|
|
6
|
+
workspaceId: 'workspace_42',
|
|
7
|
+
realtimeEndpointUrl: 'https://realtime.example.com',
|
|
8
|
+
features: ['Subscriptions', 'Presence'],
|
|
9
|
+
dataSources: ['primary'],
|
|
10
|
+
};
|
|
11
|
+
// No history field exists in this response, so omit history entirely.
|
|
12
|
+
return (
|
|
13
|
+
<ResourceDetailPage
|
|
14
|
+
readOnly
|
|
15
|
+
name="Realtime configuration"
|
|
16
|
+
overviewTitle="Connection details"
|
|
17
|
+
details={[
|
|
18
|
+
{ id: 'workspace', label: 'Workspace ID', value: configuration.workspaceId, mono: true },
|
|
19
|
+
{ id: 'endpoint', label: 'Endpoint', value: configuration.realtimeEndpointUrl, mono: true },
|
|
20
|
+
{ id: 'features', label: 'Features', value: configuration.features.join(', ') },
|
|
21
|
+
{ id: 'sources', label: 'Data sources', value: configuration.dataSources.join(', ') },
|
|
22
|
+
]}
|
|
23
|
+
shell={shell}
|
|
24
|
+
/>
|
|
25
|
+
);
|
|
26
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { StatusEvent } from '@gntik-ai/blocks';
|
|
2
|
+
import ResourceDetailPage from '../Page';
|
|
3
|
+
import { shell } from './shell';
|
|
4
|
+
|
|
5
|
+
export default function RuntimeWithTransition() {
|
|
6
|
+
const runtime = { state: 'Ready', lastTransitionAt: '2026-05-01T10:00:00Z' };
|
|
7
|
+
// Map only the source's state and transition time; it supplies no actor or earlier events.
|
|
8
|
+
const history: readonly StatusEvent[] = runtime.lastTransitionAt
|
|
9
|
+
? [
|
|
10
|
+
{
|
|
11
|
+
id: 'transition',
|
|
12
|
+
status: runtime.state,
|
|
13
|
+
tone: 'success',
|
|
14
|
+
title: 'Runtime ready',
|
|
15
|
+
at: runtime.lastTransitionAt,
|
|
16
|
+
},
|
|
17
|
+
]
|
|
18
|
+
: [];
|
|
19
|
+
return (
|
|
20
|
+
<ResourceDetailPage
|
|
21
|
+
readOnly
|
|
22
|
+
name="Function runtime"
|
|
23
|
+
status="running"
|
|
24
|
+
overviewTitle="Runtime details"
|
|
25
|
+
details={[{ id: 'state', label: 'State', value: runtime.state }]}
|
|
26
|
+
history={history}
|
|
27
|
+
shell={shell}
|
|
28
|
+
/>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { StatusEvent } from '@gntik-ai/blocks';
|
|
2
|
+
import ResourceDetailPage from '../Page';
|
|
3
|
+
import { shell } from './shell';
|
|
4
|
+
|
|
5
|
+
export default function RuntimeWithoutTransition() {
|
|
6
|
+
const runtime: { state: string; lastTransitionAt: string | null } = {
|
|
7
|
+
state: 'Pending',
|
|
8
|
+
lastTransitionAt: null,
|
|
9
|
+
};
|
|
10
|
+
// Null transition time means explicit empty history, not an invented event or an omitted card.
|
|
11
|
+
const history: readonly StatusEvent[] = runtime.lastTransitionAt
|
|
12
|
+
? [
|
|
13
|
+
{
|
|
14
|
+
id: 'transition',
|
|
15
|
+
status: runtime.state,
|
|
16
|
+
tone: 'warning',
|
|
17
|
+
title: 'Runtime pending',
|
|
18
|
+
at: runtime.lastTransitionAt,
|
|
19
|
+
},
|
|
20
|
+
]
|
|
21
|
+
: [];
|
|
22
|
+
return (
|
|
23
|
+
<ResourceDetailPage
|
|
24
|
+
readOnly
|
|
25
|
+
name="Function runtime"
|
|
26
|
+
status="pending"
|
|
27
|
+
overviewTitle="Runtime details"
|
|
28
|
+
details={[{ id: 'state', label: 'State', value: runtime.state }]}
|
|
29
|
+
history={history}
|
|
30
|
+
shell={shell}
|
|
31
|
+
/>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ConsoleShellProps } from '../../shared/ConsoleShell';
|
|
2
|
+
|
|
3
|
+
/** Explicit frame props prevent ConsoleShell's editable-demo fixtures from appearing. */
|
|
4
|
+
export const shell: Omit<ConsoleShellProps, 'children'> = {
|
|
5
|
+
nav: [],
|
|
6
|
+
breadcrumbs: [],
|
|
7
|
+
workspaces: null,
|
|
8
|
+
user: null,
|
|
9
|
+
usage: null,
|
|
10
|
+
helpHref: null,
|
|
11
|
+
topbar: { environment: null, search: null, notifications: null, theme: null },
|
|
12
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { TemplateMeta } from '../meta';
|
|
2
|
+
|
|
3
|
+
export const meta: TemplateMeta = {
|
|
4
|
+
name: 'Resource detail',
|
|
5
|
+
family: 'Resources',
|
|
6
|
+
priority: 'P1',
|
|
7
|
+
status: 'beta',
|
|
8
|
+
description:
|
|
9
|
+
'Resource detail with editable Overview, Activity and Settings tabs by default; optional readOnly (boolean, default false) uses only explicit header and detail props and one Overview tab, with optional history (readonly StatusEvent[], default undefined): omitted hides the history card, [] shows the named empty state, and events render exactly as supplied without invented actors. Read-only ignores activity, events, dangerActions, defaultTab, onSaveSettings and onDangerAction; explicit actions and onEditDetails are honored. The Resource sections tablist retains Tab, arrow, Home/End and Enter/Space keyboard navigation; history is a named group containing the Status history list or empty-state region. Uses token styling in dark (default), light and high_contrast. Examples: runtime-with-transition maps state and lastTransitionAt to one actor-free event, runtime-without-transition maps null to [], and realtime-configuration and postgres-table-detail omit history. ConsoleShell retains sample frame defaults unless explicit shell props are supplied, as in the examples.',
|
|
10
|
+
layout: 'SidebarLayout + Page',
|
|
11
|
+
blocks: [
|
|
12
|
+
'app-sidebar',
|
|
13
|
+
'app-topbar',
|
|
14
|
+
'page-header',
|
|
15
|
+
'description-list-card',
|
|
16
|
+
'status-timeline',
|
|
17
|
+
'activity-feed',
|
|
18
|
+
'form-section',
|
|
19
|
+
'danger-zone',
|
|
20
|
+
],
|
|
21
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { render, screen, waitFor } from '@testing-library/react';
|
|
2
|
+
import userEvent from '@testing-library/user-event';
|
|
3
|
+
import { expectNoAxeViolations } from '../test/a11y';
|
|
4
|
+
import ResourceFormPage from './Page';
|
|
5
|
+
|
|
6
|
+
describe('ResourceFormPage', () => {
|
|
7
|
+
it('renders the form sections and a clean save bar', { timeout: 15000 }, async () => {
|
|
8
|
+
const { container } = render(<ResourceFormPage />);
|
|
9
|
+
expect(screen.getByRole('main')).toBeInTheDocument();
|
|
10
|
+
expect(screen.getByRole('heading', { level: 1, name: 'Edit project' })).toBeInTheDocument();
|
|
11
|
+
for (const name of ['General', 'Region', 'Billing'])
|
|
12
|
+
expect(screen.getByRole('heading', { level: 2, name })).toBeInTheDocument();
|
|
13
|
+
expect(screen.getByRole('button', { name: 'Save changes' })).toBeDisabled();
|
|
14
|
+
// ValidationSummary links target control ids.
|
|
15
|
+
expect(screen.getByRole('combobox', { name: /Region/ })).toHaveAttribute('id', 'region');
|
|
16
|
+
await expectNoAxeViolations(container);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('shows the validation summary and focuses the field from its link', { timeout: 15000 }, async () => {
|
|
20
|
+
const onSubmit = vi.fn();
|
|
21
|
+
const { container } = render(<ResourceFormPage onSubmit={onSubmit} />);
|
|
22
|
+
const email = screen.getByRole('textbox', { name: /Billing email/ });
|
|
23
|
+
await userEvent.clear(email);
|
|
24
|
+
await userEvent.type(email, 'not-an-email');
|
|
25
|
+
expect(screen.getByText('You have unsaved changes')).toBeInTheDocument();
|
|
26
|
+
await userEvent.click(screen.getByRole('button', { name: 'Save changes' }));
|
|
27
|
+
expect(onSubmit).not.toHaveBeenCalled();
|
|
28
|
+
const summary = screen.getByRole('region', { name: /There is 1 problem/ });
|
|
29
|
+
await waitFor(() => expect(summary).toHaveFocus());
|
|
30
|
+
await userEvent.click(screen.getByRole('link', { name: /Billing email/ }));
|
|
31
|
+
expect(email).toHaveFocus();
|
|
32
|
+
await expectNoAxeViolations(container);
|
|
33
|
+
|
|
34
|
+
await userEvent.clear(email);
|
|
35
|
+
await userEvent.type(email, 'finance@example.com');
|
|
36
|
+
await userEvent.click(screen.getByRole('button', { name: 'Save changes' }));
|
|
37
|
+
expect(onSubmit).toHaveBeenCalledWith(expect.objectContaining({ billingEmail: 'finance@example.com' }));
|
|
38
|
+
await waitFor(() => expect(screen.getByRole('button', { name: 'Save changes' })).toBeDisabled());
|
|
39
|
+
});
|
|
40
|
+
});
|