feat(mannschaften): add matches/table tabs on team detail pages
This commit is contained in:
@@ -76,40 +76,64 @@
|
|||||||
<!-- Aktueller Spielplan -->
|
<!-- Aktueller Spielplan -->
|
||||||
<div class="bg-white rounded-xl shadow-lg overflow-hidden">
|
<div class="bg-white rounded-xl shadow-lg overflow-hidden">
|
||||||
<div class="px-6 py-4 border-b border-gray-200">
|
<div class="px-6 py-4 border-b border-gray-200">
|
||||||
<h2 class="text-2xl font-semibold text-gray-900">
|
<div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
||||||
Aktueller Spielplan
|
<div>
|
||||||
</h2>
|
<h2 class="text-2xl font-semibold text-gray-900">
|
||||||
<p
|
Aktueller Spielplan
|
||||||
v-if="spielplanSeasonLabel"
|
</h2>
|
||||||
class="text-sm text-gray-600 mt-1"
|
<p
|
||||||
>
|
v-if="spielplanSeasonLabel"
|
||||||
Saison {{ spielplanSeasonLabel }}
|
class="text-sm text-gray-600 mt-1"
|
||||||
</p>
|
>
|
||||||
|
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>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
v-if="isSpielplanLoading"
|
v-if="activePanelTab === 'matches' && isSpielplanLoading"
|
||||||
class="p-6 text-sm text-gray-600"
|
class="p-6 text-sm text-gray-600"
|
||||||
>
|
>
|
||||||
Spielplan wird geladen...
|
Spielplan wird geladen...
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
v-else-if="spielplanError"
|
v-else-if="activePanelTab === 'matches' && spielplanError"
|
||||||
class="p-6 text-sm text-red-600"
|
class="p-6 text-sm text-red-600"
|
||||||
>
|
>
|
||||||
{{ spielplanError }}
|
{{ spielplanError }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
v-else-if="mannschaftSpielplan.length === 0"
|
v-else-if="activePanelTab === 'matches' && mannschaftSpielplan.length === 0"
|
||||||
class="p-6 text-sm text-gray-600"
|
class="p-6 text-sm text-gray-600"
|
||||||
>
|
>
|
||||||
Für diese Mannschaft sind im aktuellen Spielplan keine Spiele vorhanden.
|
Für diese Mannschaft sind im aktuellen Spielplan keine Spiele vorhanden.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
v-else
|
v-else-if="activePanelTab === 'matches'"
|
||||||
class="overflow-x-auto"
|
class="overflow-x-auto"
|
||||||
>
|
>
|
||||||
<table class="min-w-full divide-y divide-gray-200">
|
<table class="min-w-full divide-y divide-gray-200">
|
||||||
@@ -170,6 +194,89 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</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>
|
</div>
|
||||||
|
|
||||||
<!-- Links -->
|
<!-- Links -->
|
||||||
@@ -242,6 +349,15 @@ const mannschaftSpielplan = ref([])
|
|||||||
const spielplanSeason = ref('')
|
const spielplanSeason = ref('')
|
||||||
const isSpielplanLoading = ref(false)
|
const isSpielplanLoading = ref(false)
|
||||||
const spielplanError = ref('')
|
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 spielplanSeasonLabel = computed(() => {
|
||||||
const match = String(spielplanSeason.value || '').match(/^(\d{2})--(\d{2})$/)
|
const match = String(spielplanSeason.value || '').match(/^(\d{2})--(\d{2})$/)
|
||||||
@@ -317,13 +433,52 @@ const loadMannschaften = async () => {
|
|||||||
useHead({
|
useHead({
|
||||||
title: `${mannschaft.value.mannschaft} - Harheimer TC`,
|
title: `${mannschaft.value.mannschaft} - Harheimer TC`,
|
||||||
})
|
})
|
||||||
await loadSpielplan()
|
await Promise.all([loadSpielplan(), loadTeamTable()])
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Fehler beim Laden der Mannschaften:', 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 getTeamVariants = (cmsMannschaft) => {
|
||||||
const mannschaftMapping = {
|
const mannschaftMapping = {
|
||||||
'Erwachsene 1': ['harheimer tc'],
|
'Erwachsene 1': ['harheimer tc'],
|
||||||
@@ -495,6 +650,27 @@ const getRowClass = (row) => {
|
|||||||
return 'bg-white'
|
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(() => {
|
onMounted(() => {
|
||||||
loadMannschaften()
|
loadMannschaften()
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user