diff --git a/site/src/pages/AgentsPage/components/AgentCreateForm.stories.tsx b/site/src/pages/AgentsPage/components/AgentCreateForm.stories.tsx index 201bea72e2..bc72dca99b 100644 --- a/site/src/pages/AgentsPage/components/AgentCreateForm.stories.tsx +++ b/site/src/pages/AgentsPage/components/AgentCreateForm.stories.tsx @@ -338,9 +338,7 @@ export const WithOrganizationPicker: Story = { const canvas = within(canvasElement); // Verify the org picker rendered (component didn't crash). await waitFor(() => { - expect( - canvas.getByTestId("organization-autocomplete"), - ).toBeInTheDocument(); + expect(canvas.getByTestId("compact-org-selector")).toBeInTheDocument(); }); // Type into the chat input to trigger re-renders. If the // permittedOrgs fallback is referentially unstable, this @@ -349,7 +347,7 @@ export const WithOrganizationPicker: Story = { await userEvent.click(input); await userEvent.keyboard("hello world"); // The org picker should still be present after typing. - expect(canvas.getByTestId("organization-autocomplete")).toBeInTheDocument(); + expect(canvas.getByTestId("compact-org-selector")).toBeInTheDocument(); }, }; diff --git a/site/src/pages/AgentsPage/components/AgentCreateForm.tsx b/site/src/pages/AgentsPage/components/AgentCreateForm.tsx index 55bac24262..a09ffa8d88 100644 --- a/site/src/pages/AgentsPage/components/AgentCreateForm.tsx +++ b/site/src/pages/AgentsPage/components/AgentCreateForm.tsx @@ -9,8 +9,6 @@ import { Alert, AlertDescription } from "#/components/Alert/Alert"; import { ErrorAlert } from "#/components/Alert/ErrorAlert"; import { Button } from "#/components/Button/Button"; import { ConfirmDialog } from "#/components/Dialogs/ConfirmDialog/ConfirmDialog"; -import { Label } from "#/components/Label/Label"; -import { OrganizationAutocomplete } from "#/components/OrganizationAutocomplete/OrganizationAutocomplete"; import { useDashboard } from "#/modules/dashboard/useDashboard"; import { docs } from "#/utils/docs"; import { useFileAttachments } from "../hooks/useFileAttachments"; @@ -27,6 +25,7 @@ import { import { AgentChatInput } from "./AgentChatInput"; import { ChatAccessDeniedAlert } from "./ChatAccessDeniedAlert"; import type { ModelSelectorOption } from "./ChatElements"; +import { CompactOrgSelector } from "./ChatElements"; import { getDefaultMCPSelection, getSavedMCPSelection, @@ -433,30 +432,23 @@ export const AgentCreateForm: FC = ({ {permittedOrgsQuery.error != null && ( )} - {showOrganizations && - !permittedOrgsQuery.isLoading && - permittedOrgs.length > 1 && ( -
- - { - const orgChanged = newOrg?.id !== selectedOrg?.id; - if (orgChanged && attachments.length > 0) { - setPendingOrgChange(newOrg); - return; - } - if (orgChanged) { - handleWorkspaceChange(null); - } - setSelectedOrg(newOrg); - }} - /> -
- )} + {showOrganizations && permittedOrgs.length > 1 && ( + { + const orgChanged = newOrg.id !== selectedOrg?.id; + if (orgChanged && attachments.length > 0) { + setPendingOrgChange(newOrg); + return; + } + if (orgChanged) { + handleWorkspaceChange(null); + } + setSelectedOrg(newOrg); + }} + /> + )} = { + title: "pages/AgentsPage/ChatElements/CompactOrgSelector", + component: CompactOrgSelector, + decorators: [ + (Story) => ( +
+ +
+ ), + ], + args: { + options: mockOrgs, + value: mockOrgs[0], + onChange: fn(), + }, +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = {}; + +export const Disabled: Story = { + args: { + disabled: true, + value: mockOrgs[0], + }, +}; + +export const NoSelection: Story = { + args: { + value: null, + }, +}; + +export const SingleOption: Story = { + args: { + options: [mockOrgs[0]], + value: mockOrgs[0], + }, +}; diff --git a/site/src/pages/AgentsPage/components/ChatElements/CompactOrgSelector.tsx b/site/src/pages/AgentsPage/components/ChatElements/CompactOrgSelector.tsx new file mode 100644 index 0000000000..572b62c304 --- /dev/null +++ b/site/src/pages/AgentsPage/components/ChatElements/CompactOrgSelector.tsx @@ -0,0 +1,126 @@ +import { Check } from "lucide-react"; +import { type FC, useState } from "react"; +import type { Organization } from "#/api/typesGenerated"; +import { ChevronDownIcon } from "#/components/AnimatedIcons/ChevronDown"; +import { Avatar } from "#/components/Avatar/Avatar"; +import { + Command, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList, +} from "#/components/Command/Command"; +import { + Popover, + PopoverContent, + PopoverTrigger, +} from "#/components/Popover/Popover"; +import { cn } from "#/utils/cn"; + +interface CompactOrgSelectorProps { + value: Organization | null; + onChange?: (organization: Organization) => void; + options: readonly Organization[]; + disabled?: boolean; + className?: string; + dropdownSide?: "top" | "bottom" | "left" | "right"; + dropdownAlign?: "start" | "center" | "end"; +} + +export const CompactOrgSelector: FC = ({ + value, + onChange, + options, + disabled = false, + className, + dropdownSide = "bottom", + dropdownAlign = "start", +}) => { + const [open, setOpen] = useState(false); + const isDisabled = disabled || options.length === 0; + + return ( + + + + + + + + + + No organizations found + + + {options.map((org) => ( + { + onChange?.(org); + setOpen(false); + }} + > + {" "} + + + {org.display_name || org.name} + + {value?.id === org.id && ( + + )} + + ))} + + + + + + ); +}; diff --git a/site/src/pages/AgentsPage/components/ChatElements/index.ts b/site/src/pages/AgentsPage/components/ChatElements/index.ts index f7435961e5..fb5c6c4875 100644 --- a/site/src/pages/AgentsPage/components/ChatElements/index.ts +++ b/site/src/pages/AgentsPage/components/ChatElements/index.ts @@ -1,3 +1,4 @@ +export { CompactOrgSelector } from "./CompactOrgSelector"; export { ConversationItem } from "./Conversation"; export { Message, MessageContent } from "./Message"; export type { ModelSelectorOption } from "./ModelSelector"; diff --git a/site/src/pages/AgentsPage/components/ChatPageContent.tsx b/site/src/pages/AgentsPage/components/ChatPageContent.tsx index 76d4ac3e17..f171826fc7 100644 --- a/site/src/pages/AgentsPage/components/ChatPageContent.tsx +++ b/site/src/pages/AgentsPage/components/ChatPageContent.tsx @@ -422,9 +422,11 @@ export const ChatPageInput: FC = ({ return (
{inputElement} -
- {modelSelectorHelp} -
+ {modelSelectorHelp && ( +
+ {modelSelectorHelp} +
+ )}
); };