From f7701d698f35a79be800c281527243925f0054dc Mon Sep 17 00:00:00 2001 From: "Torsten Schulz (local)" Date: Sun, 29 Mar 2026 14:37:49 +0200 Subject: [PATCH] Add hall key feature to member management, including UI updates for displaying and editing hall key status. Update API to handle hall key data in member records. --- components/cms/CmsMitglieder.vue | 42 +++++++++++++++++++++++-- pages/mitgliederbereich/mitglieder.vue | 43 ++++++++++++++++++++++++-- server/api/members.get.js | 1 + server/api/members.post.js | 3 +- tests/members-endpoints.spec.ts | 17 ++++++++-- 5 files changed, 96 insertions(+), 10 deletions(-) diff --git a/components/cms/CmsMitglieder.vue b/components/cms/CmsMitglieder.vue index e0e984b..cbb07cb 100644 --- a/components/cms/CmsMitglieder.vue +++ b/components/cms/CmsMitglieder.vue @@ -85,6 +85,9 @@ Mannschaft + + 🔑 + Status @@ -177,6 +180,15 @@ {{ member.isMannschaftsspieler ? 'Ja' : 'Nein' }} + + + 🔑 + +
{{ member.name }} + + 🔑 +
+
+ + +
+
{ @@ -777,7 +812,7 @@ const loadMembers = async () => { const openAddModal = () => { editingMember.value = null - formData.value = { firstName: '', lastName: '', geburtsdatum: '', email: '', phone: '', address: '', notes: '', isMannschaftsspieler: false } + formData.value = { firstName: '', lastName: '', geburtsdatum: '', email: '', phone: '', address: '', notes: '', isMannschaftsspieler: false, hasHallKey: false } showModal.value = true errorMessage.value = '' } @@ -792,7 +827,8 @@ const openEditModal = (member) => { phone: member.phone || '', address: member.address || '', notes: member.notes || '', - isMannschaftsspieler: member.isMannschaftsspieler === true + isMannschaftsspieler: member.isMannschaftsspieler === true, + hasHallKey: member.hasHallKey === true } showModal.value = true errorMessage.value = '' diff --git a/pages/mitgliederbereich/mitglieder.vue b/pages/mitgliederbereich/mitglieder.vue index cc81a58..0d8de08 100644 --- a/pages/mitgliederbereich/mitglieder.vue +++ b/pages/mitgliederbereich/mitglieder.vue @@ -96,6 +96,9 @@ Mannschaft + + 🔑 + Status @@ -184,6 +187,15 @@ {{ member.isMannschaftsspieler ? 'Ja' : 'Nein' }} + + + 🔑 + +
+ + 🔑 +
+
+ + +
+
{ @@ -910,7 +945,8 @@ const openAddModal = () => { phone: '', address: '', notes: '', - isMannschaftsspieler: false + isMannschaftsspieler: false, + hasHallKey: false } showModal.value = true errorMessage.value = '' @@ -926,7 +962,8 @@ const openEditModal = (member) => { phone: member.phone || '', address: member.address || '', notes: member.notes || '', - isMannschaftsspieler: member.isMannschaftsspieler === true + isMannschaftsspieler: member.isMannschaftsspieler === true, + hasHallKey: member.hasHallKey === true } showModal.value = true errorMessage.value = '' diff --git a/server/api/members.get.js b/server/api/members.get.js index 8a6e2cf..6550998 100644 --- a/server/api/members.get.js +++ b/server/api/members.get.js @@ -242,6 +242,7 @@ export default defineEventHandler(async (event) => { loginRole: member.loginRole, lastLogin: member.lastLogin, isMannschaftsspieler: member.isMannschaftsspieler, + hasHallKey: member.hasHallKey === true || member.hasHallenschluessel === true, notes: member.notes || '', // Sichtbarkeits-Flags explizit mitgeben showEmail: visibility.showEmail === undefined ? true : Boolean(visibility.showEmail), diff --git a/server/api/members.post.js b/server/api/members.post.js index 0b92537..ccd669d 100644 --- a/server/api/members.post.js +++ b/server/api/members.post.js @@ -48,7 +48,7 @@ export default defineEventHandler(async (event) => { } const body = await readBody(event) - const { id, firstName, lastName, geburtsdatum, email, phone, address, notes, isMannschaftsspieler, active } = body + const { id, firstName, lastName, geburtsdatum, email, phone, address, notes, isMannschaftsspieler, hasHallKey, hasHallenschluessel, active } = body if (!firstName || !lastName) { throw createError({ @@ -75,6 +75,7 @@ export default defineEventHandler(async (event) => { address: address || '', notes: notes || '', isMannschaftsspieler: isMannschaftsspieler === true || isMannschaftsspieler === 'true', + hasHallKey: hasHallKey === true || hasHallKey === 'true' || hasHallenschluessel === true || hasHallenschluessel === 'true', active: typeof active === 'boolean' ? active : true }) diff --git a/tests/members-endpoints.spec.ts b/tests/members-endpoints.spec.ts index 10eb09a..0951e97 100644 --- a/tests/members-endpoints.spec.ts +++ b/tests/members-endpoints.spec.ts @@ -4,6 +4,7 @@ import { createEvent, mockSuccessReadBody } from './setup' vi.mock('../server/utils/auth.js', () => ({ verifyToken: vi.fn(), getUserById: vi.fn(), + getUserFromToken: vi.fn(), readUsers: vi.fn(), readMembers: vi.fn(), writeUsers: vi.fn(), @@ -24,6 +25,11 @@ vi.mock('../server/utils/auth.js', () => ({ if (!user) return false const userRoles = Array.isArray(user.roles) ? user.roles : (user.role ? [user.role] : []) return roles.some(r => userRoles.includes(r)) + }), + hasRole: vi.fn((user, role) => { + if (!user) return false + const userRoles = Array.isArray(user.roles) ? user.roles : (user.role ? [user.role] : []) + return userRoles.includes(role) }) })) @@ -58,16 +64,18 @@ describe('Members API Endpoints', () => { const event = createEvent({ cookies: { auth_token: 'token' } }) authUtils.verifyToken.mockReturnValue({ id: '1' }) memberUtils.readMembers.mockResolvedValue([ - { id: 'm1', firstName: 'Anna', lastName: 'Muster', email: 'anna@club.de' } + { id: 'm1', firstName: 'Anna', lastName: 'Muster', email: 'anna@club.de', hasHallKey: true, active: true } ]) authUtils.readUsers.mockResolvedValue([ { id: 'u1', name: 'Ben Nutzer', email: 'ben@club.de', role: 'mitglied', active: true } ]) + authUtils.getUserFromToken.mockResolvedValue({ id: '1', role: 'mitglied' }) const response = await membersGetHandler(event) expect(response.success).toBe(true) expect(response.members).toHaveLength(2) + expect(response.members[0]).toHaveProperty('hasHallKey', true) }) }) @@ -76,7 +84,8 @@ describe('Members API Endpoints', () => { firstName: 'Lisa', lastName: 'Beispiel', geburtsdatum: '2000-01-01', - email: 'lisa@example.com' + email: 'lisa@example.com', + hasHallKey: true } it('verweigert Zugriff ohne Token', async () => { @@ -113,7 +122,9 @@ describe('Members API Endpoints', () => { const response = await membersPostHandler(event) expect(response.success).toBe(true) - expect(memberUtils.saveMember).toHaveBeenCalled() + expect(memberUtils.saveMember).toHaveBeenCalledWith(expect.objectContaining({ + hasHallKey: true + })) }) })