Playtester werden jetzt beworben, nicht angeklickt
Deploy / check (push) Has been cancelled
Deploy / deploy (push) Has been cancelled

Der Knopf unter dem Playtester-Aufruf hat jeden aufgenommen, der ihn gedrueckt
hat — ohne Frage, ohne Pruefung. Fuer ein Programm, dessen Teilnehmer Zugang
und Alpha-Keys bekommen, ist das die falsche Tuer.

Statt einen zweiten Pruef-Ablauf danebenzustellen, laeuft die Aufnahme jetzt
ueber die Bewerbungs-Formulare, die es laengst gibt: Modal ausfuellen,
Review-Embed im Staff-Kanal,  oder , Rolle und Antwort-DM bei Zusage. Das
ist dieselbe Maschinerie, nur ein anderer Aufhaenger — und damit auch nur eine
Stelle, an der spaeter etwas kaputtgehen kann.

Ein Formular laesst sich als Playtester-Formular markieren ("Annahme traegt als
Playtester ein"). Wer dort angenommen wird, landet zusaetzlich in der
Playtester-Liste, damit die Schluessel-Verteilung ihn kennt. Nur eines kann es
sein — sonst wuesste /playtester-setup nicht, welches es posten soll.

/playtester-setup postet jetzt den Knopf dieses Formulars. Fehlt das Formular,
fehlen Fragen oder fehlt der Review-Kanal, sagt der Befehl das, statt einen
Aufruf zu posten, der ins Leere fuehrt.

Der alte Knopf in bereits geposteten Nachrichten nimmt niemanden mehr auf: er
verweist auf den Aufruf. Austreten bleibt Selbstbedienung — dafuer braucht es
keine Freigabe.

Im Panel steht im Playtester-Bereich, ueber welches Formular die Aufnahme
laeuft, ob es schon gepostet ist, und ein Weg dorthin. Ohne markiertes Formular
steht dort, dass gerade niemand hereinkommt — das ist sonst der Grund, warum
sich tagelang niemand bewirbt.

Geprueft: Routen, SQL gegen das Schema, und im Browser beide Zustaende des
Hinweises sowie der neue Schalter — beim Wechsel zwischen zwei Formularen
folgt er dem richtigen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-01 10:04:16 +02:00
co-authored by Claude Opus 5
parent ddd7610d6c
commit 1c5c7d2470
8 changed files with 139 additions and 31 deletions
+43 -5
View File
@@ -322,7 +322,7 @@ export default function Settings({ me }) {
// Bewerbungs-Formulare
const emptyForm = {
id: null, title: '', description: '', review_channel_id: '',
approve_role_id: '', post_channel_id: '', questions: [''],
approve_role_id: '', post_channel_id: '', questions: [''], playtester: false,
};
const [appForms, setAppForms] = useState([]);
const [formDraft, setFormDraft] = useState(emptyForm);
@@ -2195,10 +2195,29 @@ export default function Settings({ me }) {
<div className="settings-section">
<h2 className="settings-title">// Playtester</h2>
<p className="section-intro">
Wer sich über den Knopf aus <strong>/playtester-setup</strong> meldet, landet
hier mit Rolle. Schlüssel gehen per Direktnachricht raus; kommt sie nicht
an, bleibt der Schlüssel im Vorrat.
Beworben wird sich über ein Formular, angenommen vom Team wer angenommen
wird, landet hier mit Rolle. Schlüssel gehen per Direktnachricht raus; kommt
sie nicht an, bleibt der Schlüssel im Vorrat.
</p>
{(() => {
// Ohne markiertes Formular gibt es keinen Weg herein — das
// ist der haeufigste Grund, warum sich niemand bewirbt
const ptForm = appForms.find((f) => f.playtester);
return ptForm ? (
<p className="section-intro">
Aufnahme läuft über <strong>{ptForm.title}</strong>
{ptForm.message_id ? '' : ' — das Formular ist noch nicht gepostet'}.
{' '}<button className="btn btn-mini" onClick={() => setTab('bewerbungen')}>Formular öffnen</button>
</p>
) : (
<p className="notice">
Noch kein Playtester-Formular. Ohne eines kommt niemand herein
unter <strong>Bewerbungen</strong> ein Formular anlegen und dort
Annahme trägt als Playtester ein" ankreuzen.
{' '}<button className="btn btn-mini" onClick={() => setTab('bewerbungen')}>Zu den Bewerbungen</button>
</p>
);
})()}
<div className="field" data-setting="playtester_role_id">
<label>Playtester-Rolle <span className="field-hint">Bewerbungs-Post via /playtester-setup; leer = aus</span></label>
<PickSelect
@@ -2367,12 +2386,16 @@ export default function Settings({ me }) {
{appForms.map((f) => (
<div className="key-row" key={f.id}>
<span className="key-name">{f.title}</span>
<span className="key-scopes">{f.questions.length} Fragen{f.message_id ? ' · gepostet' : ' · Entwurf'}</span>
<span className="key-scopes">
{f.questions.length} Fragen{f.message_id ? ' · gepostet' : ' · Entwurf'}
{f.playtester ? ' · Playtester' : ''}
</span>
<button className="btn btn-mini" onClick={() => setFormDraft({
id: f.id, title: f.title, description: f.description,
review_channel_id: f.review_channel_id ?? '', approve_role_id: f.approve_role_id ?? '',
post_channel_id: f.post_channel_id ?? '',
questions: f.questions.length ? f.questions : [''],
playtester: Boolean(f.playtester),
})}>Bearbeiten</button>
<button className="card-delete" title="Formular + Post löschen" onClick={() => removeForm(f.id)}>✕</button>
</div>
@@ -2411,6 +2434,21 @@ export default function Settings({ me }) {
/>
</div>
</div>
<div className="field">
<label className="toggle">
<input
type="checkbox"
checked={Boolean(formDraft.playtester)}
onChange={(e) => setFormDraft({ ...formDraft, playtester: e.target.checked })}
/>
<span>Annahme trägt als Playtester ein</span>
<span className="field-hint">
Angenommene landen zusätzlich in der Playtester-Liste und bekommen von dort
ihren Schlüssel. <strong>/playtester-setup</strong> postet dann diesen Knopf —
so kann sich niemand mehr selbst aufnehmen. Es kann nur ein Formular sein.
</span>
</label>
</div>
<div className="field">
<label>Fragen <span className="field-hint">max 5 (Discord-Modal-Limit)</span></label>
{formDraft.questions.map((q, i) => (