1028 lines
44 KiB
Vue
Executable File
1028 lines
44 KiB
Vue
Executable File
<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>
|