Rollen (Admin/Mitglied), Seite Kooperationen, Textanpassungen

- Gemeinsame Zugänge mit Rollen: Admin darf alles, Mitglied nutzt Kalender,
  Fahrtplaner, Treffpunkte und bearbeitet Mitglieder (kein Hinzufügen,
  Löschen, Import, keine Patches, keine Statistik-Korrektur)
- Server prüft die Rechte (403), Konten ohne Rolle gelten als Admin
- npm run user: add <name> [admin|mitglied], role <name> <rolle>
- Neue öffentliche Seite Kooperationen mit Titelbild, Anführer,
  Social-Media-Links und Beschreibung, Pflege durch Admin
- Kopfzeile bleibt bei mittlerer Breite einzeilig
- Texte auf Wunsch gekürzt

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
guyg1474andClaude Opus 5.5 committed 2026-10-12 19:47:03 +02:00
1 parent f46016487d
commit 819219fb2d
18 files changed
+661 -106

No files matched your search

+1 -1
View File
@@ -79,7 +79,7 @@
<button type="button" id="select-none" class="small">Keine</button>
</div>
</div>
<p id="trip-members-empty" class="muted" hidden>Lege zuerst Mitglieder unter <a href="/gruppe">Gruppe</a> an.</p>
<p id="trip-members-empty" class="muted" hidden>Noch keine Mitglieder. Der Admin legt sie unter <a href="/gruppe">Gruppe</a> an.</p>
<ul id="trip-members" class="list"></ul>
</div>
</div>
+4 -3
View File
@@ -18,7 +18,7 @@
<div class="split wide-right">
<div class="col">
<div class="card">
<div class="card" id="member-form-card">
<h2 id="member-form-title">Mitglied hinzufügen</h2>
<form id="member-form">
<input type="hidden" id="member-id">
@@ -75,15 +75,16 @@
<div class="col">
<div class="card">
<h2>Gruppe</h2>
<p id="members-role-hint" class="muted" hidden>Du kannst Mitglieder bearbeiten.</p>
<p id="members-empty" class="muted" hidden>Noch keine Mitglieder gespeichert.</p>
<ul id="members-list" class="list"></ul>
</div>
<div class="card">
<h2>Daten sichern</h2>
<p class="muted">Gruppe als JSON-Datei sichern oder eine gesicherte Datei wieder einspielen (ersetzt die aktuelle Gruppe).</p>
<p class="muted" id="backup-text">Gruppe als JSON-Datei sichern oder eine gesicherte Datei wieder einspielen (ersetzt die aktuelle Gruppe).</p>
<div class="row">
<button id="export-btn">Exportieren (JSON)</button>
<label class="button">Importieren
<label class="button" id="import-btn">Importieren
<input type="file" id="import-file" accept="application/json" hidden>
</label>
</div>
+21 -5
View File
@@ -1,7 +1,10 @@
import { $, api, esc, sortByName, seatsLabel, showError, renderHeader } from './common.js';
import { $, api, esc, sortByName, seatsLabel, showError, renderHeader, isAdmin } from './common.js';
renderHeader('gruppe');
// Admin: Mitglieder hinzufügen, löschen, importieren. Mitglied-Zugang: nur bearbeiten.
// Der Server prüft das ebenfalls – hier wird nur ausgeblendet, was ohnehin abgelehnt würde.
let admin = false;
let members = [];
let points = [];
const form = $('#member-form');
@@ -31,6 +34,8 @@ function resetForm() {
$('#member-seats-wrap').hidden = true;
$('#member-cancel').hidden = true;
$('#member-form-title').textContent = 'Mitglied hinzufügen';
// Ohne Admin-Rechte gibt es das Formular nur zum Bearbeiten
$('#member-form-card').hidden = !admin;
}
$('#member-cancel').addEventListener('click', resetForm);
@@ -70,8 +75,9 @@ function edit(id) {
$('#member-seats-wrap').hidden = !m.hasCar;
$('#member-cancel').hidden = false;
$('#member-form-title').textContent = 'Mitglied bearbeiten';
$('#member-name').focus();
window.scrollTo({ top: 0, behavior: 'smooth' });
$('#member-form-card').hidden = false;
$('#member-form-card').scrollIntoView({ behavior: 'smooth', block: 'start' });
$('#member-name').focus({ preventScroll: true });
}
async function remove(id) {
@@ -98,7 +104,7 @@ function render() {
<span class="badge">${m.hasCar ? `Auto · ${seatsLabel(m.seats)}${m.alwaysDrives ? ' · fährt immer' : ''}` : 'kein Auto'}</span>
<span class="actions">
<button class="link" data-edit="${m.id}">Bearbeiten</button>
<button class="link danger" data-del="${m.id}">Löschen</button>
${admin ? `<button class="link danger" data-del="${m.id}">Löschen</button>` : ''}
</span>
</li>`).join('');
}
@@ -213,4 +219,14 @@ async function load() {
render();
}
load().then(loadPoints).catch(showError);
async function init() {
admin = await isAdmin();
$('#members-role-hint').hidden = admin;
$('#import-btn').hidden = !admin;
if (!admin) $('#backup-text').textContent = 'Gruppe als JSON-Datei sichern.';
resetForm();
await load();
await loadPoints();
}
init().catch(showError);
+2 -3
View File
@@ -12,7 +12,6 @@
<div>
<span class="eyebrow">Spieltage &amp; Termine</span>
<h1>Kalender</h1>
<p>Nur Termine mit Häkchen bei „Spiel-Event“ zählen für die Statistik.</p>
</div>
<div class="actions">
<a class="button" href="/api/calendar.ics" download title="Alle kommenden Termine als .ics-Datei für Google, Apple oder Outlook">
@@ -46,7 +45,7 @@
<select id="me-select"></select>
</label>
<p id="me-hint" class="muted" hidden>Wähle deinen Namen aus, damit du Termine eintragen und zusagen kannst.
Du fehlst in der Liste? Dann trag dich unter <a href="/gruppe">Gruppe</a> ein.</p>
Du fehlst in der Liste? Dann lass dich vom Admin unter <a href="/gruppe">Gruppe</a> eintragen.</p>
</div>
<div class="card">
@@ -61,7 +60,7 @@
<input type="checkbox" id="event-game" checked>
<div>
<strong>Spiel-Event</strong>
<span>Wer zusagt, bekommt ein Spiel in der Statistik. Für Treffen, Grillen oder Basteltage das Häkchen entfernen.</span>
<span>Wer zusagt, bekommt ein Spiel in der Statistik. Für Treffen oder Grillen das Häkchen entfernen.</span>
</div>
</label>
<div class="grid2">