diff --git a/src/views/Entries.vue b/src/views/Entries.vue index b0a93d5..b2c6ecd 100644 --- a/src/views/Entries.vue +++ b/src/views/Entries.vue @@ -94,6 +94,16 @@ - +
+ + {{ getTagName(tagId) }} + +
{{ formatDuration(entry.duration) }} @@ -174,6 +184,12 @@ /> + +
+ + +
+
@@ -257,14 +273,19 @@ import AppNumberInput from '../components/AppNumberInput.vue' import AppSelect from '../components/AppSelect.vue' import AppDatePicker from '../components/AppDatePicker.vue' import AppDiscardDialog from '../components/AppDiscardDialog.vue' +import AppTagInput from '../components/AppTagInput.vue' import { useEntriesStore, type TimeEntry } from '../stores/entries' import { useProjectsStore } from '../stores/projects' +import { useTagsStore } from '../stores/tags' import { formatDate } from '../utils/locale' import { useFormGuard } from '../utils/formGuard' import { renderMarkdown } from '../utils/markdown' const entriesStore = useEntriesStore() const projectsStore = useProjectsStore() +const tagsStore = useTagsStore() +const entryTags = ref>({}) +const editEntryTags = ref([]) const { showDiscardDialog, snapshot: snapshotForm, tryClose: tryCloseForm, confirmDiscard, cancelDiscard } = useFormGuard() @@ -362,6 +383,28 @@ function formatDuration(seconds: number): string { return `${minutes}m` } +// Tag helper functions +function getTagName(tagId: number): string { + const tag = tagsStore.tags.find(t => t.id === tagId) + return tag?.name || '' +} + +function getTagColor(tagId: number): string { + const tag = tagsStore.tags.find(t => t.id === tagId) + return tag?.color || '#6B7280' +} + +async function loadEntryTags() { + const tags: Record = {} + for (const entry of entriesStore.entries) { + if (entry.id) { + const entryTagList = await tagsStore.getEntryTags(entry.id) + tags[entry.id] = entryTagList.map(t => t.id!).filter(id => id != null) + } + } + entryTags.value = tags +} + // Duplicate an entry with current timestamp async function duplicateEntry(entry: TimeEntry) { const now = new Date() @@ -375,6 +418,7 @@ async function duplicateEntry(entry: TimeEntry) { } await entriesStore.createEntry(newEntry) await entriesStore.fetchEntries() + await loadEntryTags() } // Copy yesterday's entries to today @@ -401,6 +445,7 @@ async function copyPreviousDay() { }) } await entriesStore.fetchEntries() + await loadEntryTags() } // Copy last week's entries shifted forward 7 days @@ -428,23 +473,26 @@ async function copyPreviousWeek() { }) } await entriesStore.fetchEntries() + await loadEntryTags() } // Apply filters -function applyFilters() { - entriesStore.fetchEntries(startDate.value || undefined, endDate.value || undefined) +async function applyFilters() { + await entriesStore.fetchEntries(startDate.value || undefined, endDate.value || undefined) + await loadEntryTags() } // Clear filters -function clearFilters() { +async function clearFilters() { startDate.value = '' endDate.value = '' filterProject.value = null - entriesStore.fetchEntries() + await entriesStore.fetchEntries() + await loadEntryTags() } // Open edit dialog -function openEditDialog(entry: TimeEntry) { +async function openEditDialog(entry: TimeEntry) { editingEntry.value = entry editForm.id = entry.id || 0 editForm.project_id = entry.project_id @@ -460,6 +508,14 @@ function openEditDialog(entry: TimeEntry) { editHour.value = dt.getHours() editMinute.value = dt.getMinutes() + // Load tags for this entry + if (entry.id) { + const tags = await tagsStore.getEntryTags(entry.id) + editEntryTags.value = tags.map(t => t.id!).filter(id => id != null) + } else { + editEntryTags.value = [] + } + snapshotForm(getEditFormData()) showEditDialog.value = true } @@ -488,6 +544,13 @@ async function handleEdit() { } await entriesStore.updateEntry(updatedEntry) + + // Save tags for the edited entry + if (editForm.id) { + await tagsStore.setEntryTags(editForm.id, editEntryTags.value) + await loadEntryTags() + } + closeEditDialog() } } @@ -516,9 +579,12 @@ async function handleDelete() { onMounted(async () => { await Promise.all([ entriesStore.fetchEntries(), - projectsStore.fetchProjects() + projectsStore.fetchProjects(), + tagsStore.fetchTags() ]) + await loadEntryTags() + // Set default date range to this week const now = new Date() const weekStart = new Date(now) diff --git a/src/views/Timer.vue b/src/views/Timer.vue index c4d6b51..c625cc5 100644 --- a/src/views/Timer.vue +++ b/src/views/Timer.vue @@ -104,6 +104,10 @@
+
+ + +
@@ -183,10 +187,12 @@ import { useToastStore } from '../stores/toast' import { Timer as TimerIcon, RotateCcw, ExternalLink, Star } from 'lucide-vue-next' import { invoke } from '@tauri-apps/api/core' import AppSelect from '../components/AppSelect.vue' +import AppTagInput from '../components/AppTagInput.vue' import IdlePromptDialog from '../components/IdlePromptDialog.vue' import AppTrackingPromptDialog from '../components/AppTrackingPromptDialog.vue' import { formatDateTime } from '../utils/locale' import { useFavoritesStore, type Favorite } from '../stores/favorites' +import { useTagsStore } from '../stores/tags' const timerStore = useTimerStore() const projectsStore = useProjectsStore() @@ -194,12 +200,14 @@ const entriesStore = useEntriesStore() const settingsStore = useSettingsStore() const toastStore = useToastStore() const favoritesStore = useFavoritesStore() +const tagsStore = useTagsStore() const favorites = computed(() => favoritesStore.favorites) // Local state for inputs const selectedProject = ref(timerStore.selectedProjectId) const selectedTask = ref(timerStore.selectedTaskId) const description = ref(timerStore.description) +const selectedTags = ref([]) const projectTasks = ref([]) // Split timer into parts for colon animation @@ -300,7 +308,7 @@ async function openMiniTimer() { } // Toggle timer -function toggleTimer() { +async function toggleTimer() { if (timerStore.isStopped) { if (!selectedProject.value) { toastStore.info('Please select a project before starting the timer') @@ -309,7 +317,16 @@ function toggleTimer() { timerStore.start() } else { timerStore.stop() - entriesStore.fetchEntries() + await entriesStore.fetchEntries() + + // Save tags for the new entry if any were selected + if (selectedTags.value.length > 0) { + const entries = entriesStore.entries + if (entries.length > 0 && entries[0].id) { + await tagsStore.setEntryTags(entries[0].id, selectedTags.value) + } + } + selectedTags.value = [] } } @@ -390,7 +407,8 @@ onMounted(async () => { projectsStore.fetchProjects(), entriesStore.fetchEntries(), settingsStore.fetchSettings(), - favoritesStore.fetchFavorites() + favoritesStore.fetchFavorites(), + tagsStore.fetchTags() ]) // Restore timer state