mirror of
https://github.com/coder/coder.git
synced 2026-09-22 05:05:20 +08:00
refactor(site/src/pages/AgentsPage): centralize setup notice rendering (#26134)
This commit is contained in:
@@ -63,7 +63,6 @@ import {
|
||||
} from "./AgentChatPageView";
|
||||
import type { AgentsOutletContext } from "./AgentsPage";
|
||||
import type { ChatMessageInputRef } from "./components/AgentChatInput";
|
||||
import { AgentSetupNotice } from "./components/AgentSetupNotice";
|
||||
import { normalizeChatErrorPayload } from "./components/ChatConversation/chatError";
|
||||
import {
|
||||
getParentChatID,
|
||||
@@ -1130,31 +1129,6 @@ 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 (
|
||||
<AgentSetupNotice
|
||||
isAdmin
|
||||
providerCount={providerCount}
|
||||
modelCount={modelCount}
|
||||
/>
|
||||
);
|
||||
}
|
||||
// Member: show when no models are available
|
||||
if (!isAdmin && modelCount !== undefined && modelCount === 0) {
|
||||
return (
|
||||
<AgentSetupNotice isAdmin={false} providerCount={0} modelCount={0} />
|
||||
);
|
||||
}
|
||||
return undefined;
|
||||
})();
|
||||
const isSubmissionPending =
|
||||
isSendPending || isEditPending || isInterruptPending;
|
||||
const isChatSettingsPending =
|
||||
@@ -1626,7 +1600,9 @@ const AgentChatPage: FC = () => {
|
||||
modelOptions={modelOptions}
|
||||
modelSelectorPlaceholder={modelSelectorPlaceholder}
|
||||
modelSelectorHelp={modelSelectorHelp}
|
||||
agentSetupNotice={agentSetupNotice}
|
||||
canConfigureAgentSetup={permissions.editDeploymentConfig}
|
||||
providerCount={providerCount}
|
||||
modelCount={modelCount}
|
||||
hasModelOptions={hasModelOptions}
|
||||
isModelCatalogLoading={isModelCatalogLoading}
|
||||
planModeEnabled={planModeEnabled}
|
||||
|
||||
@@ -25,7 +25,6 @@ import {
|
||||
AgentChatPageNotFoundView,
|
||||
AgentChatPageView,
|
||||
} from "./AgentChatPageView";
|
||||
import { AgentSetupNotice } from "./components/AgentSetupNotice";
|
||||
import {
|
||||
createChatStore,
|
||||
useChatSelector,
|
||||
@@ -190,6 +189,9 @@ const StoryAgentChatPageView: FC<StoryProps> = ({ editing, ...overrides }) => {
|
||||
onMCPSelectionChange: fn(),
|
||||
onMCPAuthComplete: fn(),
|
||||
canShareChat: false,
|
||||
canConfigureAgentSetup: true,
|
||||
providerCount: 1,
|
||||
modelCount: 1,
|
||||
...overrides,
|
||||
store,
|
||||
messageCount: overrides.messageCount ?? messageCount,
|
||||
@@ -528,9 +530,9 @@ export const NoModelOptions: Story = {
|
||||
export const MissingProviderAndModelSetup: Story = {
|
||||
render: () => (
|
||||
<StoryAgentChatPageView
|
||||
agentSetupNotice={
|
||||
<AgentSetupNotice isAdmin providerCount={0} modelCount={0} />
|
||||
}
|
||||
canConfigureAgentSetup
|
||||
providerCount={0}
|
||||
modelCount={0}
|
||||
hasModelOptions={false}
|
||||
modelOptions={[]}
|
||||
isInputDisabled
|
||||
@@ -563,9 +565,9 @@ export const MissingProviderAndModelSetup: Story = {
|
||||
export const MissingModelSetup: Story = {
|
||||
render: () => (
|
||||
<StoryAgentChatPageView
|
||||
agentSetupNotice={
|
||||
<AgentSetupNotice isAdmin providerCount={1} modelCount={0} />
|
||||
}
|
||||
canConfigureAgentSetup
|
||||
providerCount={1}
|
||||
modelCount={0}
|
||||
hasModelOptions={false}
|
||||
modelOptions={[]}
|
||||
isInputDisabled
|
||||
@@ -594,9 +596,9 @@ export const MissingModelSetup: Story = {
|
||||
export const MissingProviderSetup: Story = {
|
||||
render: () => (
|
||||
<StoryAgentChatPageView
|
||||
agentSetupNotice={
|
||||
<AgentSetupNotice isAdmin providerCount={0} modelCount={1} />
|
||||
}
|
||||
canConfigureAgentSetup
|
||||
providerCount={0}
|
||||
modelCount={1}
|
||||
/>
|
||||
),
|
||||
play: async ({ canvasElement }) => {
|
||||
@@ -622,9 +624,9 @@ export const MissingProviderSetup: Story = {
|
||||
export const MemberNoModelsAvailable: Story = {
|
||||
render: () => (
|
||||
<StoryAgentChatPageView
|
||||
agentSetupNotice={
|
||||
<AgentSetupNotice isAdmin={false} providerCount={0} modelCount={0} />
|
||||
}
|
||||
canConfigureAgentSetup={false}
|
||||
providerCount={0}
|
||||
modelCount={0}
|
||||
hasModelOptions={false}
|
||||
modelOptions={[]}
|
||||
isInputDisabled
|
||||
|
||||
@@ -128,7 +128,9 @@ interface AgentChatPageViewProps {
|
||||
modelOptions: readonly ModelSelectorOption[];
|
||||
modelSelectorPlaceholder: string;
|
||||
modelSelectorHelp?: ReactNode;
|
||||
agentSetupNotice?: ReactNode;
|
||||
canConfigureAgentSetup: boolean;
|
||||
providerCount?: number;
|
||||
modelCount?: number;
|
||||
hasModelOptions: boolean;
|
||||
isModelCatalogLoading?: boolean;
|
||||
planModeEnabled?: boolean;
|
||||
@@ -263,7 +265,9 @@ export const AgentChatPageView: FC<AgentChatPageViewProps> = ({
|
||||
modelOptions,
|
||||
modelSelectorPlaceholder,
|
||||
modelSelectorHelp,
|
||||
agentSetupNotice,
|
||||
canConfigureAgentSetup,
|
||||
providerCount,
|
||||
modelCount,
|
||||
hasModelOptions,
|
||||
isModelCatalogLoading = false,
|
||||
planModeEnabled,
|
||||
@@ -748,12 +752,14 @@ export const AgentChatPageView: FC<AgentChatPageViewProps> = ({
|
||||
isSendPending={isSubmissionPending}
|
||||
isInterruptPending={isInterruptPending}
|
||||
hasModelOptions={hasModelOptions}
|
||||
canConfigureAgentSetup={canConfigureAgentSetup}
|
||||
providerCount={providerCount}
|
||||
modelCount={modelCount}
|
||||
selectedModel={effectiveSelectedModel}
|
||||
onModelChange={setSelectedModel}
|
||||
modelOptions={modelOptions}
|
||||
modelSelectorPlaceholder={modelSelectorPlaceholder}
|
||||
modelSelectorHelp={modelSelectorHelp}
|
||||
agentSetupNotice={agentSetupNotice}
|
||||
planModeEnabled={planModeEnabled}
|
||||
onPlanModeToggle={onPlanModeToggle}
|
||||
isModelCatalogLoading={isModelCatalogLoading}
|
||||
@@ -911,6 +917,7 @@ export const AgentChatPageLoadingView: FC<AgentChatPageLoadingViewProps> = ({
|
||||
onPlanModeToggle={onPlanModeToggle}
|
||||
isModelCatalogLoading={isModelCatalogLoading}
|
||||
hasModelOptions={hasModelOptions}
|
||||
canConfigureAgentSetup={false}
|
||||
/>
|
||||
</div>{" "}
|
||||
</div>
|
||||
|
||||
@@ -21,7 +21,6 @@ import {
|
||||
type CreateChatOptions,
|
||||
} from "./components/AgentCreateForm";
|
||||
import { AgentPageHeader } from "./components/AgentPageHeader";
|
||||
import { AgentSetupNotice } from "./components/AgentSetupNotice";
|
||||
import { ChimeButton } from "./components/ChimeButton";
|
||||
import { WebPushButton } from "./components/WebPushButton";
|
||||
import { getAgentChatSendShortcut } from "./utils/agentChatSendShortcut";
|
||||
@@ -73,29 +72,6 @@ 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 (
|
||||
<AgentSetupNotice
|
||||
isAdmin
|
||||
providerCount={providerCount}
|
||||
modelCount={modelCount}
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (!isAdmin && modelCount !== undefined && modelCount === 0) {
|
||||
return (
|
||||
<AgentSetupNotice isAdmin={false} providerCount={0} modelCount={0} />
|
||||
);
|
||||
}
|
||||
return undefined;
|
||||
})();
|
||||
|
||||
const handleCreateChat = async ({
|
||||
message,
|
||||
@@ -181,7 +157,9 @@ const AgentCreatePage: FC = () => {
|
||||
canCreateChat={permissions.createChat}
|
||||
modelCatalog={chatModelsQuery.data}
|
||||
modelOptions={catalogModelOptions}
|
||||
agentSetupNotice={agentSetupNotice}
|
||||
canConfigureAgentSetup={permissions.editDeploymentConfig}
|
||||
providerCount={providerCount}
|
||||
modelCount={modelCount}
|
||||
modelConfigs={chatModelConfigsQuery.data ?? []}
|
||||
isModelCatalogLoading={chatModelsQuery.isLoading}
|
||||
isModelConfigsLoading={chatModelConfigsQuery.isLoading}
|
||||
|
||||
@@ -67,6 +67,7 @@ import {
|
||||
isChatAttachmentFile,
|
||||
} from "../utils/chatAttachments";
|
||||
import { formatProviderLabel } from "../utils/modelOptions";
|
||||
import { AgentSetupNotice } from "./AgentSetupNotice";
|
||||
import {
|
||||
AttachmentPreview,
|
||||
isUploadInProgress,
|
||||
@@ -183,7 +184,9 @@ interface AgentChatInputProps {
|
||||
sshCommand?: string;
|
||||
attachedWorkspace?: AttachedWorkspaceInfo;
|
||||
folder?: string;
|
||||
agentSetupNotice?: React.ReactNode;
|
||||
canConfigureAgentSetup: boolean;
|
||||
providerCount?: number;
|
||||
modelCount?: number;
|
||||
}
|
||||
|
||||
export interface AttachedWorkspaceInfo {
|
||||
@@ -381,9 +384,16 @@ export const AgentChatInput: FC<AgentChatInputProps> = ({
|
||||
sshCommand,
|
||||
attachedWorkspace,
|
||||
folder,
|
||||
agentSetupNotice,
|
||||
canConfigureAgentSetup,
|
||||
providerCount,
|
||||
modelCount,
|
||||
}) => {
|
||||
const [chatFullWidth] = useChatFullWidth();
|
||||
const showAgentSetupNotice = canConfigureAgentSetup
|
||||
? providerCount !== undefined &&
|
||||
modelCount !== undefined &&
|
||||
(providerCount === 0 || modelCount === 0)
|
||||
: modelCount !== undefined && modelCount === 0;
|
||||
const internalRef = useRef<ChatMessageInputRef>(null);
|
||||
const [previewImage, setPreviewImage] = useState<string | null>(null);
|
||||
const [previewText, setPreviewText] = useState<string | null>(null);
|
||||
@@ -1044,15 +1054,31 @@ export const AgentChatInput: FC<AgentChatInputProps> = ({
|
||||
className="mb-2"
|
||||
/>
|
||||
)}
|
||||
{agentSetupNotice && (
|
||||
<div className="relative z-0 mb-[-2.5rem]">{agentSetupNotice}</div>
|
||||
{showAgentSetupNotice && (
|
||||
<div className="relative z-0 mb-[-2.5rem]">
|
||||
{canConfigureAgentSetup &&
|
||||
providerCount !== undefined &&
|
||||
modelCount !== undefined ? (
|
||||
<AgentSetupNotice
|
||||
isAdmin
|
||||
providerCount={providerCount}
|
||||
modelCount={modelCount}
|
||||
/>
|
||||
) : (
|
||||
<AgentSetupNotice
|
||||
isAdmin={false}
|
||||
providerCount={0}
|
||||
modelCount={0}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
ref={setComposerElement}
|
||||
data-testid="chat-composer"
|
||||
className={cn(
|
||||
"relative z-10 rounded-2xl border border-border-default/80 bg-surface-secondary sm:bg-surface-secondary/45 p-1 shadow-sm has-[textarea:focus]:ring-2 has-[textarea:focus]:ring-content-link/40",
|
||||
agentSetupNotice && "sm:bg-surface-secondary",
|
||||
showAgentSetupNotice && "sm:bg-surface-secondary",
|
||||
isDragging && "ring-2 ring-content-link/40",
|
||||
isEditingHistoryMessage &&
|
||||
"shadow-[0_0_0_2px_hsla(var(--border-warning),0.6)]",
|
||||
@@ -1177,7 +1203,9 @@ export const AgentChatInput: FC<AgentChatInputProps> = ({
|
||||
size="icon"
|
||||
className="size-7 shrink-0 rounded-full [&>svg]:!size-icon-sm [&>svg]:p-0"
|
||||
disabled={
|
||||
isDisabled && !agentSetupNotice && !canUseWorkspacePicker
|
||||
isDisabled &&
|
||||
!showAgentSetupNotice &&
|
||||
!canUseWorkspacePicker
|
||||
}
|
||||
aria-label="More options"
|
||||
>
|
||||
|
||||
@@ -18,7 +18,6 @@ import {
|
||||
} from "#/testHelpers/entities";
|
||||
import { withDashboardProvider } from "#/testHelpers/storybook";
|
||||
import { AgentCreateForm } from "./AgentCreateForm";
|
||||
import { AgentSetupNotice } from "./AgentSetupNotice";
|
||||
|
||||
// Query key used by permittedOrganizations() in the form.
|
||||
const permittedOrgsKey = [
|
||||
@@ -471,9 +470,9 @@ export const NoModelsConfigured: Story = {
|
||||
export const MissingProviderAndModelSetup: Story = {
|
||||
args: {
|
||||
...defaultArgs,
|
||||
agentSetupNotice: (
|
||||
<AgentSetupNotice isAdmin providerCount={0} modelCount={0} />
|
||||
),
|
||||
canConfigureAgentSetup: true,
|
||||
providerCount: 0,
|
||||
modelCount: 0,
|
||||
modelCatalog: { providers: [] },
|
||||
modelOptions: [],
|
||||
isModelCatalogLoading: false,
|
||||
|
||||
@@ -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";
|
||||
@@ -132,7 +125,9 @@ interface AgentCreateFormProps {
|
||||
canCreateChat: boolean;
|
||||
modelCatalog: TypesGen.ChatModelsResponse | null | undefined;
|
||||
modelOptions: readonly ChatModelOption[];
|
||||
agentSetupNotice?: ReactNode;
|
||||
canConfigureAgentSetup: boolean;
|
||||
providerCount?: number;
|
||||
modelCount?: number;
|
||||
isModelCatalogLoading: boolean;
|
||||
modelConfigs: readonly TypesGen.ChatModelConfig[];
|
||||
isModelConfigsLoading: boolean;
|
||||
@@ -154,7 +149,9 @@ export const AgentCreateForm: FC<AgentCreateFormProps> = ({
|
||||
canCreateChat,
|
||||
modelCatalog,
|
||||
modelOptions,
|
||||
agentSetupNotice,
|
||||
canConfigureAgentSetup,
|
||||
providerCount,
|
||||
modelCount,
|
||||
modelConfigs,
|
||||
isModelCatalogLoading,
|
||||
isModelConfigsLoading,
|
||||
@@ -548,7 +545,9 @@ export const AgentCreateForm: FC<AgentCreateFormProps> = ({
|
||||
selectedWorkspaceId={effectiveWorkspaceId}
|
||||
onWorkspaceChange={handleWorkspaceChange}
|
||||
isWorkspaceLoading={isWorkspacesLoading}
|
||||
agentSetupNotice={agentSetupNotice}
|
||||
canConfigureAgentSetup={canConfigureAgentSetup}
|
||||
providerCount={providerCount}
|
||||
modelCount={modelCount}
|
||||
/>
|
||||
{modelSelectorHelp ? (
|
||||
<div className="px-3 pt-1 text-2xs text-content-secondary">
|
||||
|
||||
@@ -169,7 +169,9 @@ interface ChatPageInputProps {
|
||||
modelOptions: readonly ModelSelectorOption[];
|
||||
modelSelectorPlaceholder: string;
|
||||
modelSelectorHelp?: ReactNode;
|
||||
agentSetupNotice?: ReactNode;
|
||||
canConfigureAgentSetup: boolean;
|
||||
providerCount?: number;
|
||||
modelCount?: number;
|
||||
planModeEnabled?: boolean;
|
||||
onPlanModeToggle?: (enabled: boolean) => void;
|
||||
isModelCatalogLoading?: boolean;
|
||||
@@ -234,7 +236,9 @@ export const ChatPageInput: FC<ChatPageInputProps> = ({
|
||||
modelOptions,
|
||||
modelSelectorPlaceholder,
|
||||
modelSelectorHelp,
|
||||
agentSetupNotice,
|
||||
canConfigureAgentSetup,
|
||||
providerCount,
|
||||
modelCount,
|
||||
planModeEnabled,
|
||||
onPlanModeToggle,
|
||||
isModelCatalogLoading = false,
|
||||
@@ -490,7 +494,9 @@ export const ChatPageInput: FC<ChatPageInputProps> = ({
|
||||
sshCommand={sshCommand}
|
||||
attachedWorkspace={attachedWorkspace}
|
||||
folder={folder}
|
||||
agentSetupNotice={agentSetupNotice}
|
||||
canConfigureAgentSetup={canConfigureAgentSetup}
|
||||
providerCount={providerCount}
|
||||
modelCount={modelCount}
|
||||
/>
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user