fix(site): clarify chat model permission labels (#28579)

Renames chat model sharing controls to permissions terminology so the UI
reflects that administrators are managing model access.

Uses “Use” as the user-facing role while preserving the backend `read`
ACL value, and updates the Storybook interactions for the new labels.
This commit is contained in:
Ethan
2026-08-25 17:23:08 +01:00
committed by GitHub
parent c07bde80c4
commit 816f6fbeef
4 changed files with 51 additions and 34 deletions
@@ -92,7 +92,7 @@ const ReopenableSharingDialog = () => {
return (
<>
<button type="button" onClick={() => setOpen(true)}>
Open sharing
Open permissions
</button>
<ChatModelSharingDialog
open={open}
@@ -126,9 +126,19 @@ export const EmptyACL: Story = {
play: async ({ canvasElement }) => {
const body = within(canvasElement.ownerDocument.body);
expect(
await body.findByText("No shared members or groups yet"),
await body.findByRole("dialog", { name: "Model permissions" }),
).toHaveAttribute("data-state", "open");
expect(
body.getByText(
`Manage which organization members and groups can use ${mockGPT5.display_name}.`,
),
).toBeInTheDocument();
expect(body.getByRole("button", { name: "Save sharing" })).toBeDisabled();
expect(
body.getByText("No members or groups have permission yet"),
).toBeInTheDocument();
expect(
body.getByRole("button", { name: "Save permissions" }),
).toBeDisabled();
},
};
@@ -137,7 +147,7 @@ export const Loading: Story = {
play: async ({ canvasElement }) => {
const body = within(canvasElement.ownerDocument.body);
expect(await body.findByRole("status")).toHaveTextContent(
"Loading model sharing",
"Loading model permissions",
);
},
};
@@ -147,7 +157,9 @@ export const LoadError: Story = {
play: async ({ canvasElement }) => {
const body = within(canvasElement.ownerDocument.body);
expect(await body.findByText("Unable to load ACL")).toBeInTheDocument();
expect(body.getByRole("button", { name: "Save sharing" })).toBeDisabled();
expect(
body.getByRole("button", { name: "Save permissions" }),
).toBeDisabled();
},
};
@@ -161,12 +173,12 @@ export const AddUser: Story = {
new RegExp(MockOrganizationMember2.email, "i"),
);
expect(
body.getByRole("row", {
name: new RegExp(MockOrganizationMember2.username, "i"),
}),
).toBeVisible();
const saveButton = body.getByRole("button", { name: "Save sharing" });
const userRow = body.getByRole("row", {
name: new RegExp(MockOrganizationMember2.username, "i"),
});
expect(userRow).toBeVisible();
expect(userRow).toHaveTextContent("Use");
const saveButton = body.getByRole("button", { name: "Save permissions" });
expect(saveButton).toBeEnabled();
await userEvent.click(saveButton);
@@ -197,7 +209,7 @@ export const AddGroup: Story = {
name: new RegExp(MockGroup2.display_name || MockGroup2.name, "i"),
}),
).toBeVisible();
const saveButton = body.getByRole("button", { name: "Save sharing" });
const saveButton = body.getByRole("button", { name: "Save permissions" });
expect(saveButton).toBeEnabled();
await userEvent.click(saveButton);
@@ -262,7 +274,9 @@ export const SaveRemovalsAsSparseDelta: Story = {
name: `Remove ${MockOrganizationMember2.username}`,
}),
);
await userEvent.click(body.getByRole("button", { name: "Save sharing" }));
await userEvent.click(
body.getByRole("button", { name: "Save permissions" }),
);
await waitFor(() =>
expect(API.experimental.updateChatModelACL).toHaveBeenCalledWith(
@@ -298,12 +312,14 @@ export const ReopenUsesFreshACL: Story = {
await userEvent.click(body.getByRole("button", { name: "Cancel" }));
await waitFor(() =>
expect(
body.queryByRole("dialog", { name: "Share model" }),
body.queryByRole("dialog", { name: "Model permissions" }),
).not.toBeInTheDocument(),
);
currentServerACL = refreshedACL;
await userEvent.click(body.getByRole("button", { name: "Open sharing" }));
await userEvent.click(
body.getByRole("button", { name: "Open permissions" }),
);
expect(
await body.findByRole("row", {
@@ -337,14 +353,15 @@ export const SaveErrorKeepsEditorOpen: Story = {
name: `Remove ${MockGroup.display_name}`,
}),
);
await userEvent.click(body.getByRole("button", { name: "Save sharing" }));
await userEvent.click(
body.getByRole("button", { name: "Save permissions" }),
);
const alert = await body.findByRole("alert");
expect(alert).toHaveTextContent("Unable to save ACL");
expect(body.getByRole("dialog", { name: "Share model" })).toHaveAttribute(
"data-state",
"open",
);
expect(
body.getByRole("dialog", { name: "Model permissions" }),
).toHaveAttribute("data-state", "open");
expect(args.onOpenChange).not.toHaveBeenCalledWith(false);
},
};
@@ -210,7 +210,7 @@ export const ChatModelSharingDialog: FC<ChatModelSharingDialogProps> = ({
},
{
onSuccess: () => {
toast.success(`Sharing for "${modelName}" updated.`);
toast.success(`Permissions for "${modelName}" updated.`);
close();
},
},
@@ -228,9 +228,9 @@ export const ChatModelSharingDialog: FC<ChatModelSharingDialogProps> = ({
>
<DialogContent className="max-w-2xl">
<DialogHeader>
<DialogTitle>Share model</DialogTitle>
<DialogTitle>Model permissions</DialogTitle>
<DialogDescription>
Choose which organization members and groups can use {modelName}.
Manage which organization members and groups can use {modelName}.
</DialogDescription>
</DialogHeader>
@@ -244,7 +244,7 @@ export const ChatModelSharingDialog: FC<ChatModelSharingDialogProps> = ({
className="flex items-center justify-center gap-3 py-10"
>
<Spinner loading />
<span>Loading model sharing</span>
<span>Loading model permissions</span>
</div>
) : !initialized ? null : (
<div className="flex flex-col gap-4">
@@ -273,14 +273,14 @@ export const ChatModelSharingDialog: FC<ChatModelSharingDialogProps> = ({
{isEmpty ? (
<div className="rounded-md border border-solid border-border px-6 py-10 text-center">
<p className="m-0 text-sm font-medium">
No shared members or groups yet
No members or groups have permission yet
</p>
<p className="m-0 mt-2 text-sm text-content-secondary">
Add a member or group using the controls above.
</p>
</div>
) : (
<Table aria-label="Shared model members and groups">
<Table aria-label="Model permissions for members and groups">
<TableHeader>
<TableRow>
<TableHead>Member</TableHead>
@@ -301,7 +301,7 @@ export const ChatModelSharingDialog: FC<ChatModelSharingDialogProps> = ({
src={group?.avatar_url}
/>
</TableCell>
<TableCell>Read</TableCell>
<TableCell>Use</TableCell>
<TableCell>
<Button
variant="subtle"
@@ -331,7 +331,7 @@ export const ChatModelSharingDialog: FC<ChatModelSharingDialogProps> = ({
src={member?.avatar_url}
/>
</TableCell>
<TableCell>Read</TableCell>
<TableCell>Use</TableCell>
<TableCell>
<Button
variant="subtle"
@@ -355,7 +355,7 @@ export const ChatModelSharingDialog: FC<ChatModelSharingDialogProps> = ({
<DialogFooter>
<DialogActions
confirmText="Save sharing"
confirmText="Save permissions"
confirmLoading={updateMutation.isPending}
confirmDisabled={isLoading || Boolean(loadError) || !isDirty}
onConfirm={save}
@@ -317,7 +317,7 @@ export const ShareOnlyAccess: Story = {
canvas.getByRole("button", { name: /model actions/i }),
);
expect(
screen.getByRole("menuitem", { name: /share model/i }),
screen.getByRole("menuitem", { name: /manage permissions/i }),
).toBeInTheDocument();
expect(
screen.queryByRole("menuitem", { name: /duplicate model/i }),
@@ -339,7 +339,7 @@ export const FullAccessActions: Story = {
canvas.getByRole("button", { name: /model actions/i }),
);
expect(
screen.getByRole("menuitem", { name: /share model/i }),
screen.getByRole("menuitem", { name: /manage permissions/i }),
).toBeInTheDocument();
expect(
screen.getByRole("menuitem", { name: /duplicate model/i }),
@@ -2,7 +2,7 @@ import {
ArrowLeftIcon,
CopyIcon,
EllipsisVerticalIcon,
Share2Icon,
ShieldIcon,
TrashIcon,
} from "lucide-react";
import type { FC } from "react";
@@ -89,8 +89,8 @@ export const ModelFormHeader: FC<{
<DropdownMenuContent align="end">
{onShareModel && (
<DropdownMenuItem onClick={onShareModel}>
<Share2Icon className="size-icon-sm" />
Share model
<ShieldIcon className="size-icon-sm" />
Manage permissions
</DropdownMenuItem>
)}
{onDuplicate && (