feat: rounding visibility indicators on entry rows

This commit is contained in:
Your Name
2026-02-20 15:36:07 +02:00
parent a41ce44f13
commit 773ba1d338

View File

@@ -172,6 +172,15 @@
title="Non-billable"
>NB</span>
{{ formatDuration(entry.duration) }}
<span
v-if="getRoundedDuration(entry.duration) !== null"
class="ml-1 text-[0.5625rem] font-sans text-text-tertiary"
:title="'Actual: ' + formatDuration(entry.duration) + ' - Rounded: ' + formatDuration(getRoundedDuration(entry.duration)!)"
>
<Clock class="inline w-3 h-3 mr-0.5" aria-hidden="true" :stroke-width="1.5" />
<span class="sr-only">Duration rounded from {{ formatDuration(entry.duration) }} to {{ formatDuration(getRoundedDuration(entry.duration)!) }}</span>
Rounded
</span>
</div>
</td>
<td class="px-4 py-3">
@@ -734,7 +743,7 @@
<script setup lang="ts">
import { ref, reactive, computed, onMounted } from 'vue'
import { List as ListIcon, Copy, DollarSign, Receipt, Plus, Pencil, Trash2, Lock, Image, Upload } from 'lucide-vue-next'
import { List as ListIcon, Copy, DollarSign, Receipt, Plus, Pencil, Trash2, Lock, Image, Upload, Clock } from 'lucide-vue-next'
import { invoke, convertFileSrc } from '@tauri-apps/api/core'
import AppNumberInput from '../components/AppNumberInput.vue'
import AppSelect from '../components/AppSelect.vue'
@@ -751,6 +760,7 @@ import { useTagsStore } from '../stores/tags'
import { useToastStore } from '../stores/toast'
import { useEntryTemplatesStore } from '../stores/entryTemplates'
import { formatDate, formatCurrency, getCurrencySymbol } from '../utils/locale'
import { useSettingsStore } from '../stores/settings'
import { useFormGuard } from '../utils/formGuard'
import { renderMarkdown } from '../utils/markdown'
@@ -760,6 +770,7 @@ const projectsStore = useProjectsStore()
const tagsStore = useTagsStore()
const toast = useToastStore()
const entryTemplatesStore = useEntryTemplatesStore()
const settingsStore = useSettingsStore()
const showTemplatePicker = ref(false)
const showReceiptLightbox = ref(false)
const receiptLightboxData = ref({ imageUrl: '', description: '', category: '', date: '', amount: '' })
@@ -1005,6 +1016,24 @@ function formatDuration(seconds: number): string {
return `${minutes}m`
}
// Rounding helpers
function roundDuration(seconds: number, increment: number, method: string): number {
const incrementSeconds = increment * 60
if (incrementSeconds <= 0) return seconds
if (method === 'up') return Math.ceil(seconds / incrementSeconds) * incrementSeconds
if (method === 'down') return Math.floor(seconds / incrementSeconds) * incrementSeconds
return Math.round(seconds / incrementSeconds) * incrementSeconds
}
function getRoundedDuration(seconds: number): number | null {
if (settingsStore.settings.rounding_enabled !== 'true') return null
const increment = parseInt(settingsStore.settings.rounding_increment) || 0
const method = settingsStore.settings.rounding_method || 'nearest'
if (increment <= 0) return null
const rounded = roundDuration(seconds, increment, method)
return rounded !== seconds ? rounded : null
}
// Tag helper functions
function getTagName(tagId: number): string {
const tag = tagsStore.tags.find(t => t.id === tagId)
@@ -1553,7 +1582,8 @@ onMounted(async () => {
await Promise.all([
entriesStore.fetchEntriesPaginated(),
projectsStore.fetchProjects(),
tagsStore.fetchTags()
tagsStore.fetchTags(),
settingsStore.fetchSettings()
])
await loadEntryTags()