RSS Git Download  Clone
Raw Blame History 10kB 180 lines
<script setup lang="ts">
import { ref, computed, watch } from 'vue'
import { useDisplay } from 'vuetify'
import { useI18nStore } from '../stores/i18n'
import { useRedisStateStore } from '../stores/redis-state'
import { useSettingsStore } from '../stores/settings'
import { useCommonStore } from '../stores/common.store'
import { useMainCommandStore } from '../stores/main-command'
import P3xrDialog from '../components/P3xrDialog.vue'

const props = defineProps<{ open: boolean }>()
const emit = defineEmits<{ close: [] }>()

const i18n = useI18nStore()
const strings = computed(() => i18n.strings)
const settings = useSettingsStore()
const state = useRedisStateStore()
const common = useCommonStore()
const mainCommand = useMainCommandStore()
const { width } = useDisplay()
const isWide = computed(() => width.value >= 600)

const FIELD_RANGES: Record<string, { min: number; max: number }> = {
    pageCount: { min: 10, max: 5000 },
    keyPageCount: { min: 5, max: 100 },
    maxValueDisplay: { min: -1, max: 32768 },
    maxKeys: { min: 5, max: 100000 },
}

const model = ref({
    treeSeparator: '', pageCount: 250, keyPageCount: 5,
    maxValueDisplay: 1024, maxKeys: 1000, keysSort: true,
    searchClientSide: false, searchStartsWith: false,
    jsonFormat: true, animation: true, undoEnabled: true, showDiffBeforeSave: true,
})
const errors = ref<Record<string, string>>({})

watch(() => props.open, (v) => {
    if (v) {
        model.value = {
            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 === true || settings.animation === 1 || String(settings.animation) === '1',
            undoEnabled: settings.undoEnabled,
            showDiffBeforeSave: settings.showDiffBeforeSave,
        }
        errors.value = {}
    }
})

function setField(field: string, value: any) {
    ;(model.value as any)[field] = value
    const range = FIELD_RANGES[field]
    if (range) {
        const num = Number(value)
        if (isNaN(num) || !Number.isInteger(num)) {
            errors.value[field] = strings.value?.form?.error?.integer
        } else if (num < range.min || num > range.max) {
            errors.value[field] = `${range.min} - ${range.max}`
        } else {
            delete errors.value[field]
        }
    }
}

function validateAll(): boolean {
    const newErrors: Record<string, string> = {}
    for (const [field, range] of Object.entries(FIELD_RANGES)) {
        const value = (model.value as any)[field]
        const num = Number(value)
        if (value === '' || value === undefined || value === null) {
            newErrors[field] = strings.value?.form?.error?.required
        } else if (isNaN(num) || !Number.isInteger(num)) {
            newErrors[field] = strings.value?.form?.error?.integer
        } else if (num < range.min || num > range.max) {
            newErrors[field] = `${range.min} - ${range.max}`
        }
    }
    errors.value = newErrors
    return Object.keys(newErrors).length === 0
}

async function submit() {
    if (!validateAll()) {
        common.toast(strings.value?.form?.error?.invalid)
        return
    }
    try {
        const m = model.value
        settings.setSetting('p3xr-main-treecontrol-divider', m.treeSeparator)
        settings.setSetting('p3xr-main-treecontrol-page-size', m.pageCount)
        settings.setSetting('p3xr-main-key-page-size', m.keyPageCount)
        settings.setSetting('p3xr-main-treecontrol-max-value-display', m.maxValueDisplay)
        settings.setSetting('p3xr-max-keys', m.maxKeys)
        settings.setSetting('p3xr-main-treecontrol-key-sort', m.keysSort)
        settings.setSetting('p3xr-main-treecontrol-search-client-mode', m.searchClientSide)
        settings.setSetting('p3xr-main-treecontrol-search-starts-with', m.searchStartsWith)
        settings.setSetting('p3xr-json-format', m.jsonFormat ? 4 : 2)
        settings.setSetting('p3xr-animation-settings', m.animation ? '1' : '0')
        settings.setSetting('p3xr-undo-enabled', m.undoEnabled)
        settings.setSetting('p3xr-show-diff-before-save', m.showDiffBeforeSave)
        state.page = 1
        state.redisChanged = true
        if (state.connection) await mainCommand.refresh()
        common.toast(strings.value?.status?.saved)
        emit('close')
    } catch (e) {
        common.generalHandleError(e)
    }
}

const reducedFunctions = computed(() => state.reducedFunctions)
const keysRawLength = computed(() => state.keysRaw?.length ?? 0)
const dbsize = computed(() => state.dbsize ?? 0)
</script>

