feat: Add CMS and Member Area screens with ViewModels
All checks were successful
Code Analysis and Production Deploy / analyze (push) Successful in 5m23s
Code Analysis and Production Deploy / deploy-production (push) Has been skipped
Code Analysis and Production Deploy / deploy-test (push) Successful in 2m18s

- Implemented CmsViewModel to manage CMS data loading and state.
- Created MemberAreaDetailScreens for displaying member information and news.
- Added MembersViewModel and MemberNewsViewModel for managing member data and news.
- Developed MemberAreaScreen to provide navigation and display member-related options.
- Introduced ProfileScreen and ProfileViewModel for user profile management.
- Implemented state management for loading, error handling, and form updates across screens.
This commit is contained in:
Torsten Schulz (local)
2026-05-28 08:01:35 +02:00
parent e195d5d189
commit e033d716dd
34 changed files with 1809 additions and 72 deletions

View File

@@ -141,6 +141,20 @@ describe('Config & Profil Endpoints', () => {
expect(result.user.email).toBe('max@test.de')
expect(result.user).not.toHaveProperty('password')
})
it('akzeptiert Bearer-Token für Android-Clients', async () => {
const event = createEvent({ headers: { authorization: 'Bearer android-token' } })
authUtils.verifyToken.mockReturnValue({ id: '1' })
authUtils.getUserFromToken.mockResolvedValue({
id: '1', name: 'Android User', email: 'android@test.de', roles: ['mitglied']
})
const result = await profileGetHandler(event)
expect(result.success).toBe(true)
expect(authUtils.verifyToken).toHaveBeenCalledWith('android-token')
expect(result.user.email).toBe('android@test.de')
})
})
describe('PUT /api/profile', () => {
@@ -206,6 +220,32 @@ describe('Config & Profil Endpoints', () => {
expect(authUtils.revokeRefreshSessionsForUser).not.toHaveBeenCalled()
})
it('aktualisiert Profil per Bearer-Token für Android-Clients', async () => {
const event = createEvent({ headers: { authorization: 'Bearer android-token' } })
authUtils.verifyToken.mockReturnValue({ id: '1' })
authUtils.readUsers.mockResolvedValue([
{ id: '1', name: 'Alt', email: 'max@test.de', password: 'hash', roles: ['mitglied'] }
])
authUtils.writeUsers.mockResolvedValue(undefined)
authUtils.migrateUserRoles.mockImplementation(u => ({ ...u, roles: u.roles || ['mitglied'] }))
mockSuccessReadBody({ name: 'Android Neu', email: 'android@test.de', phone: '0987' })
const result = await profilePutHandler(event)
expect(result.success).toBe(true)
expect(authUtils.verifyToken).toHaveBeenCalledWith('android-token')
expect(result.user.name).toBe('Android Neu')
})
it('lehnt widerrufene Android-Sessions beim Profil-Update ab', async () => {
const event = createEvent({ headers: { authorization: 'Bearer android-token' } })
authUtils.verifyToken.mockReturnValue({ id: '1', sid: 'session-1' })
authUtils.getUserFromToken.mockResolvedValue(null)
mockSuccessReadBody({ name: 'Android Neu', email: 'android@test.de' })
await expect(profilePutHandler(event)).rejects.toMatchObject({ statusCode: 401 })
})
it('prüft aktuelles Passwort bei Passwortänderung', async () => {
const event = createEvent({ cookies: { auth_token: 'token' } })
authUtils.verifyToken.mockReturnValue({ id: '1' })

View File

@@ -233,6 +233,26 @@ describe('Spielplan, Mannschaften & öffentliche Endpoints', () => {
expect(result.birthdays).toHaveLength(1)
})
it('akzeptiert Bearer-Token für Android-Clients', async () => {
const event = createEvent({ headers: { authorization: 'Bearer android-token' } })
const inDays = 7
const targetDate = new Date()
targetDate.setDate(targetDate.getDate() + inDays)
const geburtsdatum = `${targetDate.getFullYear() - 30}-${String(targetDate.getMonth() + 1).padStart(2, '0')}-${String(targetDate.getDate()).padStart(2, '0')}`
authUtils.verifyToken.mockReturnValue({ id: 'v1' })
authUtils.getUserFromToken.mockResolvedValue({ id: 'v1', roles: ['vorstand'], active: true })
memberUtils.readMembers.mockResolvedValue([
{ firstName: 'Android', lastName: 'Privat', geburtsdatum, visibility: { showBirthday: false } }
])
authUtils.readUsers.mockResolvedValue([])
const result = await birthdaysHandler(event)
expect(authUtils.verifyToken).toHaveBeenCalledWith('android-token')
expect(result.birthdays).toHaveLength(1)
})
it('ignoriert Mitglieder ohne Geburtsdatum', async () => {
const event = createEvent()
memberUtils.readMembers.mockResolvedValue([