fix: task sorting in table
Resolves https://kolaente.dev/vikunja/frontend/issues/2118
This commit is contained in:
parent
579cff647d
commit
4a8b7a726a
2 changed files with 190 additions and 187 deletions
|
@ -108,5 +108,6 @@ export function useTaskList(listId) {
|
||||||
loadTasks,
|
loadTasks,
|
||||||
searchTerm: search,
|
searchTerm: search,
|
||||||
params,
|
params,
|
||||||
|
sortByParam: sortBy,
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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>
|
Loading…
Reference in a new issue