From b87171086cb35929b32f84af009f1ac8dd242363 Mon Sep 17 00:00:00 2001 From: Danielle Maywood Date: Sun, 22 Mar 2026 23:58:58 +0000 Subject: [PATCH] refactor(site): restructure agents routing and directory layout (#23408) --- .../pages/AgentsPage/AgentAnalyticsPage.tsx | 50 +++++++ .../AgentsPage/AgentAnalyticsPageView.tsx | 47 +++++++ site/src/pages/AgentsPage/AgentCreatePage.tsx | 95 +++++++++++++ .../pages/AgentsPage/AgentDetail.stories.tsx | 44 +----- site/src/pages/AgentsPage/AgentDetail.test.ts | 2 +- site/src/pages/AgentsPage/AgentDetail.tsx | 76 ++++++---- site/src/pages/AgentsPage/AgentEmbedPage.tsx | 17 +-- .../pages/AgentsPage/AgentSettingsPage.tsx | 23 +++ ....tsx => AgentSettingsPageView.stories.tsx} | 10 +- ...eContent.tsx => AgentSettingsPageView.tsx} | 16 +-- site/src/pages/AgentsPage/AgentsPage.test.ts | 5 +- site/src/pages/AgentsPage/AgentsPage.tsx | 97 ++----------- .../AgentsPage/AgentsPageView.stories.tsx | 106 +++++++++----- site/src/pages/AgentsPage/AgentsPageView.tsx | 131 ++---------------- .../pages/AgentsPage/AnalyticsPageContent.tsx | 66 --------- .../AgentChatInput.stories.tsx | 0 .../{ => components}/AgentChatInput.tsx | 2 +- .../AgentCreateForm.stories.tsx | 0 .../{ => components}/AgentCreateForm.tsx | 11 +- .../AgentDetail/ChatContext.test.tsx | 0 .../AgentDetail/ChatContext.ts | 2 +- .../ConversationTimeline.stories.tsx | 0 .../AgentDetail/ConversationTimeline.tsx | 2 +- .../AgentDetail/SmoothText.test.ts | 0 .../AgentDetail/SmoothText.ts | 0 .../AgentDetail/StreamingOutput.stories.tsx | 0 .../AgentDetail/TopBar.stories.tsx | 0 .../{ => components}/AgentDetail/TopBar.tsx | 4 +- .../AgentDetail/blockUtils.test.ts | 0 .../AgentDetail/blockUtils.ts | 0 .../AgentDetail/chatHelpers.test.ts | 0 .../AgentDetail/chatHelpers.ts | 0 .../AgentDetail/chatStore.test.ts | 0 .../AgentDetail/messageParsing.test.ts | 0 .../AgentDetail/messageParsing.ts | 0 .../AgentDetail/streamState.test.ts | 0 .../AgentDetail/streamState.ts | 0 .../AgentDetail/streamingJson.test.ts | 0 .../AgentDetail/streamingJson.ts | 0 .../{ => components}/AgentDetail/types.ts | 0 .../AgentDetail/useAgentChime.test.ts | 0 .../AgentDetail/useAgentChime.ts | 0 .../useWorkspaceCreationWatcher.test.tsx | 0 .../useWorkspaceCreationWatcher.ts | 0 .../{ => components}/AgentDetailContent.tsx | 4 +- .../AgentDetailView.stories.tsx | 0 .../{ => components}/AgentDetailView.tsx | 8 +- .../AgentsPage/components/AgentPageHeader.tsx | 44 ++++++ .../AgentsSkeletons.stories.tsx | 0 .../{ => components}/AgentsSkeletons.tsx | 0 .../AttachmentPreview.stories.tsx | 0 .../ChatCostSummaryView.stories.tsx | 0 .../{ => components}/ChatCostSummaryView.tsx | 0 .../ChatModelAdminPanel.stories.tsx | 0 .../ChatModelAdminPanel.tsx | 2 +- .../ChatModelAdminPanel/ModelConfigFields.tsx | 0 .../ChatModelAdminPanel/ModelForm.tsx | 0 .../ModelsSection.stories.tsx | 0 .../ChatModelAdminPanel/ModelsSection.tsx | 0 .../ChatModelAdminPanel/ProviderForm.tsx | 2 +- .../ChatModelAdminPanel/ProviderIcon.tsx | 2 +- .../ChatModelAdminPanel/ProvidersSection.tsx | 0 .../ChatModelAdminPanel/helpers.ts | 0 .../modelConfigFormLogic.test.ts | 0 .../modelConfigFormLogic.ts | 0 .../ChatModelAdminPanel/pricingFields.test.ts | 0 .../ChatModelAdminPanel/pricingFields.ts | 0 .../{ => components}/ChimeButton.stories.tsx | 0 .../{ => components}/ChimeButton.tsx | 0 .../DiffViewer}/CommentableDiffViewer.tsx | 8 +- .../DiffViewer}/DiffStats.stories.tsx | 2 +- .../{ => components/DiffViewer}/DiffStats.tsx | 0 .../DiffViewer}/DiffViewer.stories.tsx | 6 +- .../DiffViewer}/DiffViewer.tsx | 2 +- .../DiffViewer}/LocalDiffPanel.tsx | 6 +- .../DiffViewer}/RemoteDiffPanel.tsx | 12 +- .../DiffViewer}/extractDiffContent.test.ts | 2 +- .../{ => components}/EmbedContext.tsx | 0 .../GitPanel}/GitPanel.stories.tsx | 0 .../{ => components/GitPanel}/GitPanel.tsx | 14 +- .../GitPanel}/InlinePromptInput.stories.tsx | 2 +- .../ImageLightbox.stories.tsx | 0 .../{ => components}/ImageLightbox.tsx | 0 .../{ => components}/InsightsContent.tsx | 0 .../LimitsTab/DefaultLimitSection.tsx | 0 .../LimitsTab/GroupLimitsSection.stories.tsx | 0 .../LimitsTab/GroupLimitsSection.tsx | 0 .../{ => components}/LimitsTab/LimitsTab.tsx | 0 .../UserOverridesSection.stories.tsx | 0 .../LimitsTab/UserOverridesSection.tsx | 0 .../{ => components}/LimitsTab/index.ts | 0 .../LimitsTab/limitsFormLogic.test.ts | 0 .../LimitsTab/limitsFormLogic.ts | 0 .../MCPServerAdminPanel.stories.tsx | 0 .../{ => components}/MCPServerAdminPanel.tsx | 0 .../PRInsightsView.stories.tsx | 0 .../{ => components}/PRInsightsView.tsx | 2 +- .../QueuedMessagesList.stories.tsx | 0 .../QueuedMessagesList.test.ts | 0 .../{ => components}/QueuedMessagesList.tsx | 0 .../RightPanel}/DesktopPanel.stories.tsx | 0 .../RightPanel}/DesktopPanel.tsx | 2 +- .../RightPanel}/RightPanel.stories.tsx | 0 .../RightPanel}/RightPanel.tsx | 0 .../SectionHeader.stories.tsx | 0 .../{ => components}/SectionHeader.tsx | 0 .../Sidebar}/AgentsSidebar.stories.tsx | 0 .../Sidebar}/AgentsSidebar.test.tsx | 0 .../Sidebar}/AgentsSidebar.tsx | 6 +- .../Sidebar}/SidebarTabView.stories.tsx | 0 .../Sidebar}/SidebarTabView.tsx | 2 +- .../UsageIndicator.stories.tsx | 0 .../{ => components}/UsageIndicator.tsx | 0 .../{ => components}/WebPushButton.tsx | 0 .../AgentsPage/{ => hooks}/useAgentsPWA.ts | 0 .../{ => hooks}/useAgentsPageKeybindings.ts | 0 .../{ => hooks}/useDesktopConnection.test.ts | 0 .../{ => hooks}/useDesktopConnection.ts | 0 .../{ => hooks}/useFileAttachments.ts | 2 +- .../{ => hooks}/useGitWatcher.test.ts | 0 .../AgentsPage/{ => hooks}/useGitWatcher.ts | 0 .../{ => utils}/agentWorkspaceUtils.test.ts | 0 .../{ => utils}/agentWorkspaceUtils.ts | 0 .../AgentsPage/{ => utils}/diffColors.ts | 0 .../{ => utils}/diffCommentSelection.test.ts | 0 .../{ => utils}/diffCommentSelection.ts | 0 .../{ => utils}/modelOptions.test.ts | 0 .../AgentsPage/{ => utils}/modelOptions.ts | 35 +++++ .../{ => utils}/pullRequest.test.ts | 0 .../AgentsPage/{ => utils}/pullRequest.ts | 0 .../AgentsPage/{ => utils}/timeGroups.test.ts | 0 .../AgentsPage/{ => utils}/timeGroups.ts | 0 .../{ => utils}/usageLimitMessage.test.ts | 0 .../{ => utils}/usageLimitMessage.ts | 0 site/src/router.tsx | 18 ++- 135 files changed, 532 insertions(+), 457 deletions(-) create mode 100644 site/src/pages/AgentsPage/AgentAnalyticsPage.tsx create mode 100644 site/src/pages/AgentsPage/AgentAnalyticsPageView.tsx create mode 100644 site/src/pages/AgentsPage/AgentCreatePage.tsx create mode 100644 site/src/pages/AgentsPage/AgentSettingsPage.tsx rename site/src/pages/AgentsPage/{SettingsPageContent.stories.tsx => AgentSettingsPageView.stories.tsx} (98%) rename site/src/pages/AgentsPage/{SettingsPageContent.tsx => AgentSettingsPageView.tsx} (97%) delete mode 100644 site/src/pages/AgentsPage/AnalyticsPageContent.tsx rename site/src/pages/AgentsPage/{ => components}/AgentChatInput.stories.tsx (100%) rename site/src/pages/AgentsPage/{ => components}/AgentChatInput.tsx (99%) rename site/src/pages/AgentsPage/{ => components}/AgentCreateForm.stories.tsx (100%) rename site/src/pages/AgentsPage/{ => components}/AgentCreateForm.tsx (98%) rename site/src/pages/AgentsPage/{ => components}/AgentDetail/ChatContext.test.tsx (100%) rename site/src/pages/AgentsPage/{ => components}/AgentDetail/ChatContext.ts (99%) rename site/src/pages/AgentsPage/{ => components}/AgentDetail/ConversationTimeline.stories.tsx (100%) rename site/src/pages/AgentsPage/{ => components}/AgentDetail/ConversationTimeline.tsx (99%) rename site/src/pages/AgentsPage/{ => components}/AgentDetail/SmoothText.test.ts (100%) rename site/src/pages/AgentsPage/{ => components}/AgentDetail/SmoothText.ts (100%) rename site/src/pages/AgentsPage/{ => components}/AgentDetail/StreamingOutput.stories.tsx (100%) rename site/src/pages/AgentsPage/{ => components}/AgentDetail/TopBar.stories.tsx (100%) rename site/src/pages/AgentsPage/{ => components}/AgentDetail/TopBar.tsx (98%) rename site/src/pages/AgentsPage/{ => components}/AgentDetail/blockUtils.test.ts (100%) rename site/src/pages/AgentsPage/{ => components}/AgentDetail/blockUtils.ts (100%) rename site/src/pages/AgentsPage/{ => components}/AgentDetail/chatHelpers.test.ts (100%) rename site/src/pages/AgentsPage/{ => components}/AgentDetail/chatHelpers.ts (100%) rename site/src/pages/AgentsPage/{ => components}/AgentDetail/chatStore.test.ts (100%) rename site/src/pages/AgentsPage/{ => components}/AgentDetail/messageParsing.test.ts (100%) rename site/src/pages/AgentsPage/{ => components}/AgentDetail/messageParsing.ts (100%) rename site/src/pages/AgentsPage/{ => components}/AgentDetail/streamState.test.ts (100%) rename site/src/pages/AgentsPage/{ => components}/AgentDetail/streamState.ts (100%) rename site/src/pages/AgentsPage/{ => components}/AgentDetail/streamingJson.test.ts (100%) rename site/src/pages/AgentsPage/{ => components}/AgentDetail/streamingJson.ts (100%) rename site/src/pages/AgentsPage/{ => components}/AgentDetail/types.ts (100%) rename site/src/pages/AgentsPage/{ => components}/AgentDetail/useAgentChime.test.ts (100%) rename site/src/pages/AgentsPage/{ => components}/AgentDetail/useAgentChime.ts (100%) rename site/src/pages/AgentsPage/{ => components}/AgentDetail/useWorkspaceCreationWatcher.test.tsx (100%) rename site/src/pages/AgentsPage/{ => components}/AgentDetail/useWorkspaceCreationWatcher.ts (100%) rename site/src/pages/AgentsPage/{ => components}/AgentDetailContent.tsx (98%) rename site/src/pages/AgentsPage/{ => components}/AgentDetailView.stories.tsx (100%) rename site/src/pages/AgentsPage/{ => components}/AgentDetailView.tsx (98%) create mode 100644 site/src/pages/AgentsPage/components/AgentPageHeader.tsx rename site/src/pages/AgentsPage/{ => components}/AgentsSkeletons.stories.tsx (100%) rename site/src/pages/AgentsPage/{ => components}/AgentsSkeletons.tsx (100%) rename site/src/pages/AgentsPage/{ => components}/AttachmentPreview.stories.tsx (100%) rename site/src/pages/AgentsPage/{ => components}/ChatCostSummaryView.stories.tsx (100%) rename site/src/pages/AgentsPage/{ => components}/ChatCostSummaryView.tsx (100%) rename site/src/pages/AgentsPage/{ => components}/ChatModelAdminPanel/ChatModelAdminPanel.stories.tsx (100%) rename site/src/pages/AgentsPage/{ => components}/ChatModelAdminPanel/ChatModelAdminPanel.tsx (99%) rename site/src/pages/AgentsPage/{ => components}/ChatModelAdminPanel/ModelConfigFields.tsx (100%) rename site/src/pages/AgentsPage/{ => components}/ChatModelAdminPanel/ModelForm.tsx (100%) rename site/src/pages/AgentsPage/{ => components}/ChatModelAdminPanel/ModelsSection.stories.tsx (100%) rename site/src/pages/AgentsPage/{ => components}/ChatModelAdminPanel/ModelsSection.tsx (100%) rename site/src/pages/AgentsPage/{ => components}/ChatModelAdminPanel/ProviderForm.tsx (99%) rename site/src/pages/AgentsPage/{ => components}/ChatModelAdminPanel/ProviderIcon.tsx (95%) rename site/src/pages/AgentsPage/{ => components}/ChatModelAdminPanel/ProvidersSection.tsx (100%) rename site/src/pages/AgentsPage/{ => components}/ChatModelAdminPanel/helpers.ts (100%) rename site/src/pages/AgentsPage/{ => components}/ChatModelAdminPanel/modelConfigFormLogic.test.ts (100%) rename site/src/pages/AgentsPage/{ => components}/ChatModelAdminPanel/modelConfigFormLogic.ts (100%) rename site/src/pages/AgentsPage/{ => components}/ChatModelAdminPanel/pricingFields.test.ts (100%) rename site/src/pages/AgentsPage/{ => components}/ChatModelAdminPanel/pricingFields.ts (100%) rename site/src/pages/AgentsPage/{ => components}/ChimeButton.stories.tsx (100%) rename site/src/pages/AgentsPage/{ => components}/ChimeButton.tsx (100%) rename site/src/pages/AgentsPage/{ => components/DiffViewer}/CommentableDiffViewer.tsx (97%) rename site/src/pages/AgentsPage/{ => components/DiffViewer}/DiffStats.stories.tsx (93%) rename site/src/pages/AgentsPage/{ => components/DiffViewer}/DiffStats.tsx (100%) rename site/src/pages/AgentsPage/{ => components/DiffViewer}/DiffViewer.stories.tsx (98%) rename site/src/pages/AgentsPage/{ => components/DiffViewer}/DiffViewer.tsx (99%) rename site/src/pages/AgentsPage/{ => components/DiffViewer}/LocalDiffPanel.tsx (81%) rename site/src/pages/AgentsPage/{ => components/DiffViewer}/RemoteDiffPanel.tsx (94%) rename site/src/pages/AgentsPage/{ => components/DiffViewer}/extractDiffContent.test.ts (98%) rename site/src/pages/AgentsPage/{ => components}/EmbedContext.tsx (100%) rename site/src/pages/AgentsPage/{ => components/GitPanel}/GitPanel.stories.tsx (100%) rename site/src/pages/AgentsPage/{ => components/GitPanel}/GitPanel.tsx (97%) rename site/src/pages/AgentsPage/{ => components/GitPanel}/InlinePromptInput.stories.tsx (95%) rename site/src/pages/AgentsPage/{ => components}/ImageLightbox.stories.tsx (100%) rename site/src/pages/AgentsPage/{ => components}/ImageLightbox.tsx (100%) rename site/src/pages/AgentsPage/{ => components}/InsightsContent.tsx (100%) rename site/src/pages/AgentsPage/{ => components}/LimitsTab/DefaultLimitSection.tsx (100%) rename site/src/pages/AgentsPage/{ => components}/LimitsTab/GroupLimitsSection.stories.tsx (100%) rename site/src/pages/AgentsPage/{ => components}/LimitsTab/GroupLimitsSection.tsx (100%) rename site/src/pages/AgentsPage/{ => components}/LimitsTab/LimitsTab.tsx (100%) rename site/src/pages/AgentsPage/{ => components}/LimitsTab/UserOverridesSection.stories.tsx (100%) rename site/src/pages/AgentsPage/{ => components}/LimitsTab/UserOverridesSection.tsx (100%) rename site/src/pages/AgentsPage/{ => components}/LimitsTab/index.ts (100%) rename site/src/pages/AgentsPage/{ => components}/LimitsTab/limitsFormLogic.test.ts (100%) rename site/src/pages/AgentsPage/{ => components}/LimitsTab/limitsFormLogic.ts (100%) rename site/src/pages/AgentsPage/{ => components}/MCPServerAdminPanel.stories.tsx (100%) rename site/src/pages/AgentsPage/{ => components}/MCPServerAdminPanel.tsx (100%) rename site/src/pages/AgentsPage/{ => components}/PRInsightsView.stories.tsx (100%) rename site/src/pages/AgentsPage/{ => components}/PRInsightsView.tsx (99%) rename site/src/pages/AgentsPage/{ => components}/QueuedMessagesList.stories.tsx (100%) rename site/src/pages/AgentsPage/{ => components}/QueuedMessagesList.test.ts (100%) rename site/src/pages/AgentsPage/{ => components}/QueuedMessagesList.tsx (100%) rename site/src/pages/AgentsPage/{ => components/RightPanel}/DesktopPanel.stories.tsx (100%) rename site/src/pages/AgentsPage/{ => components/RightPanel}/DesktopPanel.tsx (96%) rename site/src/pages/AgentsPage/{ => components/RightPanel}/RightPanel.stories.tsx (100%) rename site/src/pages/AgentsPage/{ => components/RightPanel}/RightPanel.tsx (100%) rename site/src/pages/AgentsPage/{ => components}/SectionHeader.stories.tsx (100%) rename site/src/pages/AgentsPage/{ => components}/SectionHeader.tsx (100%) rename site/src/pages/AgentsPage/{ => components/Sidebar}/AgentsSidebar.stories.tsx (100%) rename site/src/pages/AgentsPage/{ => components/Sidebar}/AgentsSidebar.test.tsx (100%) rename site/src/pages/AgentsPage/{ => components/Sidebar}/AgentsSidebar.tsx (99%) rename site/src/pages/AgentsPage/{ => components/Sidebar}/SidebarTabView.stories.tsx (100%) rename site/src/pages/AgentsPage/{ => components/Sidebar}/SidebarTabView.tsx (99%) rename site/src/pages/AgentsPage/{ => components}/UsageIndicator.stories.tsx (100%) rename site/src/pages/AgentsPage/{ => components}/UsageIndicator.tsx (100%) rename site/src/pages/AgentsPage/{ => components}/WebPushButton.tsx (100%) rename site/src/pages/AgentsPage/{ => hooks}/useAgentsPWA.ts (100%) rename site/src/pages/AgentsPage/{ => hooks}/useAgentsPageKeybindings.ts (100%) rename site/src/pages/AgentsPage/{ => hooks}/useDesktopConnection.test.ts (100%) rename site/src/pages/AgentsPage/{ => hooks}/useDesktopConnection.ts (100%) rename site/src/pages/AgentsPage/{ => hooks}/useFileAttachments.ts (98%) rename site/src/pages/AgentsPage/{ => hooks}/useGitWatcher.test.ts (100%) rename site/src/pages/AgentsPage/{ => hooks}/useGitWatcher.ts (100%) rename site/src/pages/AgentsPage/{ => utils}/agentWorkspaceUtils.test.ts (100%) rename site/src/pages/AgentsPage/{ => utils}/agentWorkspaceUtils.ts (100%) rename site/src/pages/AgentsPage/{ => utils}/diffColors.ts (100%) rename site/src/pages/AgentsPage/{ => utils}/diffCommentSelection.test.ts (100%) rename site/src/pages/AgentsPage/{ => utils}/diffCommentSelection.ts (100%) rename site/src/pages/AgentsPage/{ => utils}/modelOptions.test.ts (100%) rename site/src/pages/AgentsPage/{ => utils}/modelOptions.ts (82%) rename site/src/pages/AgentsPage/{ => utils}/pullRequest.test.ts (100%) rename site/src/pages/AgentsPage/{ => utils}/pullRequest.ts (100%) rename site/src/pages/AgentsPage/{ => utils}/timeGroups.test.ts (100%) rename site/src/pages/AgentsPage/{ => utils}/timeGroups.ts (100%) rename site/src/pages/AgentsPage/{ => utils}/usageLimitMessage.test.ts (100%) rename site/src/pages/AgentsPage/{ => utils}/usageLimitMessage.ts (100%) diff --git a/site/src/pages/AgentsPage/AgentAnalyticsPage.tsx b/site/src/pages/AgentsPage/AgentAnalyticsPage.tsx new file mode 100644 index 0000000000..a7c1930c0a --- /dev/null +++ b/site/src/pages/AgentsPage/AgentAnalyticsPage.tsx @@ -0,0 +1,50 @@ +import { chatCostSummary } from "api/queries/chats"; +import { useAuthContext } from "contexts/auth/AuthProvider"; +import dayjs, { type Dayjs } from "dayjs"; +import type { FC } from "react"; +import { useQuery } from "react-query"; +import { AgentAnalyticsPageView } from "./AgentAnalyticsPageView"; +import { AgentPageHeader } from "./components/AgentPageHeader"; + +const createDateRange = (now?: Dayjs) => { + const end = now ?? dayjs(); + const start = end.subtract(30, "day"); + return { + startDate: start.toISOString(), + endDate: end.toISOString(), + rangeLabel: `${start.format("MMM D")} – ${end.format("MMM D, YYYY")}`, + }; +}; + +interface AgentAnalyticsPageProps { + /** Override the current time for deterministic storybook snapshots. */ + now?: Dayjs; +} + +const AgentAnalyticsPage: FC = ({ now }) => { + const { user } = useAuthContext(); + const dateRange = createDateRange(now); + + const summaryQuery = useQuery({ + ...chatCostSummary(user?.id ?? "me", { + start_date: dateRange.startDate, + end_date: dateRange.endDate, + }), + enabled: Boolean(user?.id), + }); + + return ( + <> + + void summaryQuery.refetch()} + rangeLabel={dateRange.rangeLabel} + /> + + ); +}; + +export default AgentAnalyticsPage; diff --git a/site/src/pages/AgentsPage/AgentAnalyticsPageView.tsx b/site/src/pages/AgentsPage/AgentAnalyticsPageView.tsx new file mode 100644 index 0000000000..b36022764c --- /dev/null +++ b/site/src/pages/AgentsPage/AgentAnalyticsPageView.tsx @@ -0,0 +1,47 @@ +import type { ChatCostSummary } from "api/typesGenerated"; +import { BarChart3Icon } from "lucide-react"; +import type { FC } from "react"; +import { ChatCostSummaryView } from "./components/ChatCostSummaryView"; +import { SectionHeader } from "./components/SectionHeader"; + +interface AgentAnalyticsPageViewProps { + summary: ChatCostSummary | undefined; + isLoading: boolean; + error: unknown; + onRetry: () => void; + rangeLabel: string; +} + +export const AgentAnalyticsPageView: FC = ({ + summary, + isLoading, + error, + onRetry, + rangeLabel, +}) => { + return ( +
+
+ + + {rangeLabel} +
+ } + /> + + +
+ + ); +}; diff --git a/site/src/pages/AgentsPage/AgentCreatePage.tsx b/site/src/pages/AgentsPage/AgentCreatePage.tsx new file mode 100644 index 0000000000..db409c00a0 --- /dev/null +++ b/site/src/pages/AgentsPage/AgentCreatePage.tsx @@ -0,0 +1,95 @@ +import { chatModelConfigs, chatModels, createChat } from "api/queries/chats"; +import type * as TypesGen from "api/typesGenerated"; +import type { FC } from "react"; +import { useMutation, useQuery, useQueryClient } from "react-query"; +import { useNavigate } from "react-router"; +import { + AgentCreateForm, + type CreateChatOptions, +} from "./components/AgentCreateForm"; +import { AgentPageHeader } from "./components/AgentPageHeader"; +import { ChimeButton } from "./components/ChimeButton"; +import { WebPushButton } from "./components/WebPushButton"; +import { + buildModelConfigIDByModelID, + getModelOptionsFromCatalog, +} from "./utils/modelOptions"; + +const lastModelConfigIDStorageKey = "agents.last-model-config-id"; +const nilUUID = "00000000-0000-0000-0000-000000000000"; + +const AgentCreatePage: FC = () => { + const queryClient = useQueryClient(); + const navigate = useNavigate(); + + const chatModelsQuery = useQuery(chatModels()); + const chatModelConfigsQuery = useQuery(chatModelConfigs()); + const createMutation = useMutation(createChat(queryClient)); + + const catalogModelOptions = getModelOptionsFromCatalog( + chatModelsQuery.data, + chatModelConfigsQuery.data, + ); + const modelConfigIDByModelID = buildModelConfigIDByModelID( + chatModelConfigsQuery.data, + ); + + const handleCreateChat = async ({ + message, + fileIDs, + workspaceId, + model, + }: CreateChatOptions) => { + const modelConfigID = + (model && modelConfigIDByModelID.get(model)) || nilUUID; + const content: TypesGen.ChatInputPart[] = []; + if (message.trim()) { + content.push({ type: "text", text: message }); + } + if (fileIDs) { + for (const fileID of fileIDs) { + content.push({ type: "file", file_id: fileID }); + } + } + const createdChat = await createMutation.mutateAsync({ + content, + workspace_id: workspaceId, + model_config_id: modelConfigID, + }); + + if (typeof window !== "undefined") { + if (modelConfigID !== nilUUID) { + localStorage.setItem(lastModelConfigIDStorageKey, modelConfigID); + } else { + localStorage.removeItem(lastModelConfigIDStorageKey); + } + } + + navigate(`/agents/${createdChat.id}`); + }; + + const handleOpenAnalytics = () => navigate("/agents/analytics"); + + return ( + <> + + + + + + + ); +}; + +export default AgentCreatePage; diff --git a/site/src/pages/AgentsPage/AgentDetail.stories.tsx b/site/src/pages/AgentsPage/AgentDetail.stories.tsx index 79aac229c1..963313f580 100644 --- a/site/src/pages/AgentsPage/AgentDetail.stories.tsx +++ b/site/src/pages/AgentsPage/AgentDetail.stories.tsx @@ -51,49 +51,7 @@ const AgentDetailLayout: FC = () => { requestUnarchiveAgent: () => {}, isSidebarCollapsed: false, onToggleSidebarCollapsed: () => {}, - modelOptions: [ - { - id: "openai:gpt-4o", - provider: "openai", - model: "gpt-4o", - displayName: "GPT-4o", - }, - ], - modelConfigIDByModelID: new Map([["openai:gpt-4o", "config-1"]]), - modelIDByConfigID: new Map([["config-1", "openai:gpt-4o"]]), - modelConfigs: [ - { - id: "config-1", - provider: "openai", - model: "gpt-4o", - display_name: "GPT-4o", - enabled: true, - is_default: false, - context_limit: 200000, - compression_threshold: 70, - created_at: "2026-01-01T00:00:00Z", - updated_at: "2026-01-01T00:00:00Z", - }, - ], - modelCatalog: { - providers: [ - { - provider: "openai", - available: true, - models: [ - { - id: "openai:gpt-4o", - provider: "openai", - model: "gpt-4o", - display_name: "GPT-4o", - }, - ], - }, - ], - }, - isModelCatalogLoading: false, - modelCatalogError: null, - desktopEnabled: false, + onExpandSidebar: () => {}, } satisfies AgentsOutletContext } /> diff --git a/site/src/pages/AgentsPage/AgentDetail.test.ts b/site/src/pages/AgentsPage/AgentDetail.test.ts index f4ca6d2d1d..85d9e4f3ac 100644 --- a/site/src/pages/AgentsPage/AgentDetail.test.ts +++ b/site/src/pages/AgentsPage/AgentDetail.test.ts @@ -1,11 +1,11 @@ import { act, renderHook } from "@testing-library/react"; import { createRef } from "react"; import { beforeEach, describe, expect, it, vi } from "vitest"; -import type { ChatMessageInputRef } from "./AgentChatInput"; import { draftInputStorageKeyPrefix, useConversationEditingState, } from "./AgentDetail"; +import type { ChatMessageInputRef } from "./components/AgentChatInput"; describe("useConversationEditingState", () => { const chatID = "chat-abc-123"; diff --git a/site/src/pages/AgentsPage/AgentDetail.tsx b/site/src/pages/AgentsPage/AgentDetail.tsx index 64f1c8f6a4..d850a5aff8 100644 --- a/site/src/pages/AgentsPage/AgentDetail.tsx +++ b/site/src/pages/AgentsPage/AgentDetail.tsx @@ -3,7 +3,10 @@ import { API, watchWorkspace } from "api/api"; import { isApiError } from "api/errors"; import { chat, + chatDesktopEnabled, chatMessagesForInfiniteScroll, + chatModelConfigs, + chatModels, createChatMessage, deleteChatQueuedMessage, editChatMessage, @@ -33,24 +36,33 @@ import type { UrlTransform } from "streamdown"; import { isMobileViewport } from "utils/mobile"; import { pageTitle } from "utils/page"; import { portForwardURL } from "utils/portForward"; -import type { ChatMessageInputRef } from "./AgentChatInput"; -import { useChatStore } from "./AgentDetail/ChatContext"; -import { getParentChatID, getWorkspaceAgent } from "./AgentDetail/chatHelpers"; -import { useWorkspaceCreationWatcher } from "./AgentDetail/useWorkspaceCreationWatcher"; +import type { AgentsOutletContext } from "./AgentsPage"; +import type { ChatMessageInputRef } from "./components/AgentChatInput"; +import { useChatStore } from "./components/AgentDetail/ChatContext"; +import { + getParentChatID, + getWorkspaceAgent, +} from "./components/AgentDetail/chatHelpers"; +import { useWorkspaceCreationWatcher } from "./components/AgentDetail/useWorkspaceCreationWatcher"; import { AgentDetailLoadingView, AgentDetailNotFoundView, AgentDetailView, -} from "./AgentDetailView"; -import type { AgentsOutletContext } from "./AgentsPage"; +} from "./components/AgentDetailView"; +import { useGitWatcher } from "./hooks/useGitWatcher"; import { + buildModelConfigIDByModelID, + buildModelIDByConfigID, getModelCatalogStatusMessage, + getModelOptionsFromCatalog, getModelSelectorPlaceholder, hasConfiguredModelsInCatalog, -} from "./modelOptions"; -import { parsePullRequestUrl } from "./pullRequest"; -import { formatUsageLimitMessage, isUsageLimitData } from "./usageLimitMessage"; -import { useGitWatcher } from "./useGitWatcher"; +} from "./utils/modelOptions"; +import { parsePullRequestUrl } from "./utils/pullRequest"; +import { + formatUsageLimitMessage, + isUsageLimitData, +} from "./utils/usageLimitMessage"; /** localStorage key controlling whether the right panel is visible. */ export const RIGHT_PANEL_OPEN_KEY = "agents.right-panel-open"; @@ -222,12 +234,6 @@ export function useConversationEditingState(deps: { const AgentDetail: FC = () => { const navigate = useNavigate(); const { agentId } = useParams<{ agentId: string }>(); - const outletContext = useOutletContext(); - const queryClient = useQueryClient(); - const [selectedModel, setSelectedModel] = useState(""); - const [pendingEditMessageId, setPendingEditMessageId] = useState< - number | null - >(null); const { chatErrorReasons, setChatErrorReason, @@ -235,18 +241,14 @@ const AgentDetail: FC = () => { requestArchiveAgent, requestArchiveAndDeleteWorkspace, requestUnarchiveAgent, - onOpenAnalytics, isSidebarCollapsed, onToggleSidebarCollapsed, - modelOptions, - modelConfigIDByModelID, - modelIDByConfigID, - modelConfigs, - modelCatalog, - isModelCatalogLoading, - modelCatalogError, - desktopEnabled, - } = outletContext; + } = useOutletContext(); + const queryClient = useQueryClient(); + const [selectedModel, setSelectedModel] = useState(""); + const [pendingEditMessageId, setPendingEditMessageId] = useState< + number | null + >(null); const scrollContainerRef = useRef(null); const chatInputRef = useRef(null); const inputValueRef = useRef( @@ -293,6 +295,26 @@ const AgentDetail: FC = () => { enabled: Boolean(workspaceId), }); + const chatModelsQuery = useQuery(chatModels()); + const chatModelConfigsQuery = useQuery(chatModelConfigs()); + const desktopEnabledQuery = useQuery(chatDesktopEnabled()); + const desktopEnabled = desktopEnabledQuery.data?.enable_desktop ?? false; + + const modelOptions = getModelOptionsFromCatalog( + chatModelsQuery.data, + chatModelConfigsQuery.data, + ); + const modelConfigIDByModelID = buildModelConfigIDByModelID( + chatModelConfigsQuery.data, + ); + const modelIDByConfigID = buildModelIDByConfigID(modelConfigIDByModelID); + const modelConfigs = chatModelConfigsQuery.data ?? []; + const modelCatalog = chatModelsQuery.data; + const isModelCatalogLoading = chatModelsQuery.isLoading; + const modelCatalogError = chatModelsQuery.error; + + const handleOpenAnalytics = () => navigate("/agents/analytics"); + // Subscribe to live workspace updates so that agent status changes // (e.g. connected/disconnected) are reflected without a page refresh. useEffect(() => { @@ -839,7 +861,7 @@ const AgentDetail: FC = () => { isInterruptPending={interruptMutation.isPending} isSidebarCollapsed={isSidebarCollapsed} onToggleSidebarCollapsed={onToggleSidebarCollapsed} - onOpenAnalytics={onOpenAnalytics} + onOpenAnalytics={handleOpenAnalytics} showSidebarPanel={showSidebarPanel} onSetShowSidebarPanel={handleSetShowSidebarPanel} prNumber={prNumber} diff --git a/site/src/pages/AgentsPage/AgentEmbedPage.tsx b/site/src/pages/AgentsPage/AgentEmbedPage.tsx index 7e173a9d5f..fa05ad10d5 100644 --- a/site/src/pages/AgentsPage/AgentEmbedPage.tsx +++ b/site/src/pages/AgentsPage/AgentEmbedPage.tsx @@ -9,8 +9,11 @@ import { type FC, useEffect, useRef, useState } from "react"; import { useMutation, useQueryClient } from "react-query"; import { Outlet, useParams } from "react-router"; import type { AgentsOutletContext } from "./AgentsPage"; -import { bootstrapChatEmbedSession, EmbedProvider } from "./EmbedContext"; -import type { ChatDetailError } from "./usageLimitMessage"; +import { + bootstrapChatEmbedSession, + EmbedProvider, +} from "./components/EmbedContext"; +import type { ChatDetailError } from "./utils/usageLimitMessage"; type BootstrapMessage = { type: "coder:vscode-auth-bootstrap"; @@ -121,16 +124,8 @@ const AgentEmbedPage: FC = () => { requestArchiveAndDeleteWorkspace, isSidebarCollapsed, onToggleSidebarCollapsed, - modelOptions: [], - modelConfigIDByModelID: new Map(), - modelIDByConfigID: new Map(), - modelConfigs: [], - modelCatalog: undefined, - isModelCatalogLoading: false, - modelCatalogError: null, - desktopEnabled: false, + onExpandSidebar: () => {}, }; - // When signed out and not already bootstrapping, listen for the // postMessage from the parent frame carrying the session token. const isAwaitingBootstrapMessage = diff --git a/site/src/pages/AgentsPage/AgentSettingsPage.tsx b/site/src/pages/AgentsPage/AgentSettingsPage.tsx new file mode 100644 index 0000000000..45fe93fde3 --- /dev/null +++ b/site/src/pages/AgentsPage/AgentSettingsPage.tsx @@ -0,0 +1,23 @@ +import { useAuthenticated } from "hooks"; +import type { FC } from "react"; +import { useParams } from "react-router"; +import { AgentSettingsPageView } from "./AgentSettingsPageView"; +import { AgentPageHeader } from "./components/AgentPageHeader"; + +const AgentSettingsPage: FC = () => { + const { section } = useParams(); + const { permissions } = useAuthenticated(); + const isAgentsAdmin = permissions.editDeploymentConfig; + return ( + <> + + + + ); +}; + +export default AgentSettingsPage; diff --git a/site/src/pages/AgentsPage/SettingsPageContent.stories.tsx b/site/src/pages/AgentsPage/AgentSettingsPageView.stories.tsx similarity index 98% rename from site/src/pages/AgentsPage/SettingsPageContent.stories.tsx rename to site/src/pages/AgentsPage/AgentSettingsPageView.stories.tsx index a09fd42c4a..497a02961d 100644 --- a/site/src/pages/AgentsPage/SettingsPageContent.stories.tsx +++ b/site/src/pages/AgentsPage/AgentSettingsPageView.stories.tsx @@ -6,7 +6,7 @@ import { userByNameKey } from "api/queries/users"; import type * as TypesGen from "api/typesGenerated"; import dayjs from "dayjs"; import { expect, spyOn, userEvent, waitFor, within } from "storybook/test"; -import { SettingsPageContent } from "./SettingsPageContent"; +import { AgentSettingsPageView } from "./AgentSettingsPageView"; // ── Usage mock helpers ───────────────────────────────────────── @@ -128,8 +128,8 @@ const fixedNow = dayjs("2026-03-12T00:00:00Z"); // ── Meta ─────────────────────────────────────────────────────── const meta = { - title: "pages/AgentsPage/SettingsPageContent", - component: SettingsPageContent, + title: "pages/AgentsPage/AgentSettingsPageView", + component: AgentSettingsPageView, decorators: [withAuthProvider, withDashboardProvider], args: { activeSection: "behavior", @@ -161,10 +161,10 @@ const meta = { }); spyOn(API, "updateChatWorkspaceTTL").mockResolvedValue(); }, -} satisfies Meta; +} satisfies Meta; export default meta; -type Story = StoryObj; +type Story = StoryObj; // ── Behavior tab stories ─────────────────────────────────────── diff --git a/site/src/pages/AgentsPage/SettingsPageContent.tsx b/site/src/pages/AgentsPage/AgentSettingsPageView.tsx similarity index 97% rename from site/src/pages/AgentsPage/SettingsPageContent.tsx rename to site/src/pages/AgentsPage/AgentSettingsPageView.tsx index f93c700634..126af58147 100644 --- a/site/src/pages/AgentsPage/SettingsPageContent.tsx +++ b/site/src/pages/AgentsPage/AgentSettingsPageView.tsx @@ -56,12 +56,12 @@ import { DateRange, type DateRangeValue, } from "../TemplatePage/TemplateInsightsPage/DateRange"; -import { ChatCostSummaryView } from "./ChatCostSummaryView"; -import { ChatModelAdminPanel } from "./ChatModelAdminPanel/ChatModelAdminPanel"; -import { InsightsContent } from "./InsightsContent"; -import { LimitsTab } from "./LimitsTab"; -import { MCPServerAdminPanel } from "./MCPServerAdminPanel"; -import { SectionHeader } from "./SectionHeader"; +import { ChatCostSummaryView } from "./components/ChatCostSummaryView"; +import { ChatModelAdminPanel } from "./components/ChatModelAdminPanel/ChatModelAdminPanel"; +import { InsightsContent } from "./components/InsightsContent"; +import { LimitsTab } from "./components/LimitsTab"; +import { MCPServerAdminPanel } from "./components/MCPServerAdminPanel"; +import { SectionHeader } from "./components/SectionHeader"; const AdminBadge: FC = () => ( @@ -487,7 +487,7 @@ const UsageContent: FC = ({ now }) => { const textareaClassName = "max-h-[240px] w-full resize-none overflow-y-auto rounded-lg border border-border bg-surface-primary px-4 py-3 font-sans text-[13px] leading-relaxed text-content-primary placeholder:text-content-secondary focus:outline-none focus:ring-2 focus:ring-content-link/30 [scrollbar-width:thin]"; -interface SettingsPageContentProps { +interface AgentSettingsPageViewProps { activeSection: string; canManageChatModelConfigs: boolean; canSetSystemPrompt: boolean; @@ -496,7 +496,7 @@ interface SettingsPageContentProps { now?: dayjs.Dayjs; } -export const SettingsPageContent: FC = ({ +export const AgentSettingsPageView: FC = ({ activeSection, canManageChatModelConfigs, canSetSystemPrompt, diff --git a/site/src/pages/AgentsPage/AgentsPage.test.ts b/site/src/pages/AgentsPage/AgentsPage.test.ts index 1759a12f32..33c4fc1d56 100644 --- a/site/src/pages/AgentsPage/AgentsPage.test.ts +++ b/site/src/pages/AgentsPage/AgentsPage.test.ts @@ -1,6 +1,9 @@ import { act, renderHook } from "@testing-library/react"; import { beforeEach, describe, expect, it } from "vitest"; -import { emptyInputStorageKey, useEmptyStateDraft } from "./AgentCreateForm"; +import { + emptyInputStorageKey, + useEmptyStateDraft, +} from "./components/AgentCreateForm"; describe("useEmptyStateDraft", () => { beforeEach(() => { diff --git a/site/src/pages/AgentsPage/AgentsPage.tsx b/site/src/pages/AgentsPage/AgentsPage.tsx index 03aee13a02..8a3be3c1a1 100644 --- a/site/src/pages/AgentsPage/AgentsPage.tsx +++ b/site/src/pages/AgentsPage/AgentsPage.tsx @@ -2,12 +2,10 @@ import { API, watchChats } from "api/api"; import { getErrorMessage } from "api/errors"; import { archiveChat, - chatDesktopEnabled, chatDiffContentsKey, chatKey, chatModelConfigs, chatModels, - createChat, infiniteChats, invalidateChatListQueries, prependToInfiniteChatsCache, @@ -30,24 +28,14 @@ import { import { useNavigate, useParams } from "react-router"; import { toast } from "sonner"; import { createReconnectingWebSocket } from "utils/reconnectingWebSocket"; -import { - type CreateChatOptions, - emptyInputStorageKey, -} from "./AgentCreateForm"; -import { maybePlayChime } from "./AgentDetail/useAgentChime"; import { AgentsPageView } from "./AgentsPageView"; -import { resolveArchiveAndDeleteAction } from "./agentWorkspaceUtils"; -import { - getModelOptionsFromCatalog, - getNormalizedModelRef, -} from "./modelOptions"; -import type { ChatDetailError } from "./usageLimitMessage"; -import { useAgentsPageKeybindings } from "./useAgentsPageKeybindings"; -import { useAgentsPWA } from "./useAgentsPWA"; - -const lastModelConfigIDStorageKey = "agents.last-model-config-id"; -const nilUUID = "00000000-0000-0000-0000-000000000000"; -const EMPTY_MODEL_CONFIGS: TypesGen.ChatModelConfig[] = []; +import { emptyInputStorageKey } from "./components/AgentCreateForm"; +import { maybePlayChime } from "./components/AgentDetail/useAgentChime"; +import { useAgentsPageKeybindings } from "./hooks/useAgentsPageKeybindings"; +import { useAgentsPWA } from "./hooks/useAgentsPWA"; +import { resolveArchiveAndDeleteAction } from "./utils/agentWorkspaceUtils"; +import { getModelOptionsFromCatalog } from "./utils/modelOptions"; +import type { ChatDetailError } from "./utils/usageLimitMessage"; // Type guard for SSE events from the chat list watch endpoint. // Shallow-compare two ChatDiffStatus objects by their meaningful @@ -93,11 +81,9 @@ const AgentsPage: FC = () => { const queryClient = useQueryClient(); const navigate = useNavigate(); const { agentId } = useParams(); - const { permissions, user } = useAuthenticated(); + const { permissions } = useAuthenticated(); const { appearance } = useDashboard(); - const isAgentsAdmin = - permissions.editDeploymentConfig || - user.roles.some((role) => role.name === "owner" || role.name === "admin"); + const isAgentsAdmin = permissions.editDeploymentConfig; const [archivedFilter, setArchivedFilter] = useState<"active" | "archived">( "active", @@ -149,10 +135,12 @@ const AgentsPage: FC = () => { const chatsQuery = useInfiniteQuery( infiniteChats({ archived: archivedFilter === "archived" }), ); + // Model queries are kept here for the sidebar, which displays + // model info alongside each chat. Child routes that need models + // subscribe to the same queries independently — react-query + // deduplicates the requests. const chatModelsQuery = useQuery(chatModels()); const chatModelConfigsQuery = useQuery(chatModelConfigs()); - const desktopEnabledQuery = useQuery(chatDesktopEnabled()); - const createMutation = useMutation(createChat(queryClient)); const archiveChatBase = archiveChat(queryClient); const archiveAgentMutation = useMutation({ ...archiveChatBase, @@ -208,24 +196,6 @@ const AgentsPage: FC = () => { chatModelsQuery.data, chatModelConfigsQuery.data, ); - const modelConfigIDByModelID = (() => { - const byModelID = new Map(); - for (const config of chatModelConfigsQuery.data ?? []) { - const { provider, model } = getNormalizedModelRef(config); - if (!provider || !model) { - continue; - } - const colonRef = `${provider}:${model}`; - if (!byModelID.has(colonRef)) { - byModelID.set(colonRef, config.id); - } - const slashRef = `${provider}/${model}`; - if (!byModelID.has(slashRef)) { - byModelID.set(slashRef, config.id); - } - } - return byModelID; - })(); const setChatErrorReason = (chatId: string, reason: ChatDetailError) => { const trimmedMessage = reason.message.trim(); if (!chatId || !trimmedMessage) { @@ -317,35 +287,6 @@ const AgentsPage: FC = () => { }; const handleToggleSidebarCollapsed = () => setIsSidebarCollapsed((prev) => !prev); - const handleCreateChat = async (options: CreateChatOptions) => { - const { message, fileIDs, workspaceId, model } = options; - const modelConfigID = - (model && modelConfigIDByModelID.get(model)) || nilUUID; - const content: TypesGen.ChatInputPart[] = []; - if (message.trim()) { - content.push({ type: "text", text: message }); - } - if (fileIDs) { - for (const fileID of fileIDs) { - content.push({ type: "file", file_id: fileID }); - } - } - const createdChat = await createMutation.mutateAsync({ - content, - workspace_id: workspaceId, - model_config_id: modelConfigID, - }); - - if (typeof window !== "undefined") { - if (modelConfigID !== nilUUID) { - localStorage.setItem(lastModelConfigIDStorageKey, modelConfigID); - } else { - localStorage.removeItem(lastModelConfigIDStorageKey); - } - } - - navigate(`/agents/${createdChat.id}`); - }; const handleNewAgent = () => { // Only clear the draft when the user is already on the empty @@ -559,10 +500,10 @@ const AgentsPage: FC = () => { agentId={agentId} chatList={chatList} catalogModelOptions={catalogModelOptions} - modelConfigs={chatModelConfigsQuery.data ?? EMPTY_MODEL_CONFIGS} + modelConfigs={chatModelConfigsQuery.data ?? []} logoUrl={appearance.logo_url} handleNewAgent={handleNewAgent} - isCreating={createMutation.isPending} + isCreating={false} isArchiving={isArchiving} archivingChatId={archivingChatId} isChatsLoading={chatsQuery.isLoading} @@ -578,14 +519,6 @@ const AgentsPage: FC = () => { requestUnarchiveAgent={requestUnarchiveAgent} requestArchiveAndDeleteWorkspace={requestArchiveAndDeleteWorkspace} onToggleSidebarCollapsed={handleToggleSidebarCollapsed} - onCreateChat={handleCreateChat} - createError={createMutation.error} - modelCatalog={chatModelsQuery.data} - isModelCatalogLoading={chatModelsQuery.isLoading} - isModelConfigsLoading={chatModelConfigsQuery.isLoading} - modelCatalogError={chatModelsQuery.error} - modelConfigIDByModelID={modelConfigIDByModelID} - desktopEnabled={desktopEnabledQuery.data?.enable_desktop ?? false} isAgentsAdmin={isAgentsAdmin} hasNextPage={chatsQuery.hasNextPage} onLoadMore={() => void chatsQuery.fetchNextPage()} diff --git a/site/src/pages/AgentsPage/AgentsPageView.stories.tsx b/site/src/pages/AgentsPage/AgentsPageView.stories.tsx index d8e95bd590..6dec1fe1aa 100644 --- a/site/src/pages/AgentsPage/AgentsPageView.stories.tsx +++ b/site/src/pages/AgentsPage/AgentsPageView.stories.tsx @@ -1,4 +1,8 @@ -import { MockUserOwner } from "testHelpers/entities"; +import { + MockNoPermissions, + MockPermissions, + MockUserOwner, +} from "testHelpers/entities"; import { withAuthProvider, withDashboardProvider } from "testHelpers/storybook"; import type { Meta, StoryObj } from "@storybook/react-vite"; import { API } from "api/api"; @@ -18,6 +22,9 @@ import { within, } from "storybook/test"; import { reactRouterParameters } from "storybook-addon-remix-react-router"; +import AgentAnalyticsPage from "./AgentAnalyticsPage"; +import AgentCreatePage from "./AgentCreatePage"; +import AgentSettingsPage from "./AgentSettingsPage"; import { AgentsPageView } from "./AgentsPageView"; const defaultModelOptions: ModelSelectorOption[] = [ @@ -103,9 +110,6 @@ const mockUsageUsers: TypesGen.ChatCostUsersResponse = { ], }; -// Use local noon so the rendered range label stays stable across timezones. -const fixedAnalyticsNow = dayjs("2026-03-12T12:00:00"); - const oneWeekAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString(); const todayTimestamp = new Date().toISOString(); @@ -123,16 +127,21 @@ const buildChat = (overrides: Partial = {}): Chat => ({ ...overrides, }); -const agentsRouting = [ - { path: "/agents/settings/:section", useStoryElement: true }, - { path: "/agents/settings", useStoryElement: true }, - { path: "/agents/analytics", useStoryElement: true }, - { path: "/agents/:agentId", useStoryElement: true }, - { path: "/agents", useStoryElement: true }, -] satisfies [ - { path: string; useStoryElement: boolean }, - ...{ path: string; useStoryElement: boolean }[], -]; +// Use local noon so the rendered range label stays stable +// across timezones. +const fixedNow = dayjs("2026-03-12T12:00:00"); + +const agentsRouting = { + path: "/agents", + useStoryElement: true, + children: [ + { path: "settings", element: }, + { path: "settings/:section", element: }, + { path: "analytics", element: }, + { path: ":agentId", element:
}, + { index: true, element: }, + ], +}; const meta: Meta = { title: "pages/AgentsPage/AgentsPageView", @@ -141,6 +150,7 @@ const meta: Meta = { parameters: { layout: "fullscreen", user: MockUserOwner, + permissions: MockPermissions, reactRouter: reactRouterParameters({ location: { path: "/agents" }, routing: agentsRouting, @@ -170,35 +180,11 @@ const meta: Meta = { requestArchiveAndDeleteWorkspace: fn(), onToggleSidebarCollapsed: fn(), isAgentsAdmin: false, - analyticsNow: fixedAnalyticsNow, archivedFilter: "active" as const, onArchivedFilterChange: fn(), hasNextPage: false, onLoadMore: fn(), isFetchingNextPage: false, - onCreateChat: fn(), - createError: undefined, - modelCatalog: { - providers: [ - { - provider: "openai", - available: true, - models: [ - { - id: "openai:gpt-4o", - provider: "openai", - model: "gpt-4o", - display_name: "GPT-4o", - }, - ], - }, - ], - }, - isModelCatalogLoading: false, - isModelConfigsLoading: false, - modelCatalogError: undefined, - modelConfigIDByModelID: new Map(), - desktopEnabled: false, }, beforeEach: () => { spyOn(API, "getWorkspaces").mockResolvedValue({ @@ -217,6 +203,44 @@ const meta: Meta = { spyOn(API, "updateUserChatCustomPrompt").mockResolvedValue({ custom_prompt: "", }); + // Mocks for child route pages that fetch their own data. + spyOn(API, "getChatModels").mockResolvedValue({ + providers: [ + { + provider: "openai", + available: true, + models: [ + { + id: "openai:gpt-4o", + provider: "openai", + model: "gpt-4o", + display_name: "GPT-4o", + }, + ], + }, + ], + }); + spyOn(API, "getChatModelConfigs").mockResolvedValue([ + { + id: "config-openai-gpt-4o", + provider: "openai", + model: "gpt-4o", + display_name: "GPT-4o", + enabled: true, + is_default: false, + context_limit: 200000, + compression_threshold: 70, + created_at: "2026-02-18T00:00:00.000Z", + updated_at: "2026-02-18T00:00:00.000Z", + }, + ]); + spyOn(API, "getChatDesktopEnabled").mockResolvedValue({ + enable_desktop: false, + }); + spyOn(API, "getChatWorkspaceTTL").mockResolvedValue({ + workspace_ttl_ms: 0, + }); + spyOn(API, "updateChatWorkspaceTTL").mockResolvedValue(); }, }; @@ -489,6 +513,9 @@ export const OpensAnalyticsForNonAdmins: Story = { args: { isAgentsAdmin: false, }, + parameters: { + permissions: MockNoPermissions, + }, play: async ({ canvasElement }) => { await openAnalyticsView(canvasElement); @@ -523,6 +550,9 @@ export const OpensSettingsForNonAdmins: Story = { args: { isAgentsAdmin: false, }, + parameters: { + permissions: MockNoPermissions, + }, play: async ({ canvasElement }) => { await openSettingsView(canvasElement); diff --git a/site/src/pages/AgentsPage/AgentsPageView.tsx b/site/src/pages/AgentsPage/AgentsPageView.tsx index 9261213a19..1d4cc003f9 100644 --- a/site/src/pages/AgentsPage/AgentsPageView.tsx +++ b/site/src/pages/AgentsPage/AgentsPageView.tsx @@ -1,23 +1,14 @@ import type * as TypesGen from "api/typesGenerated"; import type { ModelSelectorOption } from "components/ai-elements"; -import { Button } from "components/Button/Button"; -import { ExternalImage } from "components/ExternalImage/ExternalImage"; -import { CoderIcon } from "components/Icons/CoderIcon"; -import type { Dayjs } from "dayjs"; -import { PanelLeftIcon } from "lucide-react"; import type { FC } from "react"; -import { NavLink, Outlet, useLocation, useNavigate } from "react-router"; +import { Outlet, useLocation } from "react-router"; import { cn } from "utils/cn"; import { pageTitle } from "utils/page"; -import { AgentCreateForm, type CreateChatOptions } from "./AgentCreateForm"; -import { AgentsSidebar, sidebarViewFromPath } from "./AgentsSidebar"; -import { AnalyticsPageContent } from "./AnalyticsPageContent"; -import { ChimeButton } from "./ChimeButton"; -import { SettingsPageContent } from "./SettingsPageContent"; -import type { ChatDetailError } from "./usageLimitMessage"; -import { WebPushButton } from "./WebPushButton"; - -type ChatModelOption = ModelSelectorOption; +import { + AgentsSidebar, + sidebarViewFromPath, +} from "./components/Sidebar/AgentsSidebar"; +import type { ChatDetailError } from "./utils/usageLimitMessage"; export interface AgentsOutletContext { chatErrorReasons: Record; @@ -31,21 +22,13 @@ export interface AgentsOutletContext { ) => void; isSidebarCollapsed: boolean; onToggleSidebarCollapsed: () => void; - onOpenAnalytics?: () => void; - modelOptions: readonly ModelSelectorOption[]; - modelConfigIDByModelID: ReadonlyMap; - modelIDByConfigID: ReadonlyMap; - modelConfigs: readonly TypesGen.ChatModelConfig[]; - modelCatalog: TypesGen.ChatModelsResponse | null | undefined; - isModelCatalogLoading: boolean; - modelCatalogError: unknown; - desktopEnabled: boolean; + onExpandSidebar: () => void; } interface AgentsPageViewProps { agentId: string | undefined; chatList: TypesGen.Chat[]; - catalogModelOptions: readonly ChatModelOption[]; + catalogModelOptions: readonly ModelSelectorOption[]; modelConfigs: readonly TypesGen.ChatModelConfig[]; logoUrl: string; handleNewAgent: () => void; @@ -69,20 +52,11 @@ interface AgentsPageViewProps { ) => void; onToggleSidebarCollapsed: () => void; isAgentsAdmin: boolean; - onCreateChat: (options: CreateChatOptions) => Promise; - createError: unknown; - modelCatalog: TypesGen.ChatModelsResponse | null | undefined; - isModelCatalogLoading: boolean; - isModelConfigsLoading: boolean; - modelCatalogError: unknown; - modelConfigIDByModelID: ReadonlyMap; - desktopEnabled: boolean; hasNextPage: boolean | undefined; onLoadMore: () => void; isFetchingNextPage: boolean; archivedFilter: "active" | "archived"; onArchivedFilterChange: (filter: "active" | "archived") => void; - analyticsNow?: Dayjs; } export const AgentsPageView: FC = ({ @@ -109,29 +83,15 @@ export const AgentsPageView: FC = ({ requestArchiveAndDeleteWorkspace, onToggleSidebarCollapsed, isAgentsAdmin, - onCreateChat, - createError, - modelCatalog, - isModelCatalogLoading, - isModelConfigsLoading, - modelCatalogError, - modelConfigIDByModelID, - desktopEnabled, hasNextPage, onLoadMore, isFetchingNextPage, archivedFilter, onArchivedFilterChange, - analyticsNow, }) => { const location = useLocation(); - const navigate = useNavigate(); const sidebarView = sidebarViewFromPath(location.pathname); - const handleOpenAnalytics = () => { - navigate("/agents/analytics"); - }; - // The sidebar expects plain string error messages, but the outlet // context now carries structured ChatDetailError objects. const sidebarChatErrorReasons = Object.fromEntries( @@ -141,16 +101,6 @@ export const AgentsPageView: FC = ({ ]), ); - const modelIDByConfigID = (() => { - const byConfigID = new Map(); - for (const [modelID, configID] of modelConfigIDByModelID.entries()) { - if (!byConfigID.has(configID)) { - byConfigID.set(configID, modelID); - } - } - return byConfigID; - })(); - const outletContextValue: AgentsOutletContext = { chatErrorReasons, setChatErrorReason, @@ -160,15 +110,7 @@ export const AgentsPageView: FC = ({ requestArchiveAndDeleteWorkspace, isSidebarCollapsed, onToggleSidebarCollapsed, - onOpenAnalytics: handleOpenAnalytics, - modelOptions: catalogModelOptions, - modelConfigIDByModelID, - modelIDByConfigID, - modelConfigs, - modelCatalog, - isModelCatalogLoading, - modelCatalogError, - desktopEnabled, + onExpandSidebar, }; return ( @@ -217,60 +159,7 @@ export const AgentsPageView: FC = ({ "order-1 md:order-none flex-none md:flex-1", )} > - {sidebarView.panel === "settings" ? ( - - ) : sidebarView.panel === "analytics" ? ( - - ) : agentId ? ( - - ) : ( - <> -
- - {logoUrl ? ( - - ) : ( - - )} - - {isSidebarCollapsed && ( - - )} -
-
- - -
-
- - - )} +
); diff --git a/site/src/pages/AgentsPage/AnalyticsPageContent.tsx b/site/src/pages/AgentsPage/AnalyticsPageContent.tsx deleted file mode 100644 index 8c6e98ade0..0000000000 --- a/site/src/pages/AgentsPage/AnalyticsPageContent.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import { chatCostSummary } from "api/queries/chats"; -import { useAuthContext } from "contexts/auth/AuthProvider"; -import dayjs from "dayjs"; -import { BarChart3Icon } from "lucide-react"; -import type { FC } from "react"; -import { useQuery } from "react-query"; -import { ChatCostSummaryView } from "./ChatCostSummaryView"; -import { SectionHeader } from "./SectionHeader"; - -const createDateRange = (now?: dayjs.Dayjs) => { - const end = now ?? dayjs(); - const start = end.subtract(30, "day"); - return { - startDate: start.toISOString(), - endDate: end.toISOString(), - rangeLabel: `${start.format("MMM D")} – ${end.format("MMM D, YYYY")}`, - }; -}; - -interface AnalyticsPageContentProps { - /** Override the current time for date range calculation. - * Used for deterministic Storybook snapshots. - */ - now?: dayjs.Dayjs; -} - -export const AnalyticsPageContent: FC = ({ - now, -}) => { - const { user } = useAuthContext(); - const dateRange = createDateRange(now); - - const summaryQuery = useQuery({ - ...chatCostSummary(user?.id ?? "me", { - start_date: dateRange.startDate, - end_date: dateRange.endDate, - }), - enabled: Boolean(user?.id), - }); - - return ( -
-
- - - {dateRange.rangeLabel} -
- } - /> - - void summaryQuery.refetch()} - loadingLabel="Loading analytics" - emptyMessage="No usage data for you in this period." - /> -
- - ); -}; diff --git a/site/src/pages/AgentsPage/AgentChatInput.stories.tsx b/site/src/pages/AgentsPage/components/AgentChatInput.stories.tsx similarity index 100% rename from site/src/pages/AgentsPage/AgentChatInput.stories.tsx rename to site/src/pages/AgentsPage/components/AgentChatInput.stories.tsx diff --git a/site/src/pages/AgentsPage/AgentChatInput.tsx b/site/src/pages/AgentsPage/components/AgentChatInput.tsx similarity index 99% rename from site/src/pages/AgentsPage/AgentChatInput.tsx rename to site/src/pages/AgentsPage/components/AgentChatInput.tsx index 10b1092572..a4320a1c9a 100644 --- a/site/src/pages/AgentsPage/AgentChatInput.tsx +++ b/site/src/pages/AgentsPage/components/AgentChatInput.tsx @@ -36,8 +36,8 @@ import { } from "react"; import { cn } from "utils/cn"; import { isMobileViewport } from "utils/mobile"; +import { formatProviderLabel } from "../utils/modelOptions"; import { ImageLightbox } from "./ImageLightbox"; -import { formatProviderLabel } from "./modelOptions"; import { QueuedMessagesList } from "./QueuedMessagesList"; export type { ChatMessageInputRef } from "components/ChatMessageInput/ChatMessageInput"; diff --git a/site/src/pages/AgentsPage/AgentCreateForm.stories.tsx b/site/src/pages/AgentsPage/components/AgentCreateForm.stories.tsx similarity index 100% rename from site/src/pages/AgentsPage/AgentCreateForm.stories.tsx rename to site/src/pages/AgentsPage/components/AgentCreateForm.stories.tsx diff --git a/site/src/pages/AgentsPage/AgentCreateForm.tsx b/site/src/pages/AgentsPage/components/AgentCreateForm.tsx similarity index 98% rename from site/src/pages/AgentsPage/AgentCreateForm.tsx rename to site/src/pages/AgentsPage/components/AgentCreateForm.tsx index f9538497b2..65286febcc 100644 --- a/site/src/pages/AgentsPage/AgentCreateForm.tsx +++ b/site/src/pages/AgentsPage/components/AgentCreateForm.tsx @@ -24,15 +24,18 @@ import { useDashboard } from "modules/dashboard/useDashboard"; import { type FC, useEffect, useRef, useState } from "react"; import { useQuery } from "react-query"; import { toast } from "sonner"; -import { AgentChatInput } from "./AgentChatInput"; +import { useFileAttachments } from "../hooks/useFileAttachments"; import { getModelCatalogStatusMessage, getModelSelectorPlaceholder, getNormalizedModelRef, hasConfiguredModelsInCatalog, -} from "./modelOptions"; -import { formatUsageLimitMessage, isUsageLimitData } from "./usageLimitMessage"; -import { useFileAttachments } from "./useFileAttachments"; +} from "../utils/modelOptions"; +import { + formatUsageLimitMessage, + isUsageLimitData, +} from "../utils/usageLimitMessage"; +import { AgentChatInput } from "./AgentChatInput"; /** @internal Exported for testing. */ export const emptyInputStorageKey = "agents.empty-input"; diff --git a/site/src/pages/AgentsPage/AgentDetail/ChatContext.test.tsx b/site/src/pages/AgentsPage/components/AgentDetail/ChatContext.test.tsx similarity index 100% rename from site/src/pages/AgentsPage/AgentDetail/ChatContext.test.tsx rename to site/src/pages/AgentsPage/components/AgentDetail/ChatContext.test.tsx diff --git a/site/src/pages/AgentsPage/AgentDetail/ChatContext.ts b/site/src/pages/AgentsPage/components/AgentDetail/ChatContext.ts similarity index 99% rename from site/src/pages/AgentsPage/AgentDetail/ChatContext.ts rename to site/src/pages/AgentsPage/components/AgentDetail/ChatContext.ts index 18e318740d..2e9e586bad 100644 --- a/site/src/pages/AgentsPage/AgentDetail/ChatContext.ts +++ b/site/src/pages/AgentsPage/components/AgentDetail/ChatContext.ts @@ -6,7 +6,7 @@ import { useEffect, useRef, useState, useSyncExternalStore } from "react"; import { type InfiniteData, useQueryClient } from "react-query"; import type { OneWayMessageEvent } from "utils/OneWayWebSocket"; import { createReconnectingWebSocket } from "utils/reconnectingWebSocket"; -import type { ChatDetailError } from "../usageLimitMessage"; +import type { ChatDetailError } from "../../utils/usageLimitMessage"; import { applyMessagePartToStreamState } from "./streamState"; import type { StreamState } from "./types"; diff --git a/site/src/pages/AgentsPage/AgentDetail/ConversationTimeline.stories.tsx b/site/src/pages/AgentsPage/components/AgentDetail/ConversationTimeline.stories.tsx similarity index 100% rename from site/src/pages/AgentsPage/AgentDetail/ConversationTimeline.stories.tsx rename to site/src/pages/AgentsPage/components/AgentDetail/ConversationTimeline.stories.tsx diff --git a/site/src/pages/AgentsPage/AgentDetail/ConversationTimeline.tsx b/site/src/pages/AgentsPage/components/AgentDetail/ConversationTimeline.tsx similarity index 99% rename from site/src/pages/AgentsPage/AgentDetail/ConversationTimeline.tsx rename to site/src/pages/AgentsPage/components/AgentDetail/ConversationTimeline.tsx index 0c858d1217..57efa423fd 100644 --- a/site/src/pages/AgentsPage/AgentDetail/ConversationTimeline.tsx +++ b/site/src/pages/AgentsPage/components/AgentDetail/ConversationTimeline.tsx @@ -29,9 +29,9 @@ import { } from "react"; import type { UrlTransform } from "streamdown"; import { cn } from "utils/cn"; +import type { ChatDetailError } from "../../utils/usageLimitMessage"; import { ImageThumbnail } from "../AgentChatInput"; import { ImageLightbox } from "../ImageLightbox"; -import type { ChatDetailError } from "../usageLimitMessage"; import { useSmoothStreamingText } from "./SmoothText"; import type { MergedTool, diff --git a/site/src/pages/AgentsPage/AgentDetail/SmoothText.test.ts b/site/src/pages/AgentsPage/components/AgentDetail/SmoothText.test.ts similarity index 100% rename from site/src/pages/AgentsPage/AgentDetail/SmoothText.test.ts rename to site/src/pages/AgentsPage/components/AgentDetail/SmoothText.test.ts diff --git a/site/src/pages/AgentsPage/AgentDetail/SmoothText.ts b/site/src/pages/AgentsPage/components/AgentDetail/SmoothText.ts similarity index 100% rename from site/src/pages/AgentsPage/AgentDetail/SmoothText.ts rename to site/src/pages/AgentsPage/components/AgentDetail/SmoothText.ts diff --git a/site/src/pages/AgentsPage/AgentDetail/StreamingOutput.stories.tsx b/site/src/pages/AgentsPage/components/AgentDetail/StreamingOutput.stories.tsx similarity index 100% rename from site/src/pages/AgentsPage/AgentDetail/StreamingOutput.stories.tsx rename to site/src/pages/AgentsPage/components/AgentDetail/StreamingOutput.stories.tsx diff --git a/site/src/pages/AgentsPage/AgentDetail/TopBar.stories.tsx b/site/src/pages/AgentsPage/components/AgentDetail/TopBar.stories.tsx similarity index 100% rename from site/src/pages/AgentsPage/AgentDetail/TopBar.stories.tsx rename to site/src/pages/AgentsPage/components/AgentDetail/TopBar.stories.tsx diff --git a/site/src/pages/AgentsPage/AgentDetail/TopBar.tsx b/site/src/pages/AgentsPage/components/AgentDetail/TopBar.tsx similarity index 98% rename from site/src/pages/AgentsPage/AgentDetail/TopBar.tsx rename to site/src/pages/AgentsPage/components/AgentDetail/TopBar.tsx index 3581a4d62c..2fc0a07548 100644 --- a/site/src/pages/AgentsPage/AgentDetail/TopBar.tsx +++ b/site/src/pages/AgentsPage/components/AgentDetail/TopBar.tsx @@ -27,9 +27,9 @@ import type { FC } from "react"; import { useNavigate } from "react-router"; import { toast } from "sonner"; import { cn } from "utils/cn"; +import { parsePullRequestUrl } from "../../utils/pullRequest"; import { useEmbedContext } from "../EmbedContext"; -import { PrStateIcon } from "../GitPanel"; -import { parsePullRequestUrl } from "../pullRequest"; +import { PrStateIcon } from "../GitPanel/GitPanel"; interface SidebarPanelState { showSidebarPanel: boolean; diff --git a/site/src/pages/AgentsPage/AgentDetail/blockUtils.test.ts b/site/src/pages/AgentsPage/components/AgentDetail/blockUtils.test.ts similarity index 100% rename from site/src/pages/AgentsPage/AgentDetail/blockUtils.test.ts rename to site/src/pages/AgentsPage/components/AgentDetail/blockUtils.test.ts diff --git a/site/src/pages/AgentsPage/AgentDetail/blockUtils.ts b/site/src/pages/AgentsPage/components/AgentDetail/blockUtils.ts similarity index 100% rename from site/src/pages/AgentsPage/AgentDetail/blockUtils.ts rename to site/src/pages/AgentsPage/components/AgentDetail/blockUtils.ts diff --git a/site/src/pages/AgentsPage/AgentDetail/chatHelpers.test.ts b/site/src/pages/AgentsPage/components/AgentDetail/chatHelpers.test.ts similarity index 100% rename from site/src/pages/AgentsPage/AgentDetail/chatHelpers.test.ts rename to site/src/pages/AgentsPage/components/AgentDetail/chatHelpers.test.ts diff --git a/site/src/pages/AgentsPage/AgentDetail/chatHelpers.ts b/site/src/pages/AgentsPage/components/AgentDetail/chatHelpers.ts similarity index 100% rename from site/src/pages/AgentsPage/AgentDetail/chatHelpers.ts rename to site/src/pages/AgentsPage/components/AgentDetail/chatHelpers.ts diff --git a/site/src/pages/AgentsPage/AgentDetail/chatStore.test.ts b/site/src/pages/AgentsPage/components/AgentDetail/chatStore.test.ts similarity index 100% rename from site/src/pages/AgentsPage/AgentDetail/chatStore.test.ts rename to site/src/pages/AgentsPage/components/AgentDetail/chatStore.test.ts diff --git a/site/src/pages/AgentsPage/AgentDetail/messageParsing.test.ts b/site/src/pages/AgentsPage/components/AgentDetail/messageParsing.test.ts similarity index 100% rename from site/src/pages/AgentsPage/AgentDetail/messageParsing.test.ts rename to site/src/pages/AgentsPage/components/AgentDetail/messageParsing.test.ts diff --git a/site/src/pages/AgentsPage/AgentDetail/messageParsing.ts b/site/src/pages/AgentsPage/components/AgentDetail/messageParsing.ts similarity index 100% rename from site/src/pages/AgentsPage/AgentDetail/messageParsing.ts rename to site/src/pages/AgentsPage/components/AgentDetail/messageParsing.ts diff --git a/site/src/pages/AgentsPage/AgentDetail/streamState.test.ts b/site/src/pages/AgentsPage/components/AgentDetail/streamState.test.ts similarity index 100% rename from site/src/pages/AgentsPage/AgentDetail/streamState.test.ts rename to site/src/pages/AgentsPage/components/AgentDetail/streamState.test.ts diff --git a/site/src/pages/AgentsPage/AgentDetail/streamState.ts b/site/src/pages/AgentsPage/components/AgentDetail/streamState.ts similarity index 100% rename from site/src/pages/AgentsPage/AgentDetail/streamState.ts rename to site/src/pages/AgentsPage/components/AgentDetail/streamState.ts diff --git a/site/src/pages/AgentsPage/AgentDetail/streamingJson.test.ts b/site/src/pages/AgentsPage/components/AgentDetail/streamingJson.test.ts similarity index 100% rename from site/src/pages/AgentsPage/AgentDetail/streamingJson.test.ts rename to site/src/pages/AgentsPage/components/AgentDetail/streamingJson.test.ts diff --git a/site/src/pages/AgentsPage/AgentDetail/streamingJson.ts b/site/src/pages/AgentsPage/components/AgentDetail/streamingJson.ts similarity index 100% rename from site/src/pages/AgentsPage/AgentDetail/streamingJson.ts rename to site/src/pages/AgentsPage/components/AgentDetail/streamingJson.ts diff --git a/site/src/pages/AgentsPage/AgentDetail/types.ts b/site/src/pages/AgentsPage/components/AgentDetail/types.ts similarity index 100% rename from site/src/pages/AgentsPage/AgentDetail/types.ts rename to site/src/pages/AgentsPage/components/AgentDetail/types.ts diff --git a/site/src/pages/AgentsPage/AgentDetail/useAgentChime.test.ts b/site/src/pages/AgentsPage/components/AgentDetail/useAgentChime.test.ts similarity index 100% rename from site/src/pages/AgentsPage/AgentDetail/useAgentChime.test.ts rename to site/src/pages/AgentsPage/components/AgentDetail/useAgentChime.test.ts diff --git a/site/src/pages/AgentsPage/AgentDetail/useAgentChime.ts b/site/src/pages/AgentsPage/components/AgentDetail/useAgentChime.ts similarity index 100% rename from site/src/pages/AgentsPage/AgentDetail/useAgentChime.ts rename to site/src/pages/AgentsPage/components/AgentDetail/useAgentChime.ts diff --git a/site/src/pages/AgentsPage/AgentDetail/useWorkspaceCreationWatcher.test.tsx b/site/src/pages/AgentsPage/components/AgentDetail/useWorkspaceCreationWatcher.test.tsx similarity index 100% rename from site/src/pages/AgentsPage/AgentDetail/useWorkspaceCreationWatcher.test.tsx rename to site/src/pages/AgentsPage/components/AgentDetail/useWorkspaceCreationWatcher.test.tsx diff --git a/site/src/pages/AgentsPage/AgentDetail/useWorkspaceCreationWatcher.ts b/site/src/pages/AgentsPage/components/AgentDetail/useWorkspaceCreationWatcher.ts similarity index 100% rename from site/src/pages/AgentsPage/AgentDetail/useWorkspaceCreationWatcher.ts rename to site/src/pages/AgentsPage/components/AgentDetail/useWorkspaceCreationWatcher.ts diff --git a/site/src/pages/AgentsPage/AgentDetailContent.tsx b/site/src/pages/AgentsPage/components/AgentDetailContent.tsx similarity index 98% rename from site/src/pages/AgentsPage/AgentDetailContent.tsx rename to site/src/pages/AgentsPage/components/AgentDetailContent.tsx index b486278176..4bf1c1729a 100644 --- a/site/src/pages/AgentsPage/AgentDetailContent.tsx +++ b/site/src/pages/AgentsPage/components/AgentDetailContent.tsx @@ -4,6 +4,8 @@ import { useDashboard } from "modules/dashboard/useDashboard"; import { type FC, useEffect } from "react"; import { toast } from "sonner"; import type { UrlTransform } from "streamdown"; +import { useFileAttachments } from "../hooks/useFileAttachments"; +import type { ChatDetailError } from "../utils/usageLimitMessage"; import { AgentChatInput, type ChatMessageInputRef, @@ -30,8 +32,6 @@ import { } from "./AgentDetail/messageParsing"; import { buildStreamTools } from "./AgentDetail/streamState"; import type { ParsedMessageEntry } from "./AgentDetail/types"; -import type { ChatDetailError } from "./usageLimitMessage"; -import { useFileAttachments } from "./useFileAttachments"; type ChatStoreHandle = ReturnType["store"]; diff --git a/site/src/pages/AgentsPage/AgentDetailView.stories.tsx b/site/src/pages/AgentsPage/components/AgentDetailView.stories.tsx similarity index 100% rename from site/src/pages/AgentsPage/AgentDetailView.stories.tsx rename to site/src/pages/AgentsPage/components/AgentDetailView.stories.tsx diff --git a/site/src/pages/AgentsPage/AgentDetailView.tsx b/site/src/pages/AgentsPage/components/AgentDetailView.tsx similarity index 98% rename from site/src/pages/AgentsPage/AgentDetailView.tsx rename to site/src/pages/AgentsPage/components/AgentDetailView.tsx index 3c983150cb..867283b7c4 100644 --- a/site/src/pages/AgentsPage/AgentDetailView.tsx +++ b/site/src/pages/AgentsPage/components/AgentDetailView.tsx @@ -7,6 +7,7 @@ import { type FC, type RefObject, useEffect, useRef, useState } from "react"; import type { UrlTransform } from "streamdown"; import { cn } from "utils/cn"; import { pageTitle } from "utils/page"; +import type { ChatDetailError } from "../utils/usageLimitMessage"; import { AgentChatInput, type ChatMessageInputRef } from "./AgentChatInput"; import { selectChatStatus, @@ -19,10 +20,9 @@ import { ChatConversationSkeleton, RightPanelSkeleton, } from "./AgentsSkeletons"; -import { GitPanel } from "./GitPanel"; -import { RightPanel } from "./RightPanel"; -import { SidebarTabView } from "./SidebarTabView"; -import type { ChatDetailError } from "./usageLimitMessage"; +import { GitPanel } from "./GitPanel/GitPanel"; +import { RightPanel } from "./RightPanel/RightPanel"; +import { SidebarTabView } from "./Sidebar/SidebarTabView"; type ChatStoreHandle = ReturnType["store"]; diff --git a/site/src/pages/AgentsPage/components/AgentPageHeader.tsx b/site/src/pages/AgentsPage/components/AgentPageHeader.tsx new file mode 100644 index 0000000000..193319a1d6 --- /dev/null +++ b/site/src/pages/AgentsPage/components/AgentPageHeader.tsx @@ -0,0 +1,44 @@ +import { Button } from "components/Button/Button"; +import { ExternalImage } from "components/ExternalImage/ExternalImage"; +import { CoderIcon } from "components/Icons/CoderIcon"; +import { PanelLeftIcon } from "lucide-react"; +import { useDashboard } from "modules/dashboard/useDashboard"; +import type { FC, ReactNode } from "react"; +import { NavLink, useOutletContext } from "react-router"; +import type { AgentsOutletContext } from "../AgentsPageView"; + +interface AgentPageHeaderProps { + children?: ReactNode; +} + +export const AgentPageHeader: FC = ({ children }) => { + const { isSidebarCollapsed, onExpandSidebar } = + useOutletContext(); + const { appearance } = useDashboard(); + const logoUrl = appearance.logo_url; + + return ( +
+ + {logoUrl ? ( + + ) : ( + + )} + + {isSidebarCollapsed && ( + + )} +
+ {children &&
{children}
} +
+ ); +}; diff --git a/site/src/pages/AgentsPage/AgentsSkeletons.stories.tsx b/site/src/pages/AgentsPage/components/AgentsSkeletons.stories.tsx similarity index 100% rename from site/src/pages/AgentsPage/AgentsSkeletons.stories.tsx rename to site/src/pages/AgentsPage/components/AgentsSkeletons.stories.tsx diff --git a/site/src/pages/AgentsPage/AgentsSkeletons.tsx b/site/src/pages/AgentsPage/components/AgentsSkeletons.tsx similarity index 100% rename from site/src/pages/AgentsPage/AgentsSkeletons.tsx rename to site/src/pages/AgentsPage/components/AgentsSkeletons.tsx diff --git a/site/src/pages/AgentsPage/AttachmentPreview.stories.tsx b/site/src/pages/AgentsPage/components/AttachmentPreview.stories.tsx similarity index 100% rename from site/src/pages/AgentsPage/AttachmentPreview.stories.tsx rename to site/src/pages/AgentsPage/components/AttachmentPreview.stories.tsx diff --git a/site/src/pages/AgentsPage/ChatCostSummaryView.stories.tsx b/site/src/pages/AgentsPage/components/ChatCostSummaryView.stories.tsx similarity index 100% rename from site/src/pages/AgentsPage/ChatCostSummaryView.stories.tsx rename to site/src/pages/AgentsPage/components/ChatCostSummaryView.stories.tsx diff --git a/site/src/pages/AgentsPage/ChatCostSummaryView.tsx b/site/src/pages/AgentsPage/components/ChatCostSummaryView.tsx similarity index 100% rename from site/src/pages/AgentsPage/ChatCostSummaryView.tsx rename to site/src/pages/AgentsPage/components/ChatCostSummaryView.tsx diff --git a/site/src/pages/AgentsPage/ChatModelAdminPanel/ChatModelAdminPanel.stories.tsx b/site/src/pages/AgentsPage/components/ChatModelAdminPanel/ChatModelAdminPanel.stories.tsx similarity index 100% rename from site/src/pages/AgentsPage/ChatModelAdminPanel/ChatModelAdminPanel.stories.tsx rename to site/src/pages/AgentsPage/components/ChatModelAdminPanel/ChatModelAdminPanel.stories.tsx diff --git a/site/src/pages/AgentsPage/ChatModelAdminPanel/ChatModelAdminPanel.tsx b/site/src/pages/AgentsPage/components/ChatModelAdminPanel/ChatModelAdminPanel.tsx similarity index 99% rename from site/src/pages/AgentsPage/ChatModelAdminPanel/ChatModelAdminPanel.tsx rename to site/src/pages/AgentsPage/components/ChatModelAdminPanel/ChatModelAdminPanel.tsx index 60ee9b0743..2c37b39bca 100644 --- a/site/src/pages/AgentsPage/ChatModelAdminPanel/ChatModelAdminPanel.tsx +++ b/site/src/pages/AgentsPage/components/ChatModelAdminPanel/ChatModelAdminPanel.tsx @@ -16,7 +16,7 @@ import { Spinner } from "components/Spinner/Spinner"; import { type FC, type ReactNode, useState } from "react"; import { useMutation, useQuery, useQueryClient } from "react-query"; import { cn } from "utils/cn"; -import { formatProviderLabel } from "../modelOptions"; +import { formatProviderLabel } from "../../utils/modelOptions"; import { normalizeProvider, readOptionalString } from "./helpers"; import { ModelsSection } from "./ModelsSection"; import { ProvidersSection } from "./ProvidersSection"; diff --git a/site/src/pages/AgentsPage/ChatModelAdminPanel/ModelConfigFields.tsx b/site/src/pages/AgentsPage/components/ChatModelAdminPanel/ModelConfigFields.tsx similarity index 100% rename from site/src/pages/AgentsPage/ChatModelAdminPanel/ModelConfigFields.tsx rename to site/src/pages/AgentsPage/components/ChatModelAdminPanel/ModelConfigFields.tsx diff --git a/site/src/pages/AgentsPage/ChatModelAdminPanel/ModelForm.tsx b/site/src/pages/AgentsPage/components/ChatModelAdminPanel/ModelForm.tsx similarity index 100% rename from site/src/pages/AgentsPage/ChatModelAdminPanel/ModelForm.tsx rename to site/src/pages/AgentsPage/components/ChatModelAdminPanel/ModelForm.tsx diff --git a/site/src/pages/AgentsPage/ChatModelAdminPanel/ModelsSection.stories.tsx b/site/src/pages/AgentsPage/components/ChatModelAdminPanel/ModelsSection.stories.tsx similarity index 100% rename from site/src/pages/AgentsPage/ChatModelAdminPanel/ModelsSection.stories.tsx rename to site/src/pages/AgentsPage/components/ChatModelAdminPanel/ModelsSection.stories.tsx diff --git a/site/src/pages/AgentsPage/ChatModelAdminPanel/ModelsSection.tsx b/site/src/pages/AgentsPage/components/ChatModelAdminPanel/ModelsSection.tsx similarity index 100% rename from site/src/pages/AgentsPage/ChatModelAdminPanel/ModelsSection.tsx rename to site/src/pages/AgentsPage/components/ChatModelAdminPanel/ModelsSection.tsx diff --git a/site/src/pages/AgentsPage/ChatModelAdminPanel/ProviderForm.tsx b/site/src/pages/AgentsPage/components/ChatModelAdminPanel/ProviderForm.tsx similarity index 99% rename from site/src/pages/AgentsPage/ChatModelAdminPanel/ProviderForm.tsx rename to site/src/pages/AgentsPage/components/ChatModelAdminPanel/ProviderForm.tsx index 5f96e0bf4c..6aca22001e 100644 --- a/site/src/pages/AgentsPage/ChatModelAdminPanel/ProviderForm.tsx +++ b/site/src/pages/AgentsPage/components/ChatModelAdminPanel/ProviderForm.tsx @@ -10,7 +10,7 @@ import { } from "components/Tooltip/Tooltip"; import { ChevronLeftIcon, InfoIcon } from "lucide-react"; import { type FC, type FormEvent, useId, useState } from "react"; -import { formatProviderLabel } from "../modelOptions"; +import { formatProviderLabel } from "../../utils/modelOptions"; import type { ProviderState } from "./ChatModelAdminPanel"; import { readOptionalString } from "./helpers"; import { ProviderIcon } from "./ProviderIcon"; diff --git a/site/src/pages/AgentsPage/ChatModelAdminPanel/ProviderIcon.tsx b/site/src/pages/AgentsPage/components/ChatModelAdminPanel/ProviderIcon.tsx similarity index 95% rename from site/src/pages/AgentsPage/ChatModelAdminPanel/ProviderIcon.tsx rename to site/src/pages/AgentsPage/components/ChatModelAdminPanel/ProviderIcon.tsx index 751180fdbf..7443c0c3a6 100644 --- a/site/src/pages/AgentsPage/ChatModelAdminPanel/ProviderIcon.tsx +++ b/site/src/pages/AgentsPage/components/ChatModelAdminPanel/ProviderIcon.tsx @@ -2,7 +2,7 @@ import { ExternalImage } from "components/ExternalImage/ExternalImage"; import { ServerIcon } from "lucide-react"; import type { FC } from "react"; import { cn } from "utils/cn"; -import { formatProviderLabel } from "../modelOptions"; +import { formatProviderLabel } from "../../utils/modelOptions"; import { normalizeProvider } from "./helpers"; const providerIconMap: Record = { diff --git a/site/src/pages/AgentsPage/ChatModelAdminPanel/ProvidersSection.tsx b/site/src/pages/AgentsPage/components/ChatModelAdminPanel/ProvidersSection.tsx similarity index 100% rename from site/src/pages/AgentsPage/ChatModelAdminPanel/ProvidersSection.tsx rename to site/src/pages/AgentsPage/components/ChatModelAdminPanel/ProvidersSection.tsx diff --git a/site/src/pages/AgentsPage/ChatModelAdminPanel/helpers.ts b/site/src/pages/AgentsPage/components/ChatModelAdminPanel/helpers.ts similarity index 100% rename from site/src/pages/AgentsPage/ChatModelAdminPanel/helpers.ts rename to site/src/pages/AgentsPage/components/ChatModelAdminPanel/helpers.ts diff --git a/site/src/pages/AgentsPage/ChatModelAdminPanel/modelConfigFormLogic.test.ts b/site/src/pages/AgentsPage/components/ChatModelAdminPanel/modelConfigFormLogic.test.ts similarity index 100% rename from site/src/pages/AgentsPage/ChatModelAdminPanel/modelConfigFormLogic.test.ts rename to site/src/pages/AgentsPage/components/ChatModelAdminPanel/modelConfigFormLogic.test.ts diff --git a/site/src/pages/AgentsPage/ChatModelAdminPanel/modelConfigFormLogic.ts b/site/src/pages/AgentsPage/components/ChatModelAdminPanel/modelConfigFormLogic.ts similarity index 100% rename from site/src/pages/AgentsPage/ChatModelAdminPanel/modelConfigFormLogic.ts rename to site/src/pages/AgentsPage/components/ChatModelAdminPanel/modelConfigFormLogic.ts diff --git a/site/src/pages/AgentsPage/ChatModelAdminPanel/pricingFields.test.ts b/site/src/pages/AgentsPage/components/ChatModelAdminPanel/pricingFields.test.ts similarity index 100% rename from site/src/pages/AgentsPage/ChatModelAdminPanel/pricingFields.test.ts rename to site/src/pages/AgentsPage/components/ChatModelAdminPanel/pricingFields.test.ts diff --git a/site/src/pages/AgentsPage/ChatModelAdminPanel/pricingFields.ts b/site/src/pages/AgentsPage/components/ChatModelAdminPanel/pricingFields.ts similarity index 100% rename from site/src/pages/AgentsPage/ChatModelAdminPanel/pricingFields.ts rename to site/src/pages/AgentsPage/components/ChatModelAdminPanel/pricingFields.ts diff --git a/site/src/pages/AgentsPage/ChimeButton.stories.tsx b/site/src/pages/AgentsPage/components/ChimeButton.stories.tsx similarity index 100% rename from site/src/pages/AgentsPage/ChimeButton.stories.tsx rename to site/src/pages/AgentsPage/components/ChimeButton.stories.tsx diff --git a/site/src/pages/AgentsPage/ChimeButton.tsx b/site/src/pages/AgentsPage/components/ChimeButton.tsx similarity index 100% rename from site/src/pages/AgentsPage/ChimeButton.tsx rename to site/src/pages/AgentsPage/components/ChimeButton.tsx diff --git a/site/src/pages/AgentsPage/CommentableDiffViewer.tsx b/site/src/pages/AgentsPage/components/DiffViewer/CommentableDiffViewer.tsx similarity index 97% rename from site/src/pages/AgentsPage/CommentableDiffViewer.tsx rename to site/src/pages/AgentsPage/components/DiffViewer/CommentableDiffViewer.tsx index 14974d8d46..8858b4775d 100644 --- a/site/src/pages/AgentsPage/CommentableDiffViewer.tsx +++ b/site/src/pages/AgentsPage/components/DiffViewer/CommentableDiffViewer.tsx @@ -12,9 +12,6 @@ import { useRef, useState, } from "react"; -import type { ChatMessageInputRef } from "./AgentChatInput"; -import type { DiffStyle } from "./DiffViewer"; -import { DiffViewer } from "./DiffViewer"; import { annotationLineForBox, annotationSideForBox, @@ -22,7 +19,10 @@ import { commentBoxFromRange, contentRangeForBox, selectedLinesForBox, -} from "./diffCommentSelection"; +} from "../../utils/diffCommentSelection"; +import type { ChatMessageInputRef } from "../AgentChatInput"; +import type { DiffStyle } from "../DiffViewer/DiffViewer"; +import { DiffViewer } from "../DiffViewer/DiffViewer"; // ------------------------------------------------------------------- // Diff content extraction diff --git a/site/src/pages/AgentsPage/DiffStats.stories.tsx b/site/src/pages/AgentsPage/components/DiffViewer/DiffStats.stories.tsx similarity index 93% rename from site/src/pages/AgentsPage/DiffStats.stories.tsx rename to site/src/pages/AgentsPage/components/DiffViewer/DiffStats.stories.tsx index d7c69299e6..587b4420d7 100644 --- a/site/src/pages/AgentsPage/DiffStats.stories.tsx +++ b/site/src/pages/AgentsPage/components/DiffViewer/DiffStats.stories.tsx @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import { DiffStatBadge } from "./DiffStats"; +import { DiffStatBadge } from "../DiffViewer/DiffStats"; const badgeMeta: Meta = { title: "pages/AgentsPage/DiffStatBadge", diff --git a/site/src/pages/AgentsPage/DiffStats.tsx b/site/src/pages/AgentsPage/components/DiffViewer/DiffStats.tsx similarity index 100% rename from site/src/pages/AgentsPage/DiffStats.tsx rename to site/src/pages/AgentsPage/components/DiffViewer/DiffStats.tsx diff --git a/site/src/pages/AgentsPage/DiffViewer.stories.tsx b/site/src/pages/AgentsPage/components/DiffViewer/DiffViewer.stories.tsx similarity index 98% rename from site/src/pages/AgentsPage/DiffViewer.stories.tsx rename to site/src/pages/AgentsPage/components/DiffViewer/DiffViewer.stories.tsx index 1428584fc5..b64d15bd2d 100644 --- a/site/src/pages/AgentsPage/DiffViewer.stories.tsx +++ b/site/src/pages/AgentsPage/components/DiffViewer/DiffViewer.stories.tsx @@ -2,9 +2,9 @@ import type { DiffLineAnnotation, SelectedLineRange } from "@pierre/diffs"; import { parsePatchFiles } from "@pierre/diffs"; import type { Meta, StoryObj } from "@storybook/react-vite"; import { expect, fn, waitFor } from "storybook/test"; -import type { DiffStyle } from "./DiffViewer"; -import { DiffViewer } from "./DiffViewer"; -import { InlinePromptInput } from "./RemoteDiffPanel"; +import type { DiffStyle } from "../DiffViewer/DiffViewer"; +import { DiffViewer } from "../DiffViewer/DiffViewer"; +import { InlinePromptInput } from "../DiffViewer/RemoteDiffPanel"; // biome-ignore format: raw diff string must preserve exact whitespace const sampleDiff = [ diff --git a/site/src/pages/AgentsPage/DiffViewer.tsx b/site/src/pages/AgentsPage/components/DiffViewer/DiffViewer.tsx similarity index 99% rename from site/src/pages/AgentsPage/DiffViewer.tsx rename to site/src/pages/AgentsPage/components/DiffViewer/DiffViewer.tsx index 67cef3dc65..6afe09a8ac 100644 --- a/site/src/pages/AgentsPage/DiffViewer.tsx +++ b/site/src/pages/AgentsPage/components/DiffViewer/DiffViewer.tsx @@ -24,7 +24,7 @@ import { useState, } from "react"; import { cn } from "utils/cn"; -import { changeColor, changeLabel } from "./diffColors"; +import { changeColor, changeLabel } from "../../utils/diffColors"; // ------------------------------------------------------------------- // Public interface diff --git a/site/src/pages/AgentsPage/LocalDiffPanel.tsx b/site/src/pages/AgentsPage/components/DiffViewer/LocalDiffPanel.tsx similarity index 81% rename from site/src/pages/AgentsPage/LocalDiffPanel.tsx rename to site/src/pages/AgentsPage/components/DiffViewer/LocalDiffPanel.tsx index b7dbaea357..338277d01e 100644 --- a/site/src/pages/AgentsPage/LocalDiffPanel.tsx +++ b/site/src/pages/AgentsPage/components/DiffViewer/LocalDiffPanel.tsx @@ -1,9 +1,9 @@ import { parsePatchFiles } from "@pierre/diffs"; import type { WorkspaceAgentRepoChanges } from "api/typesGenerated"; import type { FC, RefObject } from "react"; -import type { ChatMessageInputRef } from "./AgentChatInput"; -import { CommentableDiffViewer } from "./CommentableDiffViewer"; -import type { DiffStyle } from "./DiffViewer"; +import type { ChatMessageInputRef } from "../AgentChatInput"; +import { CommentableDiffViewer } from "../DiffViewer/CommentableDiffViewer"; +import type { DiffStyle } from "../DiffViewer/DiffViewer"; interface LocalDiffPanelProps { repo: WorkspaceAgentRepoChanges; diff --git a/site/src/pages/AgentsPage/RemoteDiffPanel.tsx b/site/src/pages/AgentsPage/components/DiffViewer/RemoteDiffPanel.tsx similarity index 94% rename from site/src/pages/AgentsPage/RemoteDiffPanel.tsx rename to site/src/pages/AgentsPage/components/DiffViewer/RemoteDiffPanel.tsx index db6e2fd015..29d1fcee52 100644 --- a/site/src/pages/AgentsPage/RemoteDiffPanel.tsx +++ b/site/src/pages/AgentsPage/components/DiffViewer/RemoteDiffPanel.tsx @@ -14,13 +14,13 @@ import { import { type FC, type RefObject, useEffect, useState } from "react"; import { useQuery } from "react-query"; import { cn } from "utils/cn"; -import type { ChatMessageInputRef } from "./AgentChatInput"; -import { CommentableDiffViewer } from "./CommentableDiffViewer"; -import { DiffStatBadge } from "./DiffStats"; -import type { DiffStyle } from "./DiffViewer"; -import { parsePullRequestUrl } from "./pullRequest"; +import { parsePullRequestUrl } from "../../utils/pullRequest"; +import type { ChatMessageInputRef } from "../AgentChatInput"; +import { CommentableDiffViewer } from "../DiffViewer/CommentableDiffViewer"; +import { DiffStatBadge } from "../DiffViewer/DiffStats"; +import type { DiffStyle } from "../DiffViewer/DiffViewer"; -export { InlinePromptInput } from "./CommentableDiffViewer"; +export { InlinePromptInput } from "../DiffViewer/CommentableDiffViewer"; // ------------------------------------------------------------------- // PR state badge diff --git a/site/src/pages/AgentsPage/extractDiffContent.test.ts b/site/src/pages/AgentsPage/components/DiffViewer/extractDiffContent.test.ts similarity index 98% rename from site/src/pages/AgentsPage/extractDiffContent.test.ts rename to site/src/pages/AgentsPage/components/DiffViewer/extractDiffContent.test.ts index 66d8269dc6..074793e12e 100644 --- a/site/src/pages/AgentsPage/extractDiffContent.test.ts +++ b/site/src/pages/AgentsPage/components/DiffViewer/extractDiffContent.test.ts @@ -1,6 +1,6 @@ import { parsePatchFiles } from "@pierre/diffs"; import { describe, expect, it } from "vitest"; -import { extractDiffContent } from "./CommentableDiffViewer"; +import { extractDiffContent } from "../DiffViewer/CommentableDiffViewer"; function parse(diffStr: string) { return parsePatchFiles(diffStr).flatMap((p) => p.files); diff --git a/site/src/pages/AgentsPage/EmbedContext.tsx b/site/src/pages/AgentsPage/components/EmbedContext.tsx similarity index 100% rename from site/src/pages/AgentsPage/EmbedContext.tsx rename to site/src/pages/AgentsPage/components/EmbedContext.tsx diff --git a/site/src/pages/AgentsPage/GitPanel.stories.tsx b/site/src/pages/AgentsPage/components/GitPanel/GitPanel.stories.tsx similarity index 100% rename from site/src/pages/AgentsPage/GitPanel.stories.tsx rename to site/src/pages/AgentsPage/components/GitPanel/GitPanel.stories.tsx diff --git a/site/src/pages/AgentsPage/GitPanel.tsx b/site/src/pages/AgentsPage/components/GitPanel/GitPanel.tsx similarity index 97% rename from site/src/pages/AgentsPage/GitPanel.tsx rename to site/src/pages/AgentsPage/components/GitPanel/GitPanel.tsx index 274d6ce292..abfa859383 100644 --- a/site/src/pages/AgentsPage/GitPanel.tsx +++ b/site/src/pages/AgentsPage/components/GitPanel/GitPanel.tsx @@ -19,11 +19,15 @@ import { } from "lucide-react"; import { type FC, type RefObject, useEffect, useRef, useState } from "react"; import { cn } from "utils/cn"; -import type { ChatMessageInputRef } from "./AgentChatInput"; -import { DiffStatBadge } from "./DiffStats"; -import { type DiffStyle, loadDiffStyle, saveDiffStyle } from "./DiffViewer"; -import { LocalDiffPanel } from "./LocalDiffPanel"; -import { RemoteDiffPanel } from "./RemoteDiffPanel"; +import type { ChatMessageInputRef } from "../AgentChatInput"; +import { DiffStatBadge } from "../DiffViewer/DiffStats"; +import { + type DiffStyle, + loadDiffStyle, + saveDiffStyle, +} from "../DiffViewer/DiffViewer"; +import { LocalDiffPanel } from "../DiffViewer/LocalDiffPanel"; +import { RemoteDiffPanel } from "../DiffViewer/RemoteDiffPanel"; type GitView = { type: "remote" } | { type: "local"; repoRoot: string }; diff --git a/site/src/pages/AgentsPage/InlinePromptInput.stories.tsx b/site/src/pages/AgentsPage/components/GitPanel/InlinePromptInput.stories.tsx similarity index 95% rename from site/src/pages/AgentsPage/InlinePromptInput.stories.tsx rename to site/src/pages/AgentsPage/components/GitPanel/InlinePromptInput.stories.tsx index df6bf4139e..dbc310b56b 100644 --- a/site/src/pages/AgentsPage/InlinePromptInput.stories.tsx +++ b/site/src/pages/AgentsPage/components/GitPanel/InlinePromptInput.stories.tsx @@ -1,6 +1,6 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; import { expect, fn, userEvent, within } from "storybook/test"; -import { InlinePromptInput } from "./RemoteDiffPanel"; +import { InlinePromptInput } from "../DiffViewer/RemoteDiffPanel"; const meta: Meta = { title: "pages/AgentsPage/InlinePromptInput", diff --git a/site/src/pages/AgentsPage/ImageLightbox.stories.tsx b/site/src/pages/AgentsPage/components/ImageLightbox.stories.tsx similarity index 100% rename from site/src/pages/AgentsPage/ImageLightbox.stories.tsx rename to site/src/pages/AgentsPage/components/ImageLightbox.stories.tsx diff --git a/site/src/pages/AgentsPage/ImageLightbox.tsx b/site/src/pages/AgentsPage/components/ImageLightbox.tsx similarity index 100% rename from site/src/pages/AgentsPage/ImageLightbox.tsx rename to site/src/pages/AgentsPage/components/ImageLightbox.tsx diff --git a/site/src/pages/AgentsPage/InsightsContent.tsx b/site/src/pages/AgentsPage/components/InsightsContent.tsx similarity index 100% rename from site/src/pages/AgentsPage/InsightsContent.tsx rename to site/src/pages/AgentsPage/components/InsightsContent.tsx diff --git a/site/src/pages/AgentsPage/LimitsTab/DefaultLimitSection.tsx b/site/src/pages/AgentsPage/components/LimitsTab/DefaultLimitSection.tsx similarity index 100% rename from site/src/pages/AgentsPage/LimitsTab/DefaultLimitSection.tsx rename to site/src/pages/AgentsPage/components/LimitsTab/DefaultLimitSection.tsx diff --git a/site/src/pages/AgentsPage/LimitsTab/GroupLimitsSection.stories.tsx b/site/src/pages/AgentsPage/components/LimitsTab/GroupLimitsSection.stories.tsx similarity index 100% rename from site/src/pages/AgentsPage/LimitsTab/GroupLimitsSection.stories.tsx rename to site/src/pages/AgentsPage/components/LimitsTab/GroupLimitsSection.stories.tsx diff --git a/site/src/pages/AgentsPage/LimitsTab/GroupLimitsSection.tsx b/site/src/pages/AgentsPage/components/LimitsTab/GroupLimitsSection.tsx similarity index 100% rename from site/src/pages/AgentsPage/LimitsTab/GroupLimitsSection.tsx rename to site/src/pages/AgentsPage/components/LimitsTab/GroupLimitsSection.tsx diff --git a/site/src/pages/AgentsPage/LimitsTab/LimitsTab.tsx b/site/src/pages/AgentsPage/components/LimitsTab/LimitsTab.tsx similarity index 100% rename from site/src/pages/AgentsPage/LimitsTab/LimitsTab.tsx rename to site/src/pages/AgentsPage/components/LimitsTab/LimitsTab.tsx diff --git a/site/src/pages/AgentsPage/LimitsTab/UserOverridesSection.stories.tsx b/site/src/pages/AgentsPage/components/LimitsTab/UserOverridesSection.stories.tsx similarity index 100% rename from site/src/pages/AgentsPage/LimitsTab/UserOverridesSection.stories.tsx rename to site/src/pages/AgentsPage/components/LimitsTab/UserOverridesSection.stories.tsx diff --git a/site/src/pages/AgentsPage/LimitsTab/UserOverridesSection.tsx b/site/src/pages/AgentsPage/components/LimitsTab/UserOverridesSection.tsx similarity index 100% rename from site/src/pages/AgentsPage/LimitsTab/UserOverridesSection.tsx rename to site/src/pages/AgentsPage/components/LimitsTab/UserOverridesSection.tsx diff --git a/site/src/pages/AgentsPage/LimitsTab/index.ts b/site/src/pages/AgentsPage/components/LimitsTab/index.ts similarity index 100% rename from site/src/pages/AgentsPage/LimitsTab/index.ts rename to site/src/pages/AgentsPage/components/LimitsTab/index.ts diff --git a/site/src/pages/AgentsPage/LimitsTab/limitsFormLogic.test.ts b/site/src/pages/AgentsPage/components/LimitsTab/limitsFormLogic.test.ts similarity index 100% rename from site/src/pages/AgentsPage/LimitsTab/limitsFormLogic.test.ts rename to site/src/pages/AgentsPage/components/LimitsTab/limitsFormLogic.test.ts diff --git a/site/src/pages/AgentsPage/LimitsTab/limitsFormLogic.ts b/site/src/pages/AgentsPage/components/LimitsTab/limitsFormLogic.ts similarity index 100% rename from site/src/pages/AgentsPage/LimitsTab/limitsFormLogic.ts rename to site/src/pages/AgentsPage/components/LimitsTab/limitsFormLogic.ts diff --git a/site/src/pages/AgentsPage/MCPServerAdminPanel.stories.tsx b/site/src/pages/AgentsPage/components/MCPServerAdminPanel.stories.tsx similarity index 100% rename from site/src/pages/AgentsPage/MCPServerAdminPanel.stories.tsx rename to site/src/pages/AgentsPage/components/MCPServerAdminPanel.stories.tsx diff --git a/site/src/pages/AgentsPage/MCPServerAdminPanel.tsx b/site/src/pages/AgentsPage/components/MCPServerAdminPanel.tsx similarity index 100% rename from site/src/pages/AgentsPage/MCPServerAdminPanel.tsx rename to site/src/pages/AgentsPage/components/MCPServerAdminPanel.tsx diff --git a/site/src/pages/AgentsPage/PRInsightsView.stories.tsx b/site/src/pages/AgentsPage/components/PRInsightsView.stories.tsx similarity index 100% rename from site/src/pages/AgentsPage/PRInsightsView.stories.tsx rename to site/src/pages/AgentsPage/components/PRInsightsView.stories.tsx diff --git a/site/src/pages/AgentsPage/PRInsightsView.tsx b/site/src/pages/AgentsPage/components/PRInsightsView.tsx similarity index 99% rename from site/src/pages/AgentsPage/PRInsightsView.tsx rename to site/src/pages/AgentsPage/components/PRInsightsView.tsx index c0094b50b9..a83cf5a76f 100644 --- a/site/src/pages/AgentsPage/PRInsightsView.tsx +++ b/site/src/pages/AgentsPage/components/PRInsightsView.tsx @@ -21,7 +21,7 @@ import type { FC } from "react"; import { Area, AreaChart, CartesianGrid, XAxis, YAxis } from "recharts"; import { cn } from "utils/cn"; import { formatCostMicros } from "utils/currency"; -import { PrStateIcon } from "./GitPanel"; +import { PrStateIcon } from "./GitPanel/GitPanel"; dayjs.extend(relativeTime); diff --git a/site/src/pages/AgentsPage/QueuedMessagesList.stories.tsx b/site/src/pages/AgentsPage/components/QueuedMessagesList.stories.tsx similarity index 100% rename from site/src/pages/AgentsPage/QueuedMessagesList.stories.tsx rename to site/src/pages/AgentsPage/components/QueuedMessagesList.stories.tsx diff --git a/site/src/pages/AgentsPage/QueuedMessagesList.test.ts b/site/src/pages/AgentsPage/components/QueuedMessagesList.test.ts similarity index 100% rename from site/src/pages/AgentsPage/QueuedMessagesList.test.ts rename to site/src/pages/AgentsPage/components/QueuedMessagesList.test.ts diff --git a/site/src/pages/AgentsPage/QueuedMessagesList.tsx b/site/src/pages/AgentsPage/components/QueuedMessagesList.tsx similarity index 100% rename from site/src/pages/AgentsPage/QueuedMessagesList.tsx rename to site/src/pages/AgentsPage/components/QueuedMessagesList.tsx diff --git a/site/src/pages/AgentsPage/DesktopPanel.stories.tsx b/site/src/pages/AgentsPage/components/RightPanel/DesktopPanel.stories.tsx similarity index 100% rename from site/src/pages/AgentsPage/DesktopPanel.stories.tsx rename to site/src/pages/AgentsPage/components/RightPanel/DesktopPanel.stories.tsx diff --git a/site/src/pages/AgentsPage/DesktopPanel.tsx b/site/src/pages/AgentsPage/components/RightPanel/DesktopPanel.tsx similarity index 96% rename from site/src/pages/AgentsPage/DesktopPanel.tsx rename to site/src/pages/AgentsPage/components/RightPanel/DesktopPanel.tsx index 10d90c1fb9..3ba56b6891 100644 --- a/site/src/pages/AgentsPage/DesktopPanel.tsx +++ b/site/src/pages/AgentsPage/components/RightPanel/DesktopPanel.tsx @@ -1,7 +1,7 @@ import { Button } from "components/Button/Button"; import { Spinner } from "components/Spinner/Spinner"; import type { FC } from "react"; -import { useDesktopConnection } from "./useDesktopConnection"; +import { useDesktopConnection } from "../../hooks/useDesktopConnection"; type DesktopConnectionStatus = | "idle" diff --git a/site/src/pages/AgentsPage/RightPanel.stories.tsx b/site/src/pages/AgentsPage/components/RightPanel/RightPanel.stories.tsx similarity index 100% rename from site/src/pages/AgentsPage/RightPanel.stories.tsx rename to site/src/pages/AgentsPage/components/RightPanel/RightPanel.stories.tsx diff --git a/site/src/pages/AgentsPage/RightPanel.tsx b/site/src/pages/AgentsPage/components/RightPanel/RightPanel.tsx similarity index 100% rename from site/src/pages/AgentsPage/RightPanel.tsx rename to site/src/pages/AgentsPage/components/RightPanel/RightPanel.tsx diff --git a/site/src/pages/AgentsPage/SectionHeader.stories.tsx b/site/src/pages/AgentsPage/components/SectionHeader.stories.tsx similarity index 100% rename from site/src/pages/AgentsPage/SectionHeader.stories.tsx rename to site/src/pages/AgentsPage/components/SectionHeader.stories.tsx diff --git a/site/src/pages/AgentsPage/SectionHeader.tsx b/site/src/pages/AgentsPage/components/SectionHeader.tsx similarity index 100% rename from site/src/pages/AgentsPage/SectionHeader.tsx rename to site/src/pages/AgentsPage/components/SectionHeader.tsx diff --git a/site/src/pages/AgentsPage/AgentsSidebar.stories.tsx b/site/src/pages/AgentsPage/components/Sidebar/AgentsSidebar.stories.tsx similarity index 100% rename from site/src/pages/AgentsPage/AgentsSidebar.stories.tsx rename to site/src/pages/AgentsPage/components/Sidebar/AgentsSidebar.stories.tsx diff --git a/site/src/pages/AgentsPage/AgentsSidebar.test.tsx b/site/src/pages/AgentsPage/components/Sidebar/AgentsSidebar.test.tsx similarity index 100% rename from site/src/pages/AgentsPage/AgentsSidebar.test.tsx rename to site/src/pages/AgentsPage/components/Sidebar/AgentsSidebar.test.tsx diff --git a/site/src/pages/AgentsPage/AgentsSidebar.tsx b/site/src/pages/AgentsPage/components/Sidebar/AgentsSidebar.tsx similarity index 99% rename from site/src/pages/AgentsPage/AgentsSidebar.tsx rename to site/src/pages/AgentsPage/components/Sidebar/AgentsSidebar.tsx index ac64605b53..3a99a8e196 100644 --- a/site/src/pages/AgentsPage/AgentsSidebar.tsx +++ b/site/src/pages/AgentsPage/components/Sidebar/AgentsSidebar.tsx @@ -68,9 +68,9 @@ import { import { Link, NavLink, useLocation, useParams } from "react-router"; import { cn } from "utils/cn"; import { shortRelativeTime } from "utils/time"; -import { getNormalizedModelRef } from "./modelOptions"; -import { getTimeGroup, TIME_GROUPS } from "./timeGroups"; -import { UsageIndicator } from "./UsageIndicator"; +import { getNormalizedModelRef } from "../../utils/modelOptions"; +import { getTimeGroup, TIME_GROUPS } from "../../utils/timeGroups"; +import { UsageIndicator } from "../UsageIndicator"; type SidebarView = | { panel: "chats" } diff --git a/site/src/pages/AgentsPage/SidebarTabView.stories.tsx b/site/src/pages/AgentsPage/components/Sidebar/SidebarTabView.stories.tsx similarity index 100% rename from site/src/pages/AgentsPage/SidebarTabView.stories.tsx rename to site/src/pages/AgentsPage/components/Sidebar/SidebarTabView.stories.tsx diff --git a/site/src/pages/AgentsPage/SidebarTabView.tsx b/site/src/pages/AgentsPage/components/Sidebar/SidebarTabView.tsx similarity index 99% rename from site/src/pages/AgentsPage/SidebarTabView.tsx rename to site/src/pages/AgentsPage/components/Sidebar/SidebarTabView.tsx index 475abdcbc2..8f3ddc465a 100644 --- a/site/src/pages/AgentsPage/SidebarTabView.tsx +++ b/site/src/pages/AgentsPage/components/Sidebar/SidebarTabView.tsx @@ -10,7 +10,7 @@ import { import type { ReactNode } from "react"; import { type FC, useEffect, useId, useRef, useState } from "react"; import { cn } from "utils/cn"; -import { DesktopPanel } from "./DesktopPanel"; +import { DesktopPanel } from "../RightPanel/DesktopPanel"; /** A single tab definition for the sidebar panel. */ export interface SidebarTab { diff --git a/site/src/pages/AgentsPage/UsageIndicator.stories.tsx b/site/src/pages/AgentsPage/components/UsageIndicator.stories.tsx similarity index 100% rename from site/src/pages/AgentsPage/UsageIndicator.stories.tsx rename to site/src/pages/AgentsPage/components/UsageIndicator.stories.tsx diff --git a/site/src/pages/AgentsPage/UsageIndicator.tsx b/site/src/pages/AgentsPage/components/UsageIndicator.tsx similarity index 100% rename from site/src/pages/AgentsPage/UsageIndicator.tsx rename to site/src/pages/AgentsPage/components/UsageIndicator.tsx diff --git a/site/src/pages/AgentsPage/WebPushButton.tsx b/site/src/pages/AgentsPage/components/WebPushButton.tsx similarity index 100% rename from site/src/pages/AgentsPage/WebPushButton.tsx rename to site/src/pages/AgentsPage/components/WebPushButton.tsx diff --git a/site/src/pages/AgentsPage/useAgentsPWA.ts b/site/src/pages/AgentsPage/hooks/useAgentsPWA.ts similarity index 100% rename from site/src/pages/AgentsPage/useAgentsPWA.ts rename to site/src/pages/AgentsPage/hooks/useAgentsPWA.ts diff --git a/site/src/pages/AgentsPage/useAgentsPageKeybindings.ts b/site/src/pages/AgentsPage/hooks/useAgentsPageKeybindings.ts similarity index 100% rename from site/src/pages/AgentsPage/useAgentsPageKeybindings.ts rename to site/src/pages/AgentsPage/hooks/useAgentsPageKeybindings.ts diff --git a/site/src/pages/AgentsPage/useDesktopConnection.test.ts b/site/src/pages/AgentsPage/hooks/useDesktopConnection.test.ts similarity index 100% rename from site/src/pages/AgentsPage/useDesktopConnection.test.ts rename to site/src/pages/AgentsPage/hooks/useDesktopConnection.test.ts diff --git a/site/src/pages/AgentsPage/useDesktopConnection.ts b/site/src/pages/AgentsPage/hooks/useDesktopConnection.ts similarity index 100% rename from site/src/pages/AgentsPage/useDesktopConnection.ts rename to site/src/pages/AgentsPage/hooks/useDesktopConnection.ts diff --git a/site/src/pages/AgentsPage/useFileAttachments.ts b/site/src/pages/AgentsPage/hooks/useFileAttachments.ts similarity index 98% rename from site/src/pages/AgentsPage/useFileAttachments.ts rename to site/src/pages/AgentsPage/hooks/useFileAttachments.ts index 78385e1995..fd730d5fe2 100644 --- a/site/src/pages/AgentsPage/useFileAttachments.ts +++ b/site/src/pages/AgentsPage/hooks/useFileAttachments.ts @@ -7,7 +7,7 @@ import { useRef, useState, } from "react"; -import type { UploadState } from "./AgentChatInput"; +import type { UploadState } from "../components/AgentChatInput"; interface UseFileAttachmentsReturn { attachments: File[]; diff --git a/site/src/pages/AgentsPage/useGitWatcher.test.ts b/site/src/pages/AgentsPage/hooks/useGitWatcher.test.ts similarity index 100% rename from site/src/pages/AgentsPage/useGitWatcher.test.ts rename to site/src/pages/AgentsPage/hooks/useGitWatcher.test.ts diff --git a/site/src/pages/AgentsPage/useGitWatcher.ts b/site/src/pages/AgentsPage/hooks/useGitWatcher.ts similarity index 100% rename from site/src/pages/AgentsPage/useGitWatcher.ts rename to site/src/pages/AgentsPage/hooks/useGitWatcher.ts diff --git a/site/src/pages/AgentsPage/agentWorkspaceUtils.test.ts b/site/src/pages/AgentsPage/utils/agentWorkspaceUtils.test.ts similarity index 100% rename from site/src/pages/AgentsPage/agentWorkspaceUtils.test.ts rename to site/src/pages/AgentsPage/utils/agentWorkspaceUtils.test.ts diff --git a/site/src/pages/AgentsPage/agentWorkspaceUtils.ts b/site/src/pages/AgentsPage/utils/agentWorkspaceUtils.ts similarity index 100% rename from site/src/pages/AgentsPage/agentWorkspaceUtils.ts rename to site/src/pages/AgentsPage/utils/agentWorkspaceUtils.ts diff --git a/site/src/pages/AgentsPage/diffColors.ts b/site/src/pages/AgentsPage/utils/diffColors.ts similarity index 100% rename from site/src/pages/AgentsPage/diffColors.ts rename to site/src/pages/AgentsPage/utils/diffColors.ts diff --git a/site/src/pages/AgentsPage/diffCommentSelection.test.ts b/site/src/pages/AgentsPage/utils/diffCommentSelection.test.ts similarity index 100% rename from site/src/pages/AgentsPage/diffCommentSelection.test.ts rename to site/src/pages/AgentsPage/utils/diffCommentSelection.test.ts diff --git a/site/src/pages/AgentsPage/diffCommentSelection.ts b/site/src/pages/AgentsPage/utils/diffCommentSelection.ts similarity index 100% rename from site/src/pages/AgentsPage/diffCommentSelection.ts rename to site/src/pages/AgentsPage/utils/diffCommentSelection.ts diff --git a/site/src/pages/AgentsPage/modelOptions.test.ts b/site/src/pages/AgentsPage/utils/modelOptions.test.ts similarity index 100% rename from site/src/pages/AgentsPage/modelOptions.test.ts rename to site/src/pages/AgentsPage/utils/modelOptions.test.ts diff --git a/site/src/pages/AgentsPage/modelOptions.ts b/site/src/pages/AgentsPage/utils/modelOptions.ts similarity index 82% rename from site/src/pages/AgentsPage/modelOptions.ts rename to site/src/pages/AgentsPage/utils/modelOptions.ts index b2ce754db9..60c9e46479 100644 --- a/site/src/pages/AgentsPage/modelOptions.ts +++ b/site/src/pages/AgentsPage/utils/modelOptions.ts @@ -41,6 +41,41 @@ export const getNormalizedModelRef = ( }; }; +/** + * Build a lookup from model reference strings (both "provider:model" and + * "provider/model" forms) to model config IDs. + */ +export const buildModelConfigIDByModelID = ( + configs: + | readonly Pick[] + | undefined, +): ReadonlyMap => { + const byModelID = new Map(); + for (const config of configs ?? []) { + const { provider, model } = getNormalizedModelRef(config); + if (!provider || !model) continue; + const colonRef = `${provider}:${model}`; + if (!byModelID.has(colonRef)) byModelID.set(colonRef, config.id); + const slashRef = `${provider}/${model}`; + if (!byModelID.has(slashRef)) byModelID.set(slashRef, config.id); + } + return byModelID; +}; + +/** + * Build a reverse lookup from model config IDs back to model reference + * strings. Uses the first matching reference for each config ID. + */ +export const buildModelIDByConfigID = ( + modelConfigIDByModelID: ReadonlyMap, +): ReadonlyMap => { + const byConfigID = new Map(); + for (const [modelID, configID] of modelConfigIDByModelID.entries()) { + if (!byConfigID.has(configID)) byConfigID.set(configID, modelID); + } + return byConfigID; +}; + const getCatalogProviders = ( catalog: ModelCatalogLike | null | undefined, ): readonly CatalogProviderLike[] => { diff --git a/site/src/pages/AgentsPage/pullRequest.test.ts b/site/src/pages/AgentsPage/utils/pullRequest.test.ts similarity index 100% rename from site/src/pages/AgentsPage/pullRequest.test.ts rename to site/src/pages/AgentsPage/utils/pullRequest.test.ts diff --git a/site/src/pages/AgentsPage/pullRequest.ts b/site/src/pages/AgentsPage/utils/pullRequest.ts similarity index 100% rename from site/src/pages/AgentsPage/pullRequest.ts rename to site/src/pages/AgentsPage/utils/pullRequest.ts diff --git a/site/src/pages/AgentsPage/timeGroups.test.ts b/site/src/pages/AgentsPage/utils/timeGroups.test.ts similarity index 100% rename from site/src/pages/AgentsPage/timeGroups.test.ts rename to site/src/pages/AgentsPage/utils/timeGroups.test.ts diff --git a/site/src/pages/AgentsPage/timeGroups.ts b/site/src/pages/AgentsPage/utils/timeGroups.ts similarity index 100% rename from site/src/pages/AgentsPage/timeGroups.ts rename to site/src/pages/AgentsPage/utils/timeGroups.ts diff --git a/site/src/pages/AgentsPage/usageLimitMessage.test.ts b/site/src/pages/AgentsPage/utils/usageLimitMessage.test.ts similarity index 100% rename from site/src/pages/AgentsPage/usageLimitMessage.test.ts rename to site/src/pages/AgentsPage/utils/usageLimitMessage.test.ts diff --git a/site/src/pages/AgentsPage/usageLimitMessage.ts b/site/src/pages/AgentsPage/utils/usageLimitMessage.ts similarity index 100% rename from site/src/pages/AgentsPage/usageLimitMessage.ts rename to site/src/pages/AgentsPage/utils/usageLimitMessage.ts diff --git a/site/src/router.tsx b/site/src/router.tsx index f555f71a8c..3c114b697c 100644 --- a/site/src/router.tsx +++ b/site/src/router.tsx @@ -349,11 +349,20 @@ const ProvisionerJobsPage = lazy( const AgentsPage = lazy(() => import("./pages/AgentsPage/AgentsPage")); const AgentDetail = lazy(() => import("./pages/AgentsPage/AgentDetail")); const AgentEmbedPage = lazy(() => import("./pages/AgentsPage/AgentEmbedPage")); +const AgentCreatePage = lazy( + () => import("./pages/AgentsPage/AgentCreatePage"), +); +const AgentSettingsPage = lazy( + () => import("./pages/AgentsPage/AgentSettingsPage"), +); +const AgentAnalyticsPage = lazy( + () => import("./pages/AgentsPage/AgentAnalyticsPage"), +); import { AgentDetailSkeleton, AgentsPageSkeleton, -} from "./pages/AgentsPage/AgentsSkeletons"; +} from "./pages/AgentsPage/components/AgentsSkeletons"; const TasksPage = lazy(() => import("./pages/TasksPage/TasksPage")); const TaskPage = lazy(() => import("./pages/TaskPage/TaskPage")); @@ -645,9 +654,10 @@ export const router = createBrowserRouter( } > - - - + } /> + } /> + } /> + } />{" "}