mirror of
https://github.com/BerriAI/litellm.git
synced 2025-04-27 19:54:13 +00:00
Easier user onboarding via SSO (#8187)
* fix(ui_sso.py): use common `get_user_object` logic across jwt + ui sso auth Allows finding users by their email, and attaching the sso user id to the user if found * Improve Team Management flow on UI (#8204) * build(teams.tsx): refactor teams page to make it easier to add members to a team make a row in table clickable -> allows user to add users to team they intended * build(teams.tsx): make it clear user should click on team id to view team details simplifies team management by putting team details on separate page * build(team_info.tsx): separately show user id and user email make it easy for user to understand the information they're seeing * build(team_info.tsx): add back in 'add member' button * build(team_info.tsx): working team member update on team_info.tsx * build(team_info.tsx): enable team member delete on ui allow user to delete accidental adds * build(internal_user_endpoints.py): expose new endpoint for ui to allow filtering on user table allows proxy admin to quickly find user they're looking for * feat(team_endpoints.py): expose new team filter endpoint for ui allows proxy admin to easily find team they're looking for * feat(user_search_modal.tsx): allow admin to filter on users when adding new user to teams * test: mark flaky test * test: mark flaky test * fix(exception_mapping_utils.py): fix anthropic text route error * fix(ui_sso.py): handle situation when user not in db
This commit is contained in:
parent
8900b18504
commit
65d3f85a69
14 changed files with 862 additions and 111 deletions
|
@ -25,7 +25,7 @@ import { fetchAvailableModelsForTeamOrKey, getModelDisplayName } from "./key_tea
|
|||
import { Select, SelectItem } from "@tremor/react";
|
||||
import { InfoCircleOutlined } from '@ant-design/icons';
|
||||
import { getGuardrailsList } from "./networking";
|
||||
|
||||
import TeamInfoView from "@/components/team/team_info";
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
|
@ -129,8 +129,9 @@ const Team: React.FC<TeamProps> = ({
|
|||
const [editModalVisible, setEditModalVisible] = useState(false);
|
||||
|
||||
const [selectedTeam, setSelectedTeam] = useState<null | any>(
|
||||
teams ? teams[0] : null
|
||||
null
|
||||
);
|
||||
const [selectedTeamId, setSelectedTeamId] = useState<string | null>(null);
|
||||
|
||||
const [isTeamModalVisible, setIsTeamModalVisible] = useState(false);
|
||||
const [isAddMemberModalVisible, setIsAddMemberModalVisible] = useState(false);
|
||||
|
@ -500,6 +501,9 @@ const Team: React.FC<TeamProps> = ({
|
|||
};
|
||||
|
||||
const is_team_admin = (team: any) => {
|
||||
if (team == null || team.members_with_roles == null) {
|
||||
return false;
|
||||
}
|
||||
for (let i = 0; i < team.members_with_roles.length; i++) {
|
||||
let member = team.members_with_roles[i];
|
||||
if (member.user_id == userID && member.role == "admin") {
|
||||
|
@ -509,58 +513,7 @@ const Team: React.FC<TeamProps> = ({
|
|||
return false;
|
||||
}
|
||||
|
||||
const _common_member_update_call = async (formValues: Record<string, any>, callType: "add" | "edit") => {
|
||||
try {
|
||||
if (accessToken != null && teams != null) {
|
||||
message.info("Adding Member");
|
||||
const user_role: Member = {
|
||||
role: formValues.role,
|
||||
user_email: formValues.user_email,
|
||||
user_id: formValues.user_id,
|
||||
};
|
||||
let response: any;
|
||||
if (callType == "add") {
|
||||
response = await teamMemberAddCall(
|
||||
accessToken,
|
||||
selectedTeam["team_id"],
|
||||
user_role
|
||||
);
|
||||
message.success("Member added");
|
||||
} else {
|
||||
response = await teamMemberUpdateCall(
|
||||
accessToken,
|
||||
selectedTeam["team_id"],
|
||||
{
|
||||
"role": formValues.role,
|
||||
"user_id": formValues.id,
|
||||
"user_email": formValues.email
|
||||
}
|
||||
);
|
||||
message.success("Member updated");
|
||||
}
|
||||
|
||||
// Checking if the team exists in the list and updating or adding accordingly
|
||||
const foundIndex = teams.findIndex((team) => {
|
||||
console.log(
|
||||
`team.team_id=${team.team_id}; response.data.team_id=${response.data.team_id}`
|
||||
);
|
||||
return team.team_id === response.data.team_id;
|
||||
});
|
||||
console.log(`foundIndex: ${foundIndex}`);
|
||||
if (foundIndex !== -1) {
|
||||
// If the team is found, update it
|
||||
const updatedTeams = [...teams]; // Copy the current state
|
||||
updatedTeams[foundIndex] = response.data; // Update the specific team
|
||||
setTeams(updatedTeams); // Set the new state
|
||||
setSelectedTeam(response.data);
|
||||
}
|
||||
setIsAddMemberModalVisible(false);
|
||||
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error creating the team:", error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const handleRefreshClick = () => {
|
||||
// Update the 'lastRefreshed' state to the current date and time
|
||||
|
@ -577,6 +530,15 @@ const Team: React.FC<TeamProps> = ({
|
|||
}
|
||||
return (
|
||||
<div className="w-full mx-4">
|
||||
{selectedTeamId ? (
|
||||
<TeamInfoView
|
||||
teamId={selectedTeamId}
|
||||
onClose={() => setSelectedTeamId(null)}
|
||||
accessToken={accessToken}
|
||||
is_team_admin={is_team_admin(teams?.find((team) => team.team_id === selectedTeamId))}
|
||||
is_proxy_admin={userRole == "Admin"}
|
||||
/>
|
||||
) : (
|
||||
<TabGroup className="gap-2 p-8 h-[75vh] w-full mt-2">
|
||||
<TabList className="flex justify-between mt-2 w-full items-center">
|
||||
<div className="flex">
|
||||
|
@ -596,6 +558,9 @@ const Team: React.FC<TeamProps> = ({
|
|||
</TabList>
|
||||
<TabPanels>
|
||||
<TabPanel>
|
||||
<Text>
|
||||
Click on "Team ID" to view team details <b>and</b> manage team members.
|
||||
</Text>
|
||||
<Grid numItems={1} className="gap-2 pt-2 pb-2 h-[75vh] w-full mt-2">
|
||||
<Col numColSpan={1}>
|
||||
<Card className="w-full mx-auto flex-auto overflow-y-auto max-h-[50vh]">
|
||||
|
@ -628,19 +593,27 @@ const Team: React.FC<TeamProps> = ({
|
|||
>
|
||||
{team["team_alias"]}
|
||||
</TableCell>
|
||||
<TableCell
|
||||
style={{
|
||||
maxWidth: "4px",
|
||||
whiteSpace: "nowrap",
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis",
|
||||
fontSize: "0.75em", // or any smaller size as needed
|
||||
}}
|
||||
>
|
||||
<Tooltip title={team.team_id}>
|
||||
{team.team_id}
|
||||
</Tooltip>
|
||||
<TableRow>
|
||||
<TableCell>
|
||||
<div className="overflow-hidden">
|
||||
<Tooltip title={team.team_id}>
|
||||
<Button
|
||||
size="xs"
|
||||
variant="light"
|
||||
className="font-mono text-blue-500 bg-blue-50 hover:bg-blue-100 text-xs font-normal px-2 py-0.5 text-left overflow-hidden truncate max-w-[200px]"
|
||||
|
||||
onClick={() => {
|
||||
// Add click handler
|
||||
setSelectedTeamId(team.team_id);
|
||||
}}
|
||||
>
|
||||
{team.team_id.slice(0, 7)}...
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
|
||||
<TableCell
|
||||
style={{
|
||||
maxWidth: "4px",
|
||||
|
@ -964,25 +937,30 @@ const Team: React.FC<TeamProps> = ({
|
|||
</Modal>
|
||||
</Col>
|
||||
) : null}
|
||||
<Col numColSpan={1}>
|
||||
{/* <Col numColSpan={1}>
|
||||
<Title level={4}>Team Members</Title>
|
||||
<Paragraph>
|
||||
If you belong to multiple teams, this setting controls which teams
|
||||
members you see.
|
||||
If you belong to multiple teams, this setting controls which teams' members you see.
|
||||
</Paragraph>
|
||||
{teams && teams.length > 0 ? (
|
||||
<Select defaultValue="0">
|
||||
{teams.map((team: any, index) => (
|
||||
<SelectItem
|
||||
key={index}
|
||||
value={String(index)}
|
||||
onClick={() => {
|
||||
setSelectedTeam(team);
|
||||
}}
|
||||
>
|
||||
{team["team_alias"]}
|
||||
</SelectItem>
|
||||
))}
|
||||
{[...teams]
|
||||
.sort((a, b) => {
|
||||
const aliasA = a.team_alias || '';
|
||||
const aliasB = b.team_alias || '';
|
||||
return aliasA.localeCompare(aliasB);
|
||||
})
|
||||
.map((team: any, index) => (
|
||||
<SelectItem
|
||||
key={index}
|
||||
value={String(index)}
|
||||
onClick={() => {
|
||||
setSelectedTeam(team);
|
||||
}}
|
||||
>
|
||||
{team.team_alias || 'Unnamed Team'}
|
||||
</SelectItem>
|
||||
))}
|
||||
</Select>
|
||||
) : (
|
||||
<Paragraph>
|
||||
|
@ -1112,7 +1090,7 @@ const Team: React.FC<TeamProps> = ({
|
|||
</div>
|
||||
</Form>
|
||||
</Modal>
|
||||
</Col>
|
||||
</Col> */}
|
||||
</Grid>
|
||||
</TabPanel>
|
||||
<TabPanel>
|
||||
|
@ -1123,7 +1101,7 @@ const Team: React.FC<TeamProps> = ({
|
|||
</TabPanel>
|
||||
</TabPanels>
|
||||
|
||||
</TabGroup>
|
||||
</TabGroup>)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue