diff --git a/components/MannschaftenUebersicht.vue b/components/MannschaftenUebersicht.vue index 3df5c21..23d8f55 100644 --- a/components/MannschaftenUebersicht.vue +++ b/components/MannschaftenUebersicht.vue @@ -52,7 +52,7 @@

- Mannschaftsaufstellung Saison 2025/26 + Mannschaftsaufstellung Saison {{ selectedSeasonLabel }}

diff --git a/package.json b/package.json index 48a5921..6fb9537 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "harheimertc-website", - "version": "1.4.7", + "version": "1.5.0", "description": "Moderne Webseite für den Harheimer Tischtennis Club", "private": true, "type": "module", diff --git a/pages/mannschaften/[slug].vue b/pages/mannschaften/[slug].vue index 6c21e3b..f90c622 100644 --- a/pages/mannschaften/[slug].vue +++ b/pages/mannschaften/[slug].vue @@ -51,7 +51,7 @@

- Mannschaftsaufstellung Saison 2025/26 + Mannschaftsaufstellung Saison {{ mannschaftSeasonLabel }}

-

- Aktueller Spielplan -

-

- Saison {{ spielplanSeasonLabel }} -

+
+
+

+ Aktueller Spielplan +

+

+ Saison {{ spielplanSeasonLabel }} +

+
+ +
+ + +
+
Spielplan wird geladen...
{{ spielplanError }}
Für diese Mannschaft sind im aktuellen Spielplan keine Spiele vorhanden.
@@ -170,6 +194,101 @@
+ +
+ Tabelle wird geladen... +
+ +
+ {{ tableError }} +
+ +
+ Für diese Mannschaft ist aktuell keine Tabelle hinterlegt. +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Platz + + Mannschaft + + Spiele + + S + + U + + N + + Sätze + + Bälle + + Punkte +
+ {{ row.table_rank ?? '-' }} + + {{ row.team_name || '-' }} + + {{ row.meetings_count ?? '-' }} + + {{ row.meetings_won ?? 0 }} + + {{ row.meetings_tie ?? 0 }} + + {{ row.meetings_lost ?? 0 }} + + {{ formatSaetze(row) }} + + {{ formatBaelle(row) }} + + {{ formatPunkte(row) }} +
+
@@ -196,14 +315,17 @@

- Zuletzt aktualisiert am: {{ formatDate(mannschaft.letzte_aktualisierung) }} + Mannschaftsinformationen aktualisiert am: {{ formatDate(mannschaft.letzte_aktualisierung) }} +

+

+ Daten aktualisiert am: {{ dataUpdatedLabel }}

