feat: entry template management in settings
This commit is contained in:
@@ -799,6 +799,35 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Divider -->
|
||||
<div class="border-t border-border-subtle" />
|
||||
|
||||
<!-- Entry Templates -->
|
||||
<h3 class="text-[0.6875rem] text-text-tertiary uppercase tracking-[0.08em] font-medium">Entry Templates</h3>
|
||||
|
||||
<div v-if="entryTemplatesStore.templates.length > 0" class="space-y-2">
|
||||
<div
|
||||
v-for="tpl in entryTemplatesStore.templates"
|
||||
:key="tpl.id"
|
||||
class="flex items-center justify-between py-2 px-3 bg-bg-inset rounded-lg"
|
||||
>
|
||||
<div class="flex-1 min-w-0">
|
||||
<p class="text-[0.8125rem] text-text-primary truncate">{{ tpl.name }}</p>
|
||||
<p class="text-[0.6875rem] text-text-tertiary">
|
||||
{{ getTemplateProjectName(tpl.project_id) }}
|
||||
<span v-if="tpl.duration"> - {{ formatTemplateDuration(tpl.duration) }}</span>
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
@click="deleteEntryTemplate(tpl.id!)"
|
||||
class="text-text-tertiary hover:text-status-error transition-colors focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent"
|
||||
:aria-label="'Delete template ' + tpl.name"
|
||||
>
|
||||
<Trash2 class="w-3.5 h-3.5" aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<p v-else class="text-[0.75rem] text-text-tertiary">No saved templates. Use "Save as Template" in the Entries view.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1279,6 +1308,7 @@ import { useToastStore } from '../stores/toast'
|
||||
import { useOnboardingStore } from '../stores/onboarding'
|
||||
import { useRecurringStore } from '../stores/recurring'
|
||||
import type { RecurringEntry } from '../stores/recurring'
|
||||
import { useEntryTemplatesStore } from '../stores/entryTemplates'
|
||||
import AppNumberInput from '../components/AppNumberInput.vue'
|
||||
import AppSelect from '../components/AppSelect.vue'
|
||||
import AppShortcutRecorder from '../components/AppShortcutRecorder.vue'
|
||||
@@ -1293,6 +1323,7 @@ import type { SoundEvent } from '../utils/audio'
|
||||
const settingsStore = useSettingsStore()
|
||||
const toastStore = useToastStore()
|
||||
const recurringStore = useRecurringStore()
|
||||
const entryTemplatesStore = useEntryTemplatesStore()
|
||||
const onboardingStore = useOnboardingStore()
|
||||
const recurringEntries = computed(() => recurringStore.entries)
|
||||
|
||||
@@ -1835,6 +1866,24 @@ function formatDuration(seconds: number): string {
|
||||
return `${m}m`
|
||||
}
|
||||
|
||||
// Entry template helpers
|
||||
function getTemplateProjectName(projectId: number): string {
|
||||
return recProjects.value.find(p => p.id === projectId)?.name || 'Unknown'
|
||||
}
|
||||
|
||||
function formatTemplateDuration(seconds: number): string {
|
||||
const h = Math.floor(seconds / 3600)
|
||||
const m = Math.floor((seconds % 3600) / 60)
|
||||
if (h > 0 && m > 0) return `${h}h ${m}m`
|
||||
if (h > 0) return `${h}h`
|
||||
if (m > 0) return `${m}m`
|
||||
return '0m'
|
||||
}
|
||||
|
||||
async function deleteEntryTemplate(id: number) {
|
||||
await entryTemplatesStore.deleteTemplate(id)
|
||||
}
|
||||
|
||||
function buildRecurrenceRule(): string {
|
||||
if (recPattern.value === 'weekly') {
|
||||
return 'weekly:' + recWeeklyDays.value.join(',')
|
||||
@@ -2113,6 +2162,9 @@ onMounted(async () => {
|
||||
await recurringStore.fetchEntries()
|
||||
recProjects.value = await invoke<Array<{ id: number; name: string; color: string }>>('get_projects')
|
||||
|
||||
// Load entry templates
|
||||
await entryTemplatesStore.fetchTemplates()
|
||||
|
||||
// Load calendar sources
|
||||
await fetchCalendarSources()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user