mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
fix(site/src/pages/AgentsPage): replace navigating buttons with anchor tags (#23426)
This commit is contained in:
@@ -68,8 +68,6 @@ const AgentCreatePage: FC = () => {
|
||||
navigate(`/agents/${createdChat.id}`);
|
||||
};
|
||||
|
||||
const handleOpenAnalytics = () => navigate("/agents/analytics");
|
||||
|
||||
return (
|
||||
<>
|
||||
<AgentPageHeader>
|
||||
@@ -86,7 +84,6 @@ const AgentCreatePage: FC = () => {
|
||||
isModelCatalogLoading={chatModelsQuery.isLoading}
|
||||
isModelConfigsLoading={chatModelConfigsQuery.isLoading}
|
||||
modelCatalogError={chatModelsQuery.error}
|
||||
onOpenAnalytics={handleOpenAnalytics}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -30,7 +30,7 @@ import {
|
||||
useQuery,
|
||||
useQueryClient,
|
||||
} from "react-query";
|
||||
import { useNavigate, useOutletContext, useParams } from "react-router";
|
||||
import { useOutletContext, useParams } from "react-router";
|
||||
import { toast } from "sonner";
|
||||
import type { UrlTransform } from "streamdown";
|
||||
import { isMobileViewport } from "utils/mobile";
|
||||
@@ -232,7 +232,6 @@ export function useConversationEditingState(deps: {
|
||||
}
|
||||
|
||||
const AgentDetail: FC = () => {
|
||||
const navigate = useNavigate();
|
||||
const { agentId } = useParams<{ agentId: string }>();
|
||||
const {
|
||||
chatErrorReasons,
|
||||
@@ -313,8 +312,6 @@ const AgentDetail: FC = () => {
|
||||
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(() => {
|
||||
@@ -861,7 +858,6 @@ const AgentDetail: FC = () => {
|
||||
isInterruptPending={interruptMutation.isPending}
|
||||
isSidebarCollapsed={isSidebarCollapsed}
|
||||
onToggleSidebarCollapsed={onToggleSidebarCollapsed}
|
||||
onOpenAnalytics={handleOpenAnalytics}
|
||||
showSidebarPanel={showSidebarPanel}
|
||||
onSetShowSidebarPanel={handleSetShowSidebarPanel}
|
||||
prNumber={prNumber}
|
||||
@@ -874,7 +870,6 @@ const AgentDetail: FC = () => {
|
||||
handleViewWorkspace={handleViewWorkspace}
|
||||
handleOpenTerminal={handleOpenTerminal}
|
||||
handleCommit={handleCommit}
|
||||
onNavigateToChat={(chatId) => navigate(`/agents/${chatId}`)}
|
||||
handleInterrupt={handleInterrupt}
|
||||
handleDeleteQueuedMessage={handleDeleteQueuedMessage}
|
||||
handlePromoteQueuedMessage={handlePromoteQueuedMessage}
|
||||
|
||||
@@ -23,6 +23,7 @@ import { Check, MonitorIcon } from "lucide-react";
|
||||
import { useDashboard } from "modules/dashboard/useDashboard";
|
||||
import { type FC, useEffect, useRef, useState } from "react";
|
||||
import { useQuery } from "react-query";
|
||||
import { Link } from "react-router";
|
||||
import { toast } from "sonner";
|
||||
import { useFileAttachments } from "../hooks/useFileAttachments";
|
||||
import {
|
||||
@@ -115,7 +116,6 @@ interface AgentCreateFormProps {
|
||||
modelConfigs: readonly TypesGen.ChatModelConfig[];
|
||||
isModelConfigsLoading: boolean;
|
||||
modelCatalogError: unknown;
|
||||
onOpenAnalytics?: () => void;
|
||||
}
|
||||
|
||||
export const AgentCreateForm: FC<AgentCreateFormProps> = ({
|
||||
@@ -128,7 +128,6 @@ export const AgentCreateForm: FC<AgentCreateFormProps> = ({
|
||||
isModelCatalogLoading,
|
||||
isModelConfigsLoading,
|
||||
modelCatalogError,
|
||||
onOpenAnalytics,
|
||||
}) => {
|
||||
const { organizations } = useDashboard();
|
||||
const { initialInputValue, handleContentChange, submitDraft, resetDraft } =
|
||||
@@ -335,11 +334,9 @@ export const AgentCreateForm: FC<AgentCreateFormProps> = ({
|
||||
severity="info"
|
||||
className="py-2"
|
||||
actions={
|
||||
onOpenAnalytics && (
|
||||
<Button variant="subtle" size="sm" onClick={onOpenAnalytics}>
|
||||
View Usage
|
||||
</Button>
|
||||
)
|
||||
<Button asChild variant="subtle" size="sm">
|
||||
<Link to="/agents/analytics">View Usage</Link>
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
{formatUsageLimitMessage(createError.response.data)}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import type * as TypesGen from "api/typesGenerated";
|
||||
import { expect, fn, userEvent, within } from "storybook/test";
|
||||
import { expect, within } from "storybook/test";
|
||||
import { ConversationTimeline } from "./ConversationTimeline";
|
||||
import { parseMessagesWithMergedTools } from "./messageParsing";
|
||||
|
||||
@@ -249,17 +249,16 @@ export const UsageLimitExceeded: Story = {
|
||||
message:
|
||||
"You've used $50.00 of your $50.00 spend limit. Your limit resets on July 1, 2025.",
|
||||
},
|
||||
onOpenAnalytics: fn(),
|
||||
|
||||
subagentTitles: new Map(),
|
||||
subagentStatusOverrides: new Map(),
|
||||
},
|
||||
play: async ({ args, canvasElement }) => {
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
expect(canvas.getByText(/spend limit/i)).toBeVisible();
|
||||
const btn = canvas.getByRole("button", { name: /view usage/i });
|
||||
expect(btn).toBeVisible();
|
||||
await userEvent.click(btn);
|
||||
expect(args.onOpenAnalytics).toHaveBeenCalled();
|
||||
const link = canvas.getByRole("link", { name: /view usage/i });
|
||||
expect(link).toBeVisible();
|
||||
expect(link).toHaveAttribute("href", "/agents/analytics");
|
||||
},
|
||||
};
|
||||
|
||||
@@ -269,7 +268,6 @@ export const GenericErrorDoesNotShowUsageAction: Story = {
|
||||
...defaultArgs,
|
||||
parsedMessages: [],
|
||||
detailError: { kind: "generic", message: "Provider request failed." },
|
||||
onOpenAnalytics: fn(),
|
||||
subagentTitles: new Map(),
|
||||
subagentStatusOverrides: new Map(),
|
||||
},
|
||||
@@ -277,7 +275,7 @@ export const GenericErrorDoesNotShowUsageAction: Story = {
|
||||
const canvas = within(canvasElement);
|
||||
expect(canvas.getByText(/provider request failed/i)).toBeVisible();
|
||||
expect(
|
||||
canvas.queryByRole("button", { name: /view usage/i }),
|
||||
canvas.queryByRole("link", { name: /view usage/i }),
|
||||
).not.toBeInTheDocument();
|
||||
},
|
||||
};
|
||||
|
||||
@@ -27,6 +27,7 @@ import {
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import { Link } from "react-router";
|
||||
import type { UrlTransform } from "streamdown";
|
||||
import { cn } from "utils/cn";
|
||||
import type { ChatDetailError } from "../../utils/usageLimitMessage";
|
||||
@@ -846,7 +847,6 @@ interface ConversationTimelineProps {
|
||||
retryState?: { attempt: number; error: string } | null;
|
||||
isAwaitingFirstStreamChunk: boolean;
|
||||
detailError?: ChatDetailError | null;
|
||||
onOpenAnalytics?: () => void;
|
||||
onEditUserMessage?: (
|
||||
messageId: number,
|
||||
text: string,
|
||||
@@ -868,7 +868,6 @@ export const ConversationTimeline: FC<ConversationTimelineProps> = ({
|
||||
retryState,
|
||||
isAwaitingFirstStreamChunk,
|
||||
detailError,
|
||||
onOpenAnalytics,
|
||||
onEditUserMessage,
|
||||
editingMessageId,
|
||||
savingMessageId,
|
||||
@@ -877,7 +876,6 @@ export const ConversationTimeline: FC<ConversationTimelineProps> = ({
|
||||
const shouldRenderStreamAfterMessages =
|
||||
hasStreamOutput && parsedMessages.length > 0;
|
||||
const isUsageLimitError = detailError?.kind === "usage-limit";
|
||||
const showUsageAction = onOpenAnalytics !== undefined && isUsageLimitError;
|
||||
|
||||
// Build a set of message IDs that appear after the message
|
||||
// currently being edited so they can be visually faded.
|
||||
@@ -954,9 +952,9 @@ export const ConversationTimeline: FC<ConversationTimelineProps> = ({
|
||||
severity={isUsageLimitError ? "info" : "error"}
|
||||
className="py-2"
|
||||
actions={
|
||||
showUsageAction && (
|
||||
<Button variant="subtle" size="sm" onClick={onOpenAnalytics}>
|
||||
View Usage
|
||||
isUsageLimitError && (
|
||||
<Button asChild variant="subtle" size="sm">
|
||||
<Link to="/agents/analytics">View Usage</Link>
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import { AgentDetailTopBar } from "./TopBar";
|
||||
|
||||
const defaultProps = {
|
||||
chatTitle: "Build authentication feature",
|
||||
onOpenParentChat: () => {},
|
||||
panel: {
|
||||
showSidebarPanel: false,
|
||||
onToggleSidebar: () => {},
|
||||
|
||||
@@ -24,7 +24,7 @@ import {
|
||||
Trash2Icon,
|
||||
} from "lucide-react";
|
||||
import type { FC } from "react";
|
||||
import { useNavigate } from "react-router";
|
||||
import { Link } from "react-router";
|
||||
import { toast } from "sonner";
|
||||
import { cn } from "utils/cn";
|
||||
import { parsePullRequestUrl } from "../../utils/pullRequest";
|
||||
@@ -48,7 +48,6 @@ interface WorkspaceActions {
|
||||
type AgentDetailTopBarProps = {
|
||||
chatTitle?: string;
|
||||
parentChat?: TypesGen.Chat;
|
||||
onOpenParentChat: (chatId: string) => void;
|
||||
panel: SidebarPanelState;
|
||||
workspace: WorkspaceActions;
|
||||
onArchiveAgent: () => void;
|
||||
@@ -64,7 +63,6 @@ type AgentDetailTopBarProps = {
|
||||
export const AgentDetailTopBar: FC<AgentDetailTopBarProps> = ({
|
||||
chatTitle,
|
||||
parentChat,
|
||||
onOpenParentChat,
|
||||
panel,
|
||||
workspace,
|
||||
onArchiveAgent,
|
||||
@@ -76,7 +74,6 @@ export const AgentDetailTopBar: FC<AgentDetailTopBarProps> = ({
|
||||
onToggleSidebarCollapsed,
|
||||
diffStatusData,
|
||||
}) => {
|
||||
const navigate = useNavigate();
|
||||
const { isEmbedded } = useEmbedContext();
|
||||
|
||||
const prUrl = diffStatusData?.url;
|
||||
@@ -93,13 +90,14 @@ export const AgentDetailTopBar: FC<AgentDetailTopBarProps> = ({
|
||||
{/* Mobile back button */}
|
||||
{!isEmbedded && (
|
||||
<Button
|
||||
asChild
|
||||
variant="subtle"
|
||||
size="icon"
|
||||
onClick={() => navigate("/agents")}
|
||||
aria-label="Back"
|
||||
className="inline-flex h-7 w-7 min-w-0 shrink-0 md:hidden"
|
||||
>
|
||||
<ArrowLeftIcon />
|
||||
<Link to="/agents" aria-label="Back">
|
||||
<ArrowLeftIcon />
|
||||
</Link>
|
||||
</Button>
|
||||
)}
|
||||
{/* Desktop expand button: visible when sidebar is manually collapsed. */}
|
||||
@@ -121,12 +119,14 @@ export const AgentDetailTopBar: FC<AgentDetailTopBarProps> = ({
|
||||
{parentChat && (
|
||||
<>
|
||||
<Button
|
||||
asChild
|
||||
size="sm"
|
||||
variant="subtle"
|
||||
className="h-auto max-w-[16rem] rounded-sm px-1 py-0.5 text-sm text-content-secondary shadow-none hover:bg-transparent hover:text-content-primary"
|
||||
onClick={() => onOpenParentChat(parentChat.id)}
|
||||
>
|
||||
<span className="truncate">{parentChat.title}</span>
|
||||
<Link to={`/agents/${parentChat.id}`}>
|
||||
<span className="truncate">{parentChat.title}</span>
|
||||
</Link>
|
||||
</Button>
|
||||
<ChevronRightIcon className="h-3.5 w-3.5 shrink-0 text-content-secondary/70 -ml-0.5" />
|
||||
</>
|
||||
|
||||
@@ -42,7 +42,6 @@ const isChatMessage = (
|
||||
interface AgentDetailTimelineProps {
|
||||
store: ChatStoreHandle;
|
||||
persistedErrorReason: ChatDetailError | undefined;
|
||||
onOpenAnalytics?: () => void;
|
||||
onEditUserMessage?: (
|
||||
messageId: number,
|
||||
text: string,
|
||||
@@ -59,7 +58,6 @@ interface AgentDetailTimelineProps {
|
||||
const MessageListProvider: FC<AgentDetailTimelineProps> = ({
|
||||
store,
|
||||
persistedErrorReason,
|
||||
onOpenAnalytics,
|
||||
onEditUserMessage,
|
||||
editingMessageId,
|
||||
savingMessageId,
|
||||
@@ -102,7 +100,6 @@ const MessageListProvider: FC<AgentDetailTimelineProps> = ({
|
||||
detailError={detailError}
|
||||
latestMessageNeedsAssistantResponse={latestMessageNeedsAssistantResponse}
|
||||
chatStatus={chatStatus}
|
||||
onOpenAnalytics={onOpenAnalytics}
|
||||
onEditUserMessage={onEditUserMessage}
|
||||
editingMessageId={editingMessageId}
|
||||
savingMessageId={savingMessageId}
|
||||
@@ -123,7 +120,6 @@ const StreamingBridge: FC<{
|
||||
detailError: ChatDetailError | undefined;
|
||||
latestMessageNeedsAssistantResponse: boolean;
|
||||
chatStatus: TypesGen.ChatStatus | null;
|
||||
onOpenAnalytics?: () => void;
|
||||
onEditUserMessage?: (
|
||||
messageId: number,
|
||||
text: string,
|
||||
@@ -142,7 +138,6 @@ const StreamingBridge: FC<{
|
||||
detailError,
|
||||
latestMessageNeedsAssistantResponse,
|
||||
chatStatus,
|
||||
onOpenAnalytics,
|
||||
onEditUserMessage,
|
||||
editingMessageId,
|
||||
savingMessageId,
|
||||
@@ -168,7 +163,6 @@ const StreamingBridge: FC<{
|
||||
retryState={retryState}
|
||||
isAwaitingFirstStreamChunk={isAwaitingFirstStreamChunk}
|
||||
detailError={detailError}
|
||||
onOpenAnalytics={onOpenAnalytics}
|
||||
onEditUserMessage={onEditUserMessage}
|
||||
editingMessageId={editingMessageId}
|
||||
savingMessageId={savingMessageId}
|
||||
|
||||
@@ -128,7 +128,6 @@ const meta: Meta<typeof AgentDetailView> = {
|
||||
handleViewWorkspace: fn(),
|
||||
handleOpenTerminal: fn(),
|
||||
handleCommit: fn(),
|
||||
onNavigateToChat: fn(),
|
||||
handleInterrupt: fn(),
|
||||
handleDeleteQueuedMessage: fn(),
|
||||
handlePromoteQueuedMessage: fn(),
|
||||
|
||||
@@ -83,7 +83,6 @@ interface AgentDetailViewProps {
|
||||
// Sidebar / panel state.
|
||||
isSidebarCollapsed: boolean;
|
||||
onToggleSidebarCollapsed: () => void;
|
||||
onOpenAnalytics?: () => void;
|
||||
|
||||
// Right panel state (owned by the parent so loading and
|
||||
// loaded views share the same layout).
|
||||
@@ -107,9 +106,6 @@ interface AgentDetailViewProps {
|
||||
handleOpenTerminal: () => void;
|
||||
handleCommit: (repoRoot: string) => void;
|
||||
|
||||
// Navigation.
|
||||
onNavigateToChat: (chatId: string) => void;
|
||||
|
||||
// Chat action handlers.
|
||||
handleInterrupt: () => void;
|
||||
handleDeleteQueuedMessage: (id: number) => Promise<void>;
|
||||
@@ -158,7 +154,6 @@ export const AgentDetailView: FC<AgentDetailViewProps> = ({
|
||||
isInterruptPending,
|
||||
isSidebarCollapsed,
|
||||
onToggleSidebarCollapsed,
|
||||
onOpenAnalytics,
|
||||
showSidebarPanel,
|
||||
onSetShowSidebarPanel,
|
||||
prNumber,
|
||||
@@ -171,7 +166,6 @@ export const AgentDetailView: FC<AgentDetailViewProps> = ({
|
||||
handleViewWorkspace,
|
||||
handleOpenTerminal,
|
||||
handleCommit,
|
||||
onNavigateToChat,
|
||||
handleInterrupt,
|
||||
handleDeleteQueuedMessage,
|
||||
handlePromoteQueuedMessage,
|
||||
@@ -221,7 +215,6 @@ export const AgentDetailView: FC<AgentDetailViewProps> = ({
|
||||
<AgentDetailTopBar
|
||||
chatTitle={chatTitle}
|
||||
parentChat={parentChat}
|
||||
onOpenParentChat={(chatId) => onNavigateToChat(chatId)}
|
||||
panel={{
|
||||
showSidebarPanel,
|
||||
onToggleSidebar: () => onSetShowSidebarPanel((prev) => !prev),
|
||||
@@ -275,7 +268,6 @@ export const AgentDetailView: FC<AgentDetailViewProps> = ({
|
||||
? { kind: "generic" as const, message: chatRecord.last_error }
|
||||
: undefined)
|
||||
}
|
||||
onOpenAnalytics={onOpenAnalytics}
|
||||
onEditUserMessage={editing.handleEditUserMessage}
|
||||
editingMessageId={editing.editingMessageId}
|
||||
savingMessageId={pendingEditMessageId}
|
||||
@@ -408,7 +400,6 @@ export const AgentDetailLoadingView: FC<AgentDetailLoadingViewProps> = ({
|
||||
onOpenTerminal: () => {},
|
||||
sshCommand: undefined,
|
||||
}}
|
||||
onOpenParentChat={() => {}}
|
||||
onArchiveAgent={() => {}}
|
||||
onUnarchiveAgent={() => {}}
|
||||
onArchiveAndDeleteWorkspace={() => {}}
|
||||
@@ -482,7 +473,6 @@ export const AgentDetailNotFoundView: FC<AgentDetailNotFoundViewProps> = ({
|
||||
onOpenTerminal: () => {},
|
||||
sshCommand: undefined,
|
||||
}}
|
||||
onOpenParentChat={() => {}}
|
||||
onArchiveAgent={() => {}}
|
||||
onUnarchiveAgent={() => {}}
|
||||
onArchiveAndDeleteWorkspace={() => {}}
|
||||
|
||||
Reference in New Issue
Block a user