<template>
    <P3xrDialog v-if="open" :open="true" :title="strings?.form?.treeSettings?.label?.formName" @close="emit('close')">
        <v-text-field v-model="model.treeSeparator" :label="strings?.form?.treeSettings?.field?.treeSeparator" variant="outlined" density="comfortable" hide-details class="mb-3" />
        <v-text-field v-model.number="model.pageCount" :label="strings?.form?.treeSettings?.field?.page" type="number" variant="outlined" density="comfortable"
            :error="!!errors.pageCount" :error-messages="errors.pageCount" :hint="!errors.pageCount ? strings?.form?.treeSettings?.error?.page : ''" persistent-hint
            class="mb-3" @update:model-value="v => setField('pageCount', v)" />
        <v-text-field v-model.number="model.keyPageCount" :label="strings?.form?.treeSettings?.field?.keyPageCount" type="number" variant="outlined" density="comfortable"
            :error="!!errors.keyPageCount" :error-messages="errors.keyPageCount" :hint="!errors.keyPageCount ? strings?.form?.treeSettings?.error?.keyPageCount : ''" persistent-hint
            class="mb-3" @update:model-value="v => setField('keyPageCount', v)" />
        <v-text-field v-model.number="model.maxValueDisplay" :label="strings?.form?.treeSettings?.maxValueDisplay" type="number" variant="outlined" density="comfortable"
            :error="!!errors.maxValueDisplay" :error-messages="errors.maxValueDisplay" :hint="!errors.maxValueDisplay ? strings?.form?.treeSettings?.maxValueDisplayInfo : ''" persistent-hint
            class="mb-3" @update:model-value="v => setField('maxValueDisplay', v)" />
        <v-text-field v-model.number="model.maxKeys" :label="strings?.form?.treeSettings?.maxKeys" type="number" variant="outlined" density="comfortable"
            :error="!!errors.maxKeys" :error-messages="errors.maxKeys" :hint="!errors.maxKeys ? strings?.form?.treeSettings?.maxKeysInfo : ''" persistent-hint
            class="mb-3" @update:model-value="v => setField('maxKeys', v)" />

        <template v-if="!reducedFunctions">
            <div class="p3xr-settings-switch-row"><v-switch v-model="model.keysSort" :label="model.keysSort ? strings?.label?.keysSort?.on : strings?.label?.keysSort?.off" density="comfortable" hide-details /></div>
            <div class="p3xr-settings-switch-row"><v-switch v-model="model.searchClientSide" :disabled="dbsize > settings.maxLightKeysCount"
                :label="model.searchClientSide ? strings?.form?.treeSettings?.label?.searchModeClient : strings?.form?.treeSettings?.label?.searchModeServer"
                density="comfortable" hide-details /></div>
        </template>
        <div v-if="reducedFunctions" style="margin-top: 8px; padding: 12px; background: rgba(128,128,128,0.1); border-radius: 4px; font-size: 13px; opacity: 0.8;">
            {{ typeof strings?.label?.tooManyKeys === 'function' ? strings.label.tooManyKeys({ count: keysRawLength, maxLightKeysCount: settings.maxLightKeysCount }) : '' }}
        </div>

        <div class="p3xr-settings-switch-row"><v-switch v-model="model.searchStartsWith" :label="model.searchStartsWith ? strings?.form?.treeSettings?.label?.searchModeStartsWith : strings?.form?.treeSettings?.label?.searchModeIncludes" density="comfortable" hide-details /></div>
        <div class="p3xr-settings-switch-row"><v-switch v-model="model.jsonFormat" :label="model.jsonFormat ? strings?.form?.treeSettings?.label?.jsonFormatFourSpace : strings?.form?.treeSettings?.label?.jsonFormatTwoSpace" density="comfortable" hide-details /></div>
        <div class="p3xr-settings-switch-row"><v-switch v-model="model.animation" :label="model.animation ? strings?.form?.treeSettings?.label?.animation : strings?.form?.treeSettings?.label?.noAnimation" density="comfortable" hide-details /></div>
        <div class="p3xr-settings-switch-row"><v-switch v-model="model.undoEnabled" :label="model.undoEnabled ? strings?.form?.treeSettings?.label?.undoEnabled : strings?.form?.treeSettings?.label?.undoDisabled" density="comfortable" hide-details /></div>
        <div style="font-size: 12px; opacity: 0.7; margin-left: 50px; margin-top: -4px; margin-bottom: 4px;">{{ strings?.form?.treeSettings?.undoHint }}</div>
        <div class="p3xr-settings-switch-row"><v-switch v-model="model.showDiffBeforeSave" :label="model.showDiffBeforeSave ? strings?.form?.treeSettings?.label?.diffEnabled : strings?.form?.treeSettings?.label?.diffDisabled" density="comfortable" hide-details /></div>

        <template #actions>
            <v-btn variant="flat" color="warning" @click="emit('close')">
                <v-icon :class="{ 'mr-1': isWide }">mdi-close-circle</v-icon>
                <span v-if="isWide">{{ strings?.intention?.cancel }}</span>
                <v-tooltip v-if="!isWide" activator="parent" location="top">{{ strings?.intention?.cancel }}</v-tooltip>
            </v-btn>
            <v-btn variant="flat" color="primary" @click="submit">
                <v-icon class="mr-1">mdi-check</v-icon>
                <span>{{ strings?.intention?.save }}</span>
            </v-btn>
        </template>
    </P3xrDialog>
</template>

<style>
.p3xr-settings-switch-row {
    display: block;
    margin-bottom: 4px;
}
.p3xr-settings-switch-row .v-input {
    display: flex !important;
}
</style>