'use client'; import { useId, type ReactNode } from 'react'; import { RotateCcw } from '@/components/icons'; import { SettingItem, ToggleSwitch } from '@/components/settings/settings-section'; import { cn } from '@/lib/utils'; import { Badge } from './badge'; /** * Rows for the server configuration pages (Server, Authentication, * Branding). Each shows where the value comes from and offers to drop an * admin override; the page keeps the edits and saves them as one. */ export type ConfigSource = 'admin' | 'env' | 'default'; export const inputClass = 'h-9 rounded-md border border-input bg-background px-3 text-sm text-foreground placeholder:text-muted-foreground ' + 'hover:border-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:border-ring ' + 'disabled:cursor-not-allowed disabled:opacity-50 transition-colors'; export function SourceBadge({ source, label }: { source?: string; label?: string }) { if (!source || source === 'default') return null; return ( {label ?? source} ); } export function RevertButton({ onClick, title = 'Revert to default' }: { onClick: () => void; title?: string }) { return ( ); } interface ConfigItemBase { label: string; description?: string; configKey: string; source?: string; onChange: (key: string, value: unknown) => void; onRevert?: (key: string) => void; /** Rows that only matter in unusual setups, hidden behind the Advanced switch. */ advanced?: boolean; disabled?: boolean; } interface ConfigTextItemProps extends ConfigItemBase { value: string | undefined | null; placeholder?: string; type?: 'text' | 'url' | 'password' | 'number'; mono?: boolean; /** Wider field, for URLs and lists. */ wide?: boolean; } export function ConfigTextItem({ label, description, configKey, value, source, onChange, onRevert, placeholder, type = 'text', mono, wide, advanced, disabled }: ConfigTextItemProps) { const id = useId(); return (
onChange(configKey, e.target.value)} placeholder={placeholder} aria-label={label} autoComplete="off" spellCheck={false} className={cn(inputClass, 'w-full min-w-0', wide ? 'sm:w-80' : 'sm:w-64', mono && 'font-mono')} /> {source === 'admin' && onRevert && onRevert(configKey)} />}
); } interface ConfigToggleItemProps extends ConfigItemBase { value: boolean | undefined | null; } export function ConfigToggleItem({ label, description, configKey, value, source, onChange, onRevert, advanced, disabled }: ConfigToggleItemProps) { return (
onChange(configKey, checked)} /> {source === 'admin' && onRevert && onRevert(configKey)} />}
); } interface ConfigSelectItemProps extends ConfigItemBase { value: string | undefined | null; options: ReadonlyArray; } export function ConfigSelectItem({ label, description, configKey, value, source, options, onChange, onRevert, advanced, disabled }: ConfigSelectItemProps) { const normalized = options.map((o) => (typeof o === 'string' ? { value: o, label: o } : o)); return (
{source === 'admin' && onRevert && onRevert(configKey)} />}
); } /** A note under a group of rows, for a warning or an explanation. */ export function ConfigNote({ tone = 'info', children }: { tone?: 'info' | 'warning'; children: ReactNode }) { return (
{children}
); }