73 lines
4.2 KiB
TypeScript
73 lines
4.2 KiB
TypeScript
'use client'
|
|
|
|
export type AdminSetting = { key: string; value: string | null; isSet: boolean; source: string; sensitive: boolean }
|
|
type Option = { value: string; label: string }
|
|
type Props = {
|
|
setting: AdminSetting
|
|
label: string
|
|
value: string
|
|
help?: string
|
|
placeholder?: string
|
|
boolean?: boolean
|
|
numeric?: boolean
|
|
multiline?: boolean
|
|
options?: Option[]
|
|
optionsUnavailable?: boolean
|
|
onChange: (value: string) => void
|
|
}
|
|
|
|
const SELECTS: Record<string, Option[]> = {
|
|
log_level: ['DEBUG', 'INFO', 'WARNING', 'ERROR'].map((value) => ({ value, label: value })),
|
|
issue_confirmation_contact_attempts: Array.from({ length: 11 }, (_, index) => ({ value: String(index), label: index === 0 ? 'None — close when fixed' : String(index) })),
|
|
issue_confirmation_interval_unit: ['days', 'weeks', 'months'].map((value) => ({ value, label: value[0].toUpperCase() + value.slice(1) })),
|
|
artwork_cache_mode: [{ value: 'remote', label: 'Load from the internet' }, { value: 'cache', label: 'Store locally' }],
|
|
site_banner_tone: ['info', 'warning', 'error', 'maintenance'].map((value) => ({ value, label: value[0].toUpperCase() + value.slice(1) })),
|
|
magent_notify_push_provider: ['ntfy', 'gotify', 'pushover', 'webhook', 'telegram', 'discord'].map((value) => ({ value, label: value })),
|
|
requests_data_source: [{ value: 'always_js', label: 'Read directly from Seerr' }, { value: 'prefer_cache', label: 'Use saved requests' }],
|
|
}
|
|
|
|
export default function SettingField(props: Props) {
|
|
const { setting, label, value, help, placeholder, onChange } = props
|
|
const id = `setting-${setting.key}`
|
|
const options = props.options ?? SELECTS[setting.key] ?? (setting.key === 'log_http_client_level' || setting.key === 'log_background_sync_level' ? SELECTS.log_level : undefined)
|
|
const selectedOptions = options && value && !options.some((option) => option.value === value)
|
|
? [{ value, label: `Current selection (${value})` }, ...options] : options
|
|
const isTime = setting.key === 'requests_full_sync_time' || setting.key === 'requests_cleanup_time'
|
|
const zeroAllowed = setting.key === 'log_file_backup_count'
|
|
const minimum = zeroAllowed ? 0 : 1
|
|
const maximum = setting.key === 'issue_confirmation_interval_value' ? 365 : setting.key.endsWith('_port') ? 65535 : undefined
|
|
const aria = { id, name: setting.key, 'aria-describedby': help ? `${id}-help` : undefined }
|
|
|
|
if (props.boolean) {
|
|
return (
|
|
<div className="setting-field setting-switch">
|
|
<div><label htmlFor={id}>{label}</label>{help && <p id={`${id}-help`}>{help}</p>}</div>
|
|
<input {...aria} type="checkbox" role="switch" checked={value.toLowerCase() === 'true'} onChange={(event) => onChange(String(event.target.checked))} />
|
|
</div>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<div className={`setting-field ${props.multiline ? 'field-span-full' : ''}`}>
|
|
<label htmlFor={id}>{label}{setting.sensitive && setting.isSet && <small>Saved</small>}</label>
|
|
{props.optionsUnavailable ? (
|
|
<select {...aria} disabled value={value}><option value={value}>Save the connection, then reload available options</option></select>
|
|
) : selectedOptions ? (
|
|
<select {...aria} value={value} onChange={(event) => onChange(event.target.value)}>
|
|
{!value && <option value="">Choose an option</option>}
|
|
{selectedOptions.map((option) => <option key={option.value} value={option.value}>{option.label}</option>)}
|
|
</select>
|
|
) : props.multiline ? (
|
|
<textarea {...aria} rows={setting.key.includes('_pem') ? 6 : 3} value={value} placeholder={placeholder} onChange={(event) => onChange(event.target.value)} />
|
|
) : (
|
|
<input {...aria} type={setting.sensitive ? 'password' : props.numeric ? 'number' : isTime ? 'time' : 'text'}
|
|
value={value} min={props.numeric ? minimum : undefined} max={props.numeric ? maximum : undefined} step={props.numeric ? 1 : undefined}
|
|
autoComplete={setting.sensitive ? 'new-password' : 'off'} spellCheck={false}
|
|
placeholder={setting.sensitive && setting.isSet ? 'Leave blank to keep the saved value' : placeholder}
|
|
onChange={(event) => onChange(event.target.value)} />
|
|
)}
|
|
{help && <p id={`${id}-help`}>{help}</p>}
|
|
</div>
|
|
)
|
|
}
|