Enhance ESLint configuration to include support for .mjs and .cjs file types. Update ignored files patterns to ensure proper linting of project files. Refactor Vue component templates for improved readability and maintainability, including consistent formatting and structure across various components. Update error handling in save functions to prevent silent failures.
Some checks failed
Code Analysis (JS/Vue) / analyze (push) Failing after 52s

This commit is contained in:
Torsten Schulz (local)
2026-04-15 20:37:14 +02:00
parent 1aae808e5f
commit ef2d9353f5
24 changed files with 1238 additions and 285 deletions

View File

@@ -11,20 +11,48 @@
<div class="bg-white p-6 rounded-xl shadow-lg border border-gray-100">
<div class="flex items-center mb-4">
<div class="w-12 h-12 bg-pink-100 rounded-lg flex items-center justify-center">
<Calendar :size="20" class="text-pink-600" />
<Calendar
:size="20"
class="text-pink-600"
/>
</div>
<h2 class="ml-4 text-xl font-semibold text-gray-900">Geburtstage (nächste 4 Wochen)</h2>
<h2 class="ml-4 text-xl font-semibold text-gray-900">
Geburtstage (nächste 4 Wochen)
</h2>
</div>
<div v-if="loadingBirthdays" class="text-sm text-gray-500">Lade...</div>
<ul v-else class="space-y-2">
<li v-for="b in birthdays" :key="b.name + b.dayMonth" class="flex items-center justify-between p-3 border border-gray-100 rounded-lg">
<div
v-if="loadingBirthdays"
class="text-sm text-gray-500"
>
Lade...
</div>
<ul
v-else
class="space-y-2"
>
<li
v-for="b in birthdays"
:key="b.name + b.dayMonth"
class="flex items-center justify-between p-3 border border-gray-100 rounded-lg"
>
<div class="min-w-0">
<div class="font-medium text-gray-900 truncate">{{ b.name }}</div>
<div class="text-xs text-gray-600">{{ b.dayMonth }}</div>
<div class="font-medium text-gray-900 truncate">
{{ b.name }}
</div>
<div class="text-xs text-gray-600">
{{ b.dayMonth }}
</div>
</div>
<div class="text-sm text-gray-500">
{{ b.inDays === 0 ? 'Heute' : (b.inDays === 1 ? 'Morgen' : 'in ' + b.inDays + ' Tagen') }}
</div>
<div class="text-sm text-gray-500">{{ b.inDays === 0 ? 'Heute' : (b.inDays === 1 ? 'Morgen' : 'in ' + b.inDays + ' Tagen') }}</div>
</li>
<li v-if="birthdays.length === 0" class="text-sm text-gray-600">Keine Geburtstage in den nächsten 4 Wochen.</li>
<li
v-if="birthdays.length === 0"
class="text-sm text-gray-600"
>
Keine Geburtstage in den nächsten 4 Wochen.
</li>
</ul>
</div>
<!-- Inhalte (gruppiert) -->

View File

@@ -2,13 +2,20 @@
<div class="min-h-screen bg-gray-50">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<div class="mb-6">
<h1 class="text-3xl font-display font-bold text-gray-900">Inhalte verwalten</h1>
<p class="mt-1 text-sm text-gray-500">Redaktionelle Inhalte der Website bearbeiten</p>
<h1 class="text-3xl font-display font-bold text-gray-900">
Inhalte verwalten
</h1>
<p class="mt-1 text-sm text-gray-500">
Redaktionelle Inhalte der Website bearbeiten
</p>
</div>
<!-- Tabs -->
<div class="border-b border-gray-200 mb-6">
<nav class="-mb-px flex space-x-8 overflow-x-auto" aria-label="Tabs">
<nav
class="-mb-px flex space-x-8 overflow-x-auto"
aria-label="Tabs"
>
<button
v-for="tab in tabs"
:key="tab.id"

View File

