fix: task sorting in table

Resolves vikunja/frontend#2118
This commit is contained in:
kolaente 2022-07-13 16:19:58 +02:00
parent 579cff647d
commit 4a8b7a726a
Signed by untrusted user: konrad
GPG Key ID: F40E70337AB24C9B
2 changed files with 190 additions and 187 deletions

View File

@ -108,5 +108,6 @@ export function useTaskList(listId) {
loadTasks, loadTasks,
searchTerm: search, searchTerm: search,
params, params,
sortByParam: sortBy,
} }
} }

View File

@ -2,187 +2,187 @@
<ListWrapper class="list-table" :list-id="listId" viewName="table"> <ListWrapper class="list-table" :list-id="listId" viewName="table">
<template #header> <template #header>
<div class="filter-container"> <div class="filter-container">
<div class="items"> <div class="items">
<popup> <popup>
<template #trigger="{toggle}"> <template #trigger="{toggle}">
<x-button <x-button
@click.prevent.stop="toggle()" @click.prevent.stop="toggle()"
icon="th" icon="th"
variant="secondary" variant="secondary"
> >
{{ $t('list.table.columns') }} {{ $t('list.table.columns') }}
</x-button> </x-button>
</template> </template>
<template #content="{isOpen}"> <template #content="{isOpen}">
<card class="columns-filter" :class="{'is-open': isOpen}"> <card class="columns-filter" :class="{'is-open': isOpen}">
<fancycheckbox v-model="activeColumns.id">#</fancycheckbox> <fancycheckbox v-model="activeColumns.id">#</fancycheckbox>
<fancycheckbox v-model="activeColumns.done"> <fancycheckbox v-model="activeColumns.done">
{{ $t('task.attributes.done') }} {{ $t('task.attributes.done') }}
</fancycheckbox> </fancycheckbox>
<fancycheckbox v-model="activeColumns.title"> <fancycheckbox v-model="activeColumns.title">
{{ $t('task.attributes.title') }} {{ $t('task.attributes.title') }}
</fancycheckbox> </fancycheckbox>
<fancycheckbox v-model="activeColumns.priority"> <fancycheckbox v-model="activeColumns.priority">
{{ $t('task.attributes.priority') }} {{ $t('task.attributes.priority') }}
</fancycheckbox> </fancycheckbox>
<fancycheckbox v-model="activeColumns.labels"> <fancycheckbox v-model="activeColumns.labels">
{{ $t('task.attributes.labels') }} {{ $t('task.attributes.labels') }}
</fancycheckbox> </fancycheckbox>
<fancycheckbox v-model="activeColumns.assignees"> <fancycheckbox v-model="activeColumns.assignees">
{{ $t('task.attributes.assignees') }} {{ $t('task.attributes.assignees') }}
</fancycheckbox> </fancycheckbox>
<fancycheckbox v-model="activeColumns.dueDate"> <fancycheckbox v-model="activeColumns.dueDate">
{{ $t('task.attributes.dueDate') }} {{ $t('task.attributes.dueDate') }}
</fancycheckbox> </fancycheckbox>
<fancycheckbox v-model="activeColumns.startDate"> <fancycheckbox v-model="activeColumns.startDate">
{{ $t('task.attributes.startDate') }} {{ $t('task.attributes.startDate') }}
</fancycheckbox> </fancycheckbox>
<fancycheckbox v-model="activeColumns.endDate"> <fancycheckbox v-model="activeColumns.endDate">
{{ $t('task.attributes.endDate') }} {{ $t('task.attributes.endDate') }}
</fancycheckbox> </fancycheckbox>
<fancycheckbox v-model="activeColumns.percentDone"> <fancycheckbox v-model="activeColumns.percentDone">
{{ $t('task.attributes.percentDone') }} {{ $t('task.attributes.percentDone') }}
</fancycheckbox> </fancycheckbox>
<fancycheckbox v-model="activeColumns.created"> <fancycheckbox v-model="activeColumns.created">
{{ $t('task.attributes.created') }} {{ $t('task.attributes.created') }}
</fancycheckbox> </fancycheckbox>
<fancycheckbox v-model="activeColumns.updated"> <fancycheckbox v-model="activeColumns.updated">
{{ $t('task.attributes.updated') }} {{ $t('task.attributes.updated') }}
</fancycheckbox> </fancycheckbox>
<fancycheckbox v-model="activeColumns.createdBy"> <fancycheckbox v-model="activeColumns.createdBy">
{{ $t('task.attributes.createdBy') }} {{ $t('task.attributes.createdBy') }}
</fancycheckbox> </fancycheckbox>
</card> </card>
</template> </template>
</popup> </popup>
<filter-popup v-model="params" /> <filter-popup v-model="params"/>
</div> </div>
</div> </div>
</template> </template>
<template #default> <template #default>
<div :class="{'is-loading': loading}" class="loader-container"> <div :class="{'is-loading': loading}" class="loader-container">
<card :padding="false" :has-content="false"> <card :padding="false" :has-content="false">
<div class="has-horizontal-overflow"> <div class="has-horizontal-overflow">
<table class="table has-actions is-hoverable is-fullwidth mb-0"> <table class="table has-actions is-hoverable is-fullwidth mb-0">
<thead> <thead>
<tr> <tr>
<th v-if="activeColumns.id"> <th v-if="activeColumns.id">
# #
<Sort :order="sortBy.id" @click="sort('id')"/> <Sort :order="sortBy.id" @click="sort('id')"/>
</th> </th>
<th v-if="activeColumns.done"> <th v-if="activeColumns.done">
{{ $t('task.attributes.done') }} {{ $t('task.attributes.done') }}
<Sort :order="sortBy.done" @click="sort('done')"/> <Sort :order="sortBy.done" @click="sort('done')"/>
</th> </th>
<th v-if="activeColumns.title"> <th v-if="activeColumns.title">
{{ $t('task.attributes.title') }} {{ $t('task.attributes.title') }}
<Sort :order="sortBy.title" @click="sort('title')"/> <Sort :order="sortBy.title" @click="sort('title')"/>
</th> </th>
<th v-if="activeColumns.priority"> <th v-if="activeColumns.priority">
{{ $t('task.attributes.priority') }} {{ $t('task.attributes.priority') }}
<Sort :order="sortBy.priority" @click="sort('priority')"/> <Sort :order="sortBy.priority" @click="sort('priority')"/>
</th> </th>
<th v-if="activeColumns.labels"> <th v-if="activeColumns.labels">
{{ $t('task.attributes.labels') }} {{ $t('task.attributes.labels') }}
</th> </th>
<th v-if="activeColumns.assignees"> <th v-if="activeColumns.assignees">
{{ $t('task.attributes.assignees') }} {{ $t('task.attributes.assignees') }}
</th> </th>
<th v-if="activeColumns.dueDate"> <th v-if="activeColumns.dueDate">
{{ $t('task.attributes.dueDate') }} {{ $t('task.attributes.dueDate') }}
<Sort :order="sortBy.due_date" @click="sort('due_date')"/> <Sort :order="sortBy.due_date" @click="sort('due_date')"/>
</th> </th>
<th v-if="activeColumns.startDate"> <th v-if="activeColumns.startDate">
{{ $t('task.attributes.startDate') }} {{ $t('task.attributes.startDate') }}
<Sort :order="sortBy.start_date" @click="sort('start_date')"/> <Sort :order="sortBy.start_date" @click="sort('start_date')"/>
</th> </th>
<th v-if="activeColumns.endDate"> <th v-if="activeColumns.endDate">
{{ $t('task.attributes.endDate') }} {{ $t('task.attributes.endDate') }}
<Sort :order="sortBy.end_date" @click="sort('end_date')"/> <Sort :order="sortBy.end_date" @click="sort('end_date')"/>
</th> </th>
<th v-if="activeColumns.percentDone"> <th v-if="activeColumns.percentDone">
{{ $t('task.attributes.percentDone') }} {{ $t('task.attributes.percentDone') }}
<Sort :order="sortBy.percent_done" @click="sort('percent_done')"/> <Sort :order="sortBy.percent_done" @click="sort('percent_done')"/>
</th> </th>
<th v-if="activeColumns.created"> <th v-if="activeColumns.created">
{{ $t('task.attributes.created') }} {{ $t('task.attributes.created') }}
<Sort :order="sortBy.created" @click="sort('created')"/> <Sort :order="sortBy.created" @click="sort('created')"/>
</th> </th>
<th v-if="activeColumns.updated"> <th v-if="activeColumns.updated">
{{ $t('task.attributes.updated') }} {{ $t('task.attributes.updated') }}
<Sort :order="sortBy.updated" @click="sort('updated')"/> <Sort :order="sortBy.updated" @click="sort('updated')"/>
</th> </th>
<th v-if="activeColumns.createdBy"> <th v-if="activeColumns.createdBy">
{{ $t('task.attributes.createdBy') }} {{ $t('task.attributes.createdBy') }}
</th> </th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr :key="t.id" v-for="t in tasks"> <tr :key="t.id" v-for="t in tasks">
<td v-if="activeColumns.id"> <td v-if="activeColumns.id">
<router-link :to="taskDetailRoutes[t.id]"> <router-link :to="taskDetailRoutes[t.id]">
<template v-if="t.identifier === ''"> <template v-if="t.identifier === ''">
#{{ t.index }} #{{ t.index }}
</template> </template>
<template v-else> <template v-else>
{{ t.identifier }} {{ t.identifier }}
</template> </template>
</router-link> </router-link>
</td> </td>
<td v-if="activeColumns.done"> <td v-if="activeColumns.done">
<Done :is-done="t.done" variant="small" /> <Done :is-done="t.done" variant="small"/>
</td> </td>
<td v-if="activeColumns.title"> <td v-if="activeColumns.title">
<router-link :to="taskDetailRoutes[t.id]">{{ t.title }}</router-link> <router-link :to="taskDetailRoutes[t.id]">{{ t.title }}</router-link>
</td> </td>
<td v-if="activeColumns.priority"> <td v-if="activeColumns.priority">
<priority-label :priority="t.priority" :done="t.done" :show-all="true"/> <priority-label :priority="t.priority" :done="t.done" :show-all="true"/>
</td> </td>
<td v-if="activeColumns.labels"> <td v-if="activeColumns.labels">
<labels :labels="t.labels"/> <labels :labels="t.labels"/>
</td> </td>
<td v-if="activeColumns.assignees"> <td v-if="activeColumns.assignees">
<user <user
:avatar-size="27" :avatar-size="27"
:is-inline="true" :is-inline="true"
:key="t.id + 'assignee' + a.id + i" :key="t.id + 'assignee' + a.id + i"
:show-username="false" :show-username="false"
:user="a" :user="a"
v-for="(a, i) in t.assignees" v-for="(a, i) in t.assignees"
/> />
</td> </td>
<date-table-cell :date="t.dueDate" v-if="activeColumns.dueDate"/> <date-table-cell :date="t.dueDate" v-if="activeColumns.dueDate"/>
<date-table-cell :date="t.startDate" v-if="activeColumns.startDate"/> <date-table-cell :date="t.startDate" v-if="activeColumns.startDate"/>
<date-table-cell :date="t.endDate" v-if="activeColumns.endDate"/> <date-table-cell :date="t.endDate" v-if="activeColumns.endDate"/>
<td v-if="activeColumns.percentDone">{{ t.percentDone * 100 }}%</td> <td v-if="activeColumns.percentDone">{{ t.percentDone * 100 }}%</td>
<date-table-cell :date="t.created" v-if="activeColumns.created"/> <date-table-cell :date="t.created" v-if="activeColumns.created"/>
<date-table-cell :date="t.updated" v-if="activeColumns.updated"/> <date-table-cell :date="t.updated" v-if="activeColumns.updated"/>
<td v-if="activeColumns.createdBy"> <td v-if="activeColumns.createdBy">
<user <user
:avatar-size="27" :avatar-size="27"
:show-username="false" :show-username="false"
:user="t.createdBy"/> :user="t.createdBy"/>
</td> </td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</div> </div>
<Pagination <Pagination
:total-pages="totalPages" :total-pages="totalPages"
:current-page="currentPage" :current-page="currentPage"
/> />
</card> </card>
</div> </div>
</template> </template>
</ListWrapper> </ListWrapper>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { toRef, computed, Ref } from 'vue' import {toRef, computed, Ref} from 'vue'
import { useStorage } from '@vueuse/core' import {useStorage} from '@vueuse/core'
import ListWrapper from './ListWrapper.vue' import ListWrapper from './ListWrapper.vue'
import Done from '@/components/misc/Done.vue' import Done from '@/components/misc/Done.vue'
@ -196,7 +196,7 @@ 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.vue' import Popup from '@/components/misc/popup.vue'
import { useTaskList } from '@/composables/taskList' import {useTaskList} from '@/composables/taskList'
import TaskModel from '@/models/task' import TaskModel from '@/models/task'
const ACTIVE_COLUMNS_DEFAULT = { const ACTIVE_COLUMNS_DEFAULT = {
@ -225,24 +225,24 @@ const props = defineProps({
type Order = 'asc' | 'desc' | 'none' type Order = 'asc' | 'desc' | 'none'
interface SortBy { interface SortBy {
id : Order id: Order
done? : Order done?: Order
title? : Order title?: Order
priority? : Order priority?: Order
due_date? : Order due_date?: Order
start_date? : Order start_date?: Order
end_date? : Order end_date?: Order
percent_done? : Order percent_done?: Order
created? : Order created?: Order
updated? : Order updated?: Order
} }
const SORT_BY_DEFAULT : SortBy = { const SORT_BY_DEFAULT: SortBy = {
id: 'desc', id: 'desc',
} }
const activeColumns = useStorage('tableViewColumns', { ...ACTIVE_COLUMNS_DEFAULT }) const activeColumns = useStorage('tableViewColumns', {...ACTIVE_COLUMNS_DEFAULT})
const sortBy = useStorage<SortBy>('tableViewSortBy', { ...SORT_BY_DEFAULT }) const sortBy = useStorage<SortBy>('tableViewSortBy', {...SORT_BY_DEFAULT})
const taskList = useTaskList(toRef(props, 'listId')) const taskList = useTaskList(toRef(props, 'listId'))
@ -251,8 +251,9 @@ const {
params, params,
totalPages, totalPages,
currentPage, currentPage,
sortByParam,
} = taskList } = taskList
const tasks : Ref<TaskModel[]> = taskList.tasks const tasks: Ref<TaskModel[]> = taskList.tasks
Object.assign(params.value, { Object.assign(params.value, {
filter_by: [], filter_by: [],
@ -261,7 +262,7 @@ Object.assign(params.value, {
}) })
// FIXME: by doing this we can have multiple sort orders // FIXME: by doing this we can have multiple sort orders
function sort(property : keyof SortBy) { function sort(property: keyof SortBy) {
const order = sortBy.value[property] const order = sortBy.value[property]
if (typeof order === 'undefined' || order === 'none') { if (typeof order === 'undefined' || order === 'none') {
sortBy.value[property] = 'desc' sortBy.value[property] = 'desc'
@ -270,6 +271,7 @@ function sort(property : keyof SortBy) {
} else { } else {
delete sortBy.value[property] delete sortBy.value[property]
} }
sortByParam.value = sortBy.value
} }
// TODO: re-enable opening task detail in modal // TODO: re-enable opening task detail in modal
@ -279,7 +281,7 @@ const taskDetailRoutes = computed(() => Object.fromEntries(
id, id,
{ {
name: 'task.detail', name: 'task.detail',
params: { id }, params: {id},
// state: { backdropView: router.currentRoute.value.fullPath }, // state: { backdropView: router.currentRoute.value.fullPath },
}, },
])), ])),
@ -310,7 +312,7 @@ const taskDetailRoutes = computed(() => Object.fromEntries(
} }
.link-share-view .card { .link-share-view .card {
border: none; border: none;
box-shadow: none; box-shadow: none;
} }
</style> </style>