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:
@@ -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"
|
||||||
|
|||||||
Reference in New Issue
Block a user