mirror of
https://github.com/coder/coder.git
synced 2026-11-03 22:25:31 +08:00
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:
+37
-20
@@ -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 && (
|
||||
|
||||
Reference in New Issue
Block a user