From e0e754e7f3944aa68c112534838af91244a6d095 Mon Sep 17 00:00:00 2001 From: Danielle Maywood Date: Mon, 8 Jun 2026 11:37:36 +0000 Subject: [PATCH] refactor(site/src/pages/AgentsPage): share setup notice component --- site/src/pages/AgentsPage/AgentChatPage.tsx | 32 ++--------- .../AgentsPage/AgentChatPageView.stories.tsx | 33 ++++++----- .../pages/AgentsPage/AgentChatPageView.tsx | 3 +- site/src/pages/AgentsPage/AgentCreatePage.tsx | 30 ++-------- .../AgentsPage/components/AgentChatInput.tsx | 10 +++- .../components/AgentCreateForm.stories.tsx | 9 +-- .../AgentsPage/components/AgentCreateForm.tsx | 12 +--- .../components/AgentSetupNoticeBanner.tsx | 55 +++++++++++++++++++ .../AgentsPage/components/ChatPageContent.tsx | 3 +- 9 files changed, 107 insertions(+), 80 deletions(-) create mode 100644 site/src/pages/AgentsPage/components/AgentSetupNoticeBanner.tsx diff --git a/site/src/pages/AgentsPage/AgentChatPage.tsx b/site/src/pages/AgentsPage/AgentChatPage.tsx index 358355adbef66..eb9fd221c6980 100644 --- a/site/src/pages/AgentsPage/AgentChatPage.tsx +++ b/site/src/pages/AgentsPage/AgentChatPage.tsx @@ -63,7 +63,7 @@ import { } from "./AgentChatPageView"; import type { AgentsOutletContext } from "./AgentsPage"; import type { ChatMessageInputRef } from "./components/AgentChatInput"; -import { AgentSetupNotice } from "./components/AgentSetupNotice"; +import { getAgentSetupNoticeConfig } from "./components/AgentSetupNoticeBanner"; import { normalizeChatErrorPayload } from "./components/ChatConversation/chatError"; import { getParentChatID, @@ -1129,31 +1129,11 @@ const AgentChatPage: FC = () => { hasConfiguredModels, hasUserFixableModelProviders, }); - const isAdmin = permissions.editDeploymentConfig; - const agentSetupNotice = (() => { - // Admin: show when providers or models are missing - if ( - isAdmin && - providerCount !== undefined && - modelCount !== undefined && - (providerCount === 0 || modelCount === 0) - ) { - return ( - - ); - } - // Member: show when no models are available - if (!isAdmin && modelCount !== undefined && modelCount === 0) { - return ( - - ); - } - return undefined; - })(); + const agentSetupNotice = getAgentSetupNoticeConfig({ + canConfigureAgentSetup: permissions.editDeploymentConfig, + providerCount: providerCount ?? 0, + modelCount: modelCount ?? 0, + }); const isSubmissionPending = isSendPending || isEditPending || isInterruptPending; const isChatSettingsPending = diff --git a/site/src/pages/AgentsPage/AgentChatPageView.stories.tsx b/site/src/pages/AgentsPage/AgentChatPageView.stories.tsx index 58bf4ec44d0f6..71d98908f4549 100644 --- a/site/src/pages/AgentsPage/AgentChatPageView.stories.tsx +++ b/site/src/pages/AgentsPage/AgentChatPageView.stories.tsx @@ -25,7 +25,6 @@ import { AgentChatPageNotFoundView, AgentChatPageView, } from "./AgentChatPageView"; -import { AgentSetupNotice } from "./components/AgentSetupNotice"; import { createChatStore, useChatSelector, @@ -526,9 +525,11 @@ export const NoModelOptions: Story = { export const MissingProviderAndModelSetup: Story = { render: () => ( - } + agentSetupNotice={{ + canConfigureAgentSetup: true, + providerCount: 0, + modelCount: 0, + }} hasModelOptions={false} modelOptions={[]} isInputDisabled @@ -561,9 +562,11 @@ export const MissingProviderAndModelSetup: Story = { export const MissingModelSetup: Story = { render: () => ( - } + agentSetupNotice={{ + canConfigureAgentSetup: true, + providerCount: 1, + modelCount: 0, + }} hasModelOptions={false} modelOptions={[]} isInputDisabled @@ -592,9 +595,11 @@ export const MissingModelSetup: Story = { export const MissingProviderSetup: Story = { render: () => ( - } + agentSetupNotice={{ + canConfigureAgentSetup: true, + providerCount: 0, + modelCount: 1, + }} /> ), play: async ({ canvasElement }) => { @@ -620,9 +625,11 @@ export const MissingProviderSetup: Story = { export const MemberNoModelsAvailable: Story = { render: () => ( - } + agentSetupNotice={{ + canConfigureAgentSetup: false, + providerCount: 0, + modelCount: 0, + }} hasModelOptions={false} modelOptions={[]} isInputDisabled diff --git a/site/src/pages/AgentsPage/AgentChatPageView.tsx b/site/src/pages/AgentsPage/AgentChatPageView.tsx index c6cdd3c381518..e120bb1c1ff5e 100644 --- a/site/src/pages/AgentsPage/AgentChatPageView.tsx +++ b/site/src/pages/AgentsPage/AgentChatPageView.tsx @@ -22,6 +22,7 @@ import { AgentChatInput, type ChatMessageInputRef, } from "./components/AgentChatInput"; +import type { AgentSetupNoticeConfig } from "./components/AgentSetupNoticeBanner"; import { ChatConversationSkeleton, RightPanelSkeleton, @@ -116,7 +117,7 @@ interface AgentChatPageViewProps { modelOptions: readonly ModelSelectorOption[]; modelSelectorPlaceholder: string; modelSelectorHelp?: ReactNode; - agentSetupNotice?: ReactNode; + agentSetupNotice?: AgentSetupNoticeConfig; hasModelOptions: boolean; isModelCatalogLoading?: boolean; planModeEnabled?: boolean; diff --git a/site/src/pages/AgentsPage/AgentCreatePage.tsx b/site/src/pages/AgentsPage/AgentCreatePage.tsx index 18415a820b4bf..26bb381bc071b 100644 --- a/site/src/pages/AgentsPage/AgentCreatePage.tsx +++ b/site/src/pages/AgentsPage/AgentCreatePage.tsx @@ -21,7 +21,7 @@ import { type CreateChatOptions, } from "./components/AgentCreateForm"; import { AgentPageHeader } from "./components/AgentPageHeader"; -import { AgentSetupNotice } from "./components/AgentSetupNotice"; +import { getAgentSetupNoticeConfig } from "./components/AgentSetupNoticeBanner"; import { ChimeButton } from "./components/ChimeButton"; import { WebPushButton } from "./components/WebPushButton"; import { getAgentChatSendShortcut } from "./utils/agentChatSendShortcut"; @@ -72,29 +72,11 @@ const AgentCreatePage: FC = () => { chatModelConfigsQuery.isSuccess && chatModelsQuery.isSuccess ? catalogModelOptions.length : undefined; - const isAdmin = permissions.editDeploymentConfig; - const agentSetupNotice = (() => { - if ( - isAdmin && - providerCount !== undefined && - modelCount !== undefined && - (providerCount === 0 || modelCount === 0) - ) { - return ( - - ); - } - if (!isAdmin && modelCount !== undefined && modelCount === 0) { - return ( - - ); - } - return undefined; - })(); + const agentSetupNotice = getAgentSetupNoticeConfig({ + canConfigureAgentSetup: permissions.editDeploymentConfig, + providerCount: providerCount ?? 0, + modelCount: modelCount ?? 0, + }); const handleCreateChat = async ({ message, diff --git a/site/src/pages/AgentsPage/components/AgentChatInput.tsx b/site/src/pages/AgentsPage/components/AgentChatInput.tsx index b24ce61bd1198..ebcba1e1e00d6 100644 --- a/site/src/pages/AgentsPage/components/AgentChatInput.tsx +++ b/site/src/pages/AgentsPage/components/AgentChatInput.tsx @@ -67,6 +67,10 @@ import { isChatAttachmentFile, } from "../utils/chatAttachments"; import { formatProviderLabel } from "../utils/modelOptions"; +import { + AgentSetupNoticeBanner, + type AgentSetupNoticeConfig, +} from "./AgentSetupNoticeBanner"; import { AttachmentPreview, isUploadInProgress, @@ -183,7 +187,7 @@ interface AgentChatInputProps { sshCommand?: string; attachedWorkspace?: AttachedWorkspaceInfo; folder?: string; - agentSetupNotice?: React.ReactNode; + agentSetupNotice?: AgentSetupNoticeConfig; } export interface AttachedWorkspaceInfo { @@ -1045,7 +1049,9 @@ export const AgentChatInput: FC = ({ /> )} {agentSetupNotice && ( -
{agentSetupNotice}
+
+ +
)}
- ), + agentSetupNotice: { + canConfigureAgentSetup: true, + providerCount: 0, + modelCount: 0, + }, modelCatalog: { providers: [] }, modelOptions: [], isModelCatalogLoading: false, diff --git a/site/src/pages/AgentsPage/components/AgentCreateForm.tsx b/site/src/pages/AgentsPage/components/AgentCreateForm.tsx index d4ec3b59563fc..93462c212533f 100644 --- a/site/src/pages/AgentsPage/components/AgentCreateForm.tsx +++ b/site/src/pages/AgentsPage/components/AgentCreateForm.tsx @@ -1,11 +1,4 @@ -import { - type FC, - type ReactNode, - useEffect, - useEffectEvent, - useRef, - useState, -} from "react"; +import { type FC, useEffect, useEffectEvent, useRef, useState } from "react"; import { useQuery } from "react-query"; import { Link } from "react-router"; import { toast } from "sonner"; @@ -32,6 +25,7 @@ import { isChatUsageLimitExceededResponse, } from "../utils/usageLimitMessage"; import { AgentChatInput } from "./AgentChatInput"; +import type { AgentSetupNoticeConfig } from "./AgentSetupNoticeBanner"; import { ChatAccessDeniedAlert } from "./ChatAccessDeniedAlert"; import type { ModelSelectorOption } from "./ChatElements"; import { CompactOrgSelector } from "./ChatElements"; @@ -132,7 +126,7 @@ interface AgentCreateFormProps { canCreateChat: boolean; modelCatalog: TypesGen.ChatModelsResponse | null | undefined; modelOptions: readonly ChatModelOption[]; - agentSetupNotice?: ReactNode; + agentSetupNotice?: AgentSetupNoticeConfig; isModelCatalogLoading: boolean; modelConfigs: readonly TypesGen.ChatModelConfig[]; isModelConfigsLoading: boolean; diff --git a/site/src/pages/AgentsPage/components/AgentSetupNoticeBanner.tsx b/site/src/pages/AgentsPage/components/AgentSetupNoticeBanner.tsx new file mode 100644 index 0000000000000..bd641ec467e49 --- /dev/null +++ b/site/src/pages/AgentsPage/components/AgentSetupNoticeBanner.tsx @@ -0,0 +1,55 @@ +import { AgentSetupNotice } from "./AgentSetupNotice"; + +export interface AgentSetupNoticeConfig { + canConfigureAgentSetup: boolean; + providerCount: number; + modelCount: number; +} + +const shouldRenderAgentSetupNotice = ({ + canConfigureAgentSetup, + providerCount, + modelCount, +}: AgentSetupNoticeConfig) => { + if (!canConfigureAgentSetup) { + return modelCount === 0; + } + + return providerCount === 0 || modelCount === 0; +}; + +export const getAgentSetupNoticeConfig = ( + config: AgentSetupNoticeConfig, +): AgentSetupNoticeConfig | undefined => { + return shouldRenderAgentSetupNotice(config) ? config : undefined; +}; + +export const AgentSetupNoticeBanner = ({ + canConfigureAgentSetup, + providerCount, + modelCount, +}: AgentSetupNoticeConfig) => { + if ( + !shouldRenderAgentSetupNotice({ + canConfigureAgentSetup, + providerCount, + modelCount, + }) + ) { + return null; + } + + if (!canConfigureAgentSetup) { + return ( + + ); + } + + return ( + + ); +}; diff --git a/site/src/pages/AgentsPage/components/ChatPageContent.tsx b/site/src/pages/AgentsPage/components/ChatPageContent.tsx index 9599b273d16f9..935349371fa1b 100644 --- a/site/src/pages/AgentsPage/components/ChatPageContent.tsx +++ b/site/src/pages/AgentsPage/components/ChatPageContent.tsx @@ -19,6 +19,7 @@ import { isUploadInProgress, type UploadState, } from "./AgentChatInput"; +import type { AgentSetupNoticeConfig } from "./AgentSetupNoticeBanner"; import { ConversationTimeline } from "./ChatConversation/ConversationTimeline"; import { getLatestContextUsage } from "./ChatConversation/chatHelpers"; import { @@ -169,7 +170,7 @@ interface ChatPageInputProps { modelOptions: readonly ModelSelectorOption[]; modelSelectorPlaceholder: string; modelSelectorHelp?: ReactNode; - agentSetupNotice?: ReactNode; + agentSetupNotice?: AgentSetupNoticeConfig; planModeEnabled?: boolean; onPlanModeToggle?: (enabled: boolean) => void; isModelCatalogLoading?: boolean;