From 56fa4f75397c661a60f24228b029202a7f9ad072 Mon Sep 17 00:00:00 2001 From: Bernhard Weichel Date: Wed, 1 Oct 2025 10:07:19 +0000 Subject: [PATCH] feat: add appointment tags support to tags system - Add appointment domain to tag types and interfaces - Include appointment tags in dashboard card stats with calendar icon - Add appointment filter option in tags admin panel - Support creating, editing, and managing appointment tags - Update domain display names to include 'Termine' for appointments - Extend TanStack Query composable to fetch appointment tags - Add appointment tags count to stats calculations Co-authored-by: Ona --- src/components/tags/TagsAdmin.vue | 3 +++ src/components/tags/TagsCard.vue | 9 ++++++++- src/components/tags/useTags.ts | 9 ++++++++- src/composables/useTags.ts | 15 +++++++++++++-- 4 files changed, 32 insertions(+), 4 deletions(-) diff --git a/src/components/tags/TagsAdmin.vue b/src/components/tags/TagsAdmin.vue index 0f3511b..4a9435c 100644 --- a/src/components/tags/TagsAdmin.vue +++ b/src/components/tags/TagsAdmin.vue @@ -103,6 +103,7 @@ + @@ -232,6 +233,7 @@ + { person: 'Personen', song: 'Lieder', group: 'Gruppen', + appointment: 'Termine', } return domainNames[domain as keyof typeof domainNames] || domain } diff --git a/src/components/tags/TagsCard.vue b/src/components/tags/TagsCard.vue index 4fc80a9..3396072 100644 --- a/src/components/tags/TagsCard.vue +++ b/src/components/tags/TagsCard.vue @@ -43,7 +43,7 @@ const { data: tags, isLoading, error, refetch, isFetching, dataUpdatedAt } = use // Compute stats using the helper function const stats = computed(() => { - if (!tags.value) return { total: 0, person: 0, song: 0, group: 0 } + if (!tags.value) return { total: 0, person: 0, song: 0, group: 0, appointment: 0 } return useTagsStats(tags.value) }) @@ -74,6 +74,13 @@ const statusStats = computed(() => [ icon: '👥', type: 'warning' as const, }, + { + key: 'appointment', + value: stats.value.appointment, + label: 'Termine', + icon: '📅', + type: 'primary' as const, + }, ]) const formattedLastUpdate = computed(() => { diff --git a/src/components/tags/useTags.ts b/src/components/tags/useTags.ts index 9192749..8aba56d 100644 --- a/src/components/tags/useTags.ts +++ b/src/components/tags/useTags.ts @@ -26,6 +26,10 @@ export function useTags() { () => tags.value.filter((tag) => tag.domainType === 'group').length ) + const appointmentTagsCount = computed( + () => tags.value.filter((tag) => tag.domainType === 'appointment').length + ) + // Note: Domain filtering is now handled at API level in fetchTags // No need for client-side domain filtering since we only load the selected domain @@ -35,7 +39,9 @@ export function useTags() { error.value = null try { - const domains = selectedDomain.value ? [selectedDomain.value] : ['person', 'song', 'group'] + const domains = selectedDomain.value + ? [selectedDomain.value] + : ['person', 'song', 'group', 'appointment'] const tagPromises = domains.map(async (domain) => { try { @@ -164,6 +170,7 @@ export function useTags() { personTagsCount, songTagsCount, groupTagsCount, + appointmentTagsCount, fetchTags, createTag, diff --git a/src/composables/useTags.ts b/src/composables/useTags.ts index d15f008..b4919ab 100644 --- a/src/composables/useTags.ts +++ b/src/composables/useTags.ts @@ -7,7 +7,7 @@ export interface Tag { name: string description?: string color?: string - domainType: 'person' | 'song' | 'group' + domainType: 'person' | 'song' | 'group' | 'appointment' } type TagsApiResponse = Tag[] @@ -16,10 +16,11 @@ async function fetchTags(): Promise { const startTime = performance.now() console.log('🏷️ Fetching tags...') // Fetch tags from different domains using modern API endpoints - const [personTags, songTags, groupTags] = await Promise.allSettled([ + const [personTags, songTags, groupTags, appointmentTags] = await Promise.allSettled([ churchtoolsClient.get('/tags/person').catch(() => []), churchtoolsClient.get('/tags/song').catch(() => []), churchtoolsClient.get('/tags/group').catch(() => []), + churchtoolsClient.get('/tags/appointment').catch(() => []), ]) const allTags: Tag[] = [] @@ -42,6 +43,14 @@ async function fetchTags(): Promise { allTags.push(...groupTagsData.map((tag: any) => ({ ...tag, domainType: 'group' as const }))) } + // Process appointment tags + if (appointmentTags.status === 'fulfilled' && appointmentTags.value) { + const appointmentTagsData = Array.isArray(appointmentTags.value) ? appointmentTags.value : [] + allTags.push( + ...appointmentTagsData.map((tag: any) => ({ ...tag, domainType: 'appointment' as const })) + ) + } + const endTime = performance.now() console.log(`🏷️ Tags fetched: ${allTags.length} tags in ${Math.round(endTime - startTime)}ms`) return allTags @@ -62,11 +71,13 @@ export function useTagsStats(tags: Tag[]) { const personTagsCount = tags.filter((tag) => tag.domainType === 'person').length const songTagsCount = tags.filter((tag) => tag.domainType === 'song').length const groupTagsCount = tags.filter((tag) => tag.domainType === 'group').length + const appointmentTagsCount = tags.filter((tag) => tag.domainType === 'appointment').length return { total: tags.length, person: personTagsCount, song: songTagsCount, group: groupTagsCount, + appointment: appointmentTagsCount, } }