@gntik-ai/templates 0.0.0-stage → 0.6.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,67 @@
|
|
|
1
|
+
import { BookOpen, Code2, LifeBuoy, PlayCircle, type LucideIcon } from '@gntik-ai/icons';
|
|
2
|
+
import type { BreadcrumbItem } from '@gntik-ai/ui';
|
|
3
|
+
|
|
4
|
+
export interface SetupTask {
|
|
5
|
+
id: string;
|
|
6
|
+
title: string;
|
|
7
|
+
description: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface DocLink {
|
|
11
|
+
id: string;
|
|
12
|
+
title: string;
|
|
13
|
+
description: string;
|
|
14
|
+
href: string;
|
|
15
|
+
icon: LucideIcon;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const startBreadcrumbs: BreadcrumbItem[] = [{ label: 'Overview', href: '/overview' }, { label: 'Get started' }];
|
|
19
|
+
|
|
20
|
+
export const setupTasks: SetupTask[] = [
|
|
21
|
+
{
|
|
22
|
+
id: 'workspace',
|
|
23
|
+
title: 'Create your workspace',
|
|
24
|
+
description: 'Name it and pick the region where your data lives.',
|
|
25
|
+
},
|
|
26
|
+
{ id: 'project', title: 'Create a project', description: 'Import a repository or start from a template.' },
|
|
27
|
+
{
|
|
28
|
+
id: 'deploy',
|
|
29
|
+
title: 'Ship your first deployment',
|
|
30
|
+
description: 'Push to the main branch or deploy from the console.',
|
|
31
|
+
},
|
|
32
|
+
{ id: 'invite', title: 'Invite your team', description: 'Add teammates and choose what each of them can do.' },
|
|
33
|
+
{ id: 'billing', title: 'Add a payment method', description: 'Needed before you go over the free plan limits.' },
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
export const setupInitialDone: string[] = ['workspace', 'project'];
|
|
37
|
+
|
|
38
|
+
export const docLinks: DocLink[] = [
|
|
39
|
+
{
|
|
40
|
+
id: 'quickstart',
|
|
41
|
+
title: 'Quickstart',
|
|
42
|
+
description: 'Deploy a sample service in five minutes.',
|
|
43
|
+
href: '#quickstart',
|
|
44
|
+
icon: PlayCircle,
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
id: 'concepts',
|
|
48
|
+
title: 'Core concepts',
|
|
49
|
+
description: 'Workspaces, projects, environments and deployments.',
|
|
50
|
+
href: '#concepts',
|
|
51
|
+
icon: BookOpen,
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
id: 'api',
|
|
55
|
+
title: 'API reference',
|
|
56
|
+
description: 'Automate everything you can do in the console.',
|
|
57
|
+
href: '#api',
|
|
58
|
+
icon: Code2,
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
id: 'support',
|
|
62
|
+
title: 'Get help',
|
|
63
|
+
description: 'Guides, community forum and support.',
|
|
64
|
+
href: '#support',
|
|
65
|
+
icon: LifeBuoy,
|
|
66
|
+
},
|
|
67
|
+
];
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { TemplateMeta } from '../meta';
|
|
2
|
+
|
|
3
|
+
export const meta: TemplateMeta = {
|
|
4
|
+
name: 'Getting started',
|
|
5
|
+
family: 'Onboarding',
|
|
6
|
+
priority: 'P2',
|
|
7
|
+
status: 'beta',
|
|
8
|
+
description:
|
|
9
|
+
'Onboarding checklist in the console shell: setup progress, checkable steps with a completion message, and cards linking to the docs.',
|
|
10
|
+
layout: 'SidebarLayout + Page',
|
|
11
|
+
blocks: ['app-sidebar', 'app-topbar', 'page-header'],
|
|
12
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { render, screen, within } from '@testing-library/react';
|
|
2
|
+
import userEvent from '@testing-library/user-event';
|
|
3
|
+
import { expectNoAxeViolations } from '../test/a11y';
|
|
4
|
+
import HelpHomePage from './Page';
|
|
5
|
+
import { helpCategories } from './data';
|
|
6
|
+
|
|
7
|
+
describe('HelpHomePage', () => {
|
|
8
|
+
it('renders search, categories and popular articles', { timeout: 15000 }, async () => {
|
|
9
|
+
render(<HelpHomePage />);
|
|
10
|
+
expect(screen.getByRole('main')).toBeInTheDocument();
|
|
11
|
+
expect(screen.getByRole('heading', { level: 1, name: 'How can we help?' })).toBeInTheDocument();
|
|
12
|
+
expect(screen.getByRole('search', { name: 'Help articles' })).toBeInTheDocument();
|
|
13
|
+
expect(screen.getByRole('navigation', { name: 'Help center' })).toBeInTheDocument();
|
|
14
|
+
expect(screen.getAllByRole('heading', { level: 3 })).toHaveLength(helpCategories.length);
|
|
15
|
+
expect(within(screen.getByRole('main')).getByRole('link', { name: 'Deployments' })).toHaveAttribute(
|
|
16
|
+
'href',
|
|
17
|
+
'/help/deployments',
|
|
18
|
+
);
|
|
19
|
+
expect(screen.getByRole('heading', { level: 2, name: 'Popular articles' })).toBeInTheDocument();
|
|
20
|
+
await expectNoAxeViolations();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('filters articles and submits the query', { timeout: 15000 }, async () => {
|
|
24
|
+
const onSearch = vi.fn();
|
|
25
|
+
render(<HelpHomePage onSearch={onSearch} />);
|
|
26
|
+
await userEvent.type(screen.getByRole('searchbox', { name: 'Search help articles' }), 'webhook{Enter}');
|
|
27
|
+
expect(onSearch).toHaveBeenCalledWith('webhook');
|
|
28
|
+
const results = screen.getByRole('region', { name: 'Search results' });
|
|
29
|
+
expect(within(results).getAllByRole('listitem')).toHaveLength(1);
|
|
30
|
+
expect(screen.getByText('1 article')).toBeInTheDocument();
|
|
31
|
+
expect(screen.queryByRole('heading', { name: 'Browse by topic' })).not.toBeInTheDocument();
|
|
32
|
+
});
|
|
33
|
+
});
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { Clock, Search } from '@gntik-ai/icons';
|
|
2
|
+
import {
|
|
3
|
+
Badge,
|
|
4
|
+
ClickableCard,
|
|
5
|
+
DocsLayout,
|
|
6
|
+
EmptyState,
|
|
7
|
+
Grid,
|
|
8
|
+
Heading,
|
|
9
|
+
Input,
|
|
10
|
+
Link,
|
|
11
|
+
Logo,
|
|
12
|
+
NavList,
|
|
13
|
+
Stack,
|
|
14
|
+
Text,
|
|
15
|
+
ThemeSwitcher,
|
|
16
|
+
type NavGroup,
|
|
17
|
+
} from '@gntik-ai/ui';
|
|
18
|
+
import { useId, useState, type FormEvent, type ReactNode } from 'react';
|
|
19
|
+
import { helpArticles, helpCategories, helpNav, searchArticles, type HelpArticle, type HelpCategory } from './data';
|
|
20
|
+
|
|
21
|
+
export interface HelpHomeProps {
|
|
22
|
+
/** Logo or product name in the top bar. */
|
|
23
|
+
brand: ReactNode;
|
|
24
|
+
title: string;
|
|
25
|
+
description: string;
|
|
26
|
+
nav: NavGroup[];
|
|
27
|
+
currentHref: string;
|
|
28
|
+
categories: HelpCategory[];
|
|
29
|
+
/** Popular articles; the search filters them client-side unless `onSearch` handles it. */
|
|
30
|
+
articles: HelpArticle[];
|
|
31
|
+
/** Enter in the search field (server-side search). */
|
|
32
|
+
onSearch: (query: string) => void;
|
|
33
|
+
/** Text under the popular articles (contact support…). */
|
|
34
|
+
footer: ReactNode;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Help center home on DocsLayout: search, category cards and popular articles. */
|
|
38
|
+
export default function HelpHomePage(props: Partial<HelpHomeProps>) {
|
|
39
|
+
const {
|
|
40
|
+
brand = <Logo size={22} wordmark />,
|
|
41
|
+
title = 'How can we help?',
|
|
42
|
+
description = 'Guides, answers and troubleshooting for your workspace.',
|
|
43
|
+
nav = helpNav,
|
|
44
|
+
currentHref = '/help',
|
|
45
|
+
categories = helpCategories,
|
|
46
|
+
articles = helpArticles,
|
|
47
|
+
onSearch,
|
|
48
|
+
footer = (
|
|
49
|
+
<Text variant="supporting" tone="muted">
|
|
50
|
+
Can’t find what you need? <Link href="/help/support">Contact support</Link>.
|
|
51
|
+
</Text>
|
|
52
|
+
),
|
|
53
|
+
} = props;
|
|
54
|
+
const [query, setQuery] = useState('');
|
|
55
|
+
const ids = useId();
|
|
56
|
+
const results = searchArticles(articles, query);
|
|
57
|
+
const searching = query.trim() !== '';
|
|
58
|
+
const categoryTitle = (id: string) => categories.find((c) => c.id === id)?.title ?? id;
|
|
59
|
+
|
|
60
|
+
const submit = (e: FormEvent) => {
|
|
61
|
+
e.preventDefault();
|
|
62
|
+
onSearch?.(query.trim());
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<DocsLayout
|
|
67
|
+
fullScreen
|
|
68
|
+
header={
|
|
69
|
+
<>
|
|
70
|
+
{brand}
|
|
71
|
+
<Badge tone="neutral" size="sm">
|
|
72
|
+
Help
|
|
73
|
+
</Badge>
|
|
74
|
+
<ThemeSwitcher size="sm" className="ms-auto" />
|
|
75
|
+
</>
|
|
76
|
+
}
|
|
77
|
+
nav={<NavList label="Help center" groups={nav} currentHref={currentHref} />}
|
|
78
|
+
footer={footer}
|
|
79
|
+
>
|
|
80
|
+
<Stack gap={10}>
|
|
81
|
+
<Stack gap={4}>
|
|
82
|
+
<Heading level={1} size="xl">
|
|
83
|
+
{title}
|
|
84
|
+
</Heading>
|
|
85
|
+
<Text tone="muted">{description}</Text>
|
|
86
|
+
<form role="search" aria-label="Help articles" onSubmit={submit} className="max-w-xl">
|
|
87
|
+
<Input
|
|
88
|
+
type="search"
|
|
89
|
+
aria-label="Search help articles"
|
|
90
|
+
aria-describedby={`${ids}-count`}
|
|
91
|
+
placeholder="Search articles…"
|
|
92
|
+
leadingIcon={Search}
|
|
93
|
+
value={query}
|
|
94
|
+
onValueChange={setQuery}
|
|
95
|
+
/>
|
|
96
|
+
</form>
|
|
97
|
+
</Stack>
|
|
98
|
+
|
|
99
|
+
{!searching && (
|
|
100
|
+
<section aria-labelledby={`${ids}-categories`}>
|
|
101
|
+
<Heading id={`${ids}-categories`} level={2} size="sm" className="mb-4">
|
|
102
|
+
Browse by topic
|
|
103
|
+
</Heading>
|
|
104
|
+
<Grid cols={{ base: 1, md: 2 }} gap={3}>
|
|
105
|
+
{categories.map((c) => {
|
|
106
|
+
const Icon = c.icon;
|
|
107
|
+
return (
|
|
108
|
+
<ClickableCard
|
|
109
|
+
key={c.id}
|
|
110
|
+
href={c.href}
|
|
111
|
+
titleAs="h3"
|
|
112
|
+
icon={<Icon aria-hidden />}
|
|
113
|
+
title={c.title}
|
|
114
|
+
description={c.description}
|
|
115
|
+
meta={<span>{c.articleCount} articles</span>}
|
|
116
|
+
/>
|
|
117
|
+
);
|
|
118
|
+
})}
|
|
119
|
+
</Grid>
|
|
120
|
+
</section>
|
|
121
|
+
)}
|
|
122
|
+
|
|
123
|
+
<section aria-labelledby={`${ids}-articles`}>
|
|
124
|
+
<Heading id={`${ids}-articles`} level={2} size="sm" className="mb-1">
|
|
125
|
+
{searching ? 'Search results' : 'Popular articles'}
|
|
126
|
+
</Heading>
|
|
127
|
+
<Text id={`${ids}-count`} variant="caption" tone="muted" aria-live="polite" className="mb-3 block">
|
|
128
|
+
{searching ? (results.length === 1 ? '1 article' : `${results.length} articles`) : ''}
|
|
129
|
+
</Text>
|
|
130
|
+
{results.length === 0 ? (
|
|
131
|
+
<EmptyState
|
|
132
|
+
icon={Search}
|
|
133
|
+
size="sm"
|
|
134
|
+
titleAs="h3"
|
|
135
|
+
title="No articles match"
|
|
136
|
+
description="Try fewer words, or contact support."
|
|
137
|
+
/>
|
|
138
|
+
) : (
|
|
139
|
+
<ul className="divide-y divide-border rounded-xl border border-border">
|
|
140
|
+
{results.map((a) => (
|
|
141
|
+
<li key={a.id} className="flex flex-col gap-1 px-4 py-3.5">
|
|
142
|
+
<Link href={a.href} className="self-start text-[14px] font-medium">
|
|
143
|
+
{a.title}
|
|
144
|
+
</Link>
|
|
145
|
+
<Text variant="supporting" tone="muted">
|
|
146
|
+
{a.excerpt}
|
|
147
|
+
</Text>
|
|
148
|
+
<Text as="span" variant="caption" tone="muted" className="inline-flex items-center gap-1.5">
|
|
149
|
+
{categoryTitle(a.category)} · <Clock size={12} aria-hidden /> {a.minutes} min read
|
|
150
|
+
</Text>
|
|
151
|
+
</li>
|
|
152
|
+
))}
|
|
153
|
+
</ul>
|
|
154
|
+
)}
|
|
155
|
+
</section>
|
|
156
|
+
</Stack>
|
|
157
|
+
</DocsLayout>
|
|
158
|
+
);
|
|
159
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { BookOpen, CreditCard, LifeBuoy, Plug, Rocket, Shield, type LucideIcon } from '@gntik-ai/icons';
|
|
2
|
+
import type { NavGroup } from '@gntik-ai/ui';
|
|
3
|
+
|
|
4
|
+
export interface HelpCategory {
|
|
5
|
+
id: string;
|
|
6
|
+
title: string;
|
|
7
|
+
description: string;
|
|
8
|
+
icon: LucideIcon;
|
|
9
|
+
href: string;
|
|
10
|
+
/** Number of articles in the category. */
|
|
11
|
+
articleCount: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface HelpArticle {
|
|
15
|
+
id: string;
|
|
16
|
+
title: string;
|
|
17
|
+
excerpt: string;
|
|
18
|
+
/** Category id. */
|
|
19
|
+
category: string;
|
|
20
|
+
href: string;
|
|
21
|
+
/** Reading time in minutes. */
|
|
22
|
+
minutes: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const helpNav: NavGroup[] = [
|
|
26
|
+
{
|
|
27
|
+
label: 'Help center',
|
|
28
|
+
items: [
|
|
29
|
+
{ label: 'Home', href: '/help', icon: LifeBuoy },
|
|
30
|
+
{ label: 'Get started', href: '/help/get-started', icon: Rocket },
|
|
31
|
+
{ label: 'Deployments', href: '/help/deployments', icon: BookOpen },
|
|
32
|
+
],
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
label: 'Account',
|
|
36
|
+
items: [
|
|
37
|
+
{ label: 'Billing', href: '/help/billing', icon: CreditCard },
|
|
38
|
+
{ label: 'Security', href: '/help/security', icon: Shield },
|
|
39
|
+
{ label: 'Integrations', href: '/help/integrations', icon: Plug },
|
|
40
|
+
],
|
|
41
|
+
},
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
export const helpCategories: HelpCategory[] = [
|
|
45
|
+
{
|
|
46
|
+
id: 'get-started',
|
|
47
|
+
title: 'Get started',
|
|
48
|
+
description: 'Create a workspace, invite your team and ship a first project.',
|
|
49
|
+
icon: Rocket,
|
|
50
|
+
href: '/help/get-started',
|
|
51
|
+
articleCount: 12,
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
id: 'deployments',
|
|
55
|
+
title: 'Deployments',
|
|
56
|
+
description: 'Environments, previews, rollbacks and deploy gates.',
|
|
57
|
+
icon: BookOpen,
|
|
58
|
+
href: '/help/deployments',
|
|
59
|
+
articleCount: 24,
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
id: 'billing',
|
|
63
|
+
title: 'Billing',
|
|
64
|
+
description: 'Plans, invoices, usage limits and payment methods.',
|
|
65
|
+
icon: CreditCard,
|
|
66
|
+
href: '/help/billing',
|
|
67
|
+
articleCount: 9,
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
id: 'security',
|
|
71
|
+
title: 'Security',
|
|
72
|
+
description: 'Single sign-on, two-factor, roles and audit logs.',
|
|
73
|
+
icon: Shield,
|
|
74
|
+
href: '/help/security',
|
|
75
|
+
articleCount: 15,
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
id: 'integrations',
|
|
79
|
+
title: 'Integrations',
|
|
80
|
+
description: 'Webhooks, API keys and connected services.',
|
|
81
|
+
icon: Plug,
|
|
82
|
+
href: '/help/integrations',
|
|
83
|
+
articleCount: 18,
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
id: 'support',
|
|
87
|
+
title: 'Contact support',
|
|
88
|
+
description: 'Open a ticket or check the status page.',
|
|
89
|
+
icon: LifeBuoy,
|
|
90
|
+
href: '/help/support',
|
|
91
|
+
articleCount: 4,
|
|
92
|
+
},
|
|
93
|
+
];
|
|
94
|
+
|
|
95
|
+
export const helpArticles: HelpArticle[] = [
|
|
96
|
+
{
|
|
97
|
+
id: 'a1',
|
|
98
|
+
title: 'Deploy your first project',
|
|
99
|
+
excerpt: 'From a repository to a running deployment in five minutes.',
|
|
100
|
+
category: 'get-started',
|
|
101
|
+
href: '/help/deploy-first-project',
|
|
102
|
+
minutes: 5,
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
id: 'a2',
|
|
106
|
+
title: 'Roll back a deployment',
|
|
107
|
+
excerpt: 'Promote an earlier deployment or schedule a rollback.',
|
|
108
|
+
category: 'deployments',
|
|
109
|
+
href: '/help/roll-back',
|
|
110
|
+
minutes: 3,
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
id: 'a3',
|
|
114
|
+
title: 'Set up single sign-on',
|
|
115
|
+
excerpt: 'Connect your identity provider with SAML or OIDC.',
|
|
116
|
+
category: 'security',
|
|
117
|
+
href: '/help/sso',
|
|
118
|
+
minutes: 8,
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
id: 'a4',
|
|
122
|
+
title: 'Understand your invoice',
|
|
123
|
+
excerpt: 'Seats, usage and credits, line by line.',
|
|
124
|
+
category: 'billing',
|
|
125
|
+
href: '/help/invoice',
|
|
126
|
+
minutes: 4,
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
id: 'a5',
|
|
130
|
+
title: 'Verify webhook signatures',
|
|
131
|
+
excerpt: 'Check the HMAC header before trusting a payload.',
|
|
132
|
+
category: 'integrations',
|
|
133
|
+
href: '/help/webhook-signatures',
|
|
134
|
+
minutes: 6,
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
id: 'a6',
|
|
138
|
+
title: 'Invite members and assign roles',
|
|
139
|
+
excerpt: 'Owners, admins, members and viewers explained.',
|
|
140
|
+
category: 'get-started',
|
|
141
|
+
href: '/help/roles',
|
|
142
|
+
minutes: 4,
|
|
143
|
+
},
|
|
144
|
+
];
|
|
145
|
+
|
|
146
|
+
/** Articles whose title or excerpt contains the query. */
|
|
147
|
+
export function searchArticles(articles: readonly HelpArticle[], query: string): HelpArticle[] {
|
|
148
|
+
const q = query.trim().toLowerCase();
|
|
149
|
+
if (!q) return [...articles];
|
|
150
|
+
return articles.filter((a) => a.title.toLowerCase().includes(q) || a.excerpt.toLowerCase().includes(q));
|
|
151
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { TemplateMeta } from '../meta';
|
|
2
|
+
|
|
3
|
+
export const meta: TemplateMeta = {
|
|
4
|
+
name: 'Help home',
|
|
5
|
+
family: 'Help',
|
|
6
|
+
priority: 'P3',
|
|
7
|
+
status: 'experimental',
|
|
8
|
+
description:
|
|
9
|
+
'Help center home on DocsLayout: search with a live result count, category ClickableCards with article counts, popular articles with reading time, and a contact-support footer.',
|
|
10
|
+
layout: 'DocsLayout',
|
|
11
|
+
blocks: [],
|
|
12
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { render, screen } from '@testing-library/react';
|
|
2
|
+
import userEvent from '@testing-library/user-event';
|
|
3
|
+
import { expectNoAxeViolations } from '../test/a11y';
|
|
4
|
+
import HomeDashboardPage from './Page';
|
|
5
|
+
import { meta } from './template.meta';
|
|
6
|
+
|
|
7
|
+
describe('HomeDashboardPage', () => {
|
|
8
|
+
it('renders the shell, heading, KPIs, charts and activity', { timeout: 15000 }, async () => {
|
|
9
|
+
const { container } = render(<HomeDashboardPage />);
|
|
10
|
+
expect(screen.getByRole('main')).toBeInTheDocument();
|
|
11
|
+
expect(screen.getByRole('heading', { level: 1, name: 'Overview' })).toBeInTheDocument();
|
|
12
|
+
expect(screen.getByText('Active projects')).toBeInTheDocument();
|
|
13
|
+
expect(screen.getByRole('heading', { name: 'Infrastructure cost' })).toBeInTheDocument();
|
|
14
|
+
expect(screen.getByRole('heading', { name: 'Requests' })).toBeInTheDocument();
|
|
15
|
+
expect(screen.getByRole('region', { name: 'Recent activity' })).toBeInTheDocument();
|
|
16
|
+
await expectNoAxeViolations(container);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('reports quick actions by id', { timeout: 15000 }, async () => {
|
|
20
|
+
const onQuickAction = vi.fn();
|
|
21
|
+
render(<HomeDashboardPage onQuickAction={onQuickAction} />);
|
|
22
|
+
await userEvent.click(screen.getByRole('button', { name: 'Invite members', description: /teammates/ }));
|
|
23
|
+
expect(onQuickAction).toHaveBeenCalledWith('invite');
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('renders product charts from props', { timeout: 15000 }, () => {
|
|
27
|
+
const signups = {
|
|
28
|
+
'7d': [
|
|
29
|
+
{ week: 'W1', Signups: 10, Trials: 4 },
|
|
30
|
+
{ week: 'W2', Signups: 14, Trials: 6 },
|
|
31
|
+
],
|
|
32
|
+
};
|
|
33
|
+
render(
|
|
34
|
+
<HomeDashboardPage
|
|
35
|
+
primaryChart={{
|
|
36
|
+
title: 'Signups',
|
|
37
|
+
description: 'New accounts',
|
|
38
|
+
ranges: [{ value: '7d', label: '7d' }],
|
|
39
|
+
dataByRange: signups,
|
|
40
|
+
index: 'week',
|
|
41
|
+
categories: ['Signups', 'Trials'],
|
|
42
|
+
}}
|
|
43
|
+
secondaryChart={{ title: 'Agent runs', categories: ['Requests'] }}
|
|
44
|
+
/>,
|
|
45
|
+
);
|
|
46
|
+
expect(screen.getByRole('heading', { name: 'Signups' })).toBeInTheDocument();
|
|
47
|
+
expect(screen.queryByRole('heading', { name: 'Infrastructure cost' })).toBeNull();
|
|
48
|
+
expect(screen.getByRole('button', { name: 'Trials' })).toBeInTheDocument();
|
|
49
|
+
expect(screen.getByRole('heading', { name: 'Agent runs' })).toBeInTheDocument();
|
|
50
|
+
expect(screen.queryByRole('button', { name: 'Errors' })).toBeNull();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('declares its meta', () => {
|
|
54
|
+
expect(meta.priority).toBe('P1');
|
|
55
|
+
expect(meta.blocks).toContain('kpi-row');
|
|
56
|
+
});
|
|
57
|
+
});
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ActivityFeed,
|
|
3
|
+
ChartCard,
|
|
4
|
+
KpiRow,
|
|
5
|
+
PageHeader,
|
|
6
|
+
type ActivityItem,
|
|
7
|
+
type ChartCardProps,
|
|
8
|
+
type ChartCardRange,
|
|
9
|
+
type CostPoint,
|
|
10
|
+
type KpiItem,
|
|
11
|
+
type PageChromeAction,
|
|
12
|
+
} from '@gntik-ai/blocks';
|
|
13
|
+
import { chartFmt } from '@gntik-ai/charts';
|
|
14
|
+
import { Plus, UserPlus } from '@gntik-ai/icons';
|
|
15
|
+
import { Button, ClickableCard, Grid, GridItem, Page, Section, Stack, type BreadcrumbItem } from '@gntik-ai/ui';
|
|
16
|
+
import { ConsoleShell, type ConsoleShellProps } from '../shared/ConsoleShell';
|
|
17
|
+
import {
|
|
18
|
+
homeActivity,
|
|
19
|
+
homeBreadcrumbs,
|
|
20
|
+
homeKpis,
|
|
21
|
+
homeQuickActions,
|
|
22
|
+
homeRequestRanges,
|
|
23
|
+
homeRequestsByRange,
|
|
24
|
+
type QuickAction,
|
|
25
|
+
type RequestPoint,
|
|
26
|
+
} from './data';
|
|
27
|
+
|
|
28
|
+
/** Second-chart settings (everything but its data and ranges, which have their own props). */
|
|
29
|
+
export type HomeSecondaryChart<S extends object> = Omit<ChartCardProps<S>, 'dataByRange' | 'ranges'>;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* `P` is the row type of the first chart (default: the infrastructure-cost fixture), `S` the row
|
|
33
|
+
* type of the second one (default: requests and errors per day).
|
|
34
|
+
*/
|
|
35
|
+
export interface HomeDashboardProps<P extends object = CostPoint, S extends object = RequestPoint> {
|
|
36
|
+
title: string;
|
|
37
|
+
description: string;
|
|
38
|
+
breadcrumbs: BreadcrumbItem[];
|
|
39
|
+
/** Header actions (the last one is the primary). */
|
|
40
|
+
actions: PageChromeAction[];
|
|
41
|
+
kpis: readonly KpiItem[];
|
|
42
|
+
/**
|
|
43
|
+
* First chart: any ChartCard props (title, description, ranges, dataByRange, index, categories,
|
|
44
|
+
* colors, kind, valueFormatter…). Unset fields keep the infrastructure-cost fixture.
|
|
45
|
+
*/
|
|
46
|
+
primaryChart: ChartCardProps<P>;
|
|
47
|
+
/** Data of the second chart, per range key. */
|
|
48
|
+
requestsByRange: Readonly<Record<string, readonly S[]>>;
|
|
49
|
+
requestRanges: readonly ChartCardRange[];
|
|
50
|
+
/** Second chart: title, description, index, categories, colors, kind… (merged over the requests defaults). */
|
|
51
|
+
secondaryChart: HomeSecondaryChart<S>;
|
|
52
|
+
activity: readonly ActivityItem[];
|
|
53
|
+
quickActions: readonly QuickAction[];
|
|
54
|
+
/** Called with the id of a quick action card (cards with `href` also navigate). */
|
|
55
|
+
onQuickAction: (id: string) => void;
|
|
56
|
+
onViewAllActivity: () => void;
|
|
57
|
+
/** Console frame overrides (nav, user, workspaces, topbar). */
|
|
58
|
+
shell: Omit<ConsoleShellProps, 'children'>;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const REQUESTS_CHART: HomeSecondaryChart<RequestPoint> = {
|
|
62
|
+
title: 'Requests',
|
|
63
|
+
description: 'Requests and errors per day',
|
|
64
|
+
index: 'day',
|
|
65
|
+
categories: ['Requests', 'Errors'],
|
|
66
|
+
colors: ['primary', 'rose'],
|
|
67
|
+
kind: 'line',
|
|
68
|
+
stacked: false,
|
|
69
|
+
valueFormatter: chartFmt.num,
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
/** Home dashboard: header, KPI row, two charts, recent activity and quick actions in the console shell. */
|
|
73
|
+
export default function HomeDashboardPage<P extends object = CostPoint, S extends object = RequestPoint>({
|
|
74
|
+
title = 'Overview',
|
|
75
|
+
description = 'How your workspace is doing across projects, traffic and spend.',
|
|
76
|
+
breadcrumbs = homeBreadcrumbs,
|
|
77
|
+
actions = [
|
|
78
|
+
{ label: 'Invite members', icon: UserPlus, variant: 'secondary' },
|
|
79
|
+
{ label: 'New project', icon: Plus, variant: 'primary' },
|
|
80
|
+
],
|
|
81
|
+
kpis = homeKpis,
|
|
82
|
+
primaryChart,
|
|
83
|
+
requestsByRange = homeRequestsByRange as unknown as Readonly<Record<string, readonly S[]>>,
|
|
84
|
+
requestRanges = homeRequestRanges,
|
|
85
|
+
secondaryChart,
|
|
86
|
+
activity = homeActivity,
|
|
87
|
+
quickActions = homeQuickActions,
|
|
88
|
+
onQuickAction,
|
|
89
|
+
onViewAllActivity,
|
|
90
|
+
shell,
|
|
91
|
+
}: Partial<HomeDashboardProps<P, S>>) {
|
|
92
|
+
const secondary = { ...(REQUESTS_CHART as unknown as HomeSecondaryChart<S>), ...secondaryChart };
|
|
93
|
+
return (
|
|
94
|
+
<ConsoleShell currentHref="/overview" breadcrumbs={breadcrumbs} {...shell}>
|
|
95
|
+
<Page
|
|
96
|
+
width="wide"
|
|
97
|
+
header={
|
|
98
|
+
<PageHeader
|
|
99
|
+
breadcrumbs={null}
|
|
100
|
+
title={title}
|
|
101
|
+
description={description}
|
|
102
|
+
status=""
|
|
103
|
+
meta={[]}
|
|
104
|
+
tabs={null}
|
|
105
|
+
actions={actions}
|
|
106
|
+
/>
|
|
107
|
+
}
|
|
108
|
+
>
|
|
109
|
+
<Stack gap={6}>
|
|
110
|
+
<KpiRow items={kpis} />
|
|
111
|
+
<Grid cols={{ base: 1, lg: 2 }} gap={6}>
|
|
112
|
+
<ChartCard<P> titleAs="h2" {...primaryChart} />
|
|
113
|
+
<ChartCard<S> titleAs="h2" {...secondary} ranges={requestRanges} dataByRange={requestsByRange} />
|
|
114
|
+
</Grid>
|
|
115
|
+
<Grid cols={{ base: 1, lg: 3 }} gap={6} align="start">
|
|
116
|
+
<GridItem span={{ base: 1, lg: 2 }}>
|
|
117
|
+
<Section
|
|
118
|
+
variant="card"
|
|
119
|
+
padding="md"
|
|
120
|
+
title="Recent activity"
|
|
121
|
+
actions={
|
|
122
|
+
<Button variant="ghost" size="sm" onClick={onViewAllActivity}>
|
|
123
|
+
View all
|
|
124
|
+
</Button>
|
|
125
|
+
}
|
|
126
|
+
>
|
|
127
|
+
<ActivityFeed items={activity} label="Activity, newest first" />
|
|
128
|
+
</Section>
|
|
129
|
+
</GridItem>
|
|
130
|
+
<Section title="Quick actions" description="Common next steps.">
|
|
131
|
+
<Stack gap={3}>
|
|
132
|
+
{quickActions.map((qa) => {
|
|
133
|
+
const Icon = qa.icon;
|
|
134
|
+
return (
|
|
135
|
+
<ClickableCard
|
|
136
|
+
key={qa.id}
|
|
137
|
+
title={qa.title}
|
|
138
|
+
description={qa.description}
|
|
139
|
+
icon={<Icon aria-hidden />}
|
|
140
|
+
href={qa.href}
|
|
141
|
+
onClick={() => onQuickAction?.(qa.id)}
|
|
142
|
+
/>
|
|
143
|
+
);
|
|
144
|
+
})}
|
|
145
|
+
</Stack>
|
|
146
|
+
</Section>
|
|
147
|
+
</Grid>
|
|
148
|
+
</Stack>
|
|
149
|
+
</Page>
|
|
150
|
+
</ConsoleShell>
|
|
151
|
+
);
|
|
152
|
+
}
|