Files
harheimertc/components/Navigation.vue
Torsten Schulz (local) 82dcb1da40
All checks were successful
Code Analysis and Production Deploy / analyze (push) Successful in 10m21s
Code Analysis and Production Deploy / deploy-production (push) Has been skipped
Code Analysis and Production Deploy / deploy-test (push) Successful in 3m4s
Update Navigation component styles and bump version to 1.8.7
2026-08-26 11:54:48 +02:00

1028 lines
44 KiB
Vue
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<nav
class="fixed top-0 left-0 right-0 z-50 bg-gradient-to-r from-gray-900 via-primary-900 to-gray-900 shadow-xl h-20"
>
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 h-full">
<div class="flex flex-col justify-between h-full py-2">
<!-- Hauptmenü -->
<div class="flex min-w-0 items-center gap-3">
<!-- Logo -->
<NuxtLink
to="/"
class="flex shrink-0 items-center space-x-3 origin-left hover:scale-[1.02] transition-transform"
>
<img
src="~/assets/images/logos/Harheimer TC.svg"
alt="Harheimer TC Logo"
class="w-12 h-12"
>
<div class="hidden sm:block">
<span class="text-xl font-display font-bold text-white">Harheimer <span
class="text-primary-400"
>TC</span></span>
</div>
</NuxtLink>
<div class="flex min-w-0 flex-1 flex-col">
<!-- Desktop Navigation -->
<div
class="hidden min-w-0 max-w-full lg:flex lg:items-center lg:gap-1 lg:overflow-x-auto lg:whitespace-nowrap [scrollbar-color:theme(colors.primary.700)_transparent] [scrollbar-width:thin]"
aria-label="Hauptmenü horizontal scrollbar"
>
<NuxtLink
to="/"
class="px-4 py-2 text-gray-300 hover:text-white font-medium transition-all rounded-lg hover:bg-primary-700/50"
active-class="text-white bg-primary-600"
>
Start
</NuxtLink>
<button
class="px-4 py-2 text-gray-300 hover:text-white font-medium transition-all rounded-lg hover:bg-primary-700/50"
:class="(route.path.startsWith('/verein/') || route.path.startsWith('/vorstand') || route.path.startsWith('/vereinsmeisterschaften') || route.path.startsWith('/links') || currentSubmenu === 'verein') ? 'text-white bg-primary-600' : ''"
@click="toggleSubmenu('verein')"
>
Verein
</button>
<button
class="px-4 py-2 text-gray-300 hover:text-white font-medium transition-all rounded-lg hover:bg-primary-700/50"
:class="(route.path.startsWith('/mannschaften') || route.path.startsWith('/spielsysteme') || currentSubmenu === 'mannschaften') ? 'text-white bg-primary-600' : ''"
@click="toggleSubmenu('mannschaften')"
>
Mannschaften
</button>
<button
class="px-4 py-2 text-gray-300 hover:text-white font-medium transition-all rounded-lg hover:bg-primary-700/50"
:class="(route.path.startsWith('/training') || route.path.startsWith('/tt-regeln') || currentSubmenu === 'training') ? 'text-white bg-primary-600' : ''"
@click="toggleSubmenu('training')"
>
Training
</button>
<NuxtLink
to="/mitgliedschaft"
class="px-4 py-2 text-gray-300 hover:text-white font-medium transition-all rounded-lg hover:bg-primary-700/50"
active-class="text-white bg-primary-600"
>
Mitgliedschaft
</NuxtLink>
<NuxtLink
to="/termine"
class="px-4 py-2 text-gray-300 hover:text-white font-medium transition-all rounded-lg hover:bg-primary-700/50"
active-class="text-white bg-primary-600"
@click="currentSubmenu = null"
>
Termine
</NuxtLink>
<NuxtLink
v-if="hasGalleryImages || canAccessNewsletter"
to="/verein/galerie"
class="px-4 py-2 text-gray-300 hover:text-white font-medium transition-all rounded-lg hover:bg-primary-700/50"
active-class="text-white bg-primary-600"
@click="currentSubmenu = null"
>
Galerie
</NuxtLink>
<button
class="px-4 py-2 text-gray-300 hover:text-white font-medium transition-all rounded-lg hover:bg-primary-700/50"
:class="(route.path.startsWith('/newsletter') || currentSubmenu === 'newsletter') ? 'text-white bg-primary-600' : ''"
@click="toggleSubmenu('newsletter')"
>
Newsletter
</button>
<button
v-if="isLoggedIn"
class="px-4 py-2 text-gray-300 hover:text-white font-medium transition-all rounded-lg hover:bg-primary-700/50"
:class="(route.path.startsWith('/mitgliederbereich') || route.path.startsWith('/cms') || currentSubmenu === 'intern') ? 'text-white bg-primary-600' : ''"
@click="toggleSubmenu('intern')"
>
Intern
</button>
<NuxtLink
to="/kontakt"
class="px-4 py-2 bg-primary-600 hover:bg-primary-700 text-white font-semibold transition-all rounded-lg shadow-lg"
@click="currentSubmenu = null"
>
Kontakt
</NuxtLink>
</div>
<div class="hidden min-w-0 flex-1 lg:flex items-center h-6 border-t border-primary-700/20">
<div
v-if="currentSubmenu"
:class="currentSubmenu === 'mannschaften' ? 'relative min-w-0 flex-1' : 'flex min-w-0 items-center gap-1 overflow-x-auto whitespace-nowrap'"
>
<!-- Newsletter Submenu -->
<template v-if="currentSubmenu === 'newsletter'">
<NuxtLink
to="/newsletter/subscribe"
class="px-2.5 py-1 text-xs text-gray-300 hover:text-white hover:bg-primary-700/50 rounded transition-all"
active-class="text-white bg-primary-600"
>
Abonnieren
</NuxtLink>
<NuxtLink
to="/newsletter/unsubscribe"
class="px-2.5 py-1 text-xs text-gray-300 hover:text-white hover:bg-primary-700/50 rounded transition-all"
active-class="text-white bg-primary-600"
>
Abmelden
</NuxtLink>
</template>
<!-- Verein Submenu -->
<template v-if="currentSubmenu === 'verein'">
<NuxtLink
to="/verein/ueber-uns"
class="px-2.5 py-1 text-xs text-gray-300 hover:text-white hover:bg-primary-700/50 rounded transition-all"
active-class="text-white bg-primary-600"
>
Über uns
</NuxtLink>
<NuxtLink
to="/vorstand"
class="px-2.5 py-1 text-xs text-gray-300 hover:text-white hover:bg-primary-700/50 rounded transition-all"
active-class="text-white bg-primary-600"
>
Vorstand
</NuxtLink>
<NuxtLink
to="/verein/geschichte"
class="px-2.5 py-1 text-xs text-gray-300 hover:text-white hover:bg-primary-700/50 rounded transition-all"
active-class="text-white bg-primary-600"
>
Geschichte
</NuxtLink>
<NuxtLink
to="/verein/satzung"
class="px-2.5 py-1 text-xs text-gray-300 hover:text-white hover:bg-primary-700/50 rounded transition-all"
active-class="text-white bg-primary-600"
>
Satzung
</NuxtLink>
<NuxtLink
to="/vereinsmeisterschaften"
class="px-2.5 py-1 text-xs text-gray-300 hover:text-white hover:bg-primary-700/50 rounded transition-all"
active-class="text-white bg-primary-600"
>
Vereinsmeisterschaften
</NuxtLink>
<NuxtLink
to="/verein/galerie"
class="px-2.5 py-1 text-xs text-gray-300 hover:text-white hover:bg-primary-700/50 rounded transition-all"
active-class="text-white bg-primary-600"
>
Galerie
</NuxtLink>
<NuxtLink
to="/links"
class="px-2.5 py-1 text-xs text-gray-300 hover:text-white hover:bg-primary-700/50 rounded transition-all"
active-class="text-white bg-primary-600"
>
Links
</NuxtLink>
</template>
<!-- Mannschaften Submenu -->
<template v-if="currentSubmenu === 'mannschaften'">
<div
class="flex min-w-0 items-center gap-1 overflow-x-auto whitespace-nowrap scroll-smooth pr-7 [scrollbar-color:theme(colors.primary.700)_transparent] [scrollbar-width:thin]"
tabindex="0"
aria-label="Mannschaften-Untermenü horizontal scrollbar"
>
<NuxtLink
to="/mannschaften"
class="shrink-0 px-2.5 py-1 text-xs font-semibold text-white hover:bg-primary-700/50 rounded transition-all"
active-class="bg-primary-600"
>
Übersicht
</NuxtLink>
<div class="h-3 w-px shrink-0 bg-primary-700" />
<template
v-for="mannschaft in mannschaften"
:key="mannschaft.slug"
>
<NuxtLink
:to="`/mannschaften/${mannschaft.slug}`"
class="shrink-0 whitespace-nowrap px-2.5 py-1 text-xs text-gray-300 hover:text-white hover:bg-primary-700/50 rounded transition-all"
active-class="text-white bg-primary-600"
>
{{ mannschaft.mannschaft }}
</NuxtLink>
</template>
<div class="h-3 w-px shrink-0 bg-primary-700" />
<NuxtLink
to="/mannschaften/spielplaene"
class="shrink-0 whitespace-nowrap px-2.5 py-1 text-xs text-gray-300 hover:text-white hover:bg-primary-700/50 rounded transition-all"
active-class="text-white bg-primary-600"
>
Spielpläne
</NuxtLink>
<NuxtLink
to="/spielsysteme"
class="shrink-0 whitespace-nowrap px-2.5 py-1 text-xs text-gray-300 hover:text-white hover:bg-primary-700/50 rounded transition-all"
active-class="text-white bg-primary-600"
>
Spielsysteme
</NuxtLink>
</div>
<span
aria-hidden="true"
class="pointer-events-none absolute inset-y-0 right-0 flex w-9 items-center justify-end bg-gradient-to-l from-primary-900 via-primary-900/90 to-transparent text-primary-300"
>
</span>
</template>
<!-- Training Submenu -->
<template v-if="currentSubmenu === 'training'">
<NuxtLink
to="/training"
class="px-2.5 py-1 text-xs text-gray-300 hover:text-white hover:bg-primary-700/50 rounded transition-all"
active-class="text-white bg-primary-600"
>
Trainingszeiten
</NuxtLink>
<NuxtLink
to="/training/trainer"
class="px-2.5 py-1 text-xs text-gray-300 hover:text-white hover:bg-primary-700/50 rounded transition-all"
active-class="text-white bg-primary-600"
>
Trainer
</NuxtLink>
<NuxtLink
to="/training/anfaenger"
class="px-2.5 py-1 text-xs text-gray-300 hover:text-white hover:bg-primary-700/50 rounded transition-all"
active-class="text-white bg-primary-600"
>
Anfänger
</NuxtLink>
<NuxtLink
to="/tt-regeln"
class="px-2.5 py-1 text-xs text-gray-300 hover:text-white hover:bg-primary-700/50 rounded transition-all"
active-class="text-white bg-primary-600"
>
TT-Regeln
</NuxtLink>
</template>
<!-- Intern Submenu -->
<template v-if="currentSubmenu === 'intern'">
<NuxtLink
to="/mitgliederbereich"
class="px-2.5 py-1 text-xs font-semibold text-white hover:bg-primary-700/50 rounded transition-all"
active-class="bg-primary-600"
>
Übersicht
</NuxtLink>
<div class="h-3 w-px bg-primary-700" />
<NuxtLink
to="/mitgliederbereich/mitglieder"
class="px-2.5 py-1 text-xs text-gray-300 hover:text-white hover:bg-primary-700/50 rounded transition-all"
active-class="text-white bg-primary-600"
>
Mitgliederliste
</NuxtLink>
<NuxtLink
to="/mitgliederbereich/qttr"
class="px-2.5 py-1 text-xs text-gray-300 hover:text-white hover:bg-primary-700/50 rounded transition-all"
active-class="text-white bg-primary-600"
>
QTTR
</NuxtLink>
<NuxtLink
to="/mitgliederbereich/news"
class="px-2.5 py-1 text-xs text-gray-300 hover:text-white hover:bg-primary-700/50 rounded transition-all"
active-class="text-white bg-primary-600"
>
News
</NuxtLink>
<NuxtLink
to="/mitgliederbereich/profil"
class="px-2.5 py-1 text-xs text-gray-300 hover:text-white hover:bg-primary-700/50 rounded transition-all"
active-class="text-white bg-primary-600"
>
Mein Profil
</NuxtLink>
<div class="h-3 w-px bg-primary-700" />
<NuxtLink
to="/mitgliederbereich/api"
class="px-2.5 py-1 text-xs text-gray-300 hover:text-white hover:bg-primary-700/50 rounded transition-all"
active-class="text-white bg-primary-600"
>
API-Dokumentation
</NuxtLink>
<template v-if="canAccessNewsletter">
<div class="h-3 w-px bg-primary-700" />
<NuxtLink
to="/cms/newsletter"
class="px-2.5 py-1 text-xs text-gray-300 hover:text-white hover:bg-primary-700/50 rounded transition-all"
active-class="text-white bg-primary-600"
>
Newsletter
</NuxtLink>
</template>
<template v-if="canAccessContactRequests">
<div class="h-3 w-px bg-primary-700" />
<NuxtLink
to="/cms/kontaktanfragen"
class="px-2.5 py-1 text-xs text-gray-300 hover:text-white hover:bg-primary-700/50 rounded transition-all"
active-class="text-white bg-primary-600"
>
Kontaktanfragen
</NuxtLink>
</template>
<template v-if="isAdmin">
<div class="h-3 w-px bg-primary-700" />
<div class="relative inline-block">
<button
class="px-2.5 py-1 text-xs text-yellow-300 hover:text-white hover:bg-primary-700/50 rounded transition-all flex items-center"
:class="route.path.startsWith('/cms') ? 'text-white bg-primary-600' : ''"
@click.stop="toggleCmsDropdown"
>
CMS
<ChevronDown
:size="12"
class="ml-1"
:class="['transition-transform', showCmsDropdown ? 'rotate-180' : '']"
/>
</button>
<!-- CMS Dropdown -->
<div
v-if="showCmsDropdown"
class="absolute left-0 top-full mt-1 w-48 bg-gray-800 border border-gray-700 rounded-lg shadow-xl overflow-hidden z-50"
>
<NuxtLink
to="/cms"
class="block px-4 py-2 text-sm text-gray-300 hover:bg-primary-600 hover:text-white transition-colors"
@click="showCmsDropdown = false"
>
Übersicht
</NuxtLink>
<NuxtLink
to="/cms/startseite"
class="block px-4 py-2 text-sm text-gray-300 hover:bg-primary-600 hover:text-white transition-colors"
@click="showCmsDropdown = false"
>
Startseite
</NuxtLink>
<div class="border-t border-gray-700 my-1" />
<NuxtLink
to="/cms/inhalte"
class="block px-4 py-2 text-sm text-gray-300 hover:bg-primary-600 hover:text-white transition-colors"
@click="showCmsDropdown = false"
>
Inhalte
</NuxtLink>
<NuxtLink
to="/cms/vereinsmeisterschaften"
class="block px-4 py-2 text-sm text-gray-300 hover:bg-primary-600 hover:text-white transition-colors"
@click="showCmsDropdown = false"
>
Vereinsmeisterschaften
</NuxtLink>
<div class="border-t border-gray-700 my-1" />
<NuxtLink
to="/mitgliederbereich/news"
class="block px-4 py-2 text-sm text-gray-300 hover:bg-primary-600 hover:text-white transition-colors"
@click="showCmsDropdown = false"
>
News
</NuxtLink>
<NuxtLink
to="/cms/sportbetrieb"
class="block px-4 py-2 text-sm text-gray-300 hover:bg-primary-600 hover:text-white transition-colors"
@click="showCmsDropdown = false"
>
Sportbetrieb
</NuxtLink>
<NuxtLink
to="/cms/mitgliederverwaltung"
class="block px-4 py-2 text-sm text-gray-300 hover:bg-primary-600 hover:text-white transition-colors"
@click="showCmsDropdown = false"
>
Mitgliederverwaltung
</NuxtLink>
<NuxtLink
to="/cms/kontaktanfragen"
class="block px-4 py-2 text-sm text-gray-300 hover:bg-primary-600 hover:text-white transition-colors"
@click="showCmsDropdown = false"
>
Kontaktanfragen
</NuxtLink>
<div class="border-t border-gray-700 my-1" />
<NuxtLink
to="/cms/einstellungen"
class="block px-4 py-2 text-sm text-gray-300 hover:bg-primary-600 hover:text-white transition-colors"
@click="showCmsDropdown = false"
>
Einstellungen
</NuxtLink>
<NuxtLink
to="/cms/benutzer"
class="block px-4 py-2 text-sm text-gray-300 hover:bg-primary-600 hover:text-white transition-colors"
@click="showCmsDropdown = false"
>
Benutzerverwaltung
</NuxtLink>
</div>
</div>
</template>
</template>
</div>
</div>
<!-- Mobile Menu Button -->
<button
class="lg:hidden p-2 rounded-lg hover:bg-primary-700/50 transition-colors"
aria-label="Toggle menu"
@click="isMobileMenuOpen = !isMobileMenuOpen"
>
<X
v-if="isMobileMenuOpen"
:size="24"
class="text-white"
/>
<Menu
v-else
:size="24"
class="text-white"
/>
</button>
</div>
<!-- Untermenü (Desktop) - im gleichen Block, immer gleiche Höhe -->
</div>
</div>
</div>
<!-- Mobile Menu -->
<Transition
enter-active-class="transition duration-200 ease-out"
enter-from-class="opacity-0 transform -translate-y-2"
enter-to-class="opacity-100 transform translate-y-0"
leave-active-class="transition duration-150 ease-in"
leave-from-class="opacity-100 transform translate-y-0"
leave-to-class="opacity-0 transform -translate-y-2"
>
<div
v-if="isMobileMenuOpen"
class="lg:hidden bg-gray-800 border-t border-primary-700/30 max-h-[80vh] overflow-y-auto"
>
<div class="px-4 py-4 space-y-2">
<NuxtLink
to="/"
class="block px-4 py-3 text-gray-300 hover:text-white hover:bg-primary-700/50 rounded-lg font-medium transition-colors"
@click="isMobileMenuOpen = false"
>
Start
</NuxtLink>
<!-- Verein Mobile -->
<div>
<button
class="w-full flex items-center justify-between px-4 py-3 text-gray-300 hover:text-white hover:bg-primary-700/50 rounded-lg font-medium transition-colors"
@click="toggleMobileSubmenu('verein')"
>
Verein
<ChevronDown
:size="16"
:class="['transition-transform', mobileSubmenu === 'verein' ? 'rotate-180' : '']"
/>
</button>
<div
v-if="mobileSubmenu === 'verein'"
class="pl-4 space-y-1 mt-1 bg-primary-900/30 rounded-lg p-2"
>
<NuxtLink
to="/verein/ueber-uns"
class="block px-4 py-2 text-sm text-gray-400 hover:text-white hover:bg-primary-700/50 rounded-lg transition-colors"
@click="isMobileMenuOpen = false"
>
Über uns
</NuxtLink>
<NuxtLink
to="/vorstand"
class="block px-4 py-2 text-sm text-gray-400 hover:text-white hover:bg-primary-700/50 rounded-lg transition-colors"
@click="isMobileMenuOpen = false"
>
Vorstand
</NuxtLink>
<NuxtLink
to="/verein/geschichte"
class="block px-4 py-2 text-sm text-gray-400 hover:text-white hover:bg-primary-700/50 rounded-lg transition-colors"
@click="isMobileMenuOpen = false"
>
Geschichte
</NuxtLink>
<NuxtLink
to="/verein/satzung"
class="block px-4 py-2 text-sm text-gray-400 hover:text-white hover:bg-primary-700/50 rounded-lg transition-colors"
@click="isMobileMenuOpen = false"
>
Satzung
</NuxtLink>
<NuxtLink
to="/vereinsmeisterschaften"
class="block px-4 py-2 text-sm text-gray-400 hover:text-white hover:bg-primary-700/50 rounded-lg transition-colors"
@click="isMobileMenuOpen = false"
>
Vereinsmeisterschaften
</NuxtLink>
<NuxtLink
to="/verein/galerie"
class="block px-4 py-2 text-sm text-gray-400 hover:text-white hover:bg-primary-700/50 rounded-lg transition-colors"
@click="isMobileMenuOpen = false"
>
Galerie
</NuxtLink>
<NuxtLink
to="/links"
class="block px-4 py-2 text-sm text-gray-400 hover:text-white hover:bg-primary-700/50 rounded-lg transition-colors"
@click="isMobileMenuOpen = false"
>
Links
</NuxtLink>
<NuxtLink
to="/newsletter/subscribe"
class="block px-4 py-2 text-sm text-gray-400 hover:text-white hover:bg-primary-700/50 rounded-lg transition-colors"
@click="isMobileMenuOpen = false"
>
Newsletter abonnieren
</NuxtLink>
<NuxtLink
to="/newsletter/unsubscribe"
class="block px-4 py-2 text-sm text-gray-400 hover:text-white hover:bg-primary-700/50 rounded-lg transition-colors"
@click="isMobileMenuOpen = false"
>
Newsletter abmelden
</NuxtLink>
</div>
</div>
<!-- Mannschaften Mobile -->
<div>
<button
class="w-full flex items-center justify-between px-4 py-3 text-gray-300 hover:text-white hover:bg-primary-700/50 rounded-lg font-medium transition-colors"
@click="toggleMobileSubmenu('mannschaften')"
>
Mannschaften
<ChevronDown
:size="16"
:class="['transition-transform', mobileSubmenu === 'mannschaften' ? 'rotate-180' : '']"
/>
</button>
<div
v-if="mobileSubmenu === 'mannschaften'"
class="pl-4 space-y-1 mt-1 bg-primary-900/30 rounded-lg p-2"
>
<NuxtLink
to="/mannschaften"
class="block px-4 py-2 text-sm font-semibold text-gray-300 hover:text-white hover:bg-primary-700/50 rounded-lg transition-colors"
@click="isMobileMenuOpen = false"
>
Übersicht
</NuxtLink>
<template
v-for="mannschaft in mannschaften"
:key="mannschaft.slug"
>
<NuxtLink
:to="`/mannschaften/${mannschaft.slug}`"
class="block px-4 py-2 text-sm text-gray-400 hover:text-white hover:bg-primary-700/50 rounded-lg transition-colors"
@click="isMobileMenuOpen = false"
>
{{ mannschaft.mannschaft }}
</NuxtLink>
</template>
<div class="border-t border-primary-700/20 my-2" />
<NuxtLink
to="/mannschaften/spielplaene"
class="block px-4 py-2 text-sm text-gray-400 hover:text-white hover:bg-primary-700/50 rounded-lg transition-colors"
@click="isMobileMenuOpen = false"
>
Spielpläne
</NuxtLink>
<NuxtLink
to="/spielsysteme"
class="block px-4 py-2 text-sm text-gray-400 hover:text-white hover:bg-primary-700/50 rounded-lg transition-colors"
@click="isMobileMenuOpen = false"
>
Spielsysteme
</NuxtLink>
</div>
</div>
<!-- Training Mobile -->
<div>
<button
class="w-full flex items-center justify-between px-4 py-3 text-gray-300 hover:text-white hover:bg-primary-700/50 rounded-lg font-medium transition-colors"
@click="toggleMobileSubmenu('training')"
>
Training
<ChevronDown
:size="16"
:class="['transition-transform', mobileSubmenu === 'training' ? 'rotate-180' : '']"
/>
</button>
<div
v-if="mobileSubmenu === 'training'"
class="pl-4 space-y-1 mt-1 bg-primary-900/30 rounded-lg p-2"
>
<NuxtLink
to="/training"
class="block px-4 py-2 text-sm text-gray-400 hover:text-white hover:bg-primary-700/50 rounded-lg transition-colors"
@click="isMobileMenuOpen = false"
>
Trainingszeiten
</NuxtLink>
<NuxtLink
to="/training/trainer"
class="block px-4 py-2 text-sm text-gray-400 hover:text-white hover:bg-primary-700/50 rounded-lg transition-colors"
@click="isMobileMenuOpen = false"
>
Trainer
</NuxtLink>
<NuxtLink
to="/training/anfaenger"
class="block px-4 py-2 text-sm text-gray-400 hover:text-white hover:bg-primary-700/50 rounded-lg transition-colors"
@click="isMobileMenuOpen = false"
>
Anfänger
</NuxtLink>
<NuxtLink
to="/tt-regeln"
class="block px-4 py-2 text-sm text-gray-400 hover:text-white hover:bg-primary-700/50 rounded-lg transition-colors"
@click="isMobileMenuOpen = false"
>
TT-Regeln
</NuxtLink>
</div>
</div>
<NuxtLink
to="/mitgliedschaft"
class="block px-4 py-3 text-gray-300 hover:text-white hover:bg-primary-700/50 rounded-lg font-medium transition-colors"
@click="isMobileMenuOpen = false"
>
Mitgliedschaft
</NuxtLink>
<NuxtLink
to="/termine"
class="block px-4 py-3 text-gray-300 hover:text-white hover:bg-primary-700/50 rounded-lg font-medium transition-colors"
@click="isMobileMenuOpen = false"
>
Termine
</NuxtLink>
<NuxtLink
v-if="hasGalleryImages || canAccessNewsletter"
to="/verein/galerie"
class="block px-4 py-3 text-gray-300 hover:text-white hover:bg-primary-700/50 rounded-lg font-medium transition-colors"
@click="isMobileMenuOpen = false"
>
Galerie
</NuxtLink>
<NuxtLink
to="/newsletter/subscribe"
class="block px-4 py-3 text-gray-300 hover:text-white hover:bg-primary-700/50 rounded-lg font-medium transition-colors"
@click="isMobileMenuOpen = false"
>
Newsletter abonnieren
</NuxtLink>
<NuxtLink
to="/newsletter/unsubscribe"
class="block px-4 py-3 text-gray-300 hover:text-white hover:bg-primary-700/50 rounded-lg font-medium transition-colors"
@click="isMobileMenuOpen = false"
>
Newsletter abmelden
</NuxtLink>
<!-- Intern Mobile -->
<div v-if="isLoggedIn">
<button
class="w-full flex items-center justify-between px-4 py-3 text-gray-300 hover:text-white hover:bg-primary-700/50 rounded-lg font-medium transition-colors"
@click="toggleMobileSubmenu('intern')"
>
Intern
<ChevronDown
:size="16"
:class="['transition-transform', mobileSubmenu === 'intern' ? 'rotate-180' : '']"
/>
</button>
<div
v-if="mobileSubmenu === 'intern'"
class="pl-4 space-y-1 mt-1 bg-primary-900/30 rounded-lg p-2"
>
<NuxtLink
to="/mitgliederbereich"
class="block px-4 py-2 text-sm font-semibold text-gray-300 hover:text-white hover:bg-primary-700/50 rounded-lg transition-colors"
@click="isMobileMenuOpen = false"
>
Übersicht
</NuxtLink>
<NuxtLink
to="/mitgliederbereich/mitglieder"
class="block px-4 py-2 text-sm text-gray-400 hover:text-white hover:bg-primary-700/50 rounded-lg transition-colors"
@click="isMobileMenuOpen = false"
>
Mitgliederliste
</NuxtLink>
<NuxtLink
to="/mitgliederbereich/qttr"
class="block px-4 py-2 text-sm text-gray-400 hover:text-white hover:bg-primary-700/50 rounded-lg transition-colors"
@click="isMobileMenuOpen = false"
>
QTTR
</NuxtLink>
<NuxtLink
to="/mitgliederbereich/news"
class="block px-4 py-2 text-sm text-gray-400 hover:text-white hover:bg-primary-700/50 rounded-lg transition-colors"
@click="isMobileMenuOpen = false"
>
News
</NuxtLink>
<NuxtLink
to="/mitgliederbereich/profil"
class="block px-4 py-2 text-sm text-gray-400 hover:text-white hover:bg-primary-700/50 rounded-lg transition-colors"
@click="isMobileMenuOpen = false"
>
Mein Profil
</NuxtLink>
<template v-if="canAccessNewsletter">
<div class="border-t border-primary-700/20 my-2" />
<NuxtLink
to="/cms/newsletter"
class="block px-4 py-2 text-sm text-gray-400 hover:text-white hover:bg-primary-700/50 rounded-lg transition-colors"
@click="isMobileMenuOpen = false"
>
Newsletter
</NuxtLink>
</template>
<template v-if="canAccessContactRequests && !isAdmin">
<div class="border-t border-primary-700/20 my-2" />
<NuxtLink
to="/cms/kontaktanfragen"
class="block px-4 py-2 text-sm text-yellow-300 hover:text-white hover:bg-primary-700/50 rounded-lg transition-colors"
@click="isMobileMenuOpen = false"
>
Kontaktanfragen
</NuxtLink>
</template>
<template v-if="isAdmin">
<div class="border-t border-primary-700/20 my-2" />
<NuxtLink
to="/cms"
class="block px-4 py-2 text-sm font-semibold text-yellow-300 hover:text-white hover:bg-primary-700/50 rounded-lg transition-colors"
@click="isMobileMenuOpen = false"
>
CMS Übersicht
</NuxtLink>
<NuxtLink
to="/cms/startseite"
class="block px-4 py-2 text-sm text-yellow-300 hover:text-white hover:bg-primary-700/50 rounded-lg transition-colors"
@click="isMobileMenuOpen = false"
>
Startseite
</NuxtLink>
<NuxtLink
to="/mitgliederbereich/news"
class="block px-4 py-2 text-sm text-yellow-300 hover:text-white hover:bg-primary-700/50 rounded-lg transition-colors"
@click="isMobileMenuOpen = false"
>
News
</NuxtLink>
<NuxtLink
to="/cms/sportbetrieb"
class="block px-4 py-2 text-sm text-yellow-300 hover:text-white hover:bg-primary-700/50 rounded-lg transition-colors"
@click="isMobileMenuOpen = false"
>
Sportbetrieb
</NuxtLink>
<NuxtLink
to="/cms/mitgliederverwaltung"
class="block px-4 py-2 text-sm text-yellow-300 hover:text-white hover:bg-primary-700/50 rounded-lg transition-colors"
@click="isMobileMenuOpen = false"
>
Mitgliederverwaltung
</NuxtLink>
<NuxtLink
to="/cms/kontaktanfragen"
class="block px-4 py-2 text-sm text-yellow-300 hover:text-white hover:bg-primary-700/50 rounded-lg transition-colors"
@click="isMobileMenuOpen = false"
>
Kontaktanfragen
</NuxtLink>
<NuxtLink
to="/cms/inhalte"
class="block px-4 py-2 text-sm text-yellow-300 hover:text-white hover:bg-primary-700/50 rounded-lg transition-colors"
@click="isMobileMenuOpen = false"
>
Inhalte
</NuxtLink>
<NuxtLink
to="/cms/vereinsmeisterschaften"
class="block px-4 py-2 text-sm text-yellow-300 hover:text-white hover:bg-primary-700/50 rounded-lg transition-colors"
@click="isMobileMenuOpen = false"
>
Vereinsmeisterschaften
</NuxtLink>
<NuxtLink
to="/cms/einstellungen"
class="block px-4 py-2 text-sm text-yellow-300 hover:text-white hover:bg-primary-700/50 rounded-lg transition-colors"
@click="isMobileMenuOpen = false"
>
Einstellungen
</NuxtLink>
<NuxtLink
v-if="canManageUsers"
to="/cms/benutzer"
class="block px-4 py-2 text-sm text-yellow-300 hover:text-white hover:bg-primary-700/50 rounded-lg transition-colors"
@click="isMobileMenuOpen = false"
>
Benutzerverwaltung
</NuxtLink>
</template>
</div>
</div>
<NuxtLink
to="/kontakt"
class="block px-4 py-3 bg-primary-600 hover:bg-primary-700 text-white rounded-lg font-semibold transition-colors"
@click="isMobileMenuOpen = false"
>
Kontakt
</NuxtLink>
</div>
</div>
</Transition>
</nav>
</template>
<script setup>
import { ref, onMounted, onUnmounted, computed } from 'vue'
import { useRoute } from 'vue-router'
import { Menu, X, ChevronDown } from 'lucide-vue-next'
const route = useRoute()
const isMobileMenuOpen = ref(false)
const mobileSubmenu = ref(null)
const mannschaften = ref([])
const hasGalleryImages = ref(false)
const showCmsDropdown = ref(false)
const authStore = useAuthStore()
const isLoggedIn = computed(() => authStore.isLoggedIn)
const isAdmin = computed(() => authStore.isAdmin)
const canAccessNewsletter = computed(() => authStore.hasAnyRole('admin', 'vorstand', 'newsletter'))
const canAccessContactRequests = computed(() => authStore.hasAnyRole('admin', 'vorstand', 'trainer'))
const canManageUsers = computed(() => authStore.hasAnyRole('admin', 'vorstand'))
// Automatisches Setzen des Submenus basierend auf der Route
const currentSubmenu = computed(() => {
const path = route.path
if (path.startsWith('/verein/') || path.startsWith('/vorstand') ||
path.startsWith('/links') ||
path.startsWith('/vereinsmeisterschaften')) {
return 'verein'
}
if (path.startsWith('/mannschaften') || path.startsWith('/spielsysteme')) {
return 'mannschaften'
}
if (path.startsWith('/training') || path.startsWith('/tt-regeln')) {
return 'training'
}
if (path.startsWith('/mitgliederbereich') || path.startsWith('/cms')) {
return 'intern'
}
if (path.startsWith('/newsletter')) {
return 'newsletter'
}
return null
})
const toggleMobileSubmenu = (menu) => {
mobileSubmenu.value = mobileSubmenu.value === menu ? null : menu
}
const loadMannschaften = async () => {
try {
const attempt = async () => {
const url = `/api/mannschaften?_t=${Date.now()}`
const response = await fetch(url, { cache: 'no-store' })
if (!response.ok) return null
return await response.text()
}
let csv = null
try {
csv = await attempt()
} catch (_e) {
// 1 Retry: hilft bei kurzen Restarts/Proxy-Resets (Firefox: NS_ERROR_NET_PARTIAL_TRANSFER)
await new Promise(resolve => setTimeout(resolve, 150))
csv = await attempt()
}
if (!csv) return
const lines = csv.split('\n').filter(line => line.trim() !== '')
if (lines.length < 2) return
mannschaften.value = lines.slice(1).map(line => {
// Besserer CSV-Parser: Respektiert Anführungszeichen
const values = []
let current = ''
let inQuotes = false
for (let i = 0; i < line.length; i++) {
const char = line[i]
if (char === '"') {
inQuotes = !inQuotes
} else if (char === ',' && !inQuotes) {
values.push(current.trim())
current = ''
} else {
current += char
}
}
values.push(current.trim())
if (!values[0]) return null
return {
mannschaft: values[0].trim(),
slug: values[0].trim().toLowerCase().replace(/\s+/g, '-')
}
}).filter(mannschaft => mannschaft !== null)
} catch (error) {
console.error('Fehler beim Laden der Mannschaften:', error)
}
}
const checkGalleryImages = async () => {
try {
const response = await $fetch('/api/galerie')
hasGalleryImages.value = response && response.length > 0
} catch (error) {
console.error('Fehler beim Prüfen der Galerie-Bilder:', error)
hasGalleryImages.value = false
}
}
const toggleCmsDropdown = () => {
showCmsDropdown.value = !showCmsDropdown.value
}
const handleDocumentClick = (e) => {
if (!e.target.closest('.relative.inline-block')) {
showCmsDropdown.value = false
}
}
onMounted(() => {
loadMannschaften()
checkGalleryImages()
authStore.checkAuth()
// Close CMS dropdown when clicking outside
document.addEventListener('click', handleDocumentClick)
// Listen for global updates to mannschaften (e.g., CMS saved)
if (typeof window !== 'undefined') {
window.addEventListener('mannschaften:changed', loadMannschaften)
}
})
onUnmounted(() => {
document.removeEventListener('click', handleDocumentClick)
if (typeof window !== 'undefined') {
window.removeEventListener('mannschaften:changed', loadMannschaften)
}
})
const toggleSubmenu = (menu) => {
// Wenn wir schon im richtigen Bereich sind, nichts tun (Submenu bleibt offen)
// Wenn nicht, zur Hauptseite navigieren
const path = route.path
if (menu === 'newsletter' && !path.startsWith('/newsletter')) {
navigateTo('/newsletter/subscribe')
} else if (menu === 'verein' && !path.startsWith('/verein/') && !path.startsWith('/vorstand') && !path.startsWith('/vereinsmeisterschaften') && !path.startsWith('/links')) {
navigateTo('/verein/ueber-uns')
} else if (menu === 'mannschaften' && !path.startsWith('/mannschaften') && !path.startsWith('/spielsysteme')) {
navigateTo('/mannschaften')
} else if (menu === 'training' && !path.startsWith('/training') && !path.startsWith('/tt-regeln')) {
navigateTo('/training')
} else if (menu === 'intern' && !path.startsWith('/mitgliederbereich') && !path.startsWith('/cms')) {
navigateTo('/mitgliederbereich')
}
}
</script>