@@ -28,15 +28,24 @@
</label>
</div>
<div v-if="isLoading" class="text-center py-12 text-gray-600">
<div
v-if="isLoading"
class="text-center py-12 text-gray-600"
>
Lade Kontaktanfragen...
</div>
<div v-else-if="filteredRequests.length === 0" class="bg-white rounded-xl shadow p-8 text-center text-gray-600">
<div
v-else-if="filteredRequests.length === 0"
class="bg-white rounded-xl shadow p-8 text-center text-gray-600"
>
{{ showAnswered ? 'Aktuell liegen keine Kontaktanfragen vor.' : 'Aktuell liegen keine offenen Kontaktanfragen vor.' }}
</div>
<div v-else class="space-y-4">
<div
v-else
class="space-y-4"
>
<div
v-for="request in filteredRequests"
:key="request.id"
@@ -67,7 +76,10 @@
{{ request.message }}
</p>
<div v-if="Array.isArray(request.replies) && request.replies.length > 0" class="mt-5 border-t border-gray-100 pt-4">
<div
v-if="Array.isArray(request.replies) && request.replies.length > 0"
class="mt-5 border-t border-gray-100 pt-4"
>
<h3 class="text-sm font-semibold text-gray-700 mb-2">
Antworten
</h3>
@@ -127,7 +139,10 @@
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-600"
placeholder="Ihre Antwort..."
/>
<div v-if="errorMessage" class="mt-3 text-sm text-red-600">
<div
v-if="errorMessage"
class="mt-3 text-sm text-red-600"
>
{{ errorMessage }}
</div>
<div class="mt-5 flex justify-end gap-3">

View File

@@ -2,15 +2,25 @@
<div class="min-h-screen bg-gray-50">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<div class="mb-6">
<h1 class="text-3xl font-display font-bold text-gray-900">Mitgliederverwaltung</h1>
<p class="mt-1 text-sm text-gray-500">Anträge und Mitgliederliste verwalten</p>
<h1 class="text-3xl font-display font-bold text-gray-900">
Mitgliederverwaltung
</h1>
<p class="mt-1 text-sm text-gray-500">
Anträge und Mitgliederliste verwalten
</p>
</div>
<!-- Mitgliedschaftsanträge oben (nur sichtbar wenn Anträge vorhanden) -->
<div v-show="antraegeRef?.hasApplications" class="mb-10">
<div
v-show="antraegeRef?.hasApplications"
class="mb-10"
>
<CmsMitgliedschaftsantraege ref="antraegeRef" />
</div>
<div v-if="antraegeRef?.hasApplications" class="border-t border-gray-300 mb-10" />
<div
v-if="antraegeRef?.hasApplications"
class="border-t border-gray-300 mb-10"
/>
<!-- Mitgliederliste darunter -->
<CmsMitglieder />

View File

@@ -2,13 +2,20 @@
<div class="min-h-screen bg-gray-50">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<div class="mb-6">
<h1 class="text-3xl font-display font-bold text-gray-900">Sportbetrieb verwalten</h1>
<p class="mt-1 text-sm text-gray-500">Termine, Mannschaften und Spielpläne pflegen</p>
<h1 class="text-3xl font-display font-bold text-gray-900">
Sportbetrieb verwalten
</h1>
<p class="mt-1 text-sm text-gray-500">
Termine, Mannschaften und Spielpläne pflegen
</p>
</div>
<!-- Tabs -->
<div class="border-b border-gray-200 mb-6">
<nav class="-mb-px flex space-x-8 overflow-x-auto" aria-label="Tabs">
<nav
class="-mb-px flex space-x-8 overflow-x-auto"
aria-label="Tabs"
>
<button
v-for="tab in tabs"
:key="tab.id"

View File

@@ -58,7 +58,10 @@
>
<!-- Drag Handle -->
<div class="flex flex-col gap-1 cursor-move">
<GripVertical :size="16" class="text-gray-400" />
<GripVertical
:size="16"
class="text-gray-400"
/>
</div>
<!-- Section Info -->

View File

