- Page titles in blue primary (#222783) - Double main content margins (px-16 py-24) - Remove blue border above sidebar timer - Remove project color dot, use project color on title text - All delete buttons/icons orange (#E2953C) - Fix collapsed sidebar logo (object-cover object-left) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
105 lines
4.0 KiB
Vue
105 lines
4.0 KiB
Vue
<template>
|
||
<div class="space-y-2">
|
||
<div v-if="entries.length === 0" class="rounded-lg border border-neutral-200 bg-neutral-50 py-12 text-center text-sm text-neutral-400">
|
||
Aucune activité pour cette période
|
||
</div>
|
||
|
||
<div
|
||
v-for="entry in sortedEntries"
|
||
:key="entry.id"
|
||
class="group flex items-center gap-4 rounded-lg border border-neutral-200 bg-white px-4 py-3 cursor-pointer transition hover:border-neutral-300 hover:shadow-sm"
|
||
@click="emit('editEntry', entry)"
|
||
>
|
||
<!-- Color bar -->
|
||
<div
|
||
class="h-10 w-1 shrink-0 rounded-full"
|
||
:style="{ backgroundColor: entry.project?.color ?? '#94a3b8' }"
|
||
/>
|
||
|
||
<!-- Main info -->
|
||
<div class="min-w-0 flex-1">
|
||
<div class="flex items-center gap-2">
|
||
<span class="truncate text-sm font-semibold text-neutral-900">
|
||
{{ entry.title || 'Sans titre' }}
|
||
</span>
|
||
<span
|
||
v-for="type in entry.types"
|
||
:key="type.id"
|
||
class="shrink-0 rounded-full px-2 py-0.5 text-[10px] font-semibold text-white"
|
||
:style="{ backgroundColor: type.color }"
|
||
>
|
||
{{ type.label }}
|
||
</span>
|
||
</div>
|
||
<div class="mt-0.5 flex items-center gap-2 text-xs text-neutral-500">
|
||
<span v-if="entry.project">{{ entry.project.name }}</span>
|
||
<span v-if="entry.project && entry.description" class="text-neutral-300">·</span>
|
||
<span v-if="entry.description" class="truncate">{{ entry.description }}</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Time info -->
|
||
<div class="shrink-0 text-right">
|
||
<div class="text-sm font-semibold tabular-nums text-neutral-900">
|
||
{{ formatDuration(entry) }}
|
||
</div>
|
||
<div class="text-xs tabular-nums text-neutral-400">
|
||
{{ formatTime(entry.startedAt) }} – {{ entry.stoppedAt ? formatTime(entry.stoppedAt) : '...' }}
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Date -->
|
||
<div class="hidden shrink-0 text-xs text-neutral-400 sm:block">
|
||
{{ formatDate(entry.startedAt) }}
|
||
</div>
|
||
|
||
<!-- Delete action -->
|
||
<button
|
||
class="shrink-0 rounded-md p-1.5 text-neutral-300 opacity-0 transition hover:bg-orange-50 hover:text-[#E2953C] group-hover:opacity-100"
|
||
title="Supprimer"
|
||
@click.stop="emit('deleteEntry', entry)"
|
||
>
|
||
<Icon name="mdi:delete-outline" size="18" />
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import type { TimeEntry } from '~/services/dto/time-entry'
|
||
|
||
const props = defineProps<{
|
||
entries: TimeEntry[]
|
||
}>()
|
||
|
||
const emit = defineEmits<{
|
||
(e: 'editEntry', entry: TimeEntry): void
|
||
(e: 'deleteEntry', entry: TimeEntry): void
|
||
}>()
|
||
|
||
const sortedEntries = computed(() => {
|
||
return [...props.entries].sort((a, b) => {
|
||
return new Date(b.startedAt).getTime() - new Date(a.startedAt).getTime()
|
||
})
|
||
})
|
||
|
||
function formatDuration(entry: TimeEntry): string {
|
||
const start = new Date(entry.startedAt).getTime()
|
||
const end = entry.stoppedAt ? new Date(entry.stoppedAt).getTime() : Date.now()
|
||
const diff = end - start
|
||
const h = Math.floor(diff / 3600000)
|
||
const m = Math.floor((diff % 3600000) / 60000)
|
||
return m > 0 ? `${h}h${String(m).padStart(2, '0')}` : `${h}h`
|
||
}
|
||
|
||
function formatTime(iso: string): string {
|
||
const d = new Date(iso)
|
||
return d.toLocaleTimeString('fr-FR', { hour: '2-digit', minute: '2-digit' })
|
||
}
|
||
|
||
function formatDate(iso: string): string {
|
||
const d = new Date(iso)
|
||
return d.toLocaleDateString('fr-FR', { weekday: 'short', day: 'numeric', month: 'short' })
|
||
}
|
||
</script>
|