@sentry/junior-dashboard 0.263.0 → 0.265.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/LICENSE +201 -0
- package/README.md +2 -5
- package/dist/app.js +119 -35
- package/dist/client.js +148 -135
- package/dist/index.d.ts +0 -1
- package/dist/index.js +119 -35
- package/dist/tailwind.css +1 -1
- package/package.json +9 -4
- package/src/api/schema.ts +0 -1
- package/src/app.ts +5 -20
- package/src/client/App.tsx +29 -31
- package/src/client/api.ts +7 -0
- package/src/client/components/BottomSheet.tsx +26 -18
- package/src/client/components/CopyButton.tsx +83 -0
- package/src/client/components/DetailsPanel.tsx +133 -0
- package/src/client/components/Drawer.tsx +34 -33
- package/src/client/components/FilterBar.tsx +16 -31
- package/src/client/components/Metric.tsx +33 -23
- package/src/client/components/PageContentSkeleton.tsx +1 -1
- package/src/client/components/Pagination.tsx +1 -1
- package/src/client/components/ParticipantAvatarStack.tsx +40 -36
- package/src/client/components/ProfileMenu.tsx +27 -93
- package/src/client/components/README.md +101 -64
- package/src/client/components/SegmentedNav.tsx +1 -1
- package/src/client/components/ShimmerText.tsx +2 -6
- package/src/client/components/ThemeSwitcher.tsx +19 -15
- package/src/client/components/charts/ActivityChart.tsx +173 -288
- package/src/client/components/charts/CacheWrites.tsx +63 -50
- package/src/client/components/charts/InputCacheChart.tsx +137 -131
- package/src/client/components/charts/README.md +4 -4
- package/src/client/components/charts/SystemMetricCharts.tsx +68 -95
- package/src/client/components/charts/WorkspaceUsageChart.tsx +42 -62
- package/src/client/components/controls/DirectorySortSelect.tsx +3 -3
- package/src/client/components/controls/TimeRangeSelector.tsx +10 -15
- package/src/client/components/layout/DashboardHeader.tsx +44 -90
- package/src/client/components/layout/SecondaryNavigation.tsx +2 -1
- package/src/client/components/layout/VisualViewportShell.tsx +2 -2
- package/src/client/components/ui/accordion.tsx +77 -0
- package/src/client/components/ui/alert-dialog.tsx +89 -0
- package/src/client/components/ui/button.tsx +85 -0
- package/src/client/components/ui/chart.tsx +74 -0
- package/src/client/components/ui/checkbox.tsx +26 -0
- package/src/client/components/ui/collapsible.tsx +22 -0
- package/src/client/components/ui/field.tsx +49 -0
- package/src/client/components/ui/input.tsx +35 -0
- package/src/client/components/ui/native-select.tsx +57 -0
- package/src/client/components/ui/popover.tsx +67 -0
- package/src/client/components/ui/radio-group.tsx +32 -0
- package/src/client/components/ui/sheet.tsx +151 -0
- package/src/client/components/ui/skeleton.tsx +19 -0
- package/src/client/components/ui/table.tsx +80 -0
- package/src/client/components/ui/tabs.tsx +75 -0
- package/src/client/components/ui/textarea.tsx +17 -0
- package/src/client/components/ui/toast.tsx +215 -0
- package/src/client/components/ui/toggle-group.tsx +36 -0
- package/src/client/components/ui/toggle.tsx +37 -0
- package/src/client/components/ui/tooltip.tsx +168 -0
- package/src/client/components/ui/trace.tsx +96 -0
- package/src/client/components/ui/useSheet.ts +74 -0
- package/src/client/conversations/ChatLayout.tsx +7 -3
- package/src/client/conversations/ConversationBrief.tsx +17 -6
- package/src/client/conversations/ConversationChildAgents.tsx +87 -58
- package/src/client/conversations/ConversationComposer.tsx +26 -12
- package/src/client/conversations/ConversationDetails.tsx +198 -95
- package/src/client/conversations/ConversationEventDetails.tsx +141 -0
- package/src/client/conversations/ConversationEventLog.tsx +150 -129
- package/src/client/conversations/ConversationHeader.tsx +8 -3
- package/src/client/conversations/ConversationHeaderActions.tsx +90 -82
- package/src/client/conversations/ConversationHomeList.tsx +1 -1
- package/src/client/conversations/ConversationLinkedWork.tsx +118 -0
- package/src/client/conversations/ConversationMeta.tsx +111 -92
- package/src/client/conversations/ConversationPage.tsx +241 -110
- package/src/client/conversations/ConversationSidebar.tsx +48 -29
- package/src/client/conversations/ConversationUsageBreakdown.tsx +16 -3
- package/src/client/conversations/ConversationWatches.tsx +165 -0
- package/src/client/conversations/ConversationWorkspace.tsx +12 -22
- package/src/client/conversations/ConversationWorkspaceLoading.tsx +1 -1
- package/src/client/conversations/DetailsList.tsx +124 -0
- package/src/client/conversations/EventDetails.tsx +42 -8
- package/src/client/conversations/ForkConversationButton.tsx +27 -16
- package/src/client/conversations/ObjectCard.tsx +20 -8
- package/src/client/conversations/PendingMailboxStack.tsx +52 -33
- package/src/client/conversations/README.md +93 -19
- package/src/client/conversations/ShareConversationButton.tsx +28 -3
- package/src/client/conversations/SubagentTranscriptDrawer.tsx +21 -15
- package/src/client/conversations/ToolFrame.tsx +36 -52
- package/src/client/conversations/TranscriptActivityGroup.tsx +13 -4
- package/src/client/conversations/TranscriptAutomationTriggerView.tsx +5 -4
- package/src/client/conversations/TranscriptContextEventView.tsx +25 -11
- package/src/client/conversations/TranscriptHeadingRow.tsx +7 -7
- package/src/client/conversations/TranscriptLoading.tsx +1 -1
- package/src/client/conversations/TranscriptMarkdown.tsx +25 -20
- package/src/client/conversations/TranscriptMessageContextView.tsx +8 -2
- package/src/client/conversations/TranscriptMessageView.tsx +6 -1
- package/src/client/conversations/TranscriptReasoningView.tsx +54 -68
- package/src/client/conversations/TranscriptSubagentView.tsx +3 -3
- package/src/client/conversations/TranscriptTimestamp.tsx +9 -7
- package/src/client/conversations/TranscriptToolView.tsx +1 -1
- package/src/client/conversations/TranscriptTurnContextView.tsx +99 -27
- package/src/client/conversations/TranscriptView.tsx +63 -27
- package/src/client/conversations/eventLog.ts +57 -0
- package/src/client/conversations/eventTime.ts +60 -0
- package/src/client/conversations/queries.ts +29 -0
- package/src/client/conversations/transcriptBottomPinning.ts +34 -0
- package/src/client/format.ts +5 -0
- package/src/client/lib/utils.ts +1 -0
- package/src/client/pages/ConnectionsPage.tsx +2 -2
- package/src/client/pages/PersonalTokensPage.tsx +4 -4
- package/src/client/pages/SettingsPage.tsx +9 -10
- package/src/client/pages/admin/ServiceAccountsSection.tsx +2 -2
- package/src/client/pages/automations/AutomationCostChart.tsx +50 -71
- package/src/client/pages/automations/AutomationDeleteDialog.tsx +87 -0
- package/src/client/pages/automations/AutomationDetails.tsx +193 -0
- package/src/client/pages/automations/AutomationEditPage.tsx +7 -8
- package/src/client/pages/automations/AutomationEditor.tsx +41 -29
- package/src/client/pages/automations/AutomationEditorLoading.tsx +1 -1
- package/src/client/pages/automations/AutomationEventFields.tsx +202 -119
- package/src/client/pages/automations/AutomationExecutionChart.tsx +51 -64
- package/src/client/pages/automations/AutomationExecutionStatusChart.tsx +47 -70
- package/src/client/pages/automations/AutomationExecutionsPage.tsx +150 -126
- package/src/client/pages/automations/AutomationFilters.tsx +189 -74
- package/src/client/pages/automations/AutomationLifecycle.tsx +109 -78
- package/src/client/pages/automations/AutomationOutcomeFields.tsx +26 -24
- package/src/client/pages/automations/AutomationRecentRuns.tsx +143 -0
- package/src/client/pages/automations/AutomationRow.tsx +12 -138
- package/src/client/pages/automations/AutomationRunDetails.tsx +307 -0
- package/src/client/pages/automations/AutomationRunsPage.tsx +119 -94
- package/src/client/pages/automations/AutomationScheduleFields.tsx +51 -51
- package/src/client/pages/automations/AutomationStatus.tsx +201 -0
- package/src/client/pages/automations/AutomationVersionsPage.tsx +65 -28
- package/src/client/pages/automations/AutomationsPage.tsx +80 -68
- package/src/client/pages/automations/AutomationsPageLayout.tsx +46 -14
- package/src/client/pages/automations/README.md +57 -0
- package/src/client/pages/automations/TriggerFilters.tsx +22 -0
- package/src/client/pages/automations/automationFormat.ts +37 -23
- package/src/client/pages/code/CodeActivityChart.tsx +59 -101
- package/src/client/pages/code/CodePage.tsx +52 -55
- package/src/client/pages/code/RepositoryPage.tsx +6 -8
- package/src/client/pages/locations/LocationActivityChart.tsx +53 -70
- package/src/client/pages/locations/LocationDirectoryActivityChart.tsx +60 -80
- package/src/client/pages/memory/MemoryCostChart.tsx +79 -103
- package/src/client/pages/memory/MemoryDetails.tsx +218 -0
- package/src/client/pages/memory/MemoryPage.tsx +168 -112
- package/src/client/pages/memory/MemoryPageLayout.tsx +33 -9
- package/src/client/pages/memory/MemoryTimeline.tsx +46 -69
- package/src/client/pages/memory/memoryRecord.ts +1 -1
- package/src/client/pages/people/ContributionGrid.tsx +23 -16
- package/src/client/pages/people/PeopleActivityChart.tsx +68 -107
- package/src/client/pages/people/ProfileCodeChanges.tsx +5 -5
- package/src/client/pages/system/BaseSandboxCard.tsx +1 -1
- package/src/client/pages/system/ConversationActivityChart.tsx +49 -67
- package/src/client/pages/system/GuardianActivity.tsx +1 -2
- package/src/client/pages/system/PluginBarChart.tsx +106 -87
- package/src/client/pages/system/PluginReportRecordSet.tsx +25 -24
- package/src/client/pages/system/README.md +23 -0
- package/src/client/pages/system/SystemNavigation.tsx +5 -1
- package/src/client/pages/system/WorkspaceDeleteDialog.tsx +111 -0
- package/src/client/pages/system/WorkspaceDetails.tsx +200 -43
- package/src/client/pages/system/WorkspaceEditor.tsx +59 -61
- package/src/client/pages/system/WorkspaceFormPage.tsx +23 -47
- package/src/client/pages/system/WorkspaceList.tsx +20 -30
- package/src/client/pages/system/WorkspacesPage.tsx +117 -119
- package/src/client/pages/system/sandboxSize.ts +8 -0
- package/src/client/pages/system/workspaceQueries.ts +53 -0
- package/src/client/pages/user/PluginUserPage.tsx +51 -2
- package/src/client/pages/user/pluginUserPageData.ts +1 -6
- package/src/client/styles.ts +4 -0
- package/src/client.tsx +7 -1
- package/src/mock-reporting/automation-collection.ts +4 -2
- package/src/mock-reporting/fixtures.ts +23 -7
- package/src/mock-reporting/routes.ts +12 -0
- package/src/mock-reporting/watches.ts +94 -0
- package/src/shell.ts +4 -9
- package/src/tailwind.css +77 -10
- package/src/client/components/Button.tsx +0 -118
- package/src/client/components/Field.tsx +0 -53
- package/src/client/components/SegmentedTabs.tsx +0 -119
- package/src/client/components/Select.tsx +0 -52
- package/src/client/components/Sheet.LICENSE +0 -21
- package/src/client/components/Sheet.tsx +0 -141
- package/src/client/components/Skeleton.tsx +0 -17
- package/src/client/components/TextInput.tsx +0 -58
- package/src/client/components/Tooltip.tsx +0 -216
- package/src/client/components/charts/useChartLayout.ts +0 -27
- package/src/client/conversations/ConversationDetailsFrame.tsx +0 -91
- package/src/client/conversations/CopyMarkdownButton.tsx +0 -65
- package/src/client/pages/automations/AutomationDetailsDrawer.tsx +0 -219
- package/src/client/pages/dev/ChartFixtures.tsx +0 -325
- package/src/client/pages/dev/ComponentsPage.tsx +0 -899
- package/src/client/pages/dev/ConversationFixture.tsx +0 -231
- package/src/client/pages/dev/ObjectFixtures.tsx +0 -138
- package/src/client/pages/dev/SegmentedFixtures.tsx +0 -59
- package/src/client/pages/dev/SelectFixtures.tsx +0 -62
- package/src/client/pages/dev/TranscriptActivityFixtures.tsx +0 -206
- package/src/client/pages/memory/MemoryDetailsDrawer.tsx +0 -167
- package/src/client/pages/system/SandboxSizeSummary.tsx +0 -47
- package/src/client/pages/system/SnapshotSummary.tsx +0 -78
|
@@ -11,16 +11,17 @@ import {
|
|
|
11
11
|
formatCompactNumber,
|
|
12
12
|
formatCostSummary,
|
|
13
13
|
} from "../../format";
|
|
14
|
+
import { Area, Bar, CartesianGrid, ComposedChart, Scatter } from "recharts";
|
|
15
|
+
import { ChartContainer, ChartTooltip } from "../ui/chart";
|
|
14
16
|
import {
|
|
15
17
|
ActivityChartAverageLine,
|
|
16
|
-
ActivityChartDateLabels,
|
|
17
|
-
ActivityChartGrid,
|
|
18
|
-
ActivityChartTooltip,
|
|
19
18
|
activityChartAverage,
|
|
20
|
-
|
|
19
|
+
activityChartMargin,
|
|
20
|
+
ActivityChartTooltipContent,
|
|
21
|
+
ActivityChartXAxis,
|
|
22
|
+
ActivityChartYAxis,
|
|
21
23
|
} from "./ActivityChart";
|
|
22
24
|
import { ChartHeader } from "./ChartHeader";
|
|
23
|
-
import { useChartLayout } from "./useChartLayout";
|
|
24
25
|
import { InputCacheChart } from "./InputCacheChart";
|
|
25
26
|
|
|
26
27
|
type Metric = "costUsd" | "durationMs" | "tokens";
|
|
@@ -123,108 +124,80 @@ function MetricChart(props: {
|
|
|
123
124
|
days: ConversationMetricDay[];
|
|
124
125
|
}) {
|
|
125
126
|
const { chart, days } = props;
|
|
126
|
-
const { ref, layout } = useChartLayout(
|
|
127
|
-
150,
|
|
128
|
-
chart.metric === "costUsd" ? 72 : 64,
|
|
129
|
-
);
|
|
130
127
|
const values = days.map((day) => metricValue(day, chart.metric));
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
x: layout.left + step * index + step / 2,
|
|
135
|
-
y: layout.top + layout.plotHeight - (value / maximum) * layout.plotHeight,
|
|
128
|
+
const data = days.map((day, index) => ({
|
|
129
|
+
date: day.date,
|
|
130
|
+
value: values[index]!,
|
|
136
131
|
}));
|
|
137
|
-
const
|
|
138
|
-
? `M ${points[0]!.x} ${layout.top + layout.plotHeight} L ${points
|
|
139
|
-
.map((point) => `${point.x} ${point.y}`)
|
|
140
|
-
.join(" L ")} L ${points.at(-1)!.x} ${layout.top + layout.plotHeight} Z`
|
|
141
|
-
: "";
|
|
132
|
+
const maximum = Math.max(Number.EPSILON, ...values);
|
|
142
133
|
const total = values.reduce((sum, value) => sum + value, 0);
|
|
143
134
|
const average = activityChartAverage(values);
|
|
144
|
-
const
|
|
135
|
+
const series = {
|
|
136
|
+
dataKey: "value",
|
|
137
|
+
fill: "var(--color-value)",
|
|
138
|
+
isAnimationActive: false,
|
|
139
|
+
} as const;
|
|
145
140
|
|
|
146
141
|
return (
|
|
147
142
|
<Card>
|
|
148
143
|
<ChartHeader title={chart.title} total={chart.format(total)} />
|
|
149
|
-
<div className="px-2 py-2"
|
|
150
|
-
<
|
|
151
|
-
|
|
152
|
-
|
|
144
|
+
<div className="px-2 py-2">
|
|
145
|
+
<ChartContainer
|
|
146
|
+
className="aspect-auto h-38 w-full"
|
|
147
|
+
config={{ value: { color: chart.color, label: chart.title } }}
|
|
153
148
|
>
|
|
154
|
-
<
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
149
|
+
<ComposedChart
|
|
150
|
+
aria-label={`${chart.title} per ${props.bucketUnit === "6hour" ? "6 hours" : props.bucketUnit}`}
|
|
151
|
+
data={data}
|
|
152
|
+
margin={activityChartMargin}
|
|
153
|
+
role="img"
|
|
154
|
+
>
|
|
155
|
+
<CartesianGrid strokeDasharray="3 5" vertical={false} />
|
|
156
|
+
<ActivityChartYAxis format={chart.axisFormat} maximum={maximum} />
|
|
157
|
+
<ActivityChartXAxis keys={data.map((day) => day.date)} />
|
|
158
|
+
<ChartTooltip
|
|
159
|
+
content={
|
|
160
|
+
<ActivityChartTooltipContent
|
|
161
|
+
rows={(day: (typeof data)[number]) => [
|
|
162
|
+
[chart.title, chart.format(day.value)],
|
|
163
|
+
]}
|
|
164
|
+
/>
|
|
165
|
+
}
|
|
166
|
+
/>
|
|
167
|
+
{chart.type === "bar" ? (
|
|
168
|
+
<Bar
|
|
169
|
+
{...series}
|
|
170
|
+
fillOpacity={0.8}
|
|
171
|
+
maxBarSize={8}
|
|
172
|
+
minPointSize={(value) => (value ? 2 : 0)}
|
|
173
|
+
radius={1.5}
|
|
174
|
+
/>
|
|
175
|
+
) : chart.type === "area" ? (
|
|
176
|
+
<Area
|
|
177
|
+
{...series}
|
|
178
|
+
dot={{ fillOpacity: 0.45, r: 2.5, stroke: "none" }}
|
|
179
|
+
fillOpacity={0.08}
|
|
180
|
+
stroke="var(--color-value)"
|
|
168
181
|
strokeLinecap="round"
|
|
169
182
|
strokeLinejoin="round"
|
|
170
|
-
strokeWidth=
|
|
183
|
+
strokeWidth={2}
|
|
184
|
+
type="linear"
|
|
171
185
|
/>
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
<rect
|
|
188
|
-
fill={chart.color}
|
|
189
|
-
height={renderedBarHeight}
|
|
190
|
-
opacity={value ? 0.8 : 0.1}
|
|
191
|
-
rx="1.5"
|
|
192
|
-
tabIndex={0}
|
|
193
|
-
width={barWidth}
|
|
194
|
-
x={point.x - barWidth / 2}
|
|
195
|
-
y={layout.top + layout.plotHeight - renderedBarHeight}
|
|
196
|
-
/>
|
|
197
|
-
) : (
|
|
198
|
-
<circle
|
|
199
|
-
cx={point.x}
|
|
200
|
-
cy={point.y}
|
|
201
|
-
fill={chart.color}
|
|
202
|
-
opacity={chart.type === "scatter" ? 0.75 : 0.45}
|
|
203
|
-
r={chart.type === "scatter" ? 3.5 : 2.5}
|
|
204
|
-
tabIndex={0}
|
|
205
|
-
/>
|
|
206
|
-
)}
|
|
207
|
-
</ActivityChartTooltip>
|
|
208
|
-
);
|
|
209
|
-
})}
|
|
210
|
-
<ActivityChartAverageLine
|
|
211
|
-
average={average}
|
|
212
|
-
format={
|
|
213
|
-
chart.metric === "tokens"
|
|
214
|
-
? formatActivityChartAverage
|
|
215
|
-
: chart.format
|
|
216
|
-
}
|
|
217
|
-
layout={layout}
|
|
218
|
-
maximum={maximum}
|
|
219
|
-
stroke={chart.color}
|
|
220
|
-
unit={timeRangeBucketAverageUnit(props.bucketUnit)}
|
|
221
|
-
/>
|
|
222
|
-
<ActivityChartDateLabels
|
|
223
|
-
dates={days.map((day) => day.date)}
|
|
224
|
-
layout={layout}
|
|
225
|
-
xPosition={(index) => points[index]?.x ?? layout.left}
|
|
226
|
-
/>
|
|
227
|
-
</ChartSvg>
|
|
186
|
+
) : (
|
|
187
|
+
<Scatter {...series} fillOpacity={0.75} />
|
|
188
|
+
)}
|
|
189
|
+
<ActivityChartAverageLine
|
|
190
|
+
average={average}
|
|
191
|
+
format={
|
|
192
|
+
chart.metric === "tokens"
|
|
193
|
+
? formatActivityChartAverage
|
|
194
|
+
: chart.format
|
|
195
|
+
}
|
|
196
|
+
stroke={chart.color}
|
|
197
|
+
unit={timeRangeBucketAverageUnit(props.bucketUnit)}
|
|
198
|
+
/>
|
|
199
|
+
</ComposedChart>
|
|
200
|
+
</ChartContainer>
|
|
228
201
|
</div>
|
|
229
202
|
</Card>
|
|
230
203
|
);
|
|
@@ -1,20 +1,21 @@
|
|
|
1
|
+
import { Bar, BarChart, CartesianGrid } from "recharts";
|
|
1
2
|
import {
|
|
2
3
|
ActivityChartAverageLine,
|
|
3
|
-
ActivityChartDateLabels,
|
|
4
|
-
ActivityChartGrid,
|
|
5
4
|
activityChartAverage,
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
activityChartMargin,
|
|
6
|
+
ActivityChartTooltipContent,
|
|
7
|
+
ActivityChartXAxis,
|
|
8
|
+
ActivityChartYAxis,
|
|
10
9
|
} from "./ActivityChart";
|
|
11
10
|
import { ChartHeader } from "./ChartHeader";
|
|
12
11
|
import type { TimeRangeDays } from "../controls/TimeRangeSelector";
|
|
13
12
|
import { Card } from "../layout/Card";
|
|
13
|
+
import { ChartContainer, ChartTooltip } from "../ui/chart";
|
|
14
14
|
import { formatActivityChartAverage, formatCompactNumber } from "../../format";
|
|
15
15
|
import type { WorkspaceUsageDay } from "./workspaceUsage";
|
|
16
16
|
|
|
17
17
|
const USAGE_COLOR = "var(--color-violet-400)";
|
|
18
|
+
const chartConfig = { count: { color: USAGE_COLOR, label: "Usage" } };
|
|
18
19
|
|
|
19
20
|
/** Plot successful Workspace switches for one recipe over a trailing window. */
|
|
20
21
|
export function WorkspaceUsageChart(props: {
|
|
@@ -22,12 +23,6 @@ export function WorkspaceUsageChart(props: {
|
|
|
22
23
|
range: TimeRangeDays;
|
|
23
24
|
workspaceName: string;
|
|
24
25
|
}) {
|
|
25
|
-
const layout = createActivityChartLayout(200);
|
|
26
|
-
const step =
|
|
27
|
-
props.days.length > 0
|
|
28
|
-
? layout.plotWidth / props.days.length
|
|
29
|
-
: layout.plotWidth;
|
|
30
|
-
const barWidth = Math.max(2, Math.min(13, step * 0.68));
|
|
31
26
|
const values = props.days.map((day) => day.count);
|
|
32
27
|
const maximum = Math.max(1, ...values);
|
|
33
28
|
const average = activityChartAverage(values);
|
|
@@ -43,57 +38,42 @@ export function WorkspaceUsageChart(props: {
|
|
|
43
38
|
totalLabel={`last ${props.range} days`}
|
|
44
39
|
/>
|
|
45
40
|
<div className="relative px-2 py-3 sm:px-4 sm:py-4">
|
|
46
|
-
<
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
layout={layout}
|
|
41
|
+
<ChartContainer
|
|
42
|
+
className="aspect-auto h-50 w-full"
|
|
43
|
+
config={chartConfig}
|
|
50
44
|
>
|
|
51
|
-
<
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
})}
|
|
84
|
-
<ActivityChartAverageLine
|
|
85
|
-
average={average}
|
|
86
|
-
format={formatActivityChartAverage}
|
|
87
|
-
layout={layout}
|
|
88
|
-
maximum={maximum}
|
|
89
|
-
stroke={USAGE_COLOR}
|
|
90
|
-
/>
|
|
91
|
-
<ActivityChartDateLabels
|
|
92
|
-
dates={props.days.map((day) => day.date)}
|
|
93
|
-
layout={layout}
|
|
94
|
-
xPosition={(index) => layout.left + index * step + step / 2}
|
|
95
|
-
/>
|
|
96
|
-
</ChartSvg>
|
|
45
|
+
<BarChart
|
|
46
|
+
aria-label={`Usage for ${props.workspaceName} during the last ${props.range} days`}
|
|
47
|
+
data={props.days}
|
|
48
|
+
margin={activityChartMargin}
|
|
49
|
+
role="img"
|
|
50
|
+
>
|
|
51
|
+
<CartesianGrid strokeDasharray="3 5" vertical={false} />
|
|
52
|
+
<ActivityChartYAxis maximum={maximum} />
|
|
53
|
+
<ActivityChartXAxis keys={props.days.map((day) => day.date)} />
|
|
54
|
+
<ChartTooltip
|
|
55
|
+
content={
|
|
56
|
+
<ActivityChartTooltipContent
|
|
57
|
+
rows={(day: WorkspaceUsageDay) => [["usage", day.count]]}
|
|
58
|
+
/>
|
|
59
|
+
}
|
|
60
|
+
/>
|
|
61
|
+
<Bar
|
|
62
|
+
dataKey="count"
|
|
63
|
+
fill="var(--color-count)"
|
|
64
|
+
fillOpacity={0.82}
|
|
65
|
+
isAnimationActive={false}
|
|
66
|
+
maxBarSize={13}
|
|
67
|
+
minPointSize={(value) => (value ? 2 : 0)}
|
|
68
|
+
radius={2}
|
|
69
|
+
/>
|
|
70
|
+
<ActivityChartAverageLine
|
|
71
|
+
average={average}
|
|
72
|
+
format={formatActivityChartAverage}
|
|
73
|
+
stroke={USAGE_COLOR}
|
|
74
|
+
/>
|
|
75
|
+
</BarChart>
|
|
76
|
+
</ChartContainer>
|
|
97
77
|
{!hasUsage ? (
|
|
98
78
|
<div className="pointer-events-none absolute inset-0 grid place-items-center pt-12 font-sans text-xs text-dashboard-text-muted">
|
|
99
79
|
No usage in this period.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { NativeSelect } from "../ui/native-select";
|
|
2
2
|
|
|
3
3
|
export type DirectorySortOption = {
|
|
4
4
|
label: string;
|
|
@@ -17,7 +17,7 @@ export function DirectorySortSelect(props: {
|
|
|
17
17
|
<span className="flex h-full items-center border-r border-dashboard-border-subtle px-2 font-sans font-medium text-xs text-dashboard-text-muted">
|
|
18
18
|
Sort
|
|
19
19
|
</span>
|
|
20
|
-
<
|
|
20
|
+
<NativeSelect
|
|
21
21
|
aria-label={props.ariaLabel}
|
|
22
22
|
variant="sort"
|
|
23
23
|
value={props.value}
|
|
@@ -32,7 +32,7 @@ export function DirectorySortSelect(props: {
|
|
|
32
32
|
{option.label}
|
|
33
33
|
</option>
|
|
34
34
|
))}
|
|
35
|
-
</
|
|
35
|
+
</NativeSelect>
|
|
36
36
|
</label>
|
|
37
37
|
);
|
|
38
38
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ToggleGroup, ToggleGroupItem } from "../ui/toggle-group";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Fixed dashboard reporting window.
|
|
@@ -124,9 +124,7 @@ function sumHoursIntoSixHours<T extends { date: string }>(args: {
|
|
|
124
124
|
// Fill the trailing 28 six-hour buckets when enough hour rows exist.
|
|
125
125
|
if (args.hours.length >= 24) {
|
|
126
126
|
const lastHour = args.hours[args.hours.length - 1]?.date;
|
|
127
|
-
const endMs = lastHour
|
|
128
|
-
? Date.parse(`${lastHour}:00:00.000Z`)
|
|
129
|
-
: Date.now();
|
|
127
|
+
const endMs = lastHour ? Date.parse(`${lastHour}:00:00.000Z`) : Date.now();
|
|
130
128
|
const end = new Date(endMs);
|
|
131
129
|
end.setUTCMinutes(0, 0, 0);
|
|
132
130
|
end.setUTCHours(Math.floor(end.getUTCHours() / 6) * 6, 0, 0, 0);
|
|
@@ -155,21 +153,18 @@ export function TimeRangeSelector(props: {
|
|
|
155
153
|
value: TimeRangeDays;
|
|
156
154
|
}) {
|
|
157
155
|
return (
|
|
158
|
-
<
|
|
156
|
+
<ToggleGroup
|
|
159
157
|
aria-label="Reporting period"
|
|
160
|
-
|
|
161
|
-
|
|
158
|
+
onValueChange={([next]) => {
|
|
159
|
+
if (next) props.onChange(Number(next) as TimeRangeDays);
|
|
160
|
+
}}
|
|
161
|
+
value={[String(props.value)]}
|
|
162
162
|
>
|
|
163
163
|
{(props.options ?? DEFAULT_OPTIONS).map((days) => (
|
|
164
|
-
<
|
|
165
|
-
key={days}
|
|
166
|
-
onClick={() => props.onChange(days)}
|
|
167
|
-
pressed={props.value === days}
|
|
168
|
-
variant="segment"
|
|
169
|
-
>
|
|
164
|
+
<ToggleGroupItem key={days} value={String(days)}>
|
|
170
165
|
{timeRangeLabel(days)}
|
|
171
|
-
</
|
|
166
|
+
</ToggleGroupItem>
|
|
172
167
|
))}
|
|
173
|
-
</
|
|
168
|
+
</ToggleGroup>
|
|
174
169
|
);
|
|
175
170
|
}
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import { ArrowLeft, Menu, X } from "lucide-react";
|
|
2
|
-
import { useEffect, useRef, type ReactNode } from "react";
|
|
2
|
+
import { useEffect, useRef, type ReactNode, type RefObject } from "react";
|
|
3
3
|
import { Link, NavLink } from "react-router";
|
|
4
4
|
|
|
5
5
|
import { getDashboardAgentName } from "../../agentName";
|
|
6
|
-
import {
|
|
7
|
-
acquireBodyScrollLock,
|
|
8
|
-
releaseBodyScrollLock,
|
|
9
|
-
} from "../../bodyScrollLock";
|
|
10
6
|
import { JuniorLogo } from "../JuniorLogo";
|
|
7
|
+
import { Sheet, SheetClose, SheetContent } from "../ui/sheet";
|
|
8
|
+
import { useSheet } from "../ui/useSheet";
|
|
11
9
|
import {
|
|
12
10
|
MobileHeaderActionsSlot,
|
|
13
11
|
MobileHeaderLiveSlot,
|
|
@@ -22,15 +20,6 @@ export type DashboardHeaderNavItem = {
|
|
|
22
20
|
to: string;
|
|
23
21
|
};
|
|
24
22
|
|
|
25
|
-
const focusableSelector = [
|
|
26
|
-
"a[href]",
|
|
27
|
-
"button:not([disabled])",
|
|
28
|
-
"input:not([disabled]):not([type='hidden'])",
|
|
29
|
-
"select:not([disabled])",
|
|
30
|
-
"textarea:not([disabled])",
|
|
31
|
-
"[tabindex]:not([tabindex='-1'])",
|
|
32
|
-
].join(",");
|
|
33
|
-
|
|
34
23
|
/** Render the primary dashboard shell header and optional mobile nav sheet. */
|
|
35
24
|
export function DashboardHeader(props: {
|
|
36
25
|
/** Back target used on mobile conversation detail. */
|
|
@@ -50,9 +39,7 @@ export function DashboardHeader(props: {
|
|
|
50
39
|
version?: string;
|
|
51
40
|
workspaceActive: boolean;
|
|
52
41
|
}) {
|
|
53
|
-
const
|
|
54
|
-
const openButtonRef = useRef<HTMLButtonElement>(null);
|
|
55
|
-
const previousFocusRef = useRef<HTMLElement | undefined>(undefined);
|
|
42
|
+
const closeRef = useRef<HTMLButtonElement>(null);
|
|
56
43
|
const onOpenChangeRef = useRef(props.onMobileNavigationOpenChange);
|
|
57
44
|
onOpenChangeRef.current = props.onMobileNavigationOpenChange;
|
|
58
45
|
// Conversation chrome is path-driven. Undefined title keeps the loading
|
|
@@ -90,56 +77,7 @@ export function DashboardHeader(props: {
|
|
|
90
77
|
};
|
|
91
78
|
closeWhenDesktop();
|
|
92
79
|
mobile.addEventListener("change", closeWhenDesktop);
|
|
93
|
-
|
|
94
|
-
previousFocusRef.current =
|
|
95
|
-
document.activeElement instanceof HTMLElement
|
|
96
|
-
? document.activeElement
|
|
97
|
-
: (openButtonRef.current ?? undefined);
|
|
98
|
-
acquireBodyScrollLock();
|
|
99
|
-
const focusFrame = requestAnimationFrame(() => {
|
|
100
|
-
sheetRef.current
|
|
101
|
-
?.querySelector<HTMLElement>("[data-mobile-nav-close]")
|
|
102
|
-
?.focus();
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
const onKeyDown = (event: KeyboardEvent) => {
|
|
106
|
-
if (event.key !== "Tab") return;
|
|
107
|
-
const sheet = sheetRef.current;
|
|
108
|
-
const focusable = Array.from(
|
|
109
|
-
sheet?.querySelectorAll<HTMLElement>(focusableSelector) ?? [],
|
|
110
|
-
).filter(
|
|
111
|
-
(element) =>
|
|
112
|
-
element.tabIndex >= 0 && element.getClientRects().length > 0,
|
|
113
|
-
);
|
|
114
|
-
const first = focusable.at(0);
|
|
115
|
-
const last = focusable.at(-1);
|
|
116
|
-
if (!sheet || !first || !last) {
|
|
117
|
-
event.preventDefault();
|
|
118
|
-
return;
|
|
119
|
-
}
|
|
120
|
-
const active = document.activeElement;
|
|
121
|
-
if (event.shiftKey && (active === first || !sheet.contains(active))) {
|
|
122
|
-
event.preventDefault();
|
|
123
|
-
last.focus();
|
|
124
|
-
} else if (
|
|
125
|
-
!event.shiftKey &&
|
|
126
|
-
(active === last || !sheet.contains(active))
|
|
127
|
-
) {
|
|
128
|
-
event.preventDefault();
|
|
129
|
-
first.focus();
|
|
130
|
-
}
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
window.addEventListener("keydown", onKeyDown);
|
|
134
|
-
return () => {
|
|
135
|
-
cancelAnimationFrame(focusFrame);
|
|
136
|
-
mobile.removeEventListener("change", closeWhenDesktop);
|
|
137
|
-
window.removeEventListener("keydown", onKeyDown);
|
|
138
|
-
releaseBodyScrollLock();
|
|
139
|
-
const previousFocus = previousFocusRef.current;
|
|
140
|
-
previousFocusRef.current = undefined;
|
|
141
|
-
if (previousFocus?.isConnected) previousFocus.focus();
|
|
142
|
-
};
|
|
80
|
+
return () => mobile.removeEventListener("change", closeWhenDesktop);
|
|
143
81
|
}, [props.mobileNavigationOpen]);
|
|
144
82
|
|
|
145
83
|
return (
|
|
@@ -168,7 +106,6 @@ export function DashboardHeader(props: {
|
|
|
168
106
|
aria-label="Open navigation"
|
|
169
107
|
className="grid size-10 cursor-pointer place-items-center rounded-lg border-0 bg-transparent text-dashboard-text transition-colors hover:bg-dashboard-fill-hover focus-visible:outline focus-visible:outline-2 focus-visible:outline-dashboard-focus/70 md:hidden"
|
|
170
108
|
onClick={() => props.onMobileNavigationOpenChange(true)}
|
|
171
|
-
ref={openButtonRef}
|
|
172
109
|
tabIndex={props.mobileNavigationOpen ? -1 : undefined}
|
|
173
110
|
type="button"
|
|
174
111
|
>
|
|
@@ -225,22 +162,9 @@ export function DashboardHeader(props: {
|
|
|
225
162
|
) : null}
|
|
226
163
|
</div>
|
|
227
164
|
{props.mobileNavigationOpen ? (
|
|
228
|
-
<
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
className="fixed inset-0 z-50 flex flex-col bg-dashboard-bg-elevated md:hidden"
|
|
232
|
-
id="mobile-navigation"
|
|
233
|
-
onClick={(event) => {
|
|
234
|
-
// Close on route taps and sheet actions such as Log out.
|
|
235
|
-
if (
|
|
236
|
-
event.target instanceof Element &&
|
|
237
|
-
event.target.closest("a[href], [data-mobile-nav-dismiss]")
|
|
238
|
-
) {
|
|
239
|
-
onOpenChangeRef.current(false);
|
|
240
|
-
}
|
|
241
|
-
}}
|
|
242
|
-
ref={sheetRef}
|
|
243
|
-
role="dialog"
|
|
165
|
+
<MobileNavigationSheet
|
|
166
|
+
closeRef={closeRef}
|
|
167
|
+
onClose={() => onOpenChangeRef.current(false)}
|
|
244
168
|
>
|
|
245
169
|
<div className="flex items-center justify-between gap-3 border-b border-dashboard-border-subtle bg-dashboard-surface-raised px-3 py-2 pt-[max(0.5rem,env(safe-area-inset-top))]">
|
|
246
170
|
<Link
|
|
@@ -250,15 +174,13 @@ export function DashboardHeader(props: {
|
|
|
250
174
|
>
|
|
251
175
|
{getDashboardAgentName()}
|
|
252
176
|
</Link>
|
|
253
|
-
<
|
|
177
|
+
<SheetClose
|
|
254
178
|
aria-label="Close navigation"
|
|
255
179
|
className="grid size-10 cursor-pointer place-items-center rounded-lg border-0 bg-transparent text-dashboard-text transition-colors hover:bg-dashboard-fill-hover focus-visible:outline focus-visible:outline-2 focus-visible:outline-dashboard-focus/70"
|
|
256
|
-
|
|
257
|
-
onClick={() => props.onMobileNavigationOpenChange(false)}
|
|
258
|
-
type="button"
|
|
180
|
+
ref={closeRef}
|
|
259
181
|
>
|
|
260
182
|
<X aria-hidden="true" size={20} strokeWidth={2} />
|
|
261
|
-
</
|
|
183
|
+
</SheetClose>
|
|
262
184
|
</div>
|
|
263
185
|
<div className="flex min-h-0 flex-1 flex-col">
|
|
264
186
|
<div className="min-h-0 flex-1 overflow-y-auto overscroll-contain p-3">
|
|
@@ -307,8 +229,40 @@ export function DashboardHeader(props: {
|
|
|
307
229
|
</div>
|
|
308
230
|
) : null}
|
|
309
231
|
</div>
|
|
310
|
-
</
|
|
232
|
+
</MobileNavigationSheet>
|
|
311
233
|
) : null}
|
|
312
234
|
</header>
|
|
313
235
|
);
|
|
314
236
|
}
|
|
237
|
+
|
|
238
|
+
/** Show the mobile navigation in a full-screen shared sheet. */
|
|
239
|
+
function MobileNavigationSheet(props: {
|
|
240
|
+
children: ReactNode;
|
|
241
|
+
closeRef: RefObject<HTMLButtonElement | null>;
|
|
242
|
+
onClose(): void;
|
|
243
|
+
}) {
|
|
244
|
+
const sheet = useSheet(props.onClose, props.closeRef);
|
|
245
|
+
return (
|
|
246
|
+
<Sheet {...sheet.rootProps}>
|
|
247
|
+
<SheetContent
|
|
248
|
+
{...sheet.contentProps}
|
|
249
|
+
aria-label="Navigation"
|
|
250
|
+
className="inset-0 gap-0 bg-dashboard-bg-elevated outline-none data-[side=left]:w-full data-[side=left]:border-r-0 data-[side=left]:sm:max-w-none md:hidden"
|
|
251
|
+
id="mobile-navigation"
|
|
252
|
+
onClick={(event) => {
|
|
253
|
+
// Close on route taps and sheet actions such as Log out.
|
|
254
|
+
if (
|
|
255
|
+
event.target instanceof Element &&
|
|
256
|
+
event.target.closest("a[href], [data-mobile-nav-dismiss]")
|
|
257
|
+
) {
|
|
258
|
+
props.onClose();
|
|
259
|
+
}
|
|
260
|
+
}}
|
|
261
|
+
showCloseButton={false}
|
|
262
|
+
side="left"
|
|
263
|
+
>
|
|
264
|
+
{props.children}
|
|
265
|
+
</SheetContent>
|
|
266
|
+
</Sheet>
|
|
267
|
+
);
|
|
268
|
+
}
|
|
@@ -22,6 +22,7 @@ export type SecondaryNavigationItem = SegmentedNavItem;
|
|
|
22
22
|
*/
|
|
23
23
|
export function SecondaryNavigation(props: {
|
|
24
24
|
ariaLabel: string;
|
|
25
|
+
className?: string;
|
|
25
26
|
children: ReactNode;
|
|
26
27
|
items: SecondaryNavigationItem[];
|
|
27
28
|
}) {
|
|
@@ -34,7 +35,7 @@ export function SecondaryNavigation(props: {
|
|
|
34
35
|
);
|
|
35
36
|
|
|
36
37
|
return (
|
|
37
|
-
<div className="min-w-0">
|
|
38
|
+
<div className={cn("min-w-0", props.className)}>
|
|
38
39
|
<SecondaryNavigationPortal
|
|
39
40
|
desktop={
|
|
40
41
|
<div className={cn(dashboardContainerClass, "min-w-0 px-4 md:px-8")}>
|
|
@@ -6,13 +6,13 @@ import { cn } from "../../styles";
|
|
|
6
6
|
/**
|
|
7
7
|
* Own the dashboard visual-viewport shell.
|
|
8
8
|
*
|
|
9
|
-
* Only this component may pin
|
|
9
|
+
* Only this component may pin pages with separate scroll panes to visualViewport
|
|
10
10
|
* geometry and write the related CSS variables / body scroll lock.
|
|
11
11
|
*/
|
|
12
12
|
export function VisualViewportShell(props: {
|
|
13
13
|
children: ReactNode;
|
|
14
14
|
className?: string;
|
|
15
|
-
/** Pin
|
|
15
|
+
/** Pin pages with separate scroll panes to the mobile visual viewport. */
|
|
16
16
|
enabled: boolean;
|
|
17
17
|
style?: CSSProperties;
|
|
18
18
|
}) {
|