@@ -75,20 +75,48 @@
<div class="bg-white p-6 rounded-xl shadow-lg border border-gray-100">
<div class="flex items-center mb-4">
<div class="w-12 h-12 bg-pink-100 rounded-lg flex items-center justify-center">
<Calendar :size="20" class="text-pink-600" />
<Calendar
:size="20"
class="text-pink-600"
/>
</div>
<h2 class="ml-4 text-xl font-semibold text-gray-900">Geburtstage (nächste 4 Wochen)</h2>
<h2 class="ml-4 text-xl font-semibold text-gray-900">
Geburtstage (nächste 4 Wochen)
</h2>
</div>
<div v-if="loadingBirthdays" class="text-sm text-gray-500">Lade...</div>
<ul v-else class="space-y-2">
<li v-for="b in birthdays" :key="b.name + b.dayMonth" class="flex items-center justify-between p-3 border border-gray-100 rounded-lg">
<div
v-if="loadingBirthdays"
class="text-sm text-gray-500"
>
Lade...
</div>
<ul
v-else
class="space-y-2"
>
<li
v-for="b in birthdays"
:key="b.name + b.dayMonth"
class="flex items-center justify-between p-3 border border-gray-100 rounded-lg"
>
<div class="min-w-0">
<div class="font-medium text-gray-900 truncate">{{ b.name }}</div>
<div class="text-xs text-gray-600">{{ b.dayMonth }}</div>
<div class="font-medium text-gray-900 truncate">
{{ b.name }}
</div>
<div class="text-xs text-gray-600">
{{ b.dayMonth }}
</div>
</div>
<div class="text-sm text-gray-500">
{{ b.inDays === 0 ? 'Heute' : (b.inDays === 1 ? 'Morgen' : 'in ' + b.inDays + ' Tagen') }}
</div>
<div class="text-sm text-gray-500">{{ b.inDays === 0 ? 'Heute' : (b.inDays === 1 ? 'Morgen' : 'in ' + b.inDays + ' Tagen') }}</div>
</li>
<li v-if="birthdays.length === 0" class="text-sm text-gray-600">Keine Geburtstage in den nächsten 4 Wochen.</li>
<li
v-if="birthdays.length === 0"
class="text-sm text-gray-600"
>
Keine Geburtstage in den nächsten 4 Wochen.
</li>
</ul>
</div>
</div>

View File

@@ -57,12 +57,25 @@
<!-- Sortieroptionen -->
<div class="mb-4 flex items-center justify-between gap-4 flex-wrap">
<div class="flex items-center space-x-2">
<label for="sortMode" class="text-sm text-gray-700">Sortieren nach:</label>
<select id="sortMode" v-model="sortMode" class="px-2 py-1 border rounded">
<option value="name">Name (Vorname Nachname)</option>
<option value="lastname">Nachname (Nachname Vorname)</option>
<option value="birthday">Geburtstag</option>
</select>
<label
for="sortMode"
class="text-sm text-gray-700"
>Sortieren nach:</label>
<select
id="sortMode"
v-model="sortMode"
class="px-2 py-1 border rounded"
>
<option value="name">
Name (Vorname Nachname)
</option>
<option value="lastname">
Nachname (Nachname Vorname)
</option>
<option value="birthday">
Geburtstag
</option>
</select>
</div>
<label class="inline-flex items-center gap-2 text-sm text-gray-700">
<input
@@ -135,7 +148,10 @@
{{ member.name }}
</template>
</div>
<div v-if="member.birthday" class="text-xs text-gray-500">
<div
v-if="member.birthday"
class="text-xs text-gray-500"
>
🎂 {{ formatBirthday(member.birthday) }}
</div>
<div
@@ -283,7 +299,10 @@
<template v-else>
{{ member.name }}
</template>
<span v-if="member.birthday" class="text-xs text-gray-500 ml-2">
<span
v-if="member.birthday"
class="text-xs text-gray-500 ml-2"
>
🎂 {{ formatBirthday(member.birthday) }}
</span>
</h3>
@@ -341,30 +360,79 @@
<div class="grid sm:grid-cols-2 gap-3 text-gray-600">
<template v-if="!(member.showEmail && member.email) && !(member.showPhone && member.phone)">
<div class="col-span-2 flex items-center text-gray-500 text-sm italic">
<Mail :size="16" class="mr-2" />
<Mail
:size="16"
class="mr-2"
/>
Kontaktdaten nur für Vorstand sichtbar
</div>
</template>
<template v-else>
<div v-if="member.showEmail && member.email" class="flex items-center">
<Mail :size="16" class="mr-2 text-primary-600" />
<a :href="`mailto:${member.email}`" class="hover:text-primary-600">{{ member.email }}</a>
<div
v-if="member.showEmail && member.email"
class="flex items-center"
>
<Mail
:size="16"
class="mr-2 text-primary-600"
/>
<a
:href="`mailto:${member.email}`"
class="hover:text-primary-600"
>{{ member.email }}</a>
</div>
<div v-if="member.showPhone && member.phone" class="flex items-center">
<Phone :size="16" class="mr-2 text-primary-600" />
<a :href="`tel:${member.phone}`" class="hover:text-primary-600">{{ member.phone }}</a>
<div
v-if="member.showPhone && member.phone"
class="flex items-center"
>
<Phone
:size="16"
class="mr-2 text-primary-600"
/>
<a
:href="`tel:${member.phone}`"
class="hover:text-primary-600"
>{{ member.phone }}</a>
</div>
</template>
<!-- Sichtbarkeits-Flags anzeigen -->
<div class="col-span-2 flex items-center gap-2 mt-2 text-xs text-gray-500">
<span v-if="member.showEmail" title="E-Mail sichtbar">📧</span>
<span v-else title="E-Mail verborgen" class="opacity-40">📧</span>
<span v-if="member.showPhone" title="Telefon sichtbar">📞</span>
<span v-else title="Telefon verborgen" class="opacity-40">📞</span>
<span v-if="member.showAddress" title="Adresse sichtbar">🏠</span>
<span v-else title="Adresse verborgen" class="opacity-40">🏠</span>
<span v-if="member.showBirthday" title="Geburtstag sichtbar">🎂</span>
<span v-else title="Geburtstag verborgen" class="opacity-40">🎂</span>
<span
v-if="member.showEmail"
title="E-Mail sichtbar"
>📧</span>
<span
v-else
title="E-Mail verborgen"
class="opacity-40"
>📧</span>
<span
v-if="member.showPhone"
title="Telefon sichtbar"
>📞</span>
<span
v-else
title="Telefon verborgen"
class="opacity-40"
>📞</span>
<span
v-if="member.showAddress"
title="Adresse sichtbar"
>🏠</span>
<span
v-else
title="Adresse verborgen"
class="opacity-40"
>🏠</span>
<span
v-if="member.showBirthday"
title="Geburtstag sichtbar"
>🎂</span>
<span
v-else
title="Geburtstag verborgen"
class="opacity-40"
>🎂</span>
</div>
<div
v-if="member.address"

