diff --git a/ui/litellm-dashboard/src/components/UsageIndicator.tsx b/ui/litellm-dashboard/src/components/UsageIndicator.tsx index 47da9f78bdc..a646e07703b 100644 --- a/ui/litellm-dashboard/src/components/UsageIndicator.tsx +++ b/ui/litellm-dashboard/src/components/UsageIndicator.tsx @@ -1,5 +1,5 @@ import { useDisableUsageIndicator } from "@/app/(dashboard)/hooks/useDisableUsageIndicator"; -import { Badge } from "@tremor/react"; +import { Badge } from "@/components/ui/badge"; import { AlertTriangle, Calendar, ChevronDown, ChevronUp, Loader2, Minus, TrendingUp, UserCheck, Users } from "lucide-react"; import { useEffect, useState } from "react"; import { getRemainingUsers, getLicenseInfo, LicenseInfo } from "./networking"; @@ -146,10 +146,10 @@ export default function UsageIndicator({ accessToken, width = 220 }: UsageIndica const hasError = isOverLimit || isLicenseExpired; const hasWarning = (isNearLimit || isLicenseExpiringSoon) && !hasError; - const getStatusColor = () => { - if (hasError) return "red"; - if (hasWarning) return "yellow"; - return "green"; + const getStatusColor = (): "destructive" | "default" | "secondary" => { + if (hasError) return "destructive"; + if (hasWarning) return "secondary"; + return "default"; }; const getStatusIcon = () => { @@ -252,7 +252,7 @@ export default function UsageIndicator({ accessToken, width = 220 }: UsageIndica Usage Status {hasAnyIssue && ( - + {getStatusIcon()} )} diff --git a/ui/litellm-dashboard/src/components/edit_user.tsx b/ui/litellm-dashboard/src/components/edit_user.tsx index cfb7acd85e7..80dca68e16d 100644 --- a/ui/litellm-dashboard/src/components/edit_user.tsx +++ b/ui/litellm-dashboard/src/components/edit_user.tsx @@ -1,110 +1,229 @@ -import { useEffect, useState } from "react"; -import { TextInput, SelectItem } from "@tremor/react"; - -import { Button as Button2, Modal, Form, Select as Select2, InputNumber } from "antd"; - +import { useEffect } from "react"; +import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; import NumericalInput from "./shared/numerical_input"; import BudgetDurationDropdown from "./common_components/budget_duration_dropdown"; +import { Controller, FormProvider, useForm } from "react-hook-form"; interface EditUserModalProps { visible: boolean; possibleUIRoles: null | Record>; onCancel: () => void; + // eslint-disable-next-line @typescript-eslint/no-explicit-any user: any; + // eslint-disable-next-line @typescript-eslint/no-explicit-any onSubmit: (data: any) => void; } -const EditUserModal: React.FC = ({ visible, possibleUIRoles, onCancel, user, onSubmit }) => { - const [editedUser, setEditedUser] = useState(user); - const [form] = Form.useForm(); +interface EditUserValues { + user_email: string; + user_id: string; + user_role: string; + spend: number | null; + max_budget: number | null; + budget_duration: string | null; +} + +function LabeledRow({ + id, + label, + help, + tooltip, + children, +}: { + id?: string; + label: string; + help?: string; + tooltip?: string; + children: React.ReactNode; +}) { + return ( +
+ +
+ {children} + {help &&

{help}

} +
+
+ ); +} + +const EditUserModal: React.FC = ({ + visible, + possibleUIRoles, + onCancel, + user, + onSubmit, +}) => { + const form = useForm({ + defaultValues: { + user_email: "", + user_id: "", + user_role: "", + spend: null, + max_budget: null, + budget_duration: null, + }, + mode: "onSubmit", + }); useEffect(() => { - form.resetFields(); + if (user) { + form.reset({ + user_email: user.user_email ?? "", + user_id: user.user_id ?? "", + user_role: user.user_role ?? "", + spend: user.spend ?? null, + max_budget: user.max_budget ?? null, + budget_duration: user.budget_duration ?? null, + }); + } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [user]); - const handleCancel = async () => { - form.resetFields(); + const handleCancel = () => { + form.reset(); onCancel(); }; - const handleEditSubmit = async (formValues: Record) => { - // Call API to update team with teamId and values - onSubmit(formValues); - form.resetFields(); + const handleEditSubmit = form.handleSubmit((values) => { + onSubmit(values); + form.reset(); onCancel(); - }; + }); - if (!user) { - return null; - } + if (!user) return null; return ( - -
- <> - - - + (!o ? handleCancel() : undefined)} + > + + + Edit User {user.user_id} + + Edit user attributes including role, budget, and rate limits. + + + + + + + - + {/* hidden user_id field */} + - - - {possibleUIRoles && - Object.entries(possibleUIRoles).map(([role, { ui_label, description }]) => ( - -
- {ui_label}{" "} -

- {description} -

-
-
- ))} -
-
+ + ( + + )} + /> + - - - + + + - - - + + ( + + field.onChange(v ?? null) + } + /> + )} + /> + - - - + + ( + + field.onChange((value as string) ?? null) + } + /> + )} + /> + -
- Save -
- -
- Save -
- - -
+ + + + + + + ); };