diff --git a/package-lock.json b/package-lock.json index 3a68ec2..9d0f17d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "harheimertc-website", - "version": "1.1.0", + "version": "1.1.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "harheimertc-website", - "version": "1.1.0", + "version": "1.1.3", "hasInstallScript": true, "dependencies": { "@pinia/nuxt": "^0.11.2", @@ -36,7 +36,7 @@ "eslint-plugin-vue": "^10.6.2", "globals": "^16.5.0", "lucide-vue-next": "^0.344.0", - "postcss": "^8.4.0", + "postcss": "^8.5.12", "supertest": "^7.1.0", "tailwindcss": "^3.4.0", "vitest": "^4.0.16", @@ -11366,9 +11366,9 @@ } }, "node_modules/postcss": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", - "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "version": "8.5.12", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.12.tgz", + "integrity": "sha512-W62t/Se6rA0Az3DfCL0AqJwXuKwBeYg6nOaIgzP+xZ7N5BFCI7DYi1qs6ygUYT6rvfi6t9k65UMLJC+PHZpDAA==", "funding": [ { "type": "opencollective", diff --git a/package.json b/package.json index f7fe6c3..710fd71 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "harheimertc-website", - "version": "1.1.2", + "version": "1.1.3", "description": "Moderne Webseite für den Harheimer Tischtennis Club", "private": true, "type": "module", @@ -50,7 +50,7 @@ "eslint-plugin-vue": "^10.6.2", "globals": "^16.5.0", "lucide-vue-next": "^0.344.0", - "postcss": "^8.4.0", + "postcss": "^8.5.12", "supertest": "^7.1.0", "tailwindcss": "^3.4.0", "vitest": "^4.0.16", diff --git a/pages/cms/benutzer.vue b/pages/cms/benutzer.vue index 28dce73..e7ad38b 100644 --- a/pages/cms/benutzer.vue +++ b/pages/cms/benutzer.vue @@ -106,9 +106,31 @@
-

- Aktive Benutzer ({{ activeUsers.length }}) -

+
+

+ Aktive Benutzer ({{ sortedActiveUsers.length }}) +

+
+ + +
+
@@ -135,13 +157,13 @@
- {{ user.name }} + {{ getDisplayName(user) }}
@@ -253,7 +275,7 @@ >

- Rollen bearbeiten: {{ editingUser.name }} + Rollen bearbeiten: {{ getDisplayName(editingUser) }}

@@ -350,6 +372,7 @@ const errorMessage = ref('') const showRoleModal = ref(false) const editingUser = ref(null) const selectedRoles = ref([]) +const nameSortMode = ref('firstLast') const pendingUsers = computed(() => { return allUsers.value @@ -364,6 +387,61 @@ const activeUsers = computed(() => { return allUsers.value.filter(u => u.active === true) }) +const splitNameParts = (name = '') => { + const trimmed = (name || '').trim() + if (!trimmed) { + return { firstName: '', lastName: '' } + } + + if (trimmed.includes(',')) { + const [lastNameRaw, ...firstNameRaw] = trimmed.split(',') + return { + firstName: firstNameRaw.join(',').trim(), + lastName: (lastNameRaw || '').trim() + } + } + + const parts = trimmed.split(/\s+/).filter(Boolean) + if (parts.length <= 1) { + return { firstName: parts[0] || '', lastName: '' } + } + + return { + firstName: parts[0], + lastName: parts.slice(1).join(' ') + } +} + +const getDisplayName = (user) => { + const { firstName, lastName } = splitNameParts(user?.name || '') + + if (nameSortMode.value === 'lastFirst') { + if (!lastName) { + return firstName + } + return `${lastName}, ${firstName}`.trim() + } + + return `${firstName} ${lastName}`.trim() +} + +const sortedActiveUsers = computed(() => { + return [...activeUsers.value].sort((a, b) => { + const nameA = splitNameParts(a.name) + const nameB = splitNameParts(b.name) + + if (nameSortMode.value === 'lastFirst') { + const lastNameCompare = nameA.lastName.localeCompare(nameB.lastName, 'de', { sensitivity: 'base' }) + if (lastNameCompare !== 0) return lastNameCompare + return nameA.firstName.localeCompare(nameB.firstName, 'de', { sensitivity: 'base' }) + } + + const firstNameCompare = nameA.firstName.localeCompare(nameB.firstName, 'de', { sensitivity: 'base' }) + if (firstNameCompare !== 0) return firstNameCompare + return nameA.lastName.localeCompare(nameB.lastName, 'de', { sensitivity: 'base' }) + }) +}) + const formatDate = (dateString) => { return new Date(dateString).toLocaleString('de-DE', { year: 'numeric', diff --git a/server/utils/newsletter.js b/server/utils/newsletter.js index 6f841a3..68a9441 100644 --- a/server/utils/newsletter.js +++ b/server/utils/newsletter.js @@ -236,6 +236,22 @@ export async function getRecipientsByGroup(targetGroup) { email: m.email, name: `${m.firstName || ''} ${m.lastName || ''}`.trim() || m.name || '' })) + + // Zusätzlich aktive Trainer aus users.json anschreiben + users + .filter(u => { + if (!u.active || !u.email || !u.email.trim()) return false + const roles = Array.isArray(u.roles) ? u.roles : (u.role ? [u.role] : []) + return roles.includes('trainer') + }) + .forEach(u => { + if (!recipients.find(r => r.email.toLowerCase().trim() === u.email.toLowerCase().trim())) { + recipients.push({ + email: u.email.trim(), + name: u.name || '' + }) + } + }) break case 'mannschaftsspieler':