Implement toggle functionality for Mannschaftsspieler status in Mitgliederbereich. Add button for editing status and update local state upon toggling. Enhance API response handling to include isMannschaftsspieler attribute for user data retrieval.
This commit is contained in:
@@ -81,13 +81,30 @@
|
|||||||
<span v-else class="text-sm text-gray-400">Nur für Vorstand</span>
|
<span v-else class="text-sm text-gray-400">Nur für Vorstand</span>
|
||||||
</td>
|
</td>
|
||||||
<td class="px-4 py-3 whitespace-nowrap">
|
<td class="px-4 py-3 whitespace-nowrap">
|
||||||
<span
|
<button
|
||||||
v-if="member.isMannschaftsspieler"
|
v-if="canEdit"
|
||||||
class="px-2 py-1 bg-blue-100 text-blue-800 text-xs font-medium rounded-full"
|
@click="toggleMannschaftsspieler(member)"
|
||||||
|
:class="[
|
||||||
|
'px-2 py-1 text-xs font-medium rounded-full transition-colors',
|
||||||
|
member.isMannschaftsspieler
|
||||||
|
? 'bg-blue-100 text-blue-800 hover:bg-blue-200'
|
||||||
|
: 'bg-gray-100 text-gray-600 hover:bg-gray-200'
|
||||||
|
]"
|
||||||
|
title="Klicken zum Umschalten"
|
||||||
>
|
>
|
||||||
Ja
|
{{ member.isMannschaftsspieler ? 'Ja' : 'Nein' }}
|
||||||
|
</button>
|
||||||
|
<span
|
||||||
|
v-else
|
||||||
|
:class="[
|
||||||
|
'px-2 py-1 text-xs font-medium rounded-full',
|
||||||
|
member.isMannschaftsspieler
|
||||||
|
? 'bg-blue-100 text-blue-800'
|
||||||
|
: 'bg-gray-100 text-gray-600'
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
{{ member.isMannschaftsspieler ? 'Ja' : 'Nein' }}
|
||||||
</span>
|
</span>
|
||||||
<span v-else class="text-sm text-gray-400">-</span>
|
|
||||||
</td>
|
</td>
|
||||||
<td class="px-4 py-3 whitespace-nowrap">
|
<td class="px-4 py-3 whitespace-nowrap">
|
||||||
<div class="flex items-center space-x-2">
|
<div class="flex items-center space-x-2">
|
||||||
@@ -163,11 +180,29 @@
|
|||||||
>
|
>
|
||||||
Aus Login-System
|
Aus Login-System
|
||||||
</span>
|
</span>
|
||||||
<span
|
<button
|
||||||
v-if="member.isMannschaftsspieler"
|
v-if="canEdit"
|
||||||
class="ml-2 px-2 py-1 bg-blue-100 text-blue-800 text-xs font-medium rounded-full"
|
@click="toggleMannschaftsspieler(member)"
|
||||||
|
:class="[
|
||||||
|
'ml-2 px-2 py-1 text-xs font-medium rounded-full transition-colors',
|
||||||
|
member.isMannschaftsspieler
|
||||||
|
? 'bg-blue-100 text-blue-800 hover:bg-blue-200'
|
||||||
|
: 'bg-gray-100 text-gray-600 hover:bg-gray-200'
|
||||||
|
]"
|
||||||
|
title="Klicken zum Umschalten"
|
||||||
>
|
>
|
||||||
Mannschaftsspieler
|
Mannschaftsspieler: {{ member.isMannschaftsspieler ? 'Ja' : 'Nein' }}
|
||||||
|
</button>
|
||||||
|
<span
|
||||||
|
v-else
|
||||||
|
:class="[
|
||||||
|
'ml-2 px-2 py-1 text-xs font-medium rounded-full',
|
||||||
|
member.isMannschaftsspieler
|
||||||
|
? 'bg-blue-100 text-blue-800'
|
||||||
|
: 'bg-gray-100 text-gray-600'
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
Mannschaftsspieler: {{ member.isMannschaftsspieler ? 'Ja' : 'Nein' }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -618,6 +653,27 @@ const saveMember = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const toggleMannschaftsspieler = async (member) => {
|
||||||
|
try {
|
||||||
|
const response = await $fetch('/api/members/toggle-mannschaftsspieler', {
|
||||||
|
method: 'POST',
|
||||||
|
body: { memberId: member.id }
|
||||||
|
})
|
||||||
|
|
||||||
|
// Update local state immediately
|
||||||
|
member.isMannschaftsspieler = response.isMannschaftsspieler
|
||||||
|
|
||||||
|
// Reload to ensure consistency
|
||||||
|
await loadMembers()
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Fehler beim Umschalten des Mannschaftsspieler-Status:', error)
|
||||||
|
const errorMsg = error.data?.message || error.message || 'Fehler beim Umschalten des Status.'
|
||||||
|
if (window.showErrorModal) {
|
||||||
|
window.showErrorModal('Fehler', errorMsg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const confirmDelete = async (member) => {
|
const confirmDelete = async (member) => {
|
||||||
window.showConfirmModal('Mitglied löschen', `Möchten Sie "${member.name}" wirklich löschen?`, async () => {
|
window.showConfirmModal('Mitglied löschen', `Möchten Sie "${member.name}" wirklich löschen?`, async () => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -83,7 +83,8 @@ export default defineEventHandler(async (event) => {
|
|||||||
loginEmail: user.email,
|
loginEmail: user.email,
|
||||||
loginRoles: roles,
|
loginRoles: roles,
|
||||||
loginRole: roles[0] || 'mitglied', // Rückwärtskompatibilität
|
loginRole: roles[0] || 'mitglied', // Rückwärtskompatibilität
|
||||||
lastLogin: user.lastLogin
|
lastLogin: user.lastLogin,
|
||||||
|
isMannschaftsspieler: user.isMannschaftsspieler === true || mergedMembers[matchedManualIndex].isMannschaftsspieler === true
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Add as new member (from login system)
|
// Add as new member (from login system)
|
||||||
@@ -102,7 +103,8 @@ export default defineEventHandler(async (event) => {
|
|||||||
loginEmail: user.email,
|
loginEmail: user.email,
|
||||||
loginRoles: roles,
|
loginRoles: roles,
|
||||||
loginRole: roles[0] || 'mitglied', // Rückwärtskompatibilität
|
loginRole: roles[0] || 'mitglied', // Rückwärtskompatibilität
|
||||||
lastLogin: user.lastLogin
|
lastLogin: user.lastLogin,
|
||||||
|
isMannschaftsspieler: user.isMannschaftsspieler === true
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
105
server/api/members/toggle-mannschaftsspieler.post.js
Normal file
105
server/api/members/toggle-mannschaftsspieler.post.js
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
import { verifyToken, getUserById, hasAnyRole, readUsers, writeUsers } from '../../utils/auth.js'
|
||||||
|
import { readMembers, writeMembers, getMemberById } from '../../utils/members.js'
|
||||||
|
|
||||||
|
export default defineEventHandler(async (event) => {
|
||||||
|
try {
|
||||||
|
// Support both Cookie and Authorization Header
|
||||||
|
let token = getCookie(event, 'auth_token')
|
||||||
|
|
||||||
|
if (!token) {
|
||||||
|
const authHeader = getHeader(event, 'authorization')
|
||||||
|
if (authHeader && authHeader.startsWith('Bearer ')) {
|
||||||
|
token = authHeader.substring(7)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!token) {
|
||||||
|
throw createError({
|
||||||
|
statusCode: 401,
|
||||||
|
message: 'Nicht authentifiziert.'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const decoded = verifyToken(token)
|
||||||
|
|
||||||
|
if (!decoded) {
|
||||||
|
throw createError({
|
||||||
|
statusCode: 401,
|
||||||
|
message: 'Ungültiges Token.'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = await getUserById(decoded.id)
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
throw createError({
|
||||||
|
statusCode: 401,
|
||||||
|
message: 'Benutzer nicht gefunden.'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only admin and vorstand can toggle this flag
|
||||||
|
if (!hasAnyRole(user, 'admin', 'vorstand')) {
|
||||||
|
throw createError({
|
||||||
|
statusCode: 403,
|
||||||
|
message: 'Keine Berechtigung.'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const body = await readBody(event)
|
||||||
|
const { memberId } = body
|
||||||
|
|
||||||
|
if (!memberId) {
|
||||||
|
throw createError({
|
||||||
|
statusCode: 400,
|
||||||
|
message: 'Mitglieds-ID ist erforderlich.'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prüfe ob es ein Login-System-Mitglied ist (user.id === memberId)
|
||||||
|
const users = await readUsers()
|
||||||
|
const loginUser = users.find(u => u.id === memberId)
|
||||||
|
|
||||||
|
if (loginUser) {
|
||||||
|
// Login-System-Mitglied: Flag in users.json speichern
|
||||||
|
loginUser.isMannschaftsspieler = !loginUser.isMannschaftsspieler
|
||||||
|
await writeUsers(users)
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: 'Mannschaftsspieler-Status aktualisiert.',
|
||||||
|
isMannschaftsspieler: loginUser.isMannschaftsspieler
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Manuelles Mitglied: Flag in members.json speichern
|
||||||
|
const members = await readMembers()
|
||||||
|
const member = members.find(m => m.id === memberId)
|
||||||
|
|
||||||
|
if (!member) {
|
||||||
|
throw createError({
|
||||||
|
statusCode: 404,
|
||||||
|
message: 'Mitglied nicht gefunden.'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
member.isMannschaftsspieler = !member.isMannschaftsspieler
|
||||||
|
await writeMembers(members)
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: 'Mannschaftsspieler-Status aktualisiert.',
|
||||||
|
isMannschaftsspieler: member.isMannschaftsspieler
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Fehler beim Umschalten des Mannschaftsspieler-Status:', error)
|
||||||
|
if (error.statusCode) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
throw createError({
|
||||||
|
statusCode: error.statusCode || 500,
|
||||||
|
message: error.message || 'Fehler beim Umschalten des Mannschaftsspieler-Status.'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
Reference in New Issue
Block a user