feat: add button to clear active filters (#924)
Co-authored-by: kolaente <k@knt.li> Reviewed-on: https://kolaente.dev/vikunja/frontend/pulls/924 Reviewed-by: dpschen <dpschen@noreply.kolaente.de> Co-authored-by: konrad <k@knt.li> Co-committed-by: konrad <k@knt.li>
This commit is contained in:
parent
73651ef964
commit
31f0c384ac
11 changed files with 227 additions and 174 deletions
|
@ -219,10 +219,10 @@ describe('Lists', () => {
|
||||||
cy.get('.table-view .filter-container .items .button')
|
cy.get('.table-view .filter-container .items .button')
|
||||||
.contains('Columns')
|
.contains('Columns')
|
||||||
.click()
|
.click()
|
||||||
cy.get('.table-view .filter-container .card .card-content .fancycheckbox .check')
|
cy.get('.table-view .filter-container .card.columns-filter .card-content .fancycheckbox .check')
|
||||||
.contains('Priority')
|
.contains('Priority')
|
||||||
.click()
|
.click()
|
||||||
cy.get('.table-view .filter-container .card .card-content .fancycheckbox .check')
|
cy.get('.table-view .filter-container .card.columns-filter .card-content .fancycheckbox .check')
|
||||||
.contains('Done')
|
.contains('Done')
|
||||||
.click()
|
.click()
|
||||||
|
|
||||||
|
|
|
@ -1,37 +1,49 @@
|
||||||
<template>
|
<template>
|
||||||
<transition name="fade">
|
<x-button
|
||||||
<filters
|
v-if="hasFilters"
|
||||||
v-if="visibleInternal"
|
type="secondary"
|
||||||
v-model="value"
|
@click="clearFilters"
|
||||||
ref="filters"
|
>
|
||||||
/>
|
{{ $t('filters.clear') }}
|
||||||
</transition>
|
</x-button>
|
||||||
|
<popup>
|
||||||
|
<template #trigger="{toggle}">
|
||||||
|
<x-button
|
||||||
|
@click.prevent.stop="toggle()"
|
||||||
|
type="secondary"
|
||||||
|
icon="filter"
|
||||||
|
>
|
||||||
|
{{ $t('filters.title') }}
|
||||||
|
</x-button>
|
||||||
|
</template>
|
||||||
|
<template #content="{isOpen}">
|
||||||
|
<filters
|
||||||
|
v-model="value"
|
||||||
|
ref="filters"
|
||||||
|
class="filter-popup"
|
||||||
|
:class="{'is-open': isOpen}"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</popup>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {closeWhenClickedOutside} from '@/helpers/closeWhenClickedOutside'
|
import Filters from '@/components/list/partials/filters'
|
||||||
import Filters from '../../../components/list/partials/filters'
|
import {getDefaultParams} from '@/components/tasks/mixins/taskList'
|
||||||
|
import Popup from '@/components/misc/popup'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'filter-popup',
|
name: 'filter-popup',
|
||||||
components: {
|
components: {
|
||||||
|
Popup,
|
||||||
Filters,
|
Filters,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
modelValue: {
|
modelValue: {
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
visible: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
emits: ['update:modelValue'],
|
emits: ['update:modelValue'],
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
visibleInternal: false,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
computed: {
|
||||||
value: {
|
value: {
|
||||||
get() {
|
get() {
|
||||||
|
@ -41,34 +53,46 @@ export default {
|
||||||
this.$emit('update:modelValue', value)
|
this.$emit('update:modelValue', value)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
hasFilters() {
|
||||||
mounted() {
|
// this.value also contains the page parameter which we don't want to include in filters
|
||||||
document.addEventListener('click', this.hidePopup)
|
// eslint-disable-next-line no-unused-vars
|
||||||
},
|
const {filter_by, filter_value, filter_comparator, filter_concat, s} = this.value
|
||||||
beforeUnmount() {
|
const def = {...getDefaultParams()}
|
||||||
document.removeEventListener('click', this.hidePopup)
|
|
||||||
|
const params = {filter_by, filter_value, filter_comparator, filter_concat, s}
|
||||||
|
const defaultParams = {
|
||||||
|
filter_by: def.filter_by,
|
||||||
|
filter_value: def.filter_value,
|
||||||
|
filter_comparator: def.filter_comparator,
|
||||||
|
filter_concat: def.filter_concat,
|
||||||
|
s: s ? def.s : undefined,
|
||||||
|
}
|
||||||
|
|
||||||
|
return JSON.stringify(params) !== JSON.stringify(defaultParams)
|
||||||
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
modelValue: {
|
modelValue: {
|
||||||
handler(value) {
|
handler(value) {
|
||||||
this.params = value
|
this.value = value
|
||||||
},
|
},
|
||||||
immediate: true,
|
immediate: true,
|
||||||
},
|
},
|
||||||
visible() {
|
|
||||||
this.visibleInternal = !this.visibleInternal
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
hidePopup(e) {
|
clearFilters() {
|
||||||
if (!this.visibleInternal) {
|
this.value = {...getDefaultParams()}
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
closeWhenClickedOutside(e, this.$refs.filters.$el, () => {
|
|
||||||
this.visibleInternal = false
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.filter-popup {
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
&.is-open {
|
||||||
|
margin: 2rem 0 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -458,15 +458,7 @@ export default {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let foundDone = false
|
this.filters.done = this.params.filter_by.some((f) => f === 'done') === false
|
||||||
this.params.filter_by.forEach((f, i) => {
|
|
||||||
if (f === 'done') {
|
|
||||||
foundDone = i
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if (foundDone === false) {
|
|
||||||
this.filters.done = true
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
async prepareRelatedObjectFilter(kind, filterName = null, servicePrefix = null) {
|
async prepareRelatedObjectFilter(kind, filterName = null, servicePrefix = null) {
|
||||||
if (filterName === null) {
|
if (filterName === null) {
|
||||||
|
|
54
src/components/misc/popup.vue
Normal file
54
src/components/misc/popup.vue
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
<template>
|
||||||
|
<slot name="trigger" :isOpen="open" :toggle="toggle"></slot>
|
||||||
|
<div class="popup" :class="{'is-open': open}" ref="popup">
|
||||||
|
<slot name="content" :isOpen="open"/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {closeWhenClickedOutside} from '@/helpers/closeWhenClickedOutside'
|
||||||
|
import {onBeforeUnmount, onMounted, ref} from 'vue'
|
||||||
|
|
||||||
|
const open = ref(false)
|
||||||
|
const popup = ref(null)
|
||||||
|
|
||||||
|
const toggle = () => {
|
||||||
|
open.value = !open.value
|
||||||
|
}
|
||||||
|
|
||||||
|
function hidePopup(e) {
|
||||||
|
if (!open.value) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// we actually want to use popup.$el, not its value.
|
||||||
|
// eslint-disable-next-line vue/no-ref-as-operand
|
||||||
|
closeWhenClickedOutside(e, popup.value, () => {
|
||||||
|
open.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
document.addEventListener('click', hidePopup)
|
||||||
|
})
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
document.removeEventListener('click', hidePopup)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.popup {
|
||||||
|
transition: opacity $transition;
|
||||||
|
opacity: 0;
|
||||||
|
height: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
position: absolute;
|
||||||
|
top: 1rem;
|
||||||
|
|
||||||
|
&.is-open {
|
||||||
|
opacity: 1;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -9,12 +9,12 @@
|
||||||
>
|
>
|
||||||
{{ $t('filters.title') }}
|
{{ $t('filters.title') }}
|
||||||
</x-button>
|
</x-button>
|
||||||
|
<filter-popup
|
||||||
|
:visible="showTaskFilter"
|
||||||
|
v-model="params"
|
||||||
|
@update:modelValue="loadTasks()"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<filter-popup
|
|
||||||
:visible="showTaskFilter"
|
|
||||||
v-model="params"
|
|
||||||
@update:modelValue="loadTasks()"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="dates">
|
<div class="dates">
|
||||||
<template v-for="(y, yk) in days" :key="yk + 'year'">
|
<template v-for="(y, yk) in days" :key="yk + 'year'">
|
||||||
|
@ -347,7 +347,7 @@ export default {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let newTask = { ...taskDragged }
|
let newTask = {...taskDragged}
|
||||||
|
|
||||||
const didntHaveDates = newTask.startDate === null ? true : false
|
const didntHaveDates = newTask.startDate === null ? true : false
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
import TaskCollectionService from '@/services/taskCollection'
|
import TaskCollectionService from '@/services/taskCollection'
|
||||||
|
|
||||||
// FIXME: merge with DEFAULT_PARAMS in filters.vue
|
// FIXME: merge with DEFAULT_PARAMS in filters.vue
|
||||||
const DEFAULT_PARAMS = {
|
export const getDefaultParams = () => ({
|
||||||
sort_by: ['position', 'id'],
|
sort_by: ['position', 'id'],
|
||||||
order_by: ['asc', 'desc'],
|
order_by: ['asc', 'desc'],
|
||||||
filter_by: ['done'],
|
filter_by: ['done'],
|
||||||
filter_value: ['false'],
|
filter_value: ['false'],
|
||||||
filter_comparator: ['equals'],
|
filter_comparator: ['equals'],
|
||||||
filter_concat: 'and',
|
filter_concat: 'and',
|
||||||
}
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This mixin provides a base set of methods and properties to get tasks on a list.
|
* This mixin provides a base set of methods and properties to get tasks on a list.
|
||||||
|
@ -26,7 +26,7 @@ export default {
|
||||||
searchTerm: '',
|
searchTerm: '',
|
||||||
|
|
||||||
showTaskFilter: false,
|
showTaskFilter: false,
|
||||||
params: DEFAULT_PARAMS,
|
params: {...getDefaultParams()},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
@ -94,7 +94,7 @@ export default {
|
||||||
this.initTasks(page, search)
|
this.initTasks(page, search)
|
||||||
},
|
},
|
||||||
loadTasksOnSavedFilter() {
|
loadTasksOnSavedFilter() {
|
||||||
if(typeof this.$route.params.listId !== 'undefined' && parseInt(this.$route.params.listId) < 0) {
|
if (typeof this.$route.params.listId !== 'undefined' && parseInt(this.$route.params.listId) < 0) {
|
||||||
this.loadTasks(1, '', null, true)
|
this.loadTasks(1, '', null, true)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -344,6 +344,7 @@
|
||||||
},
|
},
|
||||||
"filters": {
|
"filters": {
|
||||||
"title": "Filters",
|
"title": "Filters",
|
||||||
|
"clear": "Clear Filters",
|
||||||
"attributes": {
|
"attributes": {
|
||||||
"title": "Title",
|
"title": "Title",
|
||||||
"titlePlaceholder": "The saved filter title goes here…",
|
"titlePlaceholder": "The saved filter title goes here…",
|
||||||
|
|
|
@ -34,7 +34,6 @@ $filter-container-top-link-share-list: -47px;
|
||||||
|
|
||||||
.card {
|
.card {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
margin-top: calc(1rem - 1px);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.fancycheckbox {
|
.fancycheckbox {
|
||||||
|
@ -47,10 +46,6 @@ $filter-container-top-link-share-list: -47px;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
margin-right: .5rem;
|
margin-right: .5rem;
|
||||||
|
|
||||||
.button, .input {
|
|
||||||
height: $switch-view-height;
|
|
||||||
}
|
|
||||||
|
|
||||||
.field {
|
.field {
|
||||||
transition: width $transition;
|
transition: width $transition;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
|
@ -2,18 +2,11 @@
|
||||||
<div class="kanban-view">
|
<div class="kanban-view">
|
||||||
<div class="filter-container" v-if="isSavedFilter">
|
<div class="filter-container" v-if="isSavedFilter">
|
||||||
<div class="items">
|
<div class="items">
|
||||||
<x-button
|
<filter-popup
|
||||||
@click.prevent.stop="toggleFilterPopup"
|
v-model="params"
|
||||||
icon="filter"
|
@update:modelValue="loadBuckets"
|
||||||
type="secondary"
|
/>
|
||||||
>
|
|
||||||
{{ $t('filters.title') }}
|
|
||||||
</x-button>
|
|
||||||
</div>
|
</div>
|
||||||
<filter-popup
|
|
||||||
:visible="showFilters"
|
|
||||||
v-model="params"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
:class="{ 'is-loading': loading && !oneTaskUpdating}"
|
:class="{ 'is-loading': loading && !oneTaskUpdating}"
|
||||||
|
@ -143,7 +136,7 @@
|
||||||
:component-data="taskDraggableTaskComponentData"
|
:component-data="taskDraggableTaskComponentData"
|
||||||
>
|
>
|
||||||
<template #item="{element: task}">
|
<template #item="{element: task}">
|
||||||
<kanban-card :task="task" />
|
<kanban-card :task="task"/>
|
||||||
</template>
|
</template>
|
||||||
</draggable>
|
</draggable>
|
||||||
</div>
|
</div>
|
||||||
|
@ -213,7 +206,7 @@
|
||||||
<!-- This router view is used to show the task popup while keeping the kanban board itself -->
|
<!-- This router view is used to show the task popup while keeping the kanban board itself -->
|
||||||
<router-view v-slot="{ Component }">
|
<router-view v-slot="{ Component }">
|
||||||
<transition name="modal">
|
<transition name="modal">
|
||||||
<component :is="Component" />
|
<component :is="Component"/>
|
||||||
</transition>
|
</transition>
|
||||||
</router-view>
|
</router-view>
|
||||||
|
|
||||||
|
@ -224,10 +217,10 @@
|
||||||
v-if="showBucketDeleteModal"
|
v-if="showBucketDeleteModal"
|
||||||
>
|
>
|
||||||
<template #header><span>{{ $t('list.kanban.deleteHeaderBucket') }}</span></template>
|
<template #header><span>{{ $t('list.kanban.deleteHeaderBucket') }}</span></template>
|
||||||
|
|
||||||
<template #text>
|
<template #text>
|
||||||
<p>{{ $t('list.kanban.deleteBucketText1') }}<br/>
|
<p>{{ $t('list.kanban.deleteBucketText1') }}<br/>
|
||||||
{{ $t('list.kanban.deleteBucketText2') }}</p>
|
{{ $t('list.kanban.deleteBucketText2') }}</p>
|
||||||
</template>
|
</template>
|
||||||
</modal>
|
</modal>
|
||||||
</transition>
|
</transition>
|
||||||
|
@ -300,7 +293,6 @@ export default {
|
||||||
filter_comparator: [],
|
filter_comparator: [],
|
||||||
filter_concat: 'and',
|
filter_concat: 'and',
|
||||||
},
|
},
|
||||||
showFilters: false,
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
|
@ -328,10 +320,10 @@ export default {
|
||||||
return {
|
return {
|
||||||
type: 'transition',
|
type: 'transition',
|
||||||
tag: 'div',
|
tag: 'div',
|
||||||
name: !this.dragBucket ? 'move-bucket': null,
|
name: !this.dragBucket ? 'move-bucket' : null,
|
||||||
class: [
|
class: [
|
||||||
'kanban-bucket-container',
|
'kanban-bucket-container',
|
||||||
{ 'dragging-disabled': !this.canWrite },
|
{'dragging-disabled': !this.canWrite},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -339,10 +331,10 @@ export default {
|
||||||
return {
|
return {
|
||||||
type: 'transition',
|
type: 'transition',
|
||||||
tag: 'div',
|
tag: 'div',
|
||||||
name: !this.drag ? 'move-card': null,
|
name: !this.drag ? 'move-card' : null,
|
||||||
class: [
|
class: [
|
||||||
'dropper',
|
'dropper',
|
||||||
{ 'dragging-disabled': !this.canWrite },
|
{'dragging-disabled': !this.canWrite},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -357,19 +349,15 @@ export default {
|
||||||
list: state => state.currentList,
|
list: state => state.currentList,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
|
||||||
toggleFilterPopup() {
|
|
||||||
this.showFilters = !this.showFilters
|
|
||||||
},
|
|
||||||
|
|
||||||
|
methods: {
|
||||||
loadBuckets() {
|
loadBuckets() {
|
||||||
// Prevent trying to load buckets if the task popup view is active
|
// Prevent trying to load buckets if the task popup view is active
|
||||||
if (this.$route.name !== 'list.kanban') {
|
if (this.$route.name !== 'list.kanban') {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const { listId, params } = this.loadBucketParameter
|
const {listId, params} = this.loadBucketParameter
|
||||||
|
|
||||||
this.collapsedBuckets = getCollapsedBucketState(listId)
|
this.collapsedBuckets = getCollapsedBucketState(listId)
|
||||||
|
|
||||||
|
@ -424,7 +412,7 @@ export default {
|
||||||
|
|
||||||
const newTask = cloneDeep(task) // cloning the task to avoid vuex store mutations
|
const newTask = cloneDeep(task) // cloning the task to avoid vuex store mutations
|
||||||
newTask.bucketId = newBucket.id,
|
newTask.bucketId = newBucket.id,
|
||||||
newTask.kanbanPosition = calculateItemPosition(taskBefore !== null ? taskBefore.kanbanPosition : null, taskAfter !== null ? taskAfter.kanbanPosition : null)
|
newTask.kanbanPosition = calculateItemPosition(taskBefore !== null ? taskBefore.kanbanPosition : null, taskAfter !== null ? taskAfter.kanbanPosition : null)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.$store.dispatch('tasks/update', newTask)
|
await this.$store.dispatch('tasks/update', newTask)
|
||||||
|
|
|
@ -41,19 +41,11 @@
|
||||||
v-if="!showTaskSearch"
|
v-if="!showTaskSearch"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<x-button
|
<filter-popup
|
||||||
@click.prevent.stop="showTaskFilter = !showTaskFilter"
|
v-model="params"
|
||||||
type="secondary"
|
@update:modelValue="loadTasks()"
|
||||||
icon="filter"
|
/>
|
||||||
>
|
|
||||||
{{ $t('filters.title') }}
|
|
||||||
</x-button>
|
|
||||||
</div>
|
</div>
|
||||||
<filter-popup
|
|
||||||
:visible="showTaskFilter"
|
|
||||||
v-model="params"
|
|
||||||
@update:modelValue="loadTasks()"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<card :padding="false" :has-content="false" class="has-overflow">
|
<card :padding="false" :has-content="false" class="has-overflow">
|
||||||
|
@ -126,7 +118,7 @@
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Pagination
|
<Pagination
|
||||||
:total-pages="taskCollectionService.totalPages"
|
:total-pages="taskCollectionService.totalPages"
|
||||||
:current-page="currentPage"
|
:current-page="currentPage"
|
||||||
/>
|
/>
|
||||||
|
@ -135,7 +127,7 @@
|
||||||
<!-- This router view is used to show the task popup while keeping the kanban board itself -->
|
<!-- This router view is used to show the task popup while keeping the kanban board itself -->
|
||||||
<router-view v-slot="{ Component }">
|
<router-view v-slot="{ Component }">
|
||||||
<transition name="modal">
|
<transition name="modal">
|
||||||
<component :is="Component" />
|
<component :is="Component"/>
|
||||||
</transition>
|
</transition>
|
||||||
</router-view>
|
</router-view>
|
||||||
</div>
|
</div>
|
||||||
|
@ -155,6 +147,7 @@ import FilterPopup from '@/components/list/partials/filter-popup.vue'
|
||||||
import {HAS_TASKS} from '@/store/mutation-types'
|
import {HAS_TASKS} from '@/store/mutation-types'
|
||||||
import Nothing from '@/components/misc/nothing.vue'
|
import Nothing from '@/components/misc/nothing.vue'
|
||||||
import Pagination from '@/components/misc/pagination.vue'
|
import Pagination from '@/components/misc/pagination.vue'
|
||||||
|
import Popup from '@/components/misc/popup'
|
||||||
|
|
||||||
import draggable from 'vuedraggable'
|
import draggable from 'vuedraggable'
|
||||||
import {calculateItemPosition} from '../../../helpers/calculateItemPosition'
|
import {calculateItemPosition} from '../../../helpers/calculateItemPosition'
|
||||||
|
@ -198,6 +191,7 @@ export default {
|
||||||
taskList,
|
taskList,
|
||||||
],
|
],
|
||||||
components: {
|
components: {
|
||||||
|
Popup,
|
||||||
Nothing,
|
Nothing,
|
||||||
FilterPopup,
|
FilterPopup,
|
||||||
SingleTaskInList,
|
SingleTaskInList,
|
||||||
|
@ -294,11 +288,11 @@ export default {
|
||||||
|
|
||||||
async saveTaskPosition(e) {
|
async saveTaskPosition(e) {
|
||||||
this.drag = false
|
this.drag = false
|
||||||
|
|
||||||
const task = this.tasks[e.newIndex]
|
const task = this.tasks[e.newIndex]
|
||||||
const taskBefore = this.tasks[e.newIndex - 1] ?? null
|
const taskBefore = this.tasks[e.newIndex - 1] ?? null
|
||||||
const taskAfter = this.tasks[e.newIndex + 1] ?? null
|
const taskAfter = this.tasks[e.newIndex + 1] ?? null
|
||||||
|
|
||||||
const newTask = {
|
const newTask = {
|
||||||
...task,
|
...task,
|
||||||
position: calculateItemPosition(taskBefore !== null ? taskBefore.position : null, taskAfter !== null ? taskAfter.position : null),
|
position: calculateItemPosition(taskBefore !== null ? taskBefore.position : null, taskAfter !== null ? taskAfter.position : null),
|
||||||
|
|
|
@ -2,67 +2,63 @@
|
||||||
<div :class="{'is-loading': taskCollectionService.loading}" class="table-view loader-container">
|
<div :class="{'is-loading': taskCollectionService.loading}" class="table-view loader-container">
|
||||||
<div class="filter-container">
|
<div class="filter-container">
|
||||||
<div class="items">
|
<div class="items">
|
||||||
<x-button
|
<popup>
|
||||||
@click.prevent.stop="() => {showActiveColumnsFilter = !showActiveColumnsFilter; showTaskFilter = false}"
|
<template #trigger="{toggle}">
|
||||||
icon="th"
|
<x-button
|
||||||
type="secondary"
|
@click.prevent.stop="toggle()"
|
||||||
>
|
icon="th"
|
||||||
{{ $t('list.table.columns') }}
|
type="secondary"
|
||||||
</x-button>
|
>
|
||||||
<x-button
|
{{ $t('list.table.columns') }}
|
||||||
@click.prevent.stop="() => {showTaskFilter = !showTaskFilter; showActiveColumnsFilter = false}"
|
</x-button>
|
||||||
icon="filter"
|
</template>
|
||||||
type="secondary"
|
<template #content="{isOpen}">
|
||||||
>
|
<card class="columns-filter" :class="{'is-open': isOpen}">
|
||||||
{{ $t('filters.title') }}
|
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.id">#</fancycheckbox>
|
||||||
</x-button>
|
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.done">
|
||||||
|
{{ $t('task.attributes.done') }}
|
||||||
|
</fancycheckbox>
|
||||||
|
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.title">
|
||||||
|
{{ $t('task.attributes.title') }}
|
||||||
|
</fancycheckbox>
|
||||||
|
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.priority">
|
||||||
|
{{ $t('task.attributes.priority') }}
|
||||||
|
</fancycheckbox>
|
||||||
|
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.labels">
|
||||||
|
{{ $t('task.attributes.labels') }}
|
||||||
|
</fancycheckbox>
|
||||||
|
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.assignees">
|
||||||
|
{{ $t('task.attributes.assignees') }}
|
||||||
|
</fancycheckbox>
|
||||||
|
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.dueDate">
|
||||||
|
{{ $t('task.attributes.dueDate') }}
|
||||||
|
</fancycheckbox>
|
||||||
|
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.startDate">
|
||||||
|
{{ $t('task.attributes.startDate') }}
|
||||||
|
</fancycheckbox>
|
||||||
|
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.endDate">
|
||||||
|
{{ $t('task.attributes.endDate') }}
|
||||||
|
</fancycheckbox>
|
||||||
|
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.percentDone">
|
||||||
|
{{ $t('task.attributes.percentDone') }}
|
||||||
|
</fancycheckbox>
|
||||||
|
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.created">
|
||||||
|
{{ $t('task.attributes.created') }}
|
||||||
|
</fancycheckbox>
|
||||||
|
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.updated">
|
||||||
|
{{ $t('task.attributes.updated') }}
|
||||||
|
</fancycheckbox>
|
||||||
|
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.createdBy">
|
||||||
|
{{ $t('task.attributes.createdBy') }}
|
||||||
|
</fancycheckbox>
|
||||||
|
</card>
|
||||||
|
</template>
|
||||||
|
</popup>
|
||||||
|
<filter-popup
|
||||||
|
v-model="params"
|
||||||
|
@update:modelValue="loadTasks()"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<transition name="fade">
|
|
||||||
<card v-if="showActiveColumnsFilter">
|
|
||||||
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.id">#</fancycheckbox>
|
|
||||||
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.done">
|
|
||||||
{{ $t('task.attributes.done') }}
|
|
||||||
</fancycheckbox>
|
|
||||||
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.title">
|
|
||||||
{{ $t('task.attributes.title') }}
|
|
||||||
</fancycheckbox>
|
|
||||||
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.priority">
|
|
||||||
{{ $t('task.attributes.priority') }}
|
|
||||||
</fancycheckbox>
|
|
||||||
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.labels">
|
|
||||||
{{ $t('task.attributes.labels') }}
|
|
||||||
</fancycheckbox>
|
|
||||||
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.assignees">
|
|
||||||
{{ $t('task.attributes.assignees') }}
|
|
||||||
</fancycheckbox>
|
|
||||||
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.dueDate">
|
|
||||||
{{ $t('task.attributes.dueDate') }}
|
|
||||||
</fancycheckbox>
|
|
||||||
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.startDate">
|
|
||||||
{{ $t('task.attributes.startDate') }}
|
|
||||||
</fancycheckbox>
|
|
||||||
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.endDate">
|
|
||||||
{{ $t('task.attributes.endDate') }}
|
|
||||||
</fancycheckbox>
|
|
||||||
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.percentDone">
|
|
||||||
{{ $t('task.attributes.percentDone') }}
|
|
||||||
</fancycheckbox>
|
|
||||||
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.created">
|
|
||||||
{{ $t('task.attributes.created') }}
|
|
||||||
</fancycheckbox>
|
|
||||||
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.updated">
|
|
||||||
{{ $t('task.attributes.updated') }}
|
|
||||||
</fancycheckbox>
|
|
||||||
<fancycheckbox @change="saveTaskColumns" v-model="activeColumns.createdBy">
|
|
||||||
{{ $t('task.attributes.createdBy') }}
|
|
||||||
</fancycheckbox>
|
|
||||||
</card>
|
|
||||||
</transition>
|
|
||||||
<filter-popup
|
|
||||||
:visible="showTaskFilter"
|
|
||||||
v-model="params"
|
|
||||||
@update:modelValue="loadTasks()"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<card :padding="false" :has-content="false">
|
<card :padding="false" :has-content="false">
|
||||||
|
@ -189,21 +185,23 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import taskList from '../../../components/tasks/mixins/taskList'
|
import taskList from '@/components/tasks/mixins/taskList'
|
||||||
import Done from '@/components/misc/Done.vue'
|
import Done from '@/components/misc/Done.vue'
|
||||||
import User from '../../../components/misc/user'
|
import User from '@/components/misc/user'
|
||||||
import PriorityLabel from '../../../components/tasks/partials/priorityLabel'
|
import PriorityLabel from '@/components/tasks/partials/priorityLabel'
|
||||||
import Labels from '../../../components/tasks/partials/labels'
|
import Labels from '@/components/tasks/partials/labels'
|
||||||
import DateTableCell from '../../../components/tasks/partials/date-table-cell'
|
import DateTableCell from '@/components/tasks/partials/date-table-cell'
|
||||||
import Fancycheckbox from '../../../components/input/fancycheckbox'
|
import Fancycheckbox from '@/components/input/fancycheckbox'
|
||||||
import Sort from '../../../components/tasks/partials/sort'
|
import Sort from '@/components/tasks/partials/sort'
|
||||||
import {saveListView} from '@/helpers/saveListView'
|
import {saveListView} from '@/helpers/saveListView'
|
||||||
import FilterPopup from '@/components/list/partials/filter-popup.vue'
|
import FilterPopup from '@/components/list/partials/filter-popup.vue'
|
||||||
import Pagination from '@/components/misc/pagination.vue'
|
import Pagination from '@/components/misc/pagination.vue'
|
||||||
|
import Popup from '@/components/misc/popup'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Table',
|
name: 'Table',
|
||||||
components: {
|
components: {
|
||||||
|
Popup,
|
||||||
Done,
|
Done,
|
||||||
FilterPopup,
|
FilterPopup,
|
||||||
Sort,
|
Sort,
|
||||||
|
@ -219,7 +217,6 @@ export default {
|
||||||
],
|
],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
showActiveColumnsFilter: false,
|
|
||||||
activeColumns: {
|
activeColumns: {
|
||||||
id: true,
|
id: true,
|
||||||
done: true,
|
done: true,
|
||||||
|
@ -323,4 +320,12 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.columns-filter {
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
&.is-open {
|
||||||
|
margin: 2rem 0 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
Loading…
Reference in a new issue