View File

@@ -98,22 +98,44 @@
<!-- Sichtbarkeits-Einstellungen -->
<div class="mt-4 border-t border-gray-100 pt-4">
<h3 class="text-sm font-medium text-gray-900 mb-2">Sichtbarkeit für andere Mitglieder</h3>
<h3 class="text-sm font-medium text-gray-900 mb-2">
Sichtbarkeit für andere Mitglieder
</h3>
<div class="flex flex-col gap-2 text-sm text-gray-700">
<label class="inline-flex items-center">
<input type="checkbox" class="mr-2" v-model="visibility.showEmail" :disabled="isSaving" />
<input
v-model="visibility.showEmail"
type="checkbox"
class="mr-2"
:disabled="isSaving"
>
E-Mail für alle eingeloggten Mitglieder sichtbar
</label>
<label class="inline-flex items-center">
<input type="checkbox" class="mr-2" v-model="visibility.showPhone" :disabled="isSaving" />
<input
v-model="visibility.showPhone"
type="checkbox"
class="mr-2"
:disabled="isSaving"
>
Telefonnummer für alle eingeloggten Mitglieder sichtbar
</label>
<label class="inline-flex items-center">
<input type="checkbox" class="mr-2" v-model="visibility.showAddress" :disabled="isSaving" />
<input
v-model="visibility.showAddress"
type="checkbox"
class="mr-2"
:disabled="isSaving"
>
Adresse für alle eingeloggten Mitglieder sichtbar
</label>
<label class="inline-flex items-center">
<input type="checkbox" class="mr-2" v-model="visibility.showBirthday" :disabled="isSaving" />
<input
v-model="visibility.showBirthday"
type="checkbox"
class="mr-2"
:disabled="isSaving"
>
Geburtstag für alle eingeloggten Mitglieder sichtbar
</label>
</div>

View File

