Entfernt die PDF-Datei 9_code_list_1759357969975.pdf und implementiert eine Sidebar-Toggle-Funktionalität in App.vue. Die Sidebar kann nun auf mobilen Geräten ein- und ausgeklappt werden, um die Benutzeroberfläche zu optimieren. Zudem wurden Titelattribute zu Navigationslinks hinzugefügt, um die Benutzerfreundlichkeit zu verbessern. Der Vuex-Store wurde aktualisiert, um den Zustand der Sidebar zu speichern und zu verwalten.

This commit is contained in:
Torsten Schulz (local)
2025-10-08 10:52:07 +02:00
parent db9e404372
commit dc0eff4e4c
3 changed files with 110 additions and 16 deletions

View File

@@ -10,7 +10,11 @@
</header>
<div class="app-container">
<aside v-if="isAuthenticated" class="sidebar">
<aside v-if="isAuthenticated" class="sidebar" :class="{ 'sidebar-collapsed': sidebarCollapsed }">
<button class="sidebar-toggle" @click="toggleSidebar">
<span v-if="sidebarCollapsed"></span>
<span v-else></span>
</button>
<div class="sidebar-content">
<div class="club-selector card">
<h3 class="card-title">Verein auswählen</h3>
@@ -29,19 +33,19 @@
<nav v-if="selectedClub" class="nav-menu">
<div class="nav-section">
<h4 class="nav-title">Verwaltung</h4>
<a href="/members" class="nav-link">
<a href="/members" class="nav-link" title="Mitglieder">
<span class="nav-icon">👥</span>
Mitglieder
</a>
<a href="/diary" class="nav-link">
<a href="/diary" class="nav-link" title="Tagebuch">
<span class="nav-icon">📝</span>
Tagebuch
</a>
<a href="/pending-approvals" class="nav-link">
<a href="/pending-approvals" class="nav-link" title="Freigaben">
<span class="nav-icon"></span>
Freigaben
</a>
<a href="/training-stats" class="nav-link">
<a href="/training-stats" class="nav-link" title="Trainings-Statistik">
<span class="nav-icon">📊</span>
Trainings-Statistik
</a>
@@ -49,23 +53,23 @@
<div class="nav-section">
<h4 class="nav-title">Organisation</h4>
<a href="/schedule" class="nav-link">
<a href="/schedule" class="nav-link" title="Spielpläne">
<span class="nav-icon">📅</span>
Spielpläne
</a>
<a href="/tournaments" class="nav-link">
<a href="/tournaments" class="nav-link" title="Interne Turniere">
<span class="nav-icon">🏆</span>
Interne Turniere
</a>
<a href="/official-tournaments" class="nav-link">
<a href="/official-tournaments" class="nav-link" title="Offizielle Turniere">
<span class="nav-icon">📄</span>
Offizielle Turniere
</a>
<a href="/predefined-activities" class="nav-link">
<a href="/predefined-activities" class="nav-link" title="Vordefinierte Aktivitäten">
<span class="nav-icon"></span>
Vordefinierte Aktivitäten
</a>
<a href="/team-management" class="nav-link">
<a href="/team-management" class="nav-link" title="Team-Verwaltung">
<span class="nav-icon">👥</span>
Team-Verwaltung
</a>
@@ -76,7 +80,7 @@
<nav class="sidebar-footer">
<div class="nav-section">
<h4 class="nav-title">Einstellungen</h4>
<a href="/mytischtennis-account" class="nav-link">
<a href="/mytischtennis-account" class="nav-link" title="myTischtennis-Account">
<span class="nav-icon">🔗</span>
myTischtennis-Account
</a>
@@ -84,7 +88,7 @@
</nav>
<div class="sidebar-footer">
<button @click="logout()" class="btn-secondary logout-btn">
<button @click="logout()" class="btn-secondary logout-btn" title="Ausloggen">
<span class="nav-icon">🚪</span>
Ausloggen
</button>
@@ -137,7 +141,7 @@ export default {
};
},
computed: {
...mapGetters(['isAuthenticated', 'currentClub', 'clubs']),
...mapGetters(['isAuthenticated', 'currentClub', 'clubs', 'sidebarCollapsed']),
},
watch: {
selectedClub(newVal) {
@@ -166,7 +170,7 @@ export default {
},
},
methods: {
...mapActions(['setCurrentClub', 'setClubs', 'logout']),
...mapActions(['setCurrentClub', 'setClubs', 'logout', 'toggleSidebar']),
async loadUserData() {
try {
@@ -530,14 +534,83 @@ export default {
}
}
/* Toggle-Button für Sidebar (nur auf mobil sichtbar) */
.sidebar-toggle {
display: none;
}
@media (max-width: 480px) {
.sidebar {
width: 100%;
position: fixed;
top: 3rem;
left: 0;
height: calc(100vh - 3rem);
z-index: 999;
transition: width 0.3s ease;
}
.sidebar.sidebar-collapsed {
width: 60px;
}
.sidebar:not(.sidebar-collapsed) {
width: 240px;
}
.sidebar-toggle {
display: block;
position: absolute;
top: 10px;
right: 10px;
background: var(--primary-color);
color: white;
border: none;
width: 32px;
height: 32px;
border-radius: 50%;
cursor: pointer;
z-index: 1000;
font-size: 16px;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
}
.sidebar-toggle:active {
transform: scale(0.95);
}
/* Im kollabierten Zustand: nur Icons zeigen */
.sidebar-collapsed .nav-link span:not(.nav-icon),
.sidebar-collapsed .nav-link text,
.sidebar-collapsed .nav-title,
.sidebar-collapsed .card-title,
.sidebar-collapsed .logout-btn span:not(.nav-icon),
.sidebar-collapsed .club-selector,
.sidebar-collapsed .select-group,
.sidebar-collapsed .btn-primary {
display: none !important;
}
/* Alle Text-Inhalte in nav-links verstecken */
.sidebar-collapsed .nav-link {
justify-content: center;
padding: 0.75rem 0.5rem;
font-size: 0;
}
.sidebar-collapsed .nav-icon {
font-size: 1.5rem;
margin: 0;
}
.sidebar-collapsed .logout-btn {
justify-content: center;
}
.sidebar-collapsed .sidebar-footer {
border-top: none;
}
.content {
@@ -557,8 +630,13 @@ export default {
}
.main-content {
margin-left: 0;
margin-left: 60px;
overflow-y: auto;
transition: margin-left 0.3s ease;
}
.sidebar:not(.sidebar-collapsed) ~ .main-content {
margin-left: 240px;
}
}

View File

@@ -16,6 +16,14 @@ const store = createStore({
})(),
dialogs: [], // Array von offenen Dialogen
dialogCounter: 0, // Zähler für eindeutige Dialog-IDs
sidebarCollapsed: (() => {
const savedState = localStorage.getItem('sidebarCollapsed');
if (savedState !== null) {
return savedState === 'true';
}
// Standardmäßig kollabiert auf mobilen Geräten
return window.innerWidth <= 480;
})(),
},
mutations: {
setToken(state, token) {
@@ -36,6 +44,10 @@ const store = createStore({
state.clubs = clubs;
localStorage.setItem('clubs', JSON.stringify(clubs));
},
setSidebarCollapsed(state, collapsed) {
state.sidebarCollapsed = collapsed;
localStorage.setItem('sidebarCollapsed', collapsed.toString());
},
clearToken(state) {
state.token = null;
localStorage.removeItem('token');
@@ -103,6 +115,9 @@ const store = createStore({
setClubs({ commit }, clubs) {
commit('setClubsMutation', clubs);
},
toggleSidebar({ commit, state }) {
commit('setSidebarCollapsed', !state.sidebarCollapsed);
},
// Dialog-Actions
openDialog({ commit }, dialog) {
commit('openDialog', dialog);
@@ -126,6 +141,7 @@ const store = createStore({
username: state => state.username,
currentClub: state => state.currentClub,
clubs: state => state.clubs,
sidebarCollapsed: state => state.sidebarCollapsed,
currentClubName: state => {
const club = state.clubs.find(club => club.id === parseInt(state.currentClub));
return club ? club.name : '';