feat: Add CMS and Member Area screens with ViewModels
- 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:
@@ -1,5 +1,5 @@
|
||||
import { readMembers, normalizeDate } from '../utils/members.js'
|
||||
import { readUsers, migrateUserRoles, getUserFromToken, verifyToken } from '../utils/auth.js'
|
||||
import { readMembers } from '../utils/members.js'
|
||||
import { readUsers, getUserFromToken, verifyToken } from '../utils/auth.js'
|
||||
|
||||
// Helper: returns array of upcoming birthdays within daysAhead (inclusive)
|
||||
function getUpcomingBirthdays(entries, daysAhead = 28) {
|
||||
@@ -41,7 +41,7 @@ function getUpcomingBirthdays(entries, daysAhead = 28) {
|
||||
export default defineEventHandler(async (event) => {
|
||||
try {
|
||||
// Determine viewer for visibility rules; token optional
|
||||
const token = getCookie(event, 'auth_token')
|
||||
const token = getCookie(event, 'auth_token') || getHeader(event, 'authorization')?.replace(/^Bearer\s+/i, '')
|
||||
let currentUser = null
|
||||
if (token) {
|
||||
const decoded = verifyToken(token)
|
||||
|
||||
@@ -2,7 +2,7 @@ import { getUserFromToken, hasAnyRole } from '../../utils/auth.js'
|
||||
import { readContactRequests } from '../../utils/contact-requests.js'
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const token = getCookie(event, 'auth_token')
|
||||
const token = getCookie(event, 'auth_token') || getHeader(event, 'authorization')?.replace(/^Bearer\s+/i, '')
|
||||
const currentUser = token ? await getUserFromToken(token) : null
|
||||
|
||||
if (!currentUser || !hasAnyRole(currentUser, 'admin', 'vorstand', 'trainer')) {
|
||||
|
||||
@@ -48,7 +48,7 @@ function summarizeAttempts(entries) {
|
||||
}
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const token = getCookie(event, 'auth_token')
|
||||
const token = getCookie(event, 'auth_token') || getHeader(event, 'authorization')?.replace(/^Bearer\s+/i, '')
|
||||
const currentUser = token ? await getUserFromToken(token) : null
|
||||
|
||||
if (!currentUser || !hasRole(currentUser, 'admin')) {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { getUserFromToken, readUsers, hasAnyRole, hasRole, migrateUserRoles } from '../../../utils/auth.js'
|
||||
import { getUserFromToken, readUsers, hasAnyRole, migrateUserRoles } from '../../../utils/auth.js'
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
try {
|
||||
const token = getCookie(event, 'auth_token')
|
||||
const token = getCookie(event, 'auth_token') || getHeader(event, 'authorization')?.replace(/^Bearer\s+/i, '')
|
||||
const currentUser = await getUserFromToken(token)
|
||||
|
||||
// Seite darf von Admin ODER Vorstand genutzt werden
|
||||
@@ -15,8 +15,6 @@ export default defineEventHandler(async (event) => {
|
||||
|
||||
const users = await readUsers()
|
||||
|
||||
const isVorstand = hasRole(currentUser, 'vorstand')
|
||||
|
||||
// Nur Admin oder Vorstand duerfen vollen Benutzer-Contact und Rollen sehen.
|
||||
const canSeePrivate = hasAnyRole(currentUser, 'admin', 'vorstand')
|
||||
|
||||
@@ -53,4 +51,3 @@ export default defineEventHandler(async (event) => {
|
||||
throw error
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ const getDataPath = (filename) => {
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
try {
|
||||
const token = getCookie(event, 'auth_token')
|
||||
const token = getCookie(event, 'auth_token') || getHeader(event, 'authorization')?.replace(/^Bearer\s+/i, '')
|
||||
|
||||
if (!token) {
|
||||
throw createError({
|
||||
@@ -58,4 +58,3 @@ export default defineEventHandler(async (event) => {
|
||||
throw error
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import { readUsers, migrateUserRoles } from '../utils/auth.js'
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
try {
|
||||
const token = getCookie(event, 'auth_token')
|
||||
const token = getCookie(event, 'auth_token') || getHeader(event, 'authorization')?.replace(/^Bearer\s+/i, '')
|
||||
|
||||
if (!token) {
|
||||
throw createError({
|
||||
@@ -203,7 +203,6 @@ export default defineEventHandler(async (event) => {
|
||||
mergedMembers.sort((a, b) => a.name.localeCompare(b.name))
|
||||
// Die Mitgliederliste ist nur für authentifizierte Nutzer sichtbar (siehe oben).
|
||||
// Respektiere individuelle Sichtbarkeitspräferenzen (user.visibility)
|
||||
const currentUserToken = token
|
||||
const isViewerAuthenticated = !!currentUser
|
||||
// Only 'vorstand' may override member visibility
|
||||
const isPrivilegedViewer = currentUser ? hasRole(currentUser, 'vorstand') : false
|
||||
@@ -218,16 +217,11 @@ export default defineEventHandler(async (event) => {
|
||||
const showPhone = visibility.showPhone === undefined ? true : Boolean(visibility.showPhone)
|
||||
const showAddress = visibility.showAddress === undefined ? false : Boolean(visibility.showAddress)
|
||||
|
||||
// Determine if contact info existed but was hidden to the viewer
|
||||
const hadEmail = !!member.email
|
||||
const hadPhone = !!member.phone
|
||||
const hadAddress = !!member.address
|
||||
const hadBirthday = !!member.geburtsdatum
|
||||
const emailVisible = (isPrivilegedViewer || (isViewerAuthenticated && showEmail))
|
||||
const phoneVisible = (isPrivilegedViewer || (isViewerAuthenticated && showPhone))
|
||||
const addressVisible = (isPrivilegedViewer || (isViewerAuthenticated && showAddress))
|
||||
const birthdayVisible = (isPrivilegedViewer || (isViewerAuthenticated && (member.visibility && member.visibility.showBirthday !== undefined ? Boolean(member.visibility.showBirthday) : true)))
|
||||
const contactHidden = (!emailVisible && hadEmail) || (!phoneVisible && hadPhone) || (!addressVisible && hadAddress)
|
||||
|
||||
return {
|
||||
id: member.id,
|
||||
@@ -260,7 +254,7 @@ export default defineEventHandler(async (event) => {
|
||||
const day = `${d.getDate()}`.padStart(2, '0')
|
||||
const month = `${d.getMonth()+1}`.padStart(2, '0')
|
||||
return `${day}.${month}`
|
||||
} catch (_e) {
|
||||
} catch {
|
||||
return undefined
|
||||
}
|
||||
})() : undefined,
|
||||
@@ -277,4 +271,3 @@ export default defineEventHandler(async (event) => {
|
||||
throw error
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import { deleteNews } from '../utils/news.js'
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
try {
|
||||
const token = getCookie(event, 'auth_token')
|
||||
const token = getCookie(event, 'auth_token') || getHeader(event, 'authorization')?.replace(/^Bearer\s+/i, '')
|
||||
|
||||
if (!token) {
|
||||
throw createError({
|
||||
@@ -53,4 +53,3 @@ export default defineEventHandler(async (event) => {
|
||||
throw error
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import { readNews } from '../utils/news.js'
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
try {
|
||||
const token = getCookie(event, 'auth_token')
|
||||
const token = getCookie(event, 'auth_token') || getHeader(event, 'authorization')?.replace(/^Bearer\s+/i, '')
|
||||
|
||||
if (!token) {
|
||||
throw createError({
|
||||
@@ -35,4 +35,3 @@ export default defineEventHandler(async (event) => {
|
||||
throw error
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import { saveNews } from '../utils/news.js'
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
try {
|
||||
const token = getCookie(event, 'auth_token')
|
||||
const token = getCookie(event, 'auth_token') || getHeader(event, 'authorization')?.replace(/^Bearer\s+/i, '')
|
||||
|
||||
if (!token) {
|
||||
throw createError({
|
||||
@@ -60,4 +60,3 @@ export default defineEventHandler(async (event) => {
|
||||
throw error
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { verifyToken, getUserFromToken } from '../utils/auth.js'
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
try {
|
||||
const token = getCookie(event, 'auth_token')
|
||||
const token = getCookie(event, 'auth_token') || getHeader(event, 'authorization')?.replace(/^Bearer\s+/i, '')
|
||||
|
||||
if (!token) {
|
||||
throw createError({ statusCode: 401, message: 'Nicht authentifiziert.' })
|
||||
@@ -27,7 +27,7 @@ export default defineEventHandler(async (event) => {
|
||||
email: user.email,
|
||||
phone: user.phone || '',
|
||||
geburtsdatum: user.geburtsdatum || '',
|
||||
visibility: Object.assign({ showBirthday: true }, (user.visibility || {}))
|
||||
visibility: Object.assign({ showBirthday: true }, (user.visibility || {}))
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -35,4 +35,3 @@ export default defineEventHandler(async (event) => {
|
||||
throw error
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { verifyToken, readUsers, writeUsers, verifyPassword, hashPassword, migrateUserRoles, revokeRefreshSessionsForUser } from '../utils/auth.js'
|
||||
import { verifyToken, getUserFromToken, readUsers, writeUsers, verifyPassword, hashPassword, migrateUserRoles, revokeRefreshSessionsForUser } from '../utils/auth.js'
|
||||
import { assertPasswordNotPwned } from '../utils/hibp.js'
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
try {
|
||||
const token = getCookie(event, 'auth_token')
|
||||
const token = getCookie(event, 'auth_token') || getHeader(event, 'authorization')?.replace(/^Bearer\s+/i, '')
|
||||
|
||||
if (!token) {
|
||||
throw createError({
|
||||
@@ -21,6 +21,16 @@ export default defineEventHandler(async (event) => {
|
||||
})
|
||||
}
|
||||
|
||||
if (decoded.sid) {
|
||||
const sessionUser = await getUserFromToken(token)
|
||||
if (!sessionUser) {
|
||||
throw createError({
|
||||
statusCode: 401,
|
||||
message: 'Ungültige Sitzung.'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const body = await readBody(event)
|
||||
const { name, email, phone, geburtsdatum, currentPassword, newPassword } = body
|
||||
|
||||
@@ -31,7 +41,7 @@ export default defineEventHandler(async (event) => {
|
||||
})
|
||||
}
|
||||
|
||||
const users = await readUsers()
|
||||
const users = await readUsers()
|
||||
const userIndex = users.findIndex(u => u.id === decoded.id)
|
||||
|
||||
if (userIndex === -1) {
|
||||
|
||||
Reference in New Issue
Block a user