@@ -173,8 +173,10 @@
</div>
<!-- Optional password toggle for passkey users - vorläufig deaktiviert -->
<!--
<div v-if="usePasskey" class="flex items-center gap-2 text-sm text-gray-700">
<div
v-if="false"
class="flex items-center gap-2 text-sm text-gray-700"
>
<input
v-model="setPasswordForPasskey"
type="checkbox"
@@ -217,17 +219,23 @@
v-if="false"
class="bg-blue-50 border border-blue-200 rounded-lg p-4 text-xs space-y-3"
>
<div class="font-semibold text-blue-900 mb-2">🔍 Debug-Informationen (QR-Code):</div>
<div class="font-semibold text-blue-900 mb-2">
🔍 Debug-Informationen (QR-Code):
</div>
<div class="space-y-1 text-blue-800">
<div><strong>Challenge:</strong> <code class="bg-blue-100 px-1 rounded break-all">{{ debugChallenge }}</code></div>
<div><strong>RP-ID:</strong> <code class="bg-blue-100 px-1 rounded">{{ debugRpId }}</code></div>
<div v-if="debugRegistrationId"><strong>Registration-ID:</strong> <code class="bg-blue-100 px-1 rounded break-all">{{ debugRegistrationId }}</code></div>
<div v-if="debugRegistrationId">
<strong>Registration-ID:</strong> <code class="bg-blue-100 px-1 rounded break-all">{{ debugRegistrationId }}</code>
</div>
<div><strong>Origin:</strong> <code class="bg-blue-100 px-1 rounded">{{ typeof window !== 'undefined' ? window.location.origin : 'N/A (SSR)' }}</code></div>
</div>
<!-- FIDO QR-Code Info -->
<div class="mt-3 p-3 bg-purple-50 border border-purple-300 rounded">
<div class="font-semibold text-purple-900 mb-2">🔐 FIDO Cross-Device Info:</div>
<div class="font-semibold text-purple-900 mb-2">
🔐 FIDO Cross-Device Info:
</div>
<div class="text-xs text-purple-800 space-y-2">
<div><strong>QR-Code-Format:</strong> FIDO-URI (enthält öffentlichen Schlüssel + Secret)</div>
<div><strong>Hinweis:</strong> Der QR-Code enthält einen FIDO-URI, der vom Smartphone gescannt werden muss.</div>
@@ -270,10 +278,19 @@
</div>
<!-- Smartphone URL -->
<div v-if="debugSmartphoneUrl" class="mt-3 p-3 bg-green-50 border border-green-300 rounded">
<div class="font-semibold text-green-900 mb-2">📱 Alternative: Smartphone-URL (manuell öffnen):</div>
<div
v-if="debugSmartphoneUrl"
class="mt-3 p-3 bg-green-50 border border-green-300 rounded"
>
<div class="font-semibold text-green-900 mb-2">
📱 Alternative: Smartphone-URL (manuell öffnen):
</div>
<div class="break-all text-xs mb-2 p-2 bg-white rounded border">
<a :href="debugSmartphoneUrl" target="_blank" class="text-blue-600 hover:underline">
<a
:href="debugSmartphoneUrl"
target="_blank"
class="text-blue-600 hover:underline"
>
{{ debugSmartphoneUrl }}
</a>
</div>
@@ -281,23 +298,26 @@
<strong>Anleitung:</strong> Falls der QR-Code nicht funktioniert, öffnen Sie diese URL manuell auf Ihrem Smartphone.
</div>
<button
@click="copyToClipboard(debugSmartphoneUrl)"
class="px-3 py-1 bg-green-600 text-white text-xs rounded hover:bg-green-700"
@click="copyToClipboard(debugSmartphoneUrl)"
>
📋 URL kopieren
</button>
</div>
<!-- Full Options JSON -->
<div v-if="debugOptions" class="mt-3">
<div
v-if="debugOptions"
class="mt-3"
>
<details class="text-xs">
<summary class="cursor-pointer font-semibold text-blue-900 hover:text-blue-700 mb-2">
📄 Vollständige Options (JSON) - Klicken zum Anzeigen
</summary>
<pre class="mt-2 p-2 bg-gray-100 rounded overflow-auto text-xs max-h-60 border">{{ JSON.stringify(debugOptions, null, 2) }}</pre>
<button
@click="copyToClipboard(JSON.stringify(debugOptions, null, 2))"
class="mt-2 px-3 py-1 bg-gray-600 text-white text-xs rounded hover:bg-gray-700"
@click="copyToClipboard(JSON.stringify(debugOptions, null, 2))"
>
📋 JSON kopieren
</button>