dialog: Diskussionsplattform mit Foren, Rollen & Moderation + RLS-Fix
Auth/RLS-Fix (Schreiben gab 400): - supabase.js: eigener supabaseAuth-Client für Login/Token-Check, damit signInWithPassword den Service-Daten-Client nicht prozessweit aufs User-Token umstellt (sonst lief insert als role=authenticated → RLS-Block). Rollen (admin > editor > user): - auth.js: roleOf() aus app_metadata.role + ADMIN_EMAILS, requireModerator. - users.js: Rolle anzeigen/setzen über GoTrue app_metadata; .env-Admins fix. Datenmodell (schema.sql): - forums (Kategorien) + threads; Seed Allgemein/Projekte/Technik/Off-Topic und Sonder-Kategorie Beiträge. Library-Beiträge werden als Threads gespiegelt (dialog-store.syncLibrary). API (routes/dialog.js, dialog-store.js): - öffentlich: /api/forums, /api/forums/:slug, /api/recent, /api/thread - eingeloggt: POST /api/threads (Thread starten, nur in Foren) - Moderation: /api/mod/* (sperren/ausblenden), Admin: /api/admin/forums CRUD - comments: Lock-Prüfung beim Schreiben, Moderation darf jede löschen. Frontend: - static/dialog.js: Router (Übersicht-Split-View | Forum | Thread), neuer Thread, Mod-Leiste, subtiles Login (dezente Zeile statt Formular). - Admin-UI: Tabs Foren + Moderation, Rollen-Dropdown bei Autor:innen. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -510,6 +510,57 @@ a.byline-author:hover, a.journal-author:hover { color: var(--accent); }
|
||||
|
||||
.dialog-composer { display: flex; flex-direction: column; gap: 0.6em; }
|
||||
.dialog-loginhint { color: var(--color-text-muted); margin: 0; }
|
||||
/* Subtiles Login: dezenter Text-Link statt dominantem Formular. */
|
||||
.dialog-loginlink {
|
||||
align-self: flex-start; font: inherit; font-size: var(--font-size-small);
|
||||
background: none; border: none; padding: 0.2em 0; cursor: pointer;
|
||||
color: var(--color-text-muted); text-decoration: underline; text-underline-offset: 3px;
|
||||
}
|
||||
.dialog-loginlink:hover { color: var(--accent); }
|
||||
.dialog-loginform { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5em; }
|
||||
.dialog-loginform .dialog-input { width: auto; flex: 1 1 9em; min-width: 8em; padding: 0.45em 0.7em; }
|
||||
.dialog-loginform .dialog-send { padding: 0.45em 1em; }
|
||||
.dialog-logincancel { align-self: center; }
|
||||
|
||||
/* ── Übersicht: Split-View (links letzte Wortmeldungen, rechts Foren) ── */
|
||||
.dialog-split { display: grid; grid-template-columns: 1.2fr 1fr; gap: 2.4em; align-items: start; }
|
||||
@media (max-width: 720px) { .dialog-split { grid-template-columns: 1fr; gap: 1.6em; } }
|
||||
.dialog-recent-list, .dialog-forum-list, .dialog-thread-list { display: flex; flex-direction: column; gap: 0.7em; }
|
||||
.dialog-recent-item, .dialog-forum-item, .dialog-thread-item {
|
||||
display: block; text-decoration: none; color: inherit;
|
||||
border: 1px solid var(--color-border); border-radius: 12px; padding: 0.85em 1em; background: var(--color-bg-primary);
|
||||
transition: border-color .12s, transform .12s;
|
||||
}
|
||||
.dialog-recent-item:hover, .dialog-forum-item:hover, .dialog-thread-item:hover { border-color: var(--accent); }
|
||||
.dialog-recent-top { display: flex; justify-content: space-between; gap: 1em; align-items: baseline; }
|
||||
.dialog-recent-author { font-weight: 600; }
|
||||
.dialog-recent-meta, .dialog-thread-meta, .dialog-forum-meta { color: var(--color-text-muted); font-size: var(--font-size-small); }
|
||||
.dialog-recent-thread { font-family: var(--font-family-serif); font-style: italic; color: var(--accent); margin: 0.15em 0; }
|
||||
.dialog-recent-body { color: var(--color-text-muted); font-size: var(--font-size-small);
|
||||
display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
|
||||
|
||||
/* Foren-Karten — linke Akzentkante aus der Forum-Farbe */
|
||||
.dialog-forum-item { border-left: 3px solid var(--forum-accent, var(--accent)); }
|
||||
.dialog-forum-name { display: block; font-weight: 600; font-size: 1.05em; }
|
||||
.dialog-forum-desc { display: block; color: var(--color-text-muted); font-size: var(--font-size-small); margin-top: 0.2em; }
|
||||
|
||||
/* Forum-Ansicht */
|
||||
.dialog-forum-head { border-left: 4px solid var(--forum-accent, var(--accent)); padding-left: 0.6em; }
|
||||
.dialog-thread-title { display: block; font-weight: 600; }
|
||||
.dialog-lock { color: var(--color-text-muted); }
|
||||
.dialog-newthread { margin: 0.4em 0 1.4em; display: flex; flex-direction: column; gap: 0.6em; }
|
||||
.dialog-newbtn { align-self: flex-start; font: inherit; cursor: pointer; padding: 0.5em 1.2em; border-radius: 999px;
|
||||
background: var(--accent); color: #fff; border: 1px solid var(--accent); }
|
||||
.dialog-newbtn:hover { background: #a23f23; }
|
||||
|
||||
/* Thread-Ansicht: Moderationsleiste */
|
||||
.dialog-modbar { display: flex; align-items: center; gap: 0.6em; margin: 0.2em 0 1em; flex-wrap: wrap; }
|
||||
.dialog-modbar:empty { display: none; }
|
||||
.dialog-modlabel { color: var(--color-text-muted); font-size: var(--font-size-small); }
|
||||
.dialog-modbtn { font: inherit; font-size: var(--font-size-small); cursor: pointer; padding: 0.3em 0.9em; border-radius: 999px;
|
||||
background: none; border: 1px solid var(--color-border); color: var(--color-text-muted); }
|
||||
.dialog-modbtn:hover { border-color: var(--accent); color: var(--accent); }
|
||||
.dialog-locked { color: var(--color-text-muted); font-style: italic; margin: 0.4em 0; }
|
||||
.dialog-textarea, .dialog-input {
|
||||
width: 100%; font: inherit; padding: 0.7em 0.9em;
|
||||
border: 1px solid var(--color-border); border-radius: 10px; background: var(--color-bg-primary);
|
||||
|
||||
Reference in New Issue
Block a user