mirror of
https://github.com/coder/coder.git
synced 2026-09-22 05:05:20 +08:00
fix(site): stay on archived chat instead of redirecting (#22505)
When archiving a chat, the frontend no longer navigates away to a different chat. Instead it stays on the current chat and shows an archived state. ## Changes **AgentsPage.tsx** — Removed the redirect logic from `requestArchiveAgent`. After a successful archive, invalidates the individual chat query so the detail view picks up the `archived` flag immediately. **AgentDetail.tsx** — Detects `chatRecord.archived` and: - Disables the chat input - Shows a banner: "This agent has been archived and is read-only." - Passes `isArchived` to the top bar - Guards `handleArchiveAgentAction` against double-archiving **AgentDetail/TopBar.tsx** — When `isArchived`: - Shows an "Archived" badge next to the chat title - Hides the "Archive Agent" dropdown menu item **AgentDetail/TopBar.stories.tsx** — Added an `Archived` story variant.
This commit is contained in:
@@ -15,6 +15,7 @@ import { workspaceById } from "api/queries/workspaces";
|
||||
import type * as TypesGen from "api/typesGenerated";
|
||||
import type { ModelSelectorOption } from "components/ai-elements";
|
||||
import { Skeleton } from "components/Skeleton/Skeleton";
|
||||
import { ArchiveIcon } from "lucide-react";
|
||||
import { getVSCodeHref, SESSION_TOKEN_PLACEHOLDER } from "modules/apps/apps";
|
||||
import {
|
||||
type FC,
|
||||
@@ -514,6 +515,7 @@ const AgentDetail: FC = () => {
|
||||
const workspaceAgent = getWorkspaceAgent(workspace, undefined);
|
||||
const chatData = chatQuery.data;
|
||||
const chatRecord = chatData?.chat;
|
||||
const isArchived = chatRecord?.archived ?? false;
|
||||
const chatMessages = chatData?.messages;
|
||||
const chatQueuedMessages = chatData?.queued_messages;
|
||||
const chatLastModelConfigID = chatRecord?.last_model_config_id;
|
||||
@@ -637,7 +639,7 @@ const AgentDetail: FC = () => {
|
||||
sendMutation.isPending ||
|
||||
editMutation.isPending ||
|
||||
interruptMutation.isPending;
|
||||
const isInputDisabled = !hasModelOptions;
|
||||
const isInputDisabled = !hasModelOptions || isArchived;
|
||||
|
||||
const handleSend = async (message: string, editedMessageID?: number) => {
|
||||
if (
|
||||
@@ -853,7 +855,7 @@ const AgentDetail: FC = () => {
|
||||
};
|
||||
|
||||
const handleArchiveAgentAction = () => {
|
||||
if (!agentId) {
|
||||
if (!agentId || isArchived) {
|
||||
return;
|
||||
}
|
||||
requestArchiveAgent(agentId);
|
||||
@@ -983,9 +985,16 @@ const AgentDetail: FC = () => {
|
||||
onViewWorkspace: handleViewWorkspace,
|
||||
}}
|
||||
onArchiveAgent={handleArchiveAgentAction}
|
||||
isArchived={isArchived}
|
||||
isSidebarCollapsed={isSidebarCollapsed}
|
||||
onToggleSidebarCollapsed={onToggleSidebarCollapsed}
|
||||
/>
|
||||
{isArchived && (
|
||||
<div className="flex shrink-0 items-center gap-2 border-b border-border-default bg-surface-secondary px-4 py-2 text-sm text-content-secondary">
|
||||
<ArchiveIcon className="h-4 w-4 shrink-0" />
|
||||
This agent has been archived and is read-only.
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
aria-hidden
|
||||
className="pointer-events-none absolute inset-x-0 top-0 z-10 h-6 bg-surface-primary"
|
||||
|
||||
@@ -91,6 +91,12 @@ export const SidebarCollapsed: Story = {
|
||||
},
|
||||
};
|
||||
|
||||
export const Archived: Story = {
|
||||
args: {
|
||||
isArchived: true,
|
||||
},
|
||||
};
|
||||
|
||||
export const NoTitle: Story = {
|
||||
args: {
|
||||
chatTitle: undefined,
|
||||
|
||||
@@ -81,6 +81,7 @@ type AgentDetailTopBarProps = {
|
||||
diff: DiffPanelState;
|
||||
workspace: WorkspaceActions;
|
||||
onArchiveAgent: () => void;
|
||||
isArchived?: boolean;
|
||||
isSidebarCollapsed: boolean;
|
||||
onToggleSidebarCollapsed: () => void;
|
||||
};
|
||||
@@ -92,6 +93,7 @@ export const AgentDetailTopBar: FC<AgentDetailTopBarProps> = ({
|
||||
diff,
|
||||
workspace,
|
||||
onArchiveAgent,
|
||||
isArchived,
|
||||
isSidebarCollapsed,
|
||||
onToggleSidebarCollapsed,
|
||||
}) => {
|
||||
@@ -143,6 +145,11 @@ export const AgentDetailTopBar: FC<AgentDetailTopBarProps> = ({
|
||||
<span className="truncate text-sm text-content-primary">
|
||||
{chatTitle}
|
||||
</span>
|
||||
{isArchived && (
|
||||
<span className="shrink-0 rounded bg-surface-tertiary px-1.5 py-0.5 text-xs text-content-secondary">
|
||||
Archived
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@@ -192,13 +199,15 @@ export const AgentDetailTopBar: FC<AgentDetailTopBarProps> = ({
|
||||
<MonitorIcon className="h-3.5 w-3.5" />
|
||||
View Workspace
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
className="text-content-destructive focus:text-content-destructive"
|
||||
onSelect={onArchiveAgent}
|
||||
>
|
||||
<ArchiveIcon className="h-3.5 w-3.5" />
|
||||
Archive Agent
|
||||
</DropdownMenuItem>
|
||||
{!isArchived && (
|
||||
<DropdownMenuItem
|
||||
className="text-content-destructive focus:text-content-destructive"
|
||||
onSelect={onArchiveAgent}
|
||||
>
|
||||
<ArchiveIcon className="h-3.5 w-3.5" />
|
||||
Archive Agent
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
<WebPushButton />
|
||||
|
||||
@@ -221,29 +221,21 @@ const AgentsPage: FC = () => {
|
||||
}
|
||||
|
||||
setArchivingChatId(chatId);
|
||||
const nextChatId = (
|
||||
queryClient.getQueryData(chats().queryKey) as
|
||||
| TypesGen.Chat[]
|
||||
| undefined
|
||||
)?.find((chat) => chat.id !== chatId)?.id;
|
||||
|
||||
try {
|
||||
await archiveMutation.mutateAsync(chatId);
|
||||
clearChatErrorReason(chatId);
|
||||
// Invalidate the individual chat query so the detail view
|
||||
// picks up the archived flag without a redirect.
|
||||
await queryClient.invalidateQueries({ queryKey: chatKey(chatId) });
|
||||
toast.success("Agent archived.");
|
||||
|
||||
if (chatId === agentId) {
|
||||
navigate(nextChatId ? `/agents/${nextChatId}` : "/agents", {
|
||||
replace: true,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
toast.error(getErrorMessage(error, "Failed to archive agent."));
|
||||
} finally {
|
||||
setArchivingChatId(null);
|
||||
}
|
||||
},
|
||||
[archiveMutation, queryClient, agentId, navigate, clearChatErrorReason],
|
||||
[archiveMutation, queryClient, clearChatErrorReason],
|
||||
);
|
||||
const handleToggleSidebarCollapsed = useCallback(
|
||||
() => setIsSidebarCollapsed((prev) => !prev),
|
||||
|
||||
Reference in New Issue
Block a user