diff --git a/ui/litellm-dashboard/src/components/organization/organization_view.tsx b/ui/litellm-dashboard/src/components/organization/organization_view.tsx index 9b4e9080f2f..d009f8b5342 100644 --- a/ui/litellm-dashboard/src/components/organization/organization_view.tsx +++ b/ui/litellm-dashboard/src/components/organization/organization_view.tsx @@ -1,31 +1,21 @@ import { useTeams } from "@/app/(dashboard)/hooks/teams/useTeams"; import { formatNumberWithCommas, copyToClipboard as utilCopyToClipboard } from "@/utils/dataUtils"; import { createTeamAliasMap } from "@/utils/teamUtils"; -import { ArrowLeftIcon, PencilAltIcon, TrashIcon } from "@heroicons/react/outline"; +import { ArrowLeftIcon } from "@heroicons/react/outline"; import { Badge, Card, Grid, - Icon, - Tab, - TabGroup, - Table, - TableBody, - TableCell, - TableHead, - TableHeaderCell, - TableRow, - TabList, - TabPanel, - TabPanels, Text, TextInput, Title, Button as TremorButton, } from "@tremor/react"; -import { Button, Form, Input, Select } from "antd"; +import { Button, Form, Input, Select, Tabs, Typography } from "antd"; +import type { ColumnsType } from "antd/es/table"; import { CheckIcon, CopyIcon } from "lucide-react"; import React, { useEffect, useMemo, useState } from "react"; +import MemberTable from "../common_components/MemberTable"; import UserSearchModal from "../common_components/user_search_modal"; import MCPServerSelector from "../mcp_server_management/MCPServerSelector"; import { ModelSelect } from "../ModelSelect/ModelSelect"; @@ -224,6 +214,41 @@ const OrganizationInfoView: React.FC = ({ } }; + const orgExtraColumns: ColumnsType = [ + { + title: "Spend (USD)", + key: "spend", + render: (_: unknown, record: Member) => { + const orgMember = + record.user_id != null + ? (orgData.members || []).find((m) => m.user_id === record.user_id) + : undefined; + return ( + + ${formatNumberWithCommas(orgMember?.spend ?? 0, 4)} + + ); + }, + }, + { + title: "Created At", + key: "created_at", + render: (_: unknown, record: Member) => { + const orgMember = + record.user_id != null + ? (orgData.members || []).find((m) => m.user_id === record.user_id) + : undefined; + return ( + + {orgMember?.created_at + ? new Date(orgMember.created_at).toLocaleString() + : "-"} + + ); + }, + }, + ]; + return (
@@ -248,325 +273,271 @@ const OrganizationInfoView: React.FC = ({
- - - Overview - Members - Settings - + + + Organization Details +
+ Created: {new Date(orgData.created_at).toLocaleDateString()} + Updated: {new Date(orgData.updated_at).toLocaleDateString()} + Created By: {orgData.created_by} +
+
- - {/* Overview Panel */} - - - - Organization Details -
- Created: {new Date(orgData.created_at).toLocaleDateString()} - Updated: {new Date(orgData.updated_at).toLocaleDateString()} - Created By: {orgData.created_by} -
-
- - - Budget Status -
- ${formatNumberWithCommas(orgData.spend, 4)} - - of{" "} - {orgData.litellm_budget_table.max_budget === null - ? "Unlimited" - : `$${formatNumberWithCommas(orgData.litellm_budget_table.max_budget, 4)}`} - - {orgData.litellm_budget_table.budget_duration && ( - Reset: {orgData.litellm_budget_table.budget_duration} - )} -
-
- - - Rate Limits -
- TPM: {orgData.litellm_budget_table.tpm_limit || "Unlimited"} - RPM: {orgData.litellm_budget_table.rpm_limit || "Unlimited"} - {orgData.litellm_budget_table.max_parallel_requests && ( - Max Parallel Requests: {orgData.litellm_budget_table.max_parallel_requests} - )} -
-
- - - Models -
- {orgData.models.length === 0 ? ( - All proxy models - ) : ( - orgData.models.map((model, index) => ( - - {model} - - )) - )} -
-
- - Teams -
- {orgData.teams?.map((team, index) => ( - - {teamAliasMap[team.team_id] || team.team_id} - - ))} -
-
- - -
-
- - -
- - - - - User ID - Role - Spend - Created At - - - - - - {orgData.members && orgData.members.length > 0 ? ( - orgData.members.map((member, index) => ( - - - {member.user_id} - - - {member.user_role} - - - ${formatNumberWithCommas(member.spend, 4)} - - - {new Date(member.created_at).toLocaleString()} - - - {canEditOrg && ( - <> - { - setSelectedEditMember({ - role: member.user_role, - user_email: member.user_email, - user_id: member.user_id, - }); - setIsEditMemberModalVisible(true); - }} - /> - { - handleMemberDelete(member); - }} - /> - - )} - - - )) - ) : ( - - - No members found - - + + Budget Status +
+ ${formatNumberWithCommas(orgData.spend, 4)} + + of{" "} + {orgData.litellm_budget_table.max_budget === null + ? "Unlimited" + : `$${formatNumberWithCommas(orgData.litellm_budget_table.max_budget, 4)}`} + + {orgData.litellm_budget_table.budget_duration && ( + Reset: {orgData.litellm_budget_table.budget_duration} )} - -
-
- {canEditOrg && ( - { - setIsAddMemberModalVisible(true); - }} - > - Add Member - - )} -
-
- - {/* Settings Panel */} - - -
- Organization Settings - {canEditOrg && !isEditing && ( - setIsEditing(true)}>Edit Settings - )} -
- - {isEditing ? ( -
- - - - - - form.setFieldValue("models", values)} - context="organization" - options={{ - includeSpecialOptions: true, - showAllProxyModelsOverride: true, - }} - /> - - - - - - - - - - - - - - - - - - - - form.setFieldValue("vector_stores", values)} - value={form.getFieldValue("vector_stores")} - accessToken={accessToken || ""} - placeholder="Select vector stores" - /> - - - - form.setFieldValue("mcp_servers_and_groups", values)} - value={form.getFieldValue("mcp_servers_and_groups")} - accessToken={accessToken || ""} - placeholder="Select MCP servers and access groups" - /> - - - - - - -
-
- setIsEditing(false)} disabled={isOrgSaving}> - Cancel - - - Save Changes - -
-
- ) : ( -
-
- Organization Name -
{orgData.organization_alias}
+ + + + Rate Limits +
+ TPM: {orgData.litellm_budget_table.tpm_limit || "Unlimited"} + RPM: {orgData.litellm_budget_table.rpm_limit || "Unlimited"} + {orgData.litellm_budget_table.max_parallel_requests && ( + Max Parallel Requests: {orgData.litellm_budget_table.max_parallel_requests} + )}
-
- Organization ID -
{orgData.organization_id}
-
-
- Created At -
{new Date(orgData.created_at).toLocaleString()}
-
-
- Models -
- {orgData.models.map((model, index) => ( + + + + Models +
+ {orgData.models.length === 0 ? ( + All proxy models + ) : ( + orgData.models.map((model, index) => ( {model} - ))} -
+ )) + )}
-
- Rate Limits -
TPM: {orgData.litellm_budget_table.tpm_limit || "Unlimited"}
-
RPM: {orgData.litellm_budget_table.rpm_limit || "Unlimited"}
-
-
- Budget -
- Max:{" "} - {orgData.litellm_budget_table.max_budget !== null - ? `$${formatNumberWithCommas(orgData.litellm_budget_table.max_budget, 4)}` - : "No Limit"} -
-
Reset: {orgData.litellm_budget_table.budget_duration || "Never"}
+ + + Teams +
+ {orgData.teams?.map((team, index) => ( + + {teamAliasMap[team.team_id] || team.team_id} + + ))}
+
- + + + ), + }, + { + key: "members", + label: "Members", + children: ( +
+ ({ + role: m.user_role || "", + user_id: m.user_id, + user_email: m.user_email, + }))} + canEdit={canEditOrg} + onEdit={(member) => { + setSelectedEditMember(member); + setIsEditMemberModalVisible(true); + }} + onDelete={(member) => handleMemberDelete(member)} + onAddMember={() => setIsAddMemberModalVisible(true)} + roleColumnTitle="Organization Role" + extraColumns={orgExtraColumns} + /> +
+ ), + }, + { + key: "settings", + label: "Settings", + children: ( + +
+ Organization Settings + {canEditOrg && !isEditing && ( + setIsEditing(true)}>Edit Settings + )}
- )} -
- - - + + {isEditing ? ( +
+ + + + + + form.setFieldValue("models", values)} + context="organization" + options={{ + includeSpecialOptions: true, + showAllProxyModelsOverride: true, + }} + /> + + + + + + + + + + + + + + + + + + + + form.setFieldValue("vector_stores", values)} + value={form.getFieldValue("vector_stores")} + accessToken={accessToken || ""} + placeholder="Select vector stores" + /> + + + + form.setFieldValue("mcp_servers_and_groups", values)} + value={form.getFieldValue("mcp_servers_and_groups")} + accessToken={accessToken || ""} + placeholder="Select MCP servers and access groups" + /> + + + + + + +
+
+ setIsEditing(false)} disabled={isOrgSaving}> + Cancel + + + Save Changes + +
+
+
+ ) : ( +
+
+ Organization Name +
{orgData.organization_alias}
+
+
+ Organization ID +
{orgData.organization_id}
+
+
+ Created At +
{new Date(orgData.created_at).toLocaleString()}
+
+
+ Models +
+ {orgData.models.map((model, index) => ( + + {model} + + ))} +
+
+
+ Rate Limits +
TPM: {orgData.litellm_budget_table.tpm_limit || "Unlimited"}
+
RPM: {orgData.litellm_budget_table.rpm_limit || "Unlimited"}
+
+
+ Budget +
+ Max:{" "} + {orgData.litellm_budget_table.max_budget !== null + ? `$${formatNumberWithCommas(orgData.litellm_budget_table.max_budget, 4)}` + : "No Limit"} +
+
Reset: {orgData.litellm_budget_table.budget_duration || "Never"}
+
+ + +
+ )} + + ), + }, + ]} + /> setIsAddMemberModalVisible(false)}