diff --git a/pages/mannschaften/[slug].vue b/pages/mannschaften/[slug].vue index 9f3ecc8..05e8665 100644 --- a/pages/mannschaften/[slug].vue +++ b/pages/mannschaften/[slug].vue @@ -51,7 +51,7 @@

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

- S/U/N + S + + + U + + + N Sätze - - Bälle - Punkte @@ -262,13 +265,16 @@ {{ row.meetings_count ?? '-' }} - {{ formatSun(row) }} + {{ row.meetings_won ?? 0 }} - {{ row.sets_relation || '-' }} + {{ row.meetings_tie ?? 0 }} - {{ row.games_relation || '-' }} + {{ row.meetings_lost ?? 0 }} + + + {{ formatSaetze(row) }} {{ formatPunkte(row) }} @@ -364,6 +370,15 @@ const spielplanSeasonLabel = computed(() => { 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)}` +}) + async function fetchCsvText(url) { const attempt = async () => { const withBuster = `${url}${url.includes('?') ? '&' : '?'}_t=${Date.now()}` @@ -650,12 +665,11 @@ const getRowClass = (row) => { return 'bg-white' } -const formatSun = (row) => { - const s = row?.meetings_won - const u = row?.meetings_tie - const n = row?.meetings_lost - if (s == null && u == null && n == null) return '-' - return `${s ?? 0}/${u ?? 0}/${n ?? 0}` +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 formatPunkte = (row) => { diff --git a/server/api/cms/save-csv.post.js b/server/api/cms/save-csv.post.js index b54d7cc..cb04f52 100644 --- a/server/api/cms/save-csv.post.js +++ b/server/api/cms/save-csv.post.js @@ -51,8 +51,9 @@ export default defineEventHandler(async (event) => { 'termine.csv', 'spielplan.csv' ] + const isSeasonalMannschaftenFile = /^mannschaften_\d{2}--\d{2}\.csv$/.test(String(filename)) - if (!allowedFiles.includes(filename)) { + if (!allowedFiles.includes(filename) && !isSeasonalMannschaftenFile) { throw createError({ statusCode: 403, statusMessage: 'Datei nicht erlaubt' @@ -105,7 +106,9 @@ export default defineEventHandler(async (event) => { 'termine.csv': [`${cwd}/server/data/public-data/termine.csv`, `${cwd}/../server/data/public-data/termine.csv`], 'spielplan.csv': [`${cwd}/server/data/public-data/spielplan.csv`, `${cwd}/../server/data/public-data/spielplan.csv`] } - const internalPaths = dataTargetsByFile[filename] || [] + const internalPaths = isSeasonalMannschaftenFile + ? [`${cwd}/server/data/public-data/${filename}`, `${cwd}/../server/data/public-data/${filename}`] + : (dataTargetsByFile[filename] || []) const uniquePaths = [...new Set([...internalPaths])] const writeResults = [] diff --git a/server/api/mannschaften.get.js b/server/api/mannschaften.get.js index 22f73d8..374528b 100644 --- a/server/api/mannschaften.get.js +++ b/server/api/mannschaften.get.js @@ -1,5 +1,10 @@ import { promises as fs } from 'fs' import path from 'path' +import { getCurrentSeasonSlug, validateSeasonSlug } from '../utils/spielplan-data.js' + +function normalizeSeasonFilename(season) { + return `mannschaften_${season}.csv` +} async function exists(p) { try { @@ -13,20 +18,36 @@ async function exists(p) { export default defineEventHandler(async (event) => { try { const cwd = process.cwd() - const filename = 'mannschaften.csv' + const query = getQuery(event) + const requestedSeason = query.season ? String(query.season).trim() : '' + + if (requestedSeason && !validateSeasonSlug(requestedSeason)) { + throw createError({ + statusCode: 400, + statusMessage: 'Ungueltiger Saison-Slug' + }) + } + + const defaultSeason = getCurrentSeasonSlug() + const candidateFileNames = requestedSeason + ? [normalizeSeasonFilename(requestedSeason), 'mannschaften.csv'] + : [normalizeSeasonFilename(defaultSeason), 'mannschaften.csv'] // Prefer CMS write target first (server/data/public-data), // then legacy locations. - const candidates = [ - path.join(cwd, 'server/data/public-data', filename), - path.join(cwd, '../server/data/public-data', filename), - path.join(cwd, '.output/server/data', filename), - path.join(cwd, 'server/data', filename), - path.join(cwd, '.output/public/data', filename), - path.join(cwd, 'public/data', filename), - path.join(cwd, '../.output/public/data', filename), - path.join(cwd, '../public/data', filename) - ] + const candidates = [] + for (const filename of candidateFileNames) { + candidates.push( + path.join(cwd, 'server/data/public-data', filename), + path.join(cwd, '../server/data/public-data', filename), + path.join(cwd, '.output/server/data', filename), + path.join(cwd, 'server/data', filename), + path.join(cwd, '.output/public/data', filename), + path.join(cwd, 'public/data', filename), + path.join(cwd, '../.output/public/data', filename), + path.join(cwd, '../public/data', filename) + ) + } let csvPath = null for (const p of candidates) {