@sentry/junior-dashboard 0.270.0 → 0.271.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/app.js +2 -2
- package/dist/client.js +77 -77
- package/dist/index.js +2 -2
- package/dist/tailwind.css +1 -1
- package/package.json +4 -4
- package/src/client/App.tsx +89 -61
- package/src/client/components/AGENTS.md +30 -59
- package/src/client/components/ProfileMenu.tsx +0 -24
- package/src/client/components/UserInputForm.tsx +38 -7
- package/src/client/components/layout/PageHeader.tsx +25 -10
- package/src/client/conversations/AGENTS.md +12 -33
- package/src/client/conversations/ConversationPage.tsx +44 -8
- package/src/client/conversations/conversationRoutes.ts +30 -3
- package/src/client/pages/automations/AGENTS.md +6 -25
- package/src/client/pages/settings/ConnectionsPage.tsx +198 -0
- package/src/client/pages/settings/McpSettingsPage.tsx +77 -0
- package/src/client/pages/settings/PersonalTokensPage.tsx +198 -0
- package/src/client/pages/{SettingsPage.tsx → settings/SettingsPage.tsx} +54 -62
- package/src/client/pages/settings/SettingsPageLayout.tsx +45 -0
- package/src/client/primaryNav.ts +1 -1
- package/src/client/review-desk/ReviewDeskPage.tsx +202 -241
- package/src/client/review-desk/ReviewDeskSidebar.tsx +253 -0
- package/src/client/review-desk/ReviewItemContext.tsx +108 -0
- package/src/client/review-desk/ReviewItemDetails.tsx +153 -0
- package/src/client/review-desk/ReviewItemView.tsx +159 -0
- package/src/client/review-desk/queries.ts +4 -2
- package/src/client/review-desk/reviewItemFormat.ts +54 -0
- package/src/client/components/charts/AGENTS.md +0 -15
- package/src/client/pages/ConnectionsPage.tsx +0 -207
- package/src/client/pages/PersonalTokensPage.tsx +0 -205
- package/src/client/pages/system/AGENTS.md +0 -12
- package/src/client/review-desk/ReviewItemDetail.tsx +0 -123
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sentry/junior-dashboard",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.271.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -39,10 +39,10 @@
|
|
|
39
39
|
"remark-gfm": "^4.0.1",
|
|
40
40
|
"shiki": "4.1.0",
|
|
41
41
|
"zod": "^4.5.4",
|
|
42
|
-
"@sentry/junior-plugin-api": "0.
|
|
42
|
+
"@sentry/junior-plugin-api": "0.271.0"
|
|
43
43
|
},
|
|
44
44
|
"peerDependencies": {
|
|
45
|
-
"@sentry/junior": "0.
|
|
45
|
+
"@sentry/junior": "0.271.0"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@oxlint/plugins": "1.79.0",
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
"tw-animate-css": "^1.4.0",
|
|
64
64
|
"typescript": "^7.0.2",
|
|
65
65
|
"vitest": "^4.1.12",
|
|
66
|
-
"@sentry/junior": "0.
|
|
66
|
+
"@sentry/junior": "0.271.0"
|
|
67
67
|
},
|
|
68
68
|
"scripts": {
|
|
69
69
|
"build": "pnpm --filter @sentry/junior-plugin-api build && pnpm --filter @sentry/junior build && pnpm run build:client && pnpm run build:css && pnpm run build:server",
|
package/src/client/App.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useEffect, useMemo, useState } from "react";
|
|
1
|
+
import { useEffect, useMemo, useState, type ReactNode } from "react";
|
|
2
2
|
import { Navigate, Route, Routes, useLocation, useParams } from "react-router";
|
|
3
3
|
|
|
4
4
|
import {
|
|
@@ -11,7 +11,10 @@ import {
|
|
|
11
11
|
import { ConnectionBanner } from "./components/ConnectionBanner";
|
|
12
12
|
import { LoadingView } from "./components/LoadingView";
|
|
13
13
|
import { VersionDriftBanner } from "./components/VersionDriftBanner";
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
PageContentSkeleton,
|
|
16
|
+
type PageContentSkeletonVariant,
|
|
17
|
+
} from "./components/PageContentSkeleton";
|
|
15
18
|
import { ProfileMenu } from "./components/ProfileMenu";
|
|
16
19
|
import {
|
|
17
20
|
DashboardChrome,
|
|
@@ -36,10 +39,12 @@ import { CodePage } from "./pages/code/CodePage";
|
|
|
36
39
|
import { LocationDetailPage } from "./pages/locations/LocationDetailPage";
|
|
37
40
|
import { LocationsPage } from "./pages/locations/LocationsPage";
|
|
38
41
|
import { PeoplePage } from "./pages/people/PeoplePage";
|
|
39
|
-
import { ConnectionsPage } from "./pages/ConnectionsPage";
|
|
40
|
-
import { PersonalTokensPage } from "./pages/PersonalTokensPage";
|
|
41
42
|
import { PersonProfilePage } from "./pages/people/PersonProfilePage";
|
|
42
|
-
import {
|
|
43
|
+
import { ConnectionsPage } from "./pages/settings/ConnectionsPage";
|
|
44
|
+
import { McpSettingsPage } from "./pages/settings/McpSettingsPage";
|
|
45
|
+
import { PersonalTokensPage } from "./pages/settings/PersonalTokensPage";
|
|
46
|
+
import { SettingsPage } from "./pages/settings/SettingsPage";
|
|
47
|
+
import { SettingsPageLayout } from "./pages/settings/SettingsPageLayout";
|
|
43
48
|
import { SystemPage } from "./pages/system/SystemPage";
|
|
44
49
|
import {
|
|
45
50
|
SystemPageLayout,
|
|
@@ -65,7 +70,10 @@ import {
|
|
|
65
70
|
} from "./pages/user/PluginUserPage";
|
|
66
71
|
import { AdminPage } from "./pages/admin/AdminPage";
|
|
67
72
|
import { buildPrimaryNavItems } from "./primaryNav";
|
|
68
|
-
import {
|
|
73
|
+
import {
|
|
74
|
+
ReviewDeskLoading,
|
|
75
|
+
ReviewDeskPage,
|
|
76
|
+
} from "./review-desk/ReviewDeskPage";
|
|
69
77
|
import { useReviewItemCountData } from "./review-desk/queries";
|
|
70
78
|
import type { DashboardCoreData } from "./types";
|
|
71
79
|
|
|
@@ -74,6 +82,56 @@ const dashboardNoise = {
|
|
|
74
82
|
"url(\"data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitchTiles'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)' opacity='0.7'/%3E%3C/svg%3E\")",
|
|
75
83
|
};
|
|
76
84
|
|
|
85
|
+
const settingsRoutes: {
|
|
86
|
+
loading: {
|
|
87
|
+
description: string;
|
|
88
|
+
label: string;
|
|
89
|
+
title: string;
|
|
90
|
+
variant?: PageContentSkeletonVariant;
|
|
91
|
+
};
|
|
92
|
+
path: string;
|
|
93
|
+
render(data: DashboardCoreData): ReactNode;
|
|
94
|
+
}[] = [
|
|
95
|
+
{
|
|
96
|
+
loading: {
|
|
97
|
+
description: "Manage your profile and preferences.",
|
|
98
|
+
label: "Loading settings",
|
|
99
|
+
title: "General",
|
|
100
|
+
},
|
|
101
|
+
path: "/settings",
|
|
102
|
+
render: (data) => <SettingsPage identity={data.me} />,
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
loading: {
|
|
106
|
+
description: "Create and revoke personal API tokens.",
|
|
107
|
+
label: "Loading API tokens",
|
|
108
|
+
title: "API Tokens",
|
|
109
|
+
variant: "list",
|
|
110
|
+
},
|
|
111
|
+
path: "/settings/api-tokens",
|
|
112
|
+
render: () => <PersonalTokensPage />,
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
loading: {
|
|
116
|
+
description: "Connect and disconnect your accounts.",
|
|
117
|
+
label: "Loading identities",
|
|
118
|
+
title: "Identities",
|
|
119
|
+
variant: "list",
|
|
120
|
+
},
|
|
121
|
+
path: "/settings/connections",
|
|
122
|
+
render: () => <ConnectionsPage />,
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
loading: {
|
|
126
|
+
description: "Connect an MCP client to your conversations.",
|
|
127
|
+
label: "Loading MCP",
|
|
128
|
+
title: "MCP",
|
|
129
|
+
},
|
|
130
|
+
path: "/settings/mcp",
|
|
131
|
+
render: () => <McpSettingsPage />,
|
|
132
|
+
},
|
|
133
|
+
];
|
|
134
|
+
|
|
77
135
|
/** Render the dashboard SPA shell and route-level loading states. */
|
|
78
136
|
export function DashboardShell() {
|
|
79
137
|
const location = useLocation();
|
|
@@ -147,6 +205,7 @@ export function DashboardShell() {
|
|
|
147
205
|
className="bg-dashboard-bg"
|
|
148
206
|
enabled={
|
|
149
207
|
workspace ||
|
|
208
|
+
location.pathname === "/review" ||
|
|
150
209
|
hasAutomationDetailsFrame(location.pathname) ||
|
|
151
210
|
isMemoryLibraryPath(location.pathname)
|
|
152
211
|
}
|
|
@@ -375,11 +434,7 @@ export function DashboardShell() {
|
|
|
375
434
|
<Route
|
|
376
435
|
element={
|
|
377
436
|
loading ? (
|
|
378
|
-
<
|
|
379
|
-
description="Items that need a person, and who can act on them."
|
|
380
|
-
label="Loading review desk"
|
|
381
|
-
title="Review desk"
|
|
382
|
-
/>
|
|
437
|
+
<ReviewDeskLoading />
|
|
383
438
|
) : loggedIn ? (
|
|
384
439
|
<ReviewDeskPage />
|
|
385
440
|
) : (
|
|
@@ -523,56 +578,29 @@ export function DashboardShell() {
|
|
|
523
578
|
}
|
|
524
579
|
path="/system/*"
|
|
525
580
|
/>
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
variant="list"
|
|
550
|
-
/>
|
|
551
|
-
) : loggedIn ? (
|
|
552
|
-
<PersonalTokensPage />
|
|
553
|
-
) : (
|
|
554
|
-
<Navigate replace to="/" />
|
|
555
|
-
)
|
|
556
|
-
}
|
|
557
|
-
path="/settings/api-tokens"
|
|
558
|
-
/>
|
|
559
|
-
<Route
|
|
560
|
-
element={
|
|
561
|
-
loading ? (
|
|
562
|
-
<PageRouteLoading
|
|
563
|
-
description="Connect and disconnect your accounts."
|
|
564
|
-
label="Loading connected accounts"
|
|
565
|
-
title="Connected accounts"
|
|
566
|
-
variant="list"
|
|
567
|
-
/>
|
|
568
|
-
) : loggedIn ? (
|
|
569
|
-
<ConnectionsPage />
|
|
570
|
-
) : (
|
|
571
|
-
<Navigate replace to="/" />
|
|
572
|
-
)
|
|
573
|
-
}
|
|
574
|
-
path="/settings/connections"
|
|
575
|
-
/>
|
|
581
|
+
{settingsRoutes.map((route) => (
|
|
582
|
+
<Route
|
|
583
|
+
element={
|
|
584
|
+
loading ? (
|
|
585
|
+
<SettingsPageLayout
|
|
586
|
+
description={route.loading.description}
|
|
587
|
+
title={route.loading.title}
|
|
588
|
+
>
|
|
589
|
+
<PageContentSkeleton
|
|
590
|
+
label={route.loading.label}
|
|
591
|
+
variant={route.loading.variant}
|
|
592
|
+
/>
|
|
593
|
+
</SettingsPageLayout>
|
|
594
|
+
) : loggedIn ? (
|
|
595
|
+
route.render(data!)
|
|
596
|
+
) : (
|
|
597
|
+
<Navigate replace to="/" />
|
|
598
|
+
)
|
|
599
|
+
}
|
|
600
|
+
key={route.path}
|
|
601
|
+
path={route.path}
|
|
602
|
+
/>
|
|
603
|
+
))}
|
|
576
604
|
<Route
|
|
577
605
|
element={
|
|
578
606
|
loading || userPagesQuery.isPending ? (
|
|
@@ -1,65 +1,36 @@
|
|
|
1
|
-
# Dashboard UI
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
- `ui
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
## Component rules
|
|
20
|
-
|
|
21
|
-
- Use `ui/alert-dialog` for every confirmation. Never use `window.confirm`.
|
|
22
|
-
- Use `ui/dialog` for a short task form. Never put a form in a `Drawer` or
|
|
23
|
-
`DetailsPanel`. Use `useSheet` for each dialog and mount it only while open.
|
|
24
|
-
- Use `ui/trace` for transcript activity rows. Put only inline elements in
|
|
25
|
-
the row button.
|
|
26
|
-
- Give an icon-only control a label with `TooltipContent size="compact"`.
|
|
27
|
-
Never use the native `title` attribute.
|
|
28
|
-
- Set `focusableWhenDisabled` on a `ui/button` tooltip trigger that can be
|
|
1
|
+
# Dashboard UI rules
|
|
2
|
+
|
|
3
|
+
These apply to all dashboard UI. `pnpm dashboard-style:check` enforces the
|
|
4
|
+
token rules.
|
|
5
|
+
|
|
6
|
+
- Add shadcn components with
|
|
7
|
+
`pnpm --filter @sentry/junior-dashboard exec shadcn add <component>`.
|
|
8
|
+
- Change a `ui/` file only when every caller needs the same style. Do not
|
|
9
|
+
wrap a `ui/` component just to rename it or its props.
|
|
10
|
+
- Do not apply the shadcn CLI's global border or body resets. Keep the
|
|
11
|
+
Tailwind radius scale.
|
|
12
|
+
- Confirmations use `ui/alert-dialog`, not `window.confirm`.
|
|
13
|
+
- Short task forms use `ui/dialog`, never a `Drawer` or `DetailsPanel`. Mount
|
|
14
|
+
each dialog through `useSheet`, only while open.
|
|
15
|
+
- Put only inline elements in a `ui/trace` row button.
|
|
16
|
+
- Label icon-only controls with `TooltipContent size="compact"`, not the
|
|
17
|
+
native `title` attribute.
|
|
18
|
+
- Set `focusableWhenDisabled` on a tooltip trigger `ui/button` that can be
|
|
29
19
|
disabled.
|
|
30
|
-
-
|
|
31
|
-
`<control id>-help`.
|
|
20
|
+
- Field ids are `<control id>-error` and `<control id>-help`.
|
|
32
21
|
- Put a checkbox or radio inside its `<label>`. Give each `RadioGroup` an
|
|
33
22
|
`aria-label`.
|
|
34
|
-
- `ui/alert` sets `role="alert"`. Pass `role="status"` for a message that is
|
|
35
|
-
not an error.
|
|
36
23
|
- A form page has one `Button variant="primary"`.
|
|
37
|
-
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
## Tokens
|
|
41
|
-
|
|
42
|
-
- Keep every color in a token in `src/tailwind.css`, so each theme can
|
|
43
|
-
redefine it. Use `light:` only for values that no token covers.
|
|
44
|
-
- Use `var(--color-*)` for chart series. Never use hex values.
|
|
45
|
-
- Do not use `white/*` or `black/*` fills, borders, or rings, or hex surface
|
|
46
|
-
colors. Shadows may use black.
|
|
24
|
+
- Do not replace `ObjectIcon` with a provider logo.
|
|
25
|
+
- Use `light:` only for values that no token covers. Chart series use
|
|
26
|
+
`var(--color-*)`, not hex.
|
|
47
27
|
- Use `outline-dashboard-focus` for keyboard focus only.
|
|
48
|
-
-
|
|
49
|
-
annotations.
|
|
50
|
-
- Labels use sentence case. Do not use uppercase or letter spacing.
|
|
51
|
-
|
|
52
|
-
## Navigation
|
|
53
|
-
|
|
28
|
+
- Labels use sentence case, with no uppercase or letter spacing.
|
|
54
29
|
- App navigation uses pills, page links use `SegmentedNav`, and panel
|
|
55
|
-
switches use `ui/tabs`.
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
- Home and create must not use `ComposerDock`.
|
|
63
|
-
- Feature surfaces stay under `conversations/` or `pages/` until two real
|
|
64
|
-
callers share them. Add a named component on the second real use.
|
|
65
|
-
- Keep Tailwind classes on the owning component.
|
|
30
|
+
switches use `ui/tabs`.
|
|
31
|
+
- Do not add viewport, offset, or safe-area math in pages.
|
|
32
|
+
`layout/VisualViewportShell`, `conversations/ChatLayout`, and
|
|
33
|
+
`conversations/ComposerDock` own it. Home and create do not use
|
|
34
|
+
`ComposerDock`.
|
|
35
|
+
- Keep a feature component under `conversations/` or `pages/` until a second
|
|
36
|
+
real caller needs it.
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Boxes,
|
|
3
3
|
ChevronDown,
|
|
4
|
-
KeyRound,
|
|
5
4
|
LogOut,
|
|
6
|
-
Plug,
|
|
7
5
|
Settings,
|
|
8
6
|
ShieldCheck,
|
|
9
7
|
UserRound,
|
|
@@ -197,22 +195,6 @@ export function ProfileMenu({
|
|
|
197
195
|
<Settings aria-hidden="true" size={16} strokeWidth={2} />
|
|
198
196
|
Settings
|
|
199
197
|
</Link>
|
|
200
|
-
<Link
|
|
201
|
-
className={profileLinkClass}
|
|
202
|
-
onClick={close}
|
|
203
|
-
to="/settings/api-tokens"
|
|
204
|
-
>
|
|
205
|
-
<KeyRound aria-hidden="true" size={16} strokeWidth={2} />
|
|
206
|
-
API tokens
|
|
207
|
-
</Link>
|
|
208
|
-
<Link
|
|
209
|
-
className={profileLinkClass}
|
|
210
|
-
onClick={close}
|
|
211
|
-
to="/settings/connections"
|
|
212
|
-
>
|
|
213
|
-
<Plug aria-hidden="true" size={16} strokeWidth={2} />
|
|
214
|
-
Connected accounts
|
|
215
|
-
</Link>
|
|
216
198
|
{identity.user.isAdmin ? (
|
|
217
199
|
<Link className={profileLinkClass} onClick={close} to="/admin">
|
|
218
200
|
<ShieldCheck aria-hidden="true" size={16} strokeWidth={2} />
|
|
@@ -282,12 +264,6 @@ export function ProfileMenu({
|
|
|
282
264
|
<Link className={sheetItemClass} to="/settings">
|
|
283
265
|
Settings
|
|
284
266
|
</Link>
|
|
285
|
-
<Link className={sheetItemClass} to="/settings/api-tokens">
|
|
286
|
-
API tokens
|
|
287
|
-
</Link>
|
|
288
|
-
<Link className={sheetItemClass} to="/settings/connections">
|
|
289
|
-
Connected accounts
|
|
290
|
-
</Link>
|
|
291
267
|
{identity.user.isAdmin ? (
|
|
292
268
|
<Link className={sheetItemClass} to="/admin">
|
|
293
269
|
Admin
|
|
@@ -35,10 +35,31 @@ const optionClass =
|
|
|
35
35
|
const choiceButtonClass =
|
|
36
36
|
"group flex w-full cursor-pointer items-center gap-3 rounded-md border border-dashboard-border bg-dashboard-surface-raised px-3 py-2.5 text-left font-sans text-sm transition-colors hover:border-dashboard-border-emphasis hover:bg-dashboard-surface-hover focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-dashboard-focus disabled:cursor-not-allowed disabled:opacity-60 data-[picked]:border-dashboard-accent/60 data-[picked]:bg-dashboard-accent/10 data-[picked]:opacity-100";
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
// Marks the choice that Junior recommends, next to its label.
|
|
39
|
+
function SuggestedLabel(props: { className?: string }) {
|
|
40
|
+
return (
|
|
41
|
+
<span
|
|
42
|
+
className={cn(
|
|
43
|
+
"ml-2 text-xs font-normal text-dashboard-accent-text/80",
|
|
44
|
+
props.className,
|
|
45
|
+
)}
|
|
46
|
+
>
|
|
47
|
+
Suggested
|
|
48
|
+
</span>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function OptionText(props: {
|
|
53
|
+
description?: string;
|
|
54
|
+
label: string;
|
|
55
|
+
recommended?: boolean;
|
|
56
|
+
}) {
|
|
39
57
|
return (
|
|
40
58
|
<span className="grid min-w-0 gap-0.5 break-words">
|
|
41
|
-
<span className="font-medium text-dashboard-text">
|
|
59
|
+
<span className="font-medium text-dashboard-text">
|
|
60
|
+
{props.label}
|
|
61
|
+
{props.recommended ? <SuggestedLabel /> : null}
|
|
62
|
+
</span>
|
|
42
63
|
{props.description ? (
|
|
43
64
|
<span className="text-xs text-dashboard-text-muted">
|
|
44
65
|
{props.description}
|
|
@@ -56,13 +77,15 @@ function ChoiceField(props: {
|
|
|
56
77
|
}) {
|
|
57
78
|
const id = useId();
|
|
58
79
|
const { field } = props;
|
|
59
|
-
//
|
|
80
|
+
// Radios already say that one choice counts. Say it for checkboxes only.
|
|
60
81
|
const legend = (
|
|
61
82
|
<legend className="mb-2 p-0 text-sm font-semibold text-dashboard-text">
|
|
62
83
|
{field.label}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
84
|
+
{field.kind === "multi_choice" ? (
|
|
85
|
+
<span className="ml-2 text-xs font-normal text-dashboard-text-muted">
|
|
86
|
+
Pick any
|
|
87
|
+
</span>
|
|
88
|
+
) : null}
|
|
66
89
|
</legend>
|
|
67
90
|
);
|
|
68
91
|
const description = field.description ? (
|
|
@@ -90,6 +113,7 @@ function ChoiceField(props: {
|
|
|
90
113
|
<OptionText
|
|
91
114
|
description={option.description}
|
|
92
115
|
label={option.label}
|
|
116
|
+
recommended={option.recommended}
|
|
93
117
|
/>
|
|
94
118
|
</label>
|
|
95
119
|
))}
|
|
@@ -119,7 +143,11 @@ function ChoiceField(props: {
|
|
|
119
143
|
)
|
|
120
144
|
}
|
|
121
145
|
/>
|
|
122
|
-
<OptionText
|
|
146
|
+
<OptionText
|
|
147
|
+
description={option.description}
|
|
148
|
+
label={option.label}
|
|
149
|
+
recommended={option.recommended}
|
|
150
|
+
/>
|
|
123
151
|
</label>
|
|
124
152
|
))}
|
|
125
153
|
</div>
|
|
@@ -261,6 +289,7 @@ export function UserInputForm(props: {
|
|
|
261
289
|
id={`${choicesId}-${index}-label`}
|
|
262
290
|
>
|
|
263
291
|
{option.label}
|
|
292
|
+
{option.recommended ? <SuggestedLabel /> : null}
|
|
264
293
|
</span>
|
|
265
294
|
{option.description ? (
|
|
266
295
|
<span
|
|
@@ -292,6 +321,8 @@ export function UserInputForm(props: {
|
|
|
292
321
|
onClick={() => pick(buttonField.id, option.label)}
|
|
293
322
|
>
|
|
294
323
|
{option.label}
|
|
324
|
+
{/* The button gap already spaces the label. */}
|
|
325
|
+
{option.recommended ? <SuggestedLabel className="ml-0" /> : null}
|
|
295
326
|
</Button>
|
|
296
327
|
))}
|
|
297
328
|
{secondary}
|
|
@@ -5,17 +5,20 @@ import {
|
|
|
5
5
|
type TimeRangeDays,
|
|
6
6
|
} from "../controls/TimeRangeSelector";
|
|
7
7
|
import { useDocumentTitle } from "../../documentTitle";
|
|
8
|
+
import { cn } from "../../styles";
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Render a spacious analytics page heading with optional controls.
|
|
11
12
|
*
|
|
12
13
|
* Also names the browser tab after the page. Pass `documentTitle` when the tab
|
|
13
|
-
* needs more context than the visible heading.
|
|
14
|
+
* needs more context than the visible heading. Pass `navigation` to put page
|
|
15
|
+
* links under the heading, with their underline on the header border.
|
|
14
16
|
*/
|
|
15
17
|
export function PageHeader(props: {
|
|
16
18
|
actions?: ReactNode;
|
|
17
|
-
description
|
|
19
|
+
description?: ReactNode;
|
|
18
20
|
documentTitle?: string;
|
|
21
|
+
navigation?: ReactNode;
|
|
19
22
|
onRangeChange?(value: TimeRangeDays): void;
|
|
20
23
|
range?: TimeRangeDays;
|
|
21
24
|
rangeOptions?: TimeRangeDays[];
|
|
@@ -36,16 +39,28 @@ export function PageHeader(props: {
|
|
|
36
39
|
const actions = props.actions ?? rangeActions;
|
|
37
40
|
|
|
38
41
|
return (
|
|
39
|
-
<header
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
42
|
+
<header
|
|
43
|
+
className={cn(
|
|
44
|
+
"min-w-0 border-b border-dashboard-border-subtle",
|
|
45
|
+
!props.navigation && "pb-4 sm:pb-6",
|
|
46
|
+
)}
|
|
47
|
+
>
|
|
48
|
+
<div className="flex min-w-0 items-center justify-between gap-6 max-md:flex-col max-md:items-stretch">
|
|
49
|
+
<div className="min-w-0">
|
|
50
|
+
<h2 className="m-0 font-display text-3xl font-light leading-none tracking-[-0.03em] text-dashboard-text sm:text-4xl">
|
|
51
|
+
{props.title}
|
|
52
|
+
</h2>
|
|
53
|
+
{props.description ? (
|
|
54
|
+
<div className="mt-2 max-w-2xl font-sans text-xs leading-relaxed text-dashboard-text-muted sm:text-sm">
|
|
55
|
+
{props.description}
|
|
56
|
+
</div>
|
|
57
|
+
) : null}
|
|
46
58
|
</div>
|
|
59
|
+
{actions ? <div className="shrink-0">{actions}</div> : null}
|
|
47
60
|
</div>
|
|
48
|
-
{
|
|
61
|
+
{props.navigation ? (
|
|
62
|
+
<div className="mt-4 sm:mt-6">{props.navigation}</div>
|
|
63
|
+
) : null}
|
|
49
64
|
</header>
|
|
50
65
|
);
|
|
51
66
|
}
|
|
@@ -1,38 +1,17 @@
|
|
|
1
1
|
# Transcript components
|
|
2
2
|
|
|
3
|
-
Keep
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
-
|
|
11
|
-
|
|
12
|
-
negative margins at call sites.
|
|
13
|
-
- `ChatLayout` keeps the Conversation header outside the transcript scroll.
|
|
14
|
-
- `eventLogRows` and `filterEventLogRows` own row text and search match. The
|
|
15
|
-
page step controls use them too.
|
|
16
|
-
|
|
17
|
-
## Rules
|
|
18
|
-
|
|
19
|
-
- The Automation trigger card shows the credentials from the dispatch record
|
|
20
|
-
of the run, not the current setting. Keep the run input collapsed, because
|
|
21
|
-
it is not from a person.
|
|
22
|
-
- Each `Transcript` sets the panel for its own rows. A nested transcript never
|
|
23
|
-
uses the panel of the page behind it.
|
|
24
|
-
- The page has one right panel. Conversation details, event details, and turn
|
|
25
|
-
context never show at the same time. Closing a panel returns focus to the
|
|
26
|
-
control that opened it.
|
|
3
|
+
- Keep horizontal padding inside each transcript row, so focus outlines are
|
|
4
|
+
not clipped. Do not add negative margins at `TranscriptSummaryTrigger` call
|
|
5
|
+
sites.
|
|
6
|
+
- The Automation trigger card shows the credentials from the run's dispatch
|
|
7
|
+
record, not the current setting. Keep the run input collapsed, because it
|
|
8
|
+
is not from a person.
|
|
9
|
+
- A nested `Transcript` never uses the panel of the page behind it.
|
|
10
|
+
- The page shows one right panel at a time. Closing a panel returns focus to
|
|
11
|
+
the control that opened it.
|
|
27
12
|
- The URL owns the selected event in the main Conversation. Standalone
|
|
28
|
-
transcripts keep
|
|
29
|
-
- A pending exit must not close a new panel or another Conversation.
|
|
30
|
-
|
|
13
|
+
transcripts keep it in `ConversationEventLog`.
|
|
14
|
+
- A pending exit must not close a new panel or another Conversation. A
|
|
15
|
+
pending fork blocks dismissal of its dialog.
|
|
31
16
|
- Raw JSON keeps the original ISO values. Persist the UTC/local mode, not the
|
|
32
17
|
timezone.
|
|
33
|
-
|
|
34
|
-
## Checks
|
|
35
|
-
|
|
36
|
-
- Review with `JUNIOR_DASHBOARD_MOCK_CONVERSATIONS=true pnpm dev`.
|
|
37
|
-
- Keep visual checks out of automated assertions. Browser tests cover
|
|
38
|
-
opening, closing, and keyboard interaction.
|