feat(frontend) : add kanban drag & drop and improve filter selects on my-tasks

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Matthieu
2026-03-13 12:06:44 +01:00
parent d3ea09319c
commit 1d0f9a28c3

View File

@@ -175,6 +175,44 @@ watch(selectedProjectId, () => {
selectedGroupId.value = null selectedGroupId.value = null
}, { flush: 'sync' }) }, { flush: 'sync' })
// Drag & drop
const dragOverStatusId = ref<number | null>(null)
const dragCounter = ref(0)
function onDragEnter(id: number) {
dragCounter.value++
dragOverStatusId.value = id
}
function onDragLeave() {
dragCounter.value--
if (dragCounter.value === 0) {
dragOverStatusId.value = null
}
}
function onDrop(event: DragEvent) {
dragCounter.value = 0
dragOverStatusId.value = null
return Number(event.dataTransfer!.getData('text/plain'))
}
async function onDropStatus(event: DragEvent, status: TaskStatus) {
const taskId = onDrop(event)
const task = tasks.value.find(t => t.id === taskId)
if (!task || task.status?.id === status.id) return
task.status = status
await taskService.update(taskId, { status: `/api/task_statuses/${status.id}` })
}
async function onDropBacklog(event: DragEvent) {
const taskId = onDrop(event)
const task = tasks.value.find(t => t.id === taskId)
if (!task || !task.status) return
task.status = null
await taskService.update(taskId, { status: null })
}
// Modal // Modal
function openTaskEdit(task: Task) { function openTaskEdit(task: Task) {
selectedTask.value = task selectedTask.value = task
@@ -222,42 +260,54 @@ onMounted(() => {
:options="projectOptions" :options="projectOptions"
label="Projet" label="Projet"
:empty-option-label="$t('myTasks.allProjects')" :empty-option-label="$t('myTasks.allProjects')"
min-width="w-48" min-width="!w-40"
text-field="text-sm"
text-value="text-sm"
/> />
<MalioSelect <MalioSelect
v-model="selectedGroupId" v-model="selectedGroupId"
:options="groupOptions" :options="groupOptions"
label="Groupe" label="Groupe"
:empty-option-label="$t('myTasks.allGroups')" :empty-option-label="$t('myTasks.allGroups')"
min-width="w-48" min-width="!w-40"
text-field="text-sm"
text-value="text-sm"
/> />
<MalioSelect <MalioSelect
v-model="selectedTagId" v-model="selectedTagId"
:options="tagOptions" :options="tagOptions"
label="Type" label="Type"
:empty-option-label="$t('myTasks.allTypes')" :empty-option-label="$t('myTasks.allTypes')"
min-width="w-48" min-width="!w-40"
text-field="text-sm"
text-value="text-sm"
/> />
<MalioSelect <MalioSelect
v-model="selectedPriorityId" v-model="selectedPriorityId"
:options="priorityOptions" :options="priorityOptions"
label="Priorité" label="Priorité"
:empty-option-label="$t('myTasks.allPriorities')" :empty-option-label="$t('myTasks.allPriorities')"
min-width="w-48" min-width="!w-40"
text-field="text-sm"
text-value="text-sm"
/> />
<MalioSelect <MalioSelect
v-model="selectedEffortId" v-model="selectedEffortId"
:options="effortOptions" :options="effortOptions"
label="Effort" label="Effort"
:empty-option-label="$t('myTasks.allEfforts')" :empty-option-label="$t('myTasks.allEfforts')"
min-width="w-48" min-width="!w-40"
text-field="text-sm"
text-value="text-sm"
/> />
<MalioSelect <MalioSelect
v-model="selectedAssigneeId" v-model="selectedAssigneeId"
:options="assigneeOptions" :options="assigneeOptions"
label="Assigné" label="Assigné"
:empty-option-label="$t('myTasks.allAssignees')" :empty-option-label="$t('myTasks.allAssignees')"
min-width="w-48" min-width="!w-40"
text-field="text-sm"
text-value="text-sm"
/> />
</div> </div>
@@ -266,7 +316,12 @@ onMounted(() => {
<!-- Backlog column (tasks without status) --> <!-- Backlog column (tasks without status) -->
<div <div
v-if="backlogTasks.length > 0" v-if="backlogTasks.length > 0"
class="flex w-72 shrink-0 flex-col rounded-lg bg-neutral-50" class="flex w-72 shrink-0 flex-col rounded-lg transition-colors"
:class="dragOverStatusId === 0 ? 'bg-neutral-200' : 'bg-neutral-50'"
@dragover.prevent
@dragenter.prevent="onDragEnter(0)"
@dragleave="onDragLeave"
@drop.prevent="onDropBacklog($event)"
> >
<div class="rounded-t-lg bg-neutral-500 px-4 py-3 text-sm font-bold text-white"> <div class="rounded-t-lg bg-neutral-500 px-4 py-3 text-sm font-bold text-white">
{{ $t('myTasks.backlog') }} ({{ backlogTasks.length }}) {{ $t('myTasks.backlog') }} ({{ backlogTasks.length }})
@@ -285,7 +340,12 @@ onMounted(() => {
<div <div
v-for="status in sortedStatuses" v-for="status in sortedStatuses"
:key="status.id" :key="status.id"
class="flex w-72 shrink-0 flex-col rounded-lg bg-neutral-50" class="flex w-72 shrink-0 flex-col rounded-lg transition-colors"
:class="dragOverStatusId === status.id ? 'bg-neutral-200' : 'bg-neutral-50'"
@dragover.prevent
@dragenter.prevent="onDragEnter(status.id)"
@dragleave="onDragLeave"
@drop.prevent="onDropStatus($event, status)"
> >
<div <div
class="rounded-t-lg px-4 py-3 text-sm font-bold text-white" class="rounded-t-lg px-4 py-3 text-sm font-bold text-white"