fix(views): transform view filter before and after loading it from the api

Previously, the actual filter was kept as-is when sending it to the api, essentially creating an invalid filter. This change fixes this, transforming the filter before saving and after loading.

Resolves vikunja/vikunja#2233
This commit is contained in:
kolaente 2024-04-02 13:20:17 +02:00
parent 81de986d8d
commit 13cab62d14
Signed by untrusted user: konrad
GPG Key ID: F40E70337AB24C9B

View File

@ -2,12 +2,65 @@
import type {IProjectView} from '@/modelTypes/IProjectView' import type {IProjectView} from '@/modelTypes/IProjectView'
import XButton from '@/components/input/button.vue' import XButton from '@/components/input/button.vue'
import FilterInput from '@/components/project/partials/FilterInput.vue' import FilterInput from '@/components/project/partials/FilterInput.vue'
import {ref} from 'vue' import {ref, watch} from 'vue'
import {transformFilterStringForApi, transformFilterStringFromApi} from '@/helpers/filters'
import {useLabelStore} from '@/stores/labels'
import {useProjectStore} from '@/stores/projects'
const {
modelValue,
} = defineProps<{
modelValue: IProjectView,
}>()
const emit = defineEmits(['update:modelValue'])
const view = ref<IProjectView>()
const labelStore = useLabelStore()
const projectStore = useProjectStore()
watch(
() => modelValue,
newValue => {
const transformed = {
...newValue,
filter: transformFilterStringFromApi(
newValue.filter,
labelId => labelStore.getLabelById(labelId)?.title,
projectId => projectStore.projects[projectId]?.title || null,
),
}
if (JSON.stringify(view.value) !== JSON.stringify(transformed)) {
view.value = transformed
}
},
{immediate: true, deep: true},
)
watch(
() => view.value,
newView => {
emit('update:modelValue', {
...newView,
filter: transformFilterStringForApi(
newView.filter,
labelTitle => labelStore.filterLabelsByQuery([], labelTitle)[0]?.id || null,
projectTitle => {
const found = projectStore.findProjectByExactname(projectTitle)
return found?.id || null
},
),
})
},
{deep: true},
)
const model = defineModel<IProjectView>()
const titleValid = ref(true) const titleValid = ref(true)
function validateTitle() { function validateTitle() {
titleValid.value = model.value.title !== '' titleValid.value = view.value?.title !== ''
} }
</script> </script>
@ -23,14 +76,14 @@ function validateTitle() {
<div class="control"> <div class="control">
<input <input
id="title" id="title"
v-model="model.title" v-model="view.title"
v-focus v-focus
class="input" class="input"
:placeholder="$t('project.share.links.namePlaceholder')" :placeholder="$t('project.share.links.namePlaceholder')"
@blur="validateTitle" @blur="validateTitle"
> >
</div> </div>
<p <p
v-if="!titleValid" v-if="!titleValid"
class="help is-danger" class="help is-danger"
> >
@ -49,7 +102,7 @@ function validateTitle() {
<div class="select"> <div class="select">
<select <select
id="kind" id="kind"
v-model="model.viewKind" v-model="view.viewKind"
> >
<option value="list"> <option value="list">
{{ $t('project.list.title') }} {{ $t('project.list.title') }}
@ -69,12 +122,12 @@ function validateTitle() {
</div> </div>
<FilterInput <FilterInput
v-model="model.filter" v-model="view.filter"
:input-label="$t('project.views.filter')" :input-label="$t('project.views.filter')"
/> />
<div <div
v-if="model.viewKind === 'kanban'" v-if="view.viewKind === 'kanban'"
class="field" class="field"
> >
<label <label
@ -87,7 +140,7 @@ function validateTitle() {
<div class="select"> <div class="select">
<select <select
id="configMode" id="configMode"
v-model="model.bucketConfigurationMode" v-model="view.bucketConfigurationMode"
> >
<option value="manual"> <option value="manual">
{{ $t('project.views.bucketConfigManual') }} {{ $t('project.views.bucketConfigManual') }}
@ -101,7 +154,7 @@ function validateTitle() {
</div> </div>
<div <div
v-if="model.viewKind === 'kanban' && model.bucketConfigurationMode === 'filter'" v-if="view.viewKind === 'kanban' && view.bucketConfigurationMode === 'filter'"
class="field" class="field"
> >
<label class="label"> <label class="label">
@ -109,15 +162,15 @@ function validateTitle() {
</label> </label>
<div class="control"> <div class="control">
<div <div
v-for="(b, index) in model.bucketConfiguration" v-for="(b, index) in view.bucketConfiguration"
:key="'bucket_'+index" :key="'bucket_'+index"
class="filter-bucket" class="filter-bucket"
> >
<button <button
class="is-danger" class="is-danger"
@click.prevent="() => model.bucketConfiguration.splice(index, 1)" @click.prevent="() => view.bucketConfiguration.splice(index, 1)"
> >
<icon icon="trash-alt" /> <icon icon="trash-alt"/>
</button> </button>
<div class="filter-bucket-form"> <div class="filter-bucket-form">
<div class="field"> <div class="field">
@ -130,7 +183,7 @@ function validateTitle() {
<div class="control"> <div class="control">
<input <input
:id="'bucket_'+index+'_title'" :id="'bucket_'+index+'_title'"
v-model="model.bucketConfiguration[index].title" v-model="view.bucketConfiguration[index].title"
class="input" class="input"
:placeholder="$t('project.share.links.namePlaceholder')" :placeholder="$t('project.share.links.namePlaceholder')"
> >
@ -138,7 +191,7 @@ function validateTitle() {
</div> </div>
<FilterInput <FilterInput
v-model="model.bucketConfiguration[index].filter" v-model="view.bucketConfiguration[index].filter"
:input-label="$t('project.views.filter')" :input-label="$t('project.views.filter')"
/> />
</div> </div>
@ -147,7 +200,7 @@ function validateTitle() {
<XButton <XButton
variant="secondary" variant="secondary"
icon="plus" icon="plus"
@click="() => model.bucketConfiguration.push({title: '', filter: ''})" @click="() => view.bucketConfiguration.push({title: '', filter: ''})"
> >
{{ $t('project.kanban.addBucket') }} {{ $t('project.kanban.addBucket') }}
</XButton> </XButton>