mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-06 02:48:13 +00:00
Merge pull request #25808 from BerriAI/litellm_sendInviteEmailToggle
[Feature] UI - Users: Add Send Invitation Email Toggle
This commit is contained in:
commit
91d6626254
2 changed files with 142 additions and 3 deletions
|
|
@ -354,4 +354,126 @@ describe("CreateUserButton", () => {
|
|||
expect(mockOrganizationMemberAddCall).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("send invitation email toggle", () => {
|
||||
it("should send send_invite_email true by default in embedded mode", async () => {
|
||||
const user = userEvent.setup();
|
||||
mockUserCreateCall.mockResolvedValue({ data: { user_id: "default-on-user" } });
|
||||
|
||||
renderWithProviders(
|
||||
<CreateUserButton {...defaultProps} possibleUIRoles={{ proxy_user: { ui_label: "User", description: "" } }} isEmbedded />,
|
||||
);
|
||||
|
||||
await user.type(screen.getByLabelText(/user email/i), "default@example.com");
|
||||
await user.click(screen.getByRole("combobox", { name: /user role/i }));
|
||||
await user.click(screen.getByText("User"));
|
||||
await user.click(screen.getByRole("button", { name: /create user/i }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockUserCreateCall).toHaveBeenCalledWith("token", null, expect.objectContaining({
|
||||
send_invite_email: true,
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
it("should send send_invite_email false when the checkbox is unchecked in embedded mode", async () => {
|
||||
const user = userEvent.setup();
|
||||
mockUserCreateCall.mockResolvedValue({ data: { user_id: "unchecked-user" } });
|
||||
|
||||
renderWithProviders(
|
||||
<CreateUserButton {...defaultProps} possibleUIRoles={{ proxy_user: { ui_label: "User", description: "" } }} isEmbedded />,
|
||||
);
|
||||
|
||||
await user.type(screen.getByLabelText(/user email/i), "off@example.com");
|
||||
await user.click(screen.getByRole("combobox", { name: /user role/i }));
|
||||
await user.click(screen.getByText("User"));
|
||||
await user.click(screen.getByRole("checkbox", { name: /send invitation email/i }));
|
||||
await user.click(screen.getByRole("button", { name: /create user/i }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockUserCreateCall).toHaveBeenCalledWith("token", null, expect.objectContaining({
|
||||
send_invite_email: false,
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
it("should send send_invite_email true by default in standalone mode", async () => {
|
||||
const user = userEvent.setup();
|
||||
mockUserCreateCall.mockResolvedValue({ data: { user_id: "standalone-default-user" } });
|
||||
mockInvitationCreateCall.mockResolvedValue({
|
||||
id: "inv-default",
|
||||
user_id: "standalone-default-user",
|
||||
has_user_setup_sso: false,
|
||||
} as any);
|
||||
|
||||
renderWithProviders(
|
||||
<CreateUserButton {...defaultProps} possibleUIRoles={{ proxy_user: { ui_label: "User", description: "" } }} />,
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByRole("button", { name: /\+ invite user/i })).toBeInTheDocument();
|
||||
});
|
||||
await user.click(screen.getByRole("button", { name: /\+ invite user/i }));
|
||||
|
||||
const dialog = screen.getByRole("dialog", { name: /invite user/i });
|
||||
await user.type(within(dialog).getByLabelText(/user email/i), "standalone-default@example.com");
|
||||
await user.click(within(dialog).getByRole("combobox", { name: /global proxy role/i }));
|
||||
await user.click(screen.getByText("User"));
|
||||
await user.click(within(dialog).getByRole("button", { name: /invite user/i }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockUserCreateCall).toHaveBeenCalledWith("token", null, expect.objectContaining({
|
||||
send_invite_email: true,
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
it("should send send_invite_email false when the checkbox is unchecked in standalone mode", async () => {
|
||||
const user = userEvent.setup();
|
||||
mockUserCreateCall.mockResolvedValue({ data: { user_id: "standalone-off-user" } });
|
||||
mockInvitationCreateCall.mockResolvedValue({
|
||||
id: "inv-off",
|
||||
user_id: "standalone-off-user",
|
||||
has_user_setup_sso: false,
|
||||
} as any);
|
||||
|
||||
renderWithProviders(
|
||||
<CreateUserButton {...defaultProps} possibleUIRoles={{ proxy_user: { ui_label: "User", description: "" } }} />,
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByRole("button", { name: /\+ invite user/i })).toBeInTheDocument();
|
||||
});
|
||||
await user.click(screen.getByRole("button", { name: /\+ invite user/i }));
|
||||
|
||||
const dialog = screen.getByRole("dialog", { name: /invite user/i });
|
||||
await user.type(within(dialog).getByLabelText(/user email/i), "standalone-off@example.com");
|
||||
await user.click(within(dialog).getByRole("combobox", { name: /global proxy role/i }));
|
||||
await user.click(screen.getByText("User"));
|
||||
await user.click(within(dialog).getByRole("checkbox", { name: /send invitation email/i }));
|
||||
await user.click(within(dialog).getByRole("button", { name: /invite user/i }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockUserCreateCall).toHaveBeenCalledWith("token", null, expect.objectContaining({
|
||||
send_invite_email: false,
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
it("should keep the checkbox checked by default when the modal is opened in standalone mode", async () => {
|
||||
const user = userEvent.setup();
|
||||
|
||||
renderWithProviders(
|
||||
<CreateUserButton {...defaultProps} possibleUIRoles={{ proxy_user: { ui_label: "User", description: "" } }} />,
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByRole("button", { name: /\+ invite user/i })).toBeInTheDocument();
|
||||
});
|
||||
await user.click(screen.getByRole("button", { name: /\+ invite user/i }));
|
||||
|
||||
const dialog = screen.getByRole("dialog", { name: /invite user/i });
|
||||
expect(within(dialog).getByRole("checkbox", { name: /send invitation email/i })).toBeChecked();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { InfoCircleOutlined, UserAddOutlined } from "@ant-design/icons";
|
|||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { useOrganizations } from "@/app/(dashboard)/hooks/organizations/useOrganizations";
|
||||
import { Accordion, AccordionBody, AccordionHeader, SelectItem, TextInput } from "@tremor/react";
|
||||
import { Alert, Button, Form, Input, Modal, Select, Select as Select2, Space, Tooltip, Typography } from "antd";
|
||||
import { Alert, Button, Checkbox, Form, Input, Modal, Select, Select as Select2, Space, Tooltip, Typography } from "antd";
|
||||
import React, { useEffect, useMemo, useState } from "react";
|
||||
import BulkCreateUsers from "./bulk_create_users_button";
|
||||
import TeamDropdown from "./common_components/team_dropdown";
|
||||
|
|
@ -113,6 +113,7 @@ export const CreateUserButton: React.FC<CreateuserProps> = ({
|
|||
user_role: string;
|
||||
organization_ids?: string[];
|
||||
organizations?: string[];
|
||||
send_invite_email?: boolean;
|
||||
}) => {
|
||||
try {
|
||||
NotificationsManager.info("Making API Call");
|
||||
|
|
@ -181,7 +182,7 @@ export const CreateUserButton: React.FC<CreateuserProps> = ({
|
|||
labelCol={{ span: 8 }}
|
||||
wrapperCol={{ span: 16 }}
|
||||
labelAlign="left"
|
||||
initialValues={{ user_role: "internal_user_viewer" }}
|
||||
initialValues={{ user_role: "internal_user_viewer", send_invite_email: true }}
|
||||
>
|
||||
<Alert
|
||||
message="Email invitations"
|
||||
|
|
@ -224,6 +225,14 @@ export const CreateUserButton: React.FC<CreateuserProps> = ({
|
|||
<Input.TextArea rows={4} placeholder="Enter metadata as JSON" />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label="Send invitation email"
|
||||
name="send_invite_email"
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Checkbox />
|
||||
</Form.Item>
|
||||
|
||||
<div style={{ textAlign: "right", marginTop: "10px" }}>
|
||||
<Button htmlType="submit">Create User</Button>
|
||||
</div>
|
||||
|
|
@ -270,7 +279,7 @@ export const CreateUserButton: React.FC<CreateuserProps> = ({
|
|||
labelCol={{ span: 8 }}
|
||||
wrapperCol={{ span: 16 }}
|
||||
labelAlign="left"
|
||||
initialValues={{ user_role: "internal_user_viewer" }}
|
||||
initialValues={{ user_role: "internal_user_viewer", send_invite_email: true }}
|
||||
>
|
||||
<Form.Item label="User Email" name="user_email">
|
||||
<Input />
|
||||
|
|
@ -326,6 +335,13 @@ export const CreateUserButton: React.FC<CreateuserProps> = ({
|
|||
<Form.Item label="Metadata" name="metadata">
|
||||
<Input.TextArea rows={4} placeholder="Enter metadata as JSON" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Send invitation email"
|
||||
name="send_invite_email"
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Checkbox />
|
||||
</Form.Item>
|
||||
<Accordion>
|
||||
<AccordionHeader>
|
||||
<Text strong>Personal Key Creation</Text>
|
||||
|
|
@ -360,6 +376,7 @@ export const CreateUserButton: React.FC<CreateuserProps> = ({
|
|||
</Form.Item>
|
||||
</AccordionBody>
|
||||
</Accordion>
|
||||
|
||||
<div style={{ textAlign: "right", marginTop: "10px" }}>
|
||||
<Button type="primary" icon={<UserAddOutlined />} htmlType="submit">
|
||||
Invite User
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue