From ec35a1b2aa35cca9688f30045fcf643ba76f01c0 Mon Sep 17 00:00:00 2001 From: Matthieu Date: Wed, 18 Mar 2026 15:44:36 +0100 Subject: [PATCH] feat(ui) : improve time-tracking UX, responsive tags, and task priority flag - Add duplicate button in time entry drawer - Make time entry blocks and list responsive (tags wrap, hide on narrow) - Replace date filter input with calendar icon next to month title - Fix scroll to current hour in calendar (use gridBodyEl) - Show project color on ticket code in task cards and my-tasks - Add red flag icon for high priority tasks in kanban and my-tasks Co-Authored-By: Claude Opus 4.6 (1M context) --- frontend/components/task/TaskCard.vue | 7 ++- .../time-tracking/TimeEntryBlock.vue | 23 ++++++++-- .../time-tracking/TimeEntryDrawer.vue | 42 ++++++++++++++--- .../time-tracking/TimeEntryList.vue | 12 ++--- .../time-tracking/TimeTrackingCalendar.vue | 9 ++-- frontend/components/ui/DateFilter.vue | 30 +++---------- frontend/pages/my-tasks.vue | 8 +++- frontend/pages/time-tracking.vue | 45 +++++++++---------- 8 files changed, 105 insertions(+), 71 deletions(-) diff --git a/frontend/components/task/TaskCard.vue b/frontend/components/task/TaskCard.vue index 2147d59..528cf2e 100644 --- a/frontend/components/task/TaskCard.vue +++ b/frontend/components/task/TaskCard.vue @@ -9,7 +9,12 @@
- {{ task.project.code }}{{ task.number }} + {{ task.project.code }}{{ task.number }} +
-
+
{{ tag.label }} + + +{{ hiddenTagCount }} +
{{ duration }}
@@ -111,6 +117,17 @@ const sizeLevel = computed(() => { return 0 }) +const showTags = computed(() => (props.totalColumns ?? 1) <= 2) + +const maxVisibleTags = computed(() => { + const total = props.totalColumns ?? 1 + if (total >= 2) return 1 + return 2 +}) + +const visibleTags = computed(() => props.entry.tags.slice(0, maxVisibleTags.value)) +const hiddenTagCount = computed(() => Math.max(0, props.entry.tags.length - maxVisibleTags.value)) + const hasProject = computed(() => !!props.entry.project) const blockStyle = computed(() => { diff --git a/frontend/components/time-tracking/TimeEntryDrawer.vue b/frontend/components/time-tracking/TimeEntryDrawer.vue index 418fc7f..9f84763 100644 --- a/frontend/components/time-tracking/TimeEntryDrawer.vue +++ b/frontend/components/time-tracking/TimeEntryDrawer.vue @@ -105,12 +105,22 @@ > Supprimer - +
+ + +
@@ -231,6 +241,26 @@ watch([() => props.modelValue, () => props.entry] as const, ([open, entry]) => { } }) +async function onDuplicate() { + if (!form.date || !form.startTime || !form.endTime) return + + const { create } = useTimeEntryService() + + const payload: Record = { + title: form.title || null, + description: form.description || null, + startedAt: toISO(form.date, form.startTime), + stoppedAt: form.endTime ? toISO(form.date, form.endTime) : null, + user: `/api/users/${form.userId}`, + project: form.projectId ? `/api/projects/${form.projectId}` : null, + tags: form.tagIds.map(id => `/api/task_tags/${id}`), + } + + await create(payload as TimeEntryWrite) + emit('saved') + isOpen.value = false +} + async function onDelete() { if (!props.entry) return const { remove } = useTimeEntryService() diff --git a/frontend/components/time-tracking/TimeEntryList.vue b/frontend/components/time-tracking/TimeEntryList.vue index f74eee0..b561af0 100644 --- a/frontend/components/time-tracking/TimeEntryList.vue +++ b/frontend/components/time-tracking/TimeEntryList.vue @@ -7,7 +7,7 @@
@@ -18,14 +18,14 @@
-
- - {{ entry.title || $t('common.untitled') }} - +
+ {{ entry.title || $t('common.untitled') }} +
+
{{ tag.label }} diff --git a/frontend/components/time-tracking/TimeTrackingCalendar.vue b/frontend/components/time-tracking/TimeTrackingCalendar.vue index 0de926a..d23a81e 100644 --- a/frontend/components/time-tracking/TimeTrackingCalendar.vue +++ b/frontend/components/time-tracking/TimeTrackingCalendar.vue @@ -201,14 +201,11 @@ function getScrollParent(): HTMLElement | null { // Scroll to current hour on mount onMounted(() => { nextTick(() => { - if (!calendarEl.value) return - const scrollParent = getScrollParent() - if (!scrollParent) return + if (!gridBodyEl.value) return const now = new Date() const currentMinutes = now.getHours() * 60 + now.getMinutes() - const calendarTop = calendarEl.value.offsetTop - const scrollTarget = calendarTop + (currentMinutes / 60) * hourHeight - scrollParent.clientHeight / 3 - scrollParent.scrollTop = Math.max(0, scrollTarget) + const scrollTarget = (currentMinutes / 60) * hourHeight - gridBodyEl.value.clientHeight / 3 + gridBodyEl.value.scrollTop = Math.max(0, scrollTarget) }) }) diff --git a/frontend/components/ui/DateFilter.vue b/frontend/components/ui/DateFilter.vue index cd69acb..842b0d9 100644 --- a/frontend/components/ui/DateFilter.vue +++ b/frontend/components/ui/DateFilter.vue @@ -1,5 +1,5 @@