import { type ReactNode } from 'react' import { type ModelEndpointConfig } from '@/stores/onboardingStore' import { TriangleAlert } from 'lucide-react' import { Input } from '@/components/ui/input' import { Card } from '@/components/ui/card' import { Checkbox } from '@/components/ui/checkbox' import { Alert, AlertTitle, AlertDescription } from '@/components/ui/alert' import { cn } from '@/lib/utils' import { API_KEY_MASKED } from '@/api/onboarding' interface ModelConfigCardProps { /** Display name shown as card title */ name: string /** Current field values */ config: ModelEndpointConfig /** Field change handler */ onChange: (config: Partial) => void /** Visual state */ state?: 'default' | 'filled' | 'error' /** Whether inputs are disabled */ disabled?: boolean /** Custom placeholders per field */ placeholders?: Partial> /** * When provided, renders a checkbox in the card header. The card body is * rendered grayed-out and inputs disabled when `enabled` is false. */ enabled?: boolean /** Called when the header checkbox is toggled. Required if `enabled` is set. */ onEnabledChange?: (enabled: boolean) => void /** Description text rendered under the title */ description?: ReactNode /** Optional row rendered below the description (e.g. recommended model link) */ recommendation?: ReactNode } const DEFAULT_PLACEHOLDERS: Record = { endpointUrl: 'e.g. http://localhost:6000/v1', modelName: 'e.g. magenticbrain-v1', apiKey: '', } const FIELDS: { key: keyof ModelEndpointConfig; label: string }[] = [ { key: 'endpointUrl', label: 'Endpoint URL' }, { key: 'modelName', label: 'Model Name' }, { key: 'apiKey', label: 'API Key (optional)' }, ] export function ModelConfigCard({ name, config, onChange, state = 'default', disabled = false, placeholders, enabled, onEnabledChange, description, recommendation, }: ModelConfigCardProps) { const isMasked = config.apiKey === API_KEY_MASKED const idPrefix = name.toLowerCase().replace(/\s+/g, '-') const hasCheckbox = enabled !== undefined // When the card supports a checkbox and is unchecked, force-disable inputs. const inputsDisabled = disabled || (hasCheckbox && !enabled) const dimmed = hasCheckbox && !enabled return (
{/* Title row. When the card has a checkbox, wrap it in a
{FIELDS.map((field) => { const isApiKeyField = field.key === 'apiKey' const showMasked = isApiKeyField && isMasked const inputId = `${idPrefix}-${field.key}` return (
onChange({ [field.key]: e.target.value })} onFocus={showMasked ? () => onChange({ apiKey: '' }) : undefined} readOnly={showMasked} placeholder={placeholders?.[field.key] ?? DEFAULT_PLACEHOLDERS[field.key]} disabled={inputsDisabled} className={cn( 'bg-background h-8 text-sm shadow-none', showMasked && 'cursor-pointer' )} />
) })}
) } /** * Shared alert for verification errors. * Renders each error line with the model name in bold. */ export function VerificationAlert({ errors }: { errors: string[] }) { if (errors.length === 0) return null return ( Verification Failed {errors.map((err, i) => { const colonIdx = err.indexOf(': ') if (colonIdx === -1) return
{err}
return (
{err.slice(0, colonIdx)} {err.slice(colonIdx)}
) })}
) } /** * Recommended-model row shown inside each ModelConfigCard's recommendation slot. * Wraps the model name in an external link. */ export function RecommendedModel({ name, url }: { name: string; url: string }) { return ( Recommended model:{' '} {name} ) }