feat(mannschaften): add matches/table tabs on team detail pages
This commit is contained in:
@@ -76,40 +76,64 @@
|
||||
<!-- Aktueller Spielplan -->
|
||||
<div class="bg-white rounded-xl shadow-lg overflow-hidden">
|
||||
<div class="px-6 py-4 border-b border-gray-200">
|
||||
<h2 class="text-2xl font-semibold text-gray-900">
|
||||
Aktueller Spielplan
|
||||
</h2>
|
||||
<p
|
||||
v-if="spielplanSeasonLabel"
|
||||
class="text-sm text-gray-600 mt-1"
|
||||
>
|
||||
Saison {{ spielplanSeasonLabel }}
|
||||
</p>
|
||||
<div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
||||
<div>
|
||||
<h2 class="text-2xl font-semibold text-gray-900">
|
||||
Aktueller Spielplan
|
||||
</h2>
|
||||
<p
|
||||
v-if="spielplanSeasonLabel"
|
||||
class="text-sm text-gray-600 mt-1"
|
||||
>
|
||||
Saison {{ spielplanSeasonLabel }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="inline-flex rounded-lg border border-gray-200 p-1 bg-gray-50">
|
||||
<button
|
||||
type="button"
|
||||
class="px-4 py-2 text-sm font-medium rounded-md transition-colors"
|
||||
:class="activePanelTab === 'matches' ? 'bg-white text-primary-700 shadow-sm' : 'text-gray-600 hover:text-gray-900'"
|
||||
@click="activePanelTab = 'matches'"
|
||||
>
|
||||
Matches
|
||||
</button>
|
||||
<button
|
||||
v-if="hasTableLink"
|
||||
type="button"
|
||||
class="px-4 py-2 text-sm font-medium rounded-md transition-colors"
|
||||
:class="activePanelTab === 'table' ? 'bg-white text-primary-700 shadow-sm' : 'text-gray-600 hover:text-gray-900'"
|
||||
@click="activePanelTab = 'table'"
|
||||
>
|
||||
Tabelle
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="isSpielplanLoading"
|
||||
v-if="activePanelTab === 'matches' && isSpielplanLoading"
|
||||
class="p-6 text-sm text-gray-600"
|
||||
>
|
||||
Spielplan wird geladen...
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-else-if="spielplanError"
|
||||
v-else-if="activePanelTab === 'matches' && spielplanError"
|
||||
class="p-6 text-sm text-red-600"
|
||||
>
|
||||
{{ spielplanError }}
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-else-if="mannschaftSpielplan.length === 0"
|
||||
v-else-if="activePanelTab === 'matches' && mannschaftSpielplan.length === 0"
|
||||
class="p-6 text-sm text-gray-600"
|
||||
>
|
||||
Für diese Mannschaft sind im aktuellen Spielplan keine Spiele vorhanden.
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-else
|
||||
v-else-if="activePanelTab === 'matches'"
|
||||
class="overflow-x-auto"
|
||||
>
|
||||
<table class="min-w-full divide-y divide-gray-200">
|
||||
@@ -170,6 +194,89 @@
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="activePanelTab === 'table' && isTableLoading"
|
||||
class="p-6 text-sm text-gray-600"
|
||||
>
|
||||
Tabelle wird geladen...
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-else-if="activePanelTab === 'table' && tableError"
|
||||
class="p-6 text-sm text-red-600"
|
||||
>
|
||||
{{ tableError }}
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-else-if="activePanelTab === 'table' && teamTableRows.length === 0"
|
||||
class="p-6 text-sm text-gray-600"
|
||||
>
|
||||
Für diese Mannschaft ist aktuell keine Tabelle hinterlegt.
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-else-if="activePanelTab === 'table'"
|
||||
class="overflow-x-auto"
|
||||
>
|
||||
<table class="min-w-full divide-y divide-gray-200">
|
||||
<thead class="bg-gray-50">
|
||||
<tr>
|
||||
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Platz
|
||||
</th>
|
||||
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Mannschaft
|
||||
</th>
|
||||
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Spiele
|
||||
</th>
|
||||
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
S/U/N
|
||||
</th>
|
||||
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Sätze
|
||||
</th>
|
||||
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Bälle
|
||||
</th>
|
||||
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Punkte
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white divide-y divide-gray-200">
|
||||
<tr
|
||||
v-for="(row, index) in teamTableRows"
|
||||
:key="`${row.team_id || row.team_name || 'row'}-${index}`"
|
||||
:class="isCurrentTeamRow(row) ? 'bg-primary-50' : 'bg-white'"
|
||||
>
|
||||
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900">
|
||||
{{ row.table_rank ?? '-' }}
|
||||
</td>
|
||||
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900 font-medium">
|
||||
{{ row.team_name || '-' }}
|
||||
</td>
|
||||
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900">
|
||||
{{ row.meetings_count ?? '-' }}
|
||||
</td>
|
||||
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900">
|
||||
{{ formatSun(row) }}
|
||||
</td>
|
||||
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900">
|
||||
{{ row.sets_relation || '-' }}
|
||||
</td>
|
||||
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900">
|
||||
{{ row.games_relation || '-' }}
|
||||
</td>
|
||||
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900">
|
||||
{{ formatPunkte(row) }}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Links -->
|
||||
@@ -242,6 +349,15 @@ 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 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})$/)
|
||||
@@ -317,13 +433,52 @@ 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 (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
|
||||
: []
|
||||
} 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'],
|
||||
@@ -495,6 +650,27 @@ 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 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()
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user