feat: rounding visibility indicators on entry rows
This commit is contained in:
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user