import { useState, useEffect } from 'react' import { TextField, Button, Switch, FormControlLabel, Tooltip, Box, useMediaQuery, } from '@mui/material' import { Done, Cancel } from '@mui/icons-material' import { useI18nStore } from '../stores/i18n.store' import { useRedisStateStore } from '../stores/redis-state.store' import { useSettingsStore } from '../stores/settings.store' import { useCommonStore } from '../stores/common.store' import { useMainCommandStore } from '../stores/main-command.store' import P3xrDialog from '../components/P3xrDialog' interface TreeSettingsDialogProps { open: boolean onClose: () => void } interface FormModel { treeSeparator: string pageCount: number keyPageCount: number maxValueDisplay: number maxKeys: number keysSort: boolean searchClientSide: boolean searchStartsWith: boolean jsonFormat: boolean animation: boolean } interface FieldRange { min: number max: number required: boolean } const FIELD_RANGES: Record = { pageCount: { min: 10, max: 5000, required: true }, keyPageCount: { min: 5, max: 100, required: true }, maxValueDisplay: { min: -1, max: 32768, required: true }, maxKeys: { min: 5, max: 100000, required: true }, } export default function TreeSettingsDialog({ open, onClose }: TreeSettingsDialogProps) { const strings = useI18nStore(s => s.strings) const settings = useSettingsStore() const state = useRedisStateStore() const { toast, generalHandleError } = useCommonStore() const { refresh } = useMainCommandStore() const isWide = useMediaQuery('(min-width: 600px)') const reducedFunctions = state.reducedFunctions const keysRawLength = state.keysRaw?.length ?? 0 const dbsize = state.dbsize ?? 0 const [model, setModel] = useState({ treeSeparator: '', pageCount: 250, keyPageCount: 5, maxValueDisplay: 1024, maxKeys: 1000, keysSort: true, searchClientSide: false, searchStartsWith: false, jsonFormat: true, animation: true, }) const [errors, setErrors] = useState>({}) useEffect(() => { if (open) { setModel({ treeSeparator: settings.redisTreeDivider, pageCount: settings.pageCount, keyPageCount: settings.keyPageCount, maxValueDisplay: settings.maxValueDisplay, maxKeys: settings.maxKeys, keysSort: settings.keysSort, searchClientSide: settings.searchClientSide, searchStartsWith: settings.searchStartsWith, jsonFormat: Number(settings.jsonFormat) !== 2, animation: settings.animation, }) setErrors({}) } }, [open, settings]) const set = (field: keyof FormModel, value: any) => { setModel(m => ({ ...m, [field]: value })) const range = FIELD_RANGES[field] if (range) { const num = Number(value) if (isNaN(num) || !Number.isInteger(num)) { setErrors(e => ({ ...e, [field]: strings?.form?.error?.integer })) } else if (num < range.min || num > range.max) { setErrors(e => ({ ...e, [field]: `${range.min} - ${range.max}` })) } else { setErrors(e => { const n = { ...e }; delete n[field]; return n }) } } } const validateAll = (): boolean => { const newErrors: Record = {} for (const [field, range] of Object.entries(FIELD_RANGES)) { const value = (model as any)[field] const num = Number(value) if (range.required && (value === '' || value === undefined || value === null)) { newErrors[field] = strings?.form?.error?.required } else if (isNaN(num) || !Number.isInteger(num)) { newErrors[field] = strings?.form?.error?.integer } else if (num < range.min || num > range.max) { newErrors[field] = `${range.min} - ${range.max}` } } setErrors(newErrors) return Object.keys(newErrors).length === 0 } const submit = async () => { if (!validateAll()) { toast(strings?.form?.error?.invalid) return } try { const s = useSettingsStore.getState() s.setSetting('p3xr-main-treecontrol-divider', model.treeSeparator) s.setSetting('p3xr-main-treecontrol-page-size', model.pageCount) s.setSetting('p3xr-main-key-page-size', model.keyPageCount) s.setSetting('p3xr-main-treecontrol-max-value-display', model.maxValueDisplay) s.setSetting('p3xr-max-keys', model.maxKeys) s.setSetting('p3xr-main-treecontrol-key-sort', model.keysSort) s.setSetting('p3xr-main-treecontrol-search-client-mode', model.searchClientSide) s.setSetting('p3xr-main-treecontrol-search-starts-with', model.searchStartsWith) s.setSetting('p3xr-json-format', model.jsonFormat ? 4 : 2) s.setSetting('p3xr-animation-settings', model.animation ? '1' : '0') useRedisStateStore.setState({ page: 1, redisChanged: true }) if (state.connection) await refresh() toast(strings?.status?.saved) onClose() } catch (e) { generalHandleError(e) } } return ( {isWide ? ( ) : ( )} } > set('treeSeparator', e.target.value)} /> set('pageCount', e.target.value === '' ? '' : Number(e.target.value))} error={!!errors.pageCount} helperText={errors.pageCount || strings?.form?.treeSettings?.error?.page} slotProps={{ htmlInput: { min: 10, max: 5000 } }} /> set('keyPageCount', e.target.value === '' ? '' : Number(e.target.value))} error={!!errors.keyPageCount} helperText={errors.keyPageCount || strings?.form?.treeSettings?.error?.keyPageCount} slotProps={{ htmlInput: { min: 5, max: 100 } }} /> set('maxValueDisplay', e.target.value === '' ? '' : Number(e.target.value))} error={!!errors.maxValueDisplay} helperText={errors.maxValueDisplay || strings?.form?.treeSettings?.maxValueDisplayInfo} slotProps={{ htmlInput: { min: -1, max: 32768 } }} /> set('maxKeys', e.target.value === '' ? '' : Number(e.target.value))} error={!!errors.maxKeys} helperText={errors.maxKeys || strings?.form?.treeSettings?.maxKeysInfo} slotProps={{ htmlInput: { min: 5, max: 100000 } }} /> {!reducedFunctions && ( set('keysSort', v)} />} label={model.keysSort ? strings?.label?.keysSort?.on : strings?.label?.keysSort?.off} /> )} {!reducedFunctions && ( set('searchClientSide', v)} disabled={dbsize > settings.maxLightKeysCount} />} label={model.searchClientSide ? strings?.form?.treeSettings?.label?.searchModeClient : strings?.form?.treeSettings?.label?.searchModeServer} /> )} {reducedFunctions && ( {(() => { const fn = strings?.label?.tooManyKeys return typeof fn === 'function' ? fn({ count: keysRawLength, maxLightKeysCount: settings.maxLightKeysCount }) : '' })()} )} set('searchStartsWith', v)} />} label={model.searchStartsWith ? strings?.form?.treeSettings?.label?.searchModeStartsWith : strings?.form?.treeSettings?.label?.searchModeIncludes} /> set('jsonFormat', v)} />} label={model.jsonFormat ? strings?.form?.treeSettings?.label?.jsonFormatFourSpace : strings?.form?.treeSettings?.label?.jsonFormatTwoSpace} /> set('animation', v)} />} label={model.animation ? strings?.form?.treeSettings?.label?.animation : strings?.form?.treeSettings?.label?.noAnimation} /> ) }