forked from vikunja/frontend
fix(filter): validate title before creating or editing a filter
Resolves #3152
This commit is contained in:
parent
4033c28a67
commit
1f40b68108
|
@ -405,7 +405,8 @@
|
|||
"create": {
|
||||
"title": "New Saved Filter",
|
||||
"description": "A saved filter is a virtual list which is computed from a set of filters each time it is accessed. Once created, it will appear in a special namespace.",
|
||||
"action": "Create new saved filter"
|
||||
"action": "Create new saved filter",
|
||||
"titleRequired": "Please provide a title for the filter."
|
||||
},
|
||||
"delete": {
|
||||
"header": "Delete this saved filter",
|
||||
|
|
|
@ -2,6 +2,7 @@ import {computed, ref, shallowReactive, unref, watch} from 'vue'
|
|||
import {useRouter} from 'vue-router'
|
||||
import {useI18n} from 'vue-i18n'
|
||||
import type {MaybeRef} from '@vueuse/core'
|
||||
import {useDebounceFn} from '@vueuse/core'
|
||||
|
||||
import type {IList} from '@/modelTypes/IList'
|
||||
import type {ISavedFilter} from '@/modelTypes/ISavedFilter'
|
||||
|
@ -133,14 +134,38 @@ export function useSavedFilter(listId?: MaybeRef<IList['id']>) {
|
|||
router.push({name: 'namespaces.index'})
|
||||
}
|
||||
|
||||
const titleValid = ref(true)
|
||||
const validateTitleField = useDebounceFn(() => {
|
||||
titleValid.value = filter.value.title !== ''
|
||||
}, 100)
|
||||
|
||||
async function createFilterWithValidation() {
|
||||
if (!titleValid.value) {
|
||||
return
|
||||
}
|
||||
return createFilter()
|
||||
}
|
||||
|
||||
async function saveFilterWithValidation() {
|
||||
if (!titleValid.value) {
|
||||
return
|
||||
}
|
||||
return saveFilter()
|
||||
}
|
||||
|
||||
return {
|
||||
createFilter,
|
||||
createFilterWithValidation,
|
||||
saveFilter,
|
||||
saveFilterWithValidation,
|
||||
deleteFilter,
|
||||
|
||||
filter,
|
||||
filters,
|
||||
|
||||
filterService,
|
||||
|
||||
titleValid,
|
||||
validateTitleField,
|
||||
}
|
||||
}
|
|
@ -3,25 +3,27 @@
|
|||
:title="$t('filters.edit.title')"
|
||||
primary-icon=""
|
||||
:primary-label="$t('misc.save')"
|
||||
@primary="saveFilter"
|
||||
@primary="saveFilterWithValidation"
|
||||
:tertiary="$t('misc.delete')"
|
||||
@tertiary="$router.push({ name: 'filter.settings.delete', params: { id: listId } })"
|
||||
>
|
||||
<form @submit.prevent="saveFilter()">
|
||||
<form @submit.prevent="saveFilterWithValidation()">
|
||||
<div class="field">
|
||||
<label class="label" for="title">{{ $t('filters.attributes.title') }}</label>
|
||||
<div class="control">
|
||||
<input
|
||||
:class="{ 'disabled': filterService.loading}"
|
||||
v-model="filter.title"
|
||||
:class="{ 'disabled': filterService.loading, 'is-danger': !titleValid }"
|
||||
:disabled="filterService.loading || undefined"
|
||||
@keyup.enter="saveFilter"
|
||||
class="input"
|
||||
id="title"
|
||||
id="Title"
|
||||
:placeholder="$t('filters.attributes.titlePlaceholder')"
|
||||
type="text"
|
||||
v-focus
|
||||
v-model="filter.title"/>
|
||||
@focusout="validateTitleField"
|
||||
/>
|
||||
</div>
|
||||
<p class="help is-danger" v-if="!titleValid">{{ $t('filters.create.titleRequired') }}</p>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label" for="description">{{ $t('filters.attributes.description') }}</label>
|
||||
|
@ -65,9 +67,11 @@ import type {IList} from '@/modelTypes/IList'
|
|||
const props = defineProps<{ listId: IList['id'] }>()
|
||||
|
||||
const {
|
||||
saveFilter,
|
||||
saveFilterWithValidation,
|
||||
filter,
|
||||
filters,
|
||||
filterService,
|
||||
titleValid,
|
||||
validateTitleField,
|
||||
} = useSavedFilter(toRef(props, 'listId'))
|
||||
</script>
|
||||
|
|
|
@ -12,15 +12,17 @@
|
|||
<div class="control">
|
||||
<input
|
||||
v-model="filter.title"
|
||||
:class="{ 'disabled': filterService.loading}"
|
||||
:class="{ 'disabled': filterService.loading, 'is-danger': !titleValid }"
|
||||
:disabled="filterService.loading || undefined"
|
||||
class="input"
|
||||
id="Title"
|
||||
:placeholder="$t('filters.attributes.titlePlaceholder')"
|
||||
type="text"
|
||||
v-focus
|
||||
@focusout="validateTitleField"
|
||||
/>
|
||||
</div>
|
||||
<p class="help is-danger" v-if="!titleValid">{{ $t('filters.create.titleRequired') }}</p>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label" for="description">{{ $t('filters.attributes.description') }}</label>
|
||||
|
@ -51,8 +53,8 @@
|
|||
<template #footer>
|
||||
<x-button
|
||||
:loading="filterService.loading"
|
||||
:disabled="filterService.loading"
|
||||
@click="createFilter()"
|
||||
:disabled="filterService.loading || !titleValid"
|
||||
@click="createFilterWithValidation()"
|
||||
class="is-fullwidth"
|
||||
>
|
||||
{{ $t('filters.create.action') }}
|
||||
|
@ -71,7 +73,9 @@ import {useSavedFilter} from '@/services/savedFilter'
|
|||
const {
|
||||
filter,
|
||||
filters,
|
||||
createFilter,
|
||||
createFilterWithValidation,
|
||||
filterService,
|
||||
titleValid,
|
||||
validateTitleField,
|
||||
} = useSavedFilter()
|
||||
</script>
|
||||
|
|
Loading…
Reference in New Issue