feat: mehrere Rollen duerfen herein, nicht nur eine

Wer EINE der eingetragenen Rollen hat, kommt ins Dashboard. Mehrere sind der
Normalfall, sobald mehr als eine Person Zugang haben soll -- Admin und Fotograf
sind selten dieselbe Rolle.

Die bisherige einzelne Rolle geht NICHT verloren: der alte Schluessel
(discord.role_id) wird weitergelesen, bis die Liste einmal gepflegt wird; dann
raeumt das Speichern ihn weg. Zwei Quellen fuer dieselbe Frage laufen sonst
auseinander. Nachgemessen: die vorhandene Rolle stand nach dem Umbau
unveraendert drin, bereit blieb true.

Getrennt wird an Komma, Semikolon, Leerzeichen und Zeilenumbruch -- wer drei
IDs untereinander aus Discord kopiert, soll sie nicht erst zu einer Zeile
zusammenschieben. Doppelte fallen zusammen.

UND DIE SCHLECHTE WIRD BEIM NAMEN GENANNT: "123 ist keine Rollen-ID (17 bis 20
Ziffern)" statt "Rollen-ID ungueltig". Bei fuenf Eintraegen ist die zweite
Meldung eine Suchaufgabe.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-11 18:53:28 +02:00
co-authored by Claude Opus 5
parent 1c5bf20eb0
commit 23405d68d3
5 changed files with 93 additions and 30 deletions
+3 -2
View File
@@ -154,7 +154,8 @@ export type Einstellungen = {
discord: {
clientId: string
guildId: string
roleId: string
/** Wer EINE davon hat, darf herein. */
roleIds: string[]
/** Nur OB eines hinterlegt ist — das Geheimnis selbst geht nie heraus. */
hatGeheimnis: boolean
bereit: boolean
@@ -163,7 +164,7 @@ export type Einstellungen = {
clientId: boolean
clientSecret: boolean
guildId: boolean
roleId: boolean
roleIds: boolean
}
}
}
+25 -11
View File
@@ -19,7 +19,7 @@ export function DiscordKarte() {
const [clientId, setClientId] = useState('')
const [geheimnis, setGeheimnis] = useState('')
const [guildId, setGuildId] = useState('')
const [roleId, setRoleId] = useState('')
const [rollen, setRollen] = useState('')
async function laden() {
setLaedt(true)
@@ -28,7 +28,7 @@ export function DiscordKarte() {
setDaten(e)
setClientId(e.discord.clientId)
setGuildId(e.discord.guildId)
setRoleId(e.discord.roleId)
setRollen(e.discord.roleIds.join('\n'))
} catch (err) {
note('fehler', err instanceof Error ? err.message : 'Unbekannt')
} finally {
@@ -51,7 +51,7 @@ export function DiscordKarte() {
// das Geheimnis wegwerfen, weil es nie im Formular steht.
...(geheimnis ? { clientSecret: geheimnis } : {}),
guildId,
roleId,
roleIds: rollen,
}),
(r) =>
r.bereit
@@ -84,7 +84,8 @@ export function DiscordKarte() {
rechts={
d.bereit ? (
<span className="flex items-center gap-1.5 text-xs text-gut">
<ShieldCheck size={14} /> eingerichtet
<ShieldCheck size={14} /> eingerichtet ·{' '}
{d.roleIds.length === 1 ? '1 Rolle' : `${d.roleIds.length} Rollen`}
</span>
) : (
<span className="flex items-center gap-1.5 text-xs text-schrift-leise">
@@ -156,13 +157,26 @@ export function DiscordKarte() {
hinweis={d.ausDerUmgebung.guildId ? 'kommt derzeit aus der Umgebung' : undefined}
onChange={(e) => setGuildId(e.target.value)}
/>
<Feld
label="Rollen-ID"
value={roleId}
placeholder="Wer diese Rolle hat, darf herein"
hinweis={d.ausDerUmgebung.roleId ? 'kommt derzeit aus der Umgebung' : undefined}
onChange={(e) => setRoleId(e.target.value)}
/>
<label className="block sm:col-span-2">
<span className="mb-1 block text-xs font-medium text-schrift-leise">
Rollen-IDs eine je Zeile
</span>
<textarea
value={rollen}
onChange={(e) => setRollen(e.target.value)}
rows={3}
spellCheck={false}
placeholder={'143902xxxxxxxxxxxxx\n143903xxxxxxxxxxxxx'}
className="w-full rounded-md border border-rand bg-grund px-3 py-1.5 font-mono
text-sm text-schrift placeholder:text-schrift-leise/60
focus:border-akzent focus:outline-none"
/>
<span className="mt-1 block text-xs text-schrift-leise">
{d.ausDerUmgebung.roleIds
? 'kommt derzeit aus der Umgebung'
: 'Wer EINE davon hat, darf herein. Komma, Semikolon oder Zeilenumbruch trennen — kopier sie einfach hinein.'}
</span>
</label>
</div>
<p className="text-xs text-schrift-leise">