← Zurück zur Übersicht @@ -237,17 +359,55 @@ import { ref, computed, onMounted } from 'vue' import { Users } from 'lucide-vue-next' const route = useRoute() + +const getCurrentSeasonSlug = () => { + const now = new Date() + const year = now.getFullYear() + const startYear = now.getMonth() >= 6 ? year : year - 1 + const endYear = startYear + 1 + return `${String(startYear).slice(-2)}--${String(endYear).slice(-2)}` +} + +const selectedSeason = computed(() => { + const value = String(route.query.season || '').trim() + return /^\d{2}--\d{2}$/.test(value) ? value : getCurrentSeasonSlug() +}) const mannschaft = ref(null) const mannschaftSpielplan = ref([]) const spielplanSeason = ref('') const isSpielplanLoading = ref(false) const spielplanError = ref('') +const activePanelTab = ref('matches') +const isTableLoading = ref(false) +const tableError = ref('') +const teamTableRows = ref([]) +const spielplanDataUpdatedAt = ref('') +const tableDataUpdatedAt = ref('') + +const hasTableLink = computed(() => { + const link = String(mannschaft.value?.weitere_informationen_link || '').trim() + return link.includes('/tabelle/') +}) const spielplanSeasonLabel = computed(() => { const match = String(spielplanSeason.value || '').match(/^(\d{2})--(\d{2})$/) return match ? `20${match[1]}/${match[2]}` : '' }) +const mannschaftSeasonLabel = computed(() => { + if (spielplanSeasonLabel.value) return spielplanSeasonLabel.value + + const now = new Date() + const year = now.getFullYear() + const start = now.getMonth() >= 6 ? year : year - 1 + return `${start}/${String(start + 1).slice(-2)}` +}) + +const dataUpdatedLabel = computed(() => { + const dateValue = tableDataUpdatedAt.value || spielplanDataUpdatedAt.value || mannschaft.value?.letzte_aktualisierung || '' + return formatDate(dateValue) +}) + async function fetchCsvText(url) { const attempt = async () => { const withBuster = `${url}${url.includes('?') ? '&' : '?'}_t=${Date.now()}` @@ -266,7 +426,9 @@ async function fetchCsvText(url) { const loadMannschaften = async () => { try { - const csv = await fetchCsvText('/api/mannschaften') + const params = new URLSearchParams() + if (selectedSeason.value) params.set('season', selectedSeason.value) + const csv = await fetchCsvText(`/api/mannschaften${params.toString() ? `?${params.toString()}` : ''}`) if (!csv) return const lines = csv.split('\n').filter(line => line.trim() !== '') @@ -317,13 +479,55 @@ const loadMannschaften = async () => { useHead({ title: `${mannschaft.value.mannschaft} - Harheimer TC`, }) - await loadSpielplan() + await Promise.all([loadSpielplan(), loadTeamTable()]) } } catch (error) { console.error('Fehler beim Laden der Mannschaften:', error) } } +const loadTeamTable = async () => { + if (!mannschaft.value) return + + if (!hasTableLink.value) { + teamTableRows.value = [] + tableError.value = '' + return + } + + isTableLoading.value = true + tableError.value = '' + + try { + const params = new URLSearchParams({ team: mannschaft.value.mannschaft }) + if (selectedSeason.value) { + params.set('season', selectedSeason.value) + } else if (spielplanSeason.value) { + params.set('season', spielplanSeason.value) + } + + const response = await fetch(`/api/spielplan/table?${params.toString()}`) + const result = await response.json() + + if (!result.success) { + tableError.value = result.message || 'Tabelle konnte nicht geladen werden.' + teamTableRows.value = [] + return + } + + teamTableRows.value = Array.isArray(result?.table?.table?.leagueTable) + ? result.table.table.leagueTable + : [] + tableDataUpdatedAt.value = result?.importedAt || '' + } catch (error) { + console.error('Fehler beim Laden der Tabelle:', error) + tableError.value = 'Tabelle konnte nicht geladen werden.' + teamTableRows.value = [] + } finally { + isTableLoading.value = false + } +} + const getTeamVariants = (cmsMannschaft) => { const mannschaftMapping = { 'Erwachsene 1': ['harheimer tc'], @@ -407,7 +611,9 @@ const loadSpielplan = async () => { spielplanError.value = '' try { - const response = await fetch('/api/spielplan') + const params = new URLSearchParams() + if (selectedSeason.value) params.set('season', selectedSeason.value) + const response = await fetch(`/api/spielplan${params.toString() ? `?${params.toString()}` : ''}`) const result = await response.json() if (!result.success) { @@ -417,6 +623,7 @@ const loadSpielplan = async () => { } spielplanSeason.value = result.season || '' + spielplanDataUpdatedAt.value = result?.source?.updatedAt || result?.source?.importedAt || '' mannschaftSpielplan.value = result.data .filter(row => isSpielForMannschaft(row, mannschaft.value.mannschaft)) .sort((a, b) => parseTerminTimestamp(a) - parseTerminTimestamp(b)) @@ -495,6 +702,33 @@ const getRowClass = (row) => { return 'bg-white' } +const formatSaetze = (row) => { + const won = row?.sets_won + const lost = row?.sets_lost + if (won == null && lost == null) return row?.sets_relation || '-' + return `${won ?? 0}:${lost ?? 0}` +} + +const formatBaelle = (row) => { + const won = row?.games_won + const lost = row?.games_lost + if (won == null && lost == null) return row?.games_relation || '-' + return `${won ?? 0}:${lost ?? 0}` +} + +const formatPunkte = (row) => { + if (row?.points_won == null && row?.points_lost == null) return '-' + return `${row?.points_won ?? 0}:${row?.points_lost ?? 0}` +} + +const isCurrentTeamRow = (row) => { + const teamName = String(row?.team_name || '').toLowerCase() + if (!teamName.includes('harheimer tc')) return false + + const variants = getTeamVariants(mannschaft.value?.mannschaft || '') + return variants.some((variant) => isExactHarheimTeam(teamName, variant)) +} + onMounted(() => { loadMannschaften() }) diff --git a/pages/mannschaften/index.vue b/pages/mannschaften/index.vue index bf20d6c..676323c 100644 --- a/pages/mannschaften/index.vue +++ b/pages/mannschaften/index.vue @@ -7,10 +7,10 @@

- Unsere aktiven Mannschaften in der Saison 2025/26 + Unsere aktiven Mannschaften in der Saison {{ selectedSeasonLabel }}

- +
@@ -21,7 +21,7 @@ Alle aktuellen Spielpläne und Ergebnisse unserer Mannschaften finden Sie hier.

Zu den Spielplänen @@ -33,8 +33,29 @@