diff --git a/ui/litellm-dashboard/src/components/shared/numerical_input.tsx b/ui/litellm-dashboard/src/components/shared/numerical_input.tsx index 2682635dc27..69ae1d2dc12 100644 --- a/ui/litellm-dashboard/src/components/shared/numerical_input.tsx +++ b/ui/litellm-dashboard/src/components/shared/numerical_input.tsx @@ -1,5 +1,6 @@ import React from "react"; -import { NumberInput } from "@tremor/react"; +import { Input } from "@/components/ui/input"; +import { cn } from "@/lib/utils"; interface NumericalInputProps { step?: number; @@ -7,20 +8,33 @@ interface NumericalInputProps { placeholder?: string; min?: number; max?: number; - onChange?: any; // Using any to avoid type conflicts with Tremor's NumberInput + // Callers pass either an onChange(numberOrNull) signature (the @tremor + // contract this component used to expose) or a React.ChangeEventHandler. + // We accept both at the signature boundary and bridge to number|null|undefined + // inside the component. + // eslint-disable-next-line @typescript-eslint/no-explicit-any + onChange?: any; + value?: number | string | null | undefined; + defaultValue?: number | string | null | undefined; + name?: string; + className?: string; + disabled?: boolean; + precision?: number; + // eslint-disable-next-line @typescript-eslint/no-explicit-any [key: string]: any; } /** - * A reusable numerical input component - * @param {Object} props - Component props - * @param {number} [props.step=0.01] - Step increment for the input - * @param {Object} [props.style] - Custom styles to apply - * @param {string} [props.placeholder="Enter a numerical value"] - Placeholder text - * @param {number} [props.min] - Minimum value - * @param {number} [props.max] - Maximum value - * @param {Function} [props.onChange] - On change handler - * @param {any} props.rest - Additional props passed to NumberInput + * Numerical input. Phase-1 shadcn-migrated replacement for the former + * @tremor NumberInput. Accepts the same prop surface so existing callers + * continue to work: + * - onChange({number | null | undefined}) \u2014 tremor-style + * - value / defaultValue as number | string | null | undefined + * - step / min / max / precision / placeholder / style / className / disabled + * + * `precision` is accepted for API-compat but not enforced (HTML input + * type=number does not clamp decimal precision; caller code typically + * rounds before persisting). */ const NumericalInput: React.FC = ({ step = 0.01, @@ -29,17 +43,50 @@ const NumericalInput: React.FC = ({ min, max, onChange, + value, + defaultValue, + className, + disabled, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + precision, ...rest }) => { + const toValue = (v: number | string | null | undefined): string => { + if (v === null || v === undefined) return ""; + return String(v); + }; + + const handleChange = (e: React.ChangeEvent) => { + if (!onChange) return; + const raw = e.target.value; + if (raw === "") { + onChange(null); + return; + } + const num = Number(raw); + if (Number.isNaN(num)) { + onChange(null); + return; + } + onChange(num); + }; + return ( - event.currentTarget.blur()} + (event.currentTarget as HTMLInputElement).blur()} step={step} style={style} placeholder={placeholder} min={min} max={max} - onChange={onChange} + value={value !== undefined ? toValue(value) : undefined} + defaultValue={ + defaultValue !== undefined ? toValue(defaultValue) : undefined + } + onChange={handleChange} + disabled={disabled} + className={cn(className)} {...rest} /> );