From 8b0dfae34824aa8856394ee47e925acf3c163f60 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 23 Apr 2026 19:07:25 +0000 Subject: [PATCH] feat(ui): team/EditMembership (MemberModal) to shadcn - antd Modal/Button + @tremor Text/TextInput \u2192 shadcn Dialog/ Button/Input. antd Form/Form.Item/Select preserved (mode='multiple' + field.rules validators + getFieldValue interactions). - Removed stray console.log debug statements that leaked in during an earlier refactor. Gates: TS \u2713 | Build \u2713. Co-authored-by: yuneng-jiang --- .../src/components/team/EditMembership.tsx | 235 ++++++++++-------- 1 file changed, 132 insertions(+), 103 deletions(-) diff --git a/ui/litellm-dashboard/src/components/team/EditMembership.tsx b/ui/litellm-dashboard/src/components/team/EditMembership.tsx index cc655ba52b9..8725e201c20 100644 --- a/ui/litellm-dashboard/src/components/team/EditMembership.tsx +++ b/ui/litellm-dashboard/src/components/team/EditMembership.tsx @@ -1,5 +1,12 @@ -import { Text, TextInput } from "@tremor/react"; -import { Button as AntButton, Form, Modal, Select } from "antd"; +import { Form, Select } from "antd"; +import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; +import { Input } from "@/components/ui/input"; import React, { useEffect, useState } from "react"; import NumericalInput from "../shared/numerical_input"; @@ -23,6 +30,7 @@ interface ModalConfig { label: string | React.ReactNode; type: "input" | "select" | "numerical" | "multi-select"; options?: Array<{ label: string; value: string }>; + // eslint-disable-next-line @typescript-eslint/no-explicit-any rules?: any[]; step?: number; min?: number; @@ -50,25 +58,20 @@ const MemberModal = ({ const [form] = Form.useForm(); const [isSubmitting, setIsSubmitting] = useState(false); - console.log("Initial Data:", initialData); - // Reset form and set initial values when modal becomes visible or initialData changes useEffect(() => { if (visible) { if (mode === "edit" && initialData) { - // For edit mode, use the initialData values + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const d = initialData as any; const formValues = { ...initialData, - // Ensure role is set correctly for editing role: initialData.role || config.defaultRole, - // Keep numeric values as numbers for NumericalInput components - max_budget_in_team: (initialData as any).max_budget_in_team || null, - tpm_limit: (initialData as any).tpm_limit || null, - rpm_limit: (initialData as any).rpm_limit || null, - // Keep array values for multi-select fields - allowed_models: (initialData as any).allowed_models || [], + max_budget_in_team: d.max_budget_in_team || null, + tpm_limit: d.tpm_limit || null, + rpm_limit: d.rpm_limit || null, + allowed_models: d.allowed_models || [], }; - console.log("Setting form values:", formValues); form.setFieldsValue(formValues); } else { // For add mode, reset to defaults @@ -80,29 +83,29 @@ const MemberModal = ({ } }, [visible, initialData, mode, form, config.defaultRole, config.roleOptions]); + // eslint-disable-next-line @typescript-eslint/no-explicit-any const handleSubmit = async (values: any) => { try { setIsSubmitting(true); - // Trim string values and clean up form data const formData = Object.entries(values).reduce((acc, [key, value]) => { if (typeof value === "string") { const trimmedValue = value.trim(); - // For empty strings on optional numeric fields, set to null - if (trimmedValue === "" && (key === "max_budget_in_team" || key === "tpm_limit" || key === "rpm_limit")) { + if ( + trimmedValue === "" && + (key === "max_budget_in_team" || + key === "tpm_limit" || + key === "rpm_limit") + ) { return { ...acc, [key]: null }; } return { ...acc, [key]: trimmedValue }; } - // For numeric values from NumericalInput, use as-is (already numbers) return { ...acc, [key]: value }; }, {}) as T; - console.log("Submitting form data:", formData); await Promise.resolve(onSubmit(formData)); form.resetFields(); - // NotificationsManager.success(`Successfully ${mode === 'add' ? 'added' : 'updated'} member`); } catch (error) { - // NotificationManager.fromBackend('Failed to submit form'); console.error("Form submission error:", error); } finally { setIsSubmitting(false); @@ -119,6 +122,7 @@ const MemberModal = ({ label: string | React.ReactNode; type: "input" | "select" | "numerical" | "multi-select"; options?: Array<{ label: string; value: string }>; + // eslint-disable-next-line @typescript-eslint/no-explicit-any rules?: any[]; step?: number; min?: number; @@ -126,7 +130,7 @@ const MemberModal = ({ }) => { switch (field.type) { case "input": - return ; + return ; case "numerical": return ( ({ }; return ( - -
- {config.showEmail && ( - - - - )} - - {config.showEmail && config.showUserId && ( -
- OR -
- )} - - {config.showUserId && ( - - - - )} - - - Role - {mode === "edit" && initialData && ( - (Current: {getRoleLabel(initialData.role)}) - )} - - } - name="role" - className="mb-4" - rules={[{ required: true, message: "Please select a role!" }]} + (!o ? onCancel() : undefined)}> + + + + {config.title || (mode === "add" ? "Add Member" : "Edit Member")} + + + - - + {config.showEmail && ( + + + + )} - {config.additionalFields?.map((field) => ( - - {renderField(field)} + {config.showEmail && config.showUserId && ( +
+ OR +
+ )} + + {config.showUserId && ( + + + + )} + + + Role + {mode === "edit" && initialData && ( + + (Current: {getRoleLabel(initialData.role)}) + + )} + + } + name="role" + className="mb-4" + rules={[{ required: true, message: "Please select a role!" }]} + > + - ))} -
- - Cancel - - - {mode === "add" - ? isSubmitting - ? "Adding..." - : "Add Member" - : isSubmitting - ? "Saving..." - : "Save Changes"} - -
- -
+ {config.additionalFields?.map((field) => ( + + {renderField(field)} + + ))} + +
+ + +
+ + + ); };