Grundgerüst-Assistent: alle fehlenden Kanäle auf einmal
Der Einzelknopf war der Anfang; wer frisch startet, klickt sich damit sechzehnmal durch. Der Assistent macht das in einem Durchlauf. Erst eine Vorschau, bevor irgendetwas passiert: was neu entsteht, was schon existiert und nur verknüpft wird, welche Kanäle privat werden und in welche Kategorie alles kommt. Die Kategorie heißt wie die Marke und wird wiederverwendet, wenn es sie schon gibt. Der Plan ist nach Einstellung entdoppelt — Devlogs und Wochen-Rückblick brauchen denselben Kanal, der soll nicht zweimal auftauchen. Rollen kommen ohne Kategorie, die gibt es dort nicht. Zwischen zwei Anlagen liegen 350 ms. Discord verträgt ein Dutzend Kanal-Erstellungen am Stück schlecht, und ein Rate-Limit mitten im Lauf wäre die unangenehmste Art zu scheitern. Scheitert ein Schritt, laufen die übrigen weiter und das Ergebnis zeigt pro Zeile, was passiert ist. Vorher hätte ein einzelner Fehler den Rest verschluckt. Im Test: 19 offene Punkte, Vorschau zeigt 16, danach bleiben 2 — die beiden ohne Vorlage (Gitea-Token und Watchdog-Adressen), die sich nicht anlegen lassen. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -305,6 +305,10 @@ export default function Settings({ me }) {
|
||||
const [moduleGroups, setModuleGroups] = useState([]);
|
||||
const [modFilter, setModFilter] = useState('alle');
|
||||
const [creating, setCreating] = useState(null); // Einstellung, die gerade angelegt wird
|
||||
// Grundgerüst-Assistent
|
||||
const [plan, setPlan] = useState(null); // Vorschau, null = zu
|
||||
const [setupRunning, setSetupRunning] = useState(false);
|
||||
const [setupResult, setSetupResult] = useState(null);
|
||||
// Text-Vorlagen (was der Bot nach außen schreibt)
|
||||
const [templates, setTemplates] = useState([]);
|
||||
const [templateGroups, setTemplateGroups] = useState([]);
|
||||
@@ -502,6 +506,35 @@ export default function Settings({ me }) {
|
||||
}
|
||||
}
|
||||
|
||||
/** Vorschau holen: was würde der Assistent anlegen? */
|
||||
async function loadPlan() {
|
||||
try {
|
||||
const res = await apiGet('/api/setup/plan');
|
||||
setPlan(res);
|
||||
} catch (error) {
|
||||
flash(`✗ ${error.body?.error ?? 'Vorschau fehlgeschlagen'}`);
|
||||
}
|
||||
}
|
||||
|
||||
/** Grundgerüst anlegen — erst nach der Vorschau erreichbar */
|
||||
async function runSetup() {
|
||||
setSetupRunning(true);
|
||||
try {
|
||||
const res = await apiPost('/api/setup/run', { kategorie: plan.kategorie });
|
||||
setModules(res.modules);
|
||||
setForm(res.settings);
|
||||
setData((d) => (d ? { ...d, settings: res.settings } : d));
|
||||
setSetupResult(res.ergebnisse);
|
||||
setPlan(null);
|
||||
const neu = res.ergebnisse.filter((r) => r.ok && !r.wiederverwendet).length;
|
||||
flash(`✓ ${neu} neu angelegt`);
|
||||
} catch (error) {
|
||||
flash(`✗ ${error.body?.error ?? 'Fehlgeschlagen'}`);
|
||||
} finally {
|
||||
setSetupRunning(false);
|
||||
}
|
||||
}
|
||||
|
||||
/** Fehlenden Kanal / fehlende Rolle vom Bot anlegen lassen */
|
||||
async function createMissing(entry) {
|
||||
setCreating(entry.setting);
|
||||
@@ -1037,8 +1070,70 @@ export default function Settings({ me }) {
|
||||
<>
|
||||
<p className="section-intro">
|
||||
Diese Module sind eingeschaltet, aber es fehlt ihnen noch etwas —
|
||||
bis dahin tun sie nichts.
|
||||
bis dahin tun sie nichts. Einzeln anlegen, oder alles auf einmal.
|
||||
</p>
|
||||
|
||||
{/* Grundgerüst: erst Vorschau, dann anlegen */}
|
||||
{!plan && (
|
||||
<div className="settings-actions" style={{ marginBottom: '1.1rem' }}>
|
||||
<button className="btn" onClick={loadPlan}>
|
||||
Alles auf einmal anlegen …
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{plan && (
|
||||
<div className="setup-plan">
|
||||
<p className="setup-plan-intro">
|
||||
{plan.schritte.filter((s) => !s.vorhanden).length} werden neu angelegt
|
||||
{plan.schritte.some((s) => s.vorhanden)
|
||||
&& ` · ${plan.schritte.filter((s) => s.vorhanden).length} sind schon da und werden nur verknüpft`}
|
||||
. Neue Kanäle landen in der Kategorie <strong>{plan.kategorie}</strong>.
|
||||
</p>
|
||||
<div className="setup-plan-list">
|
||||
{plan.schritte.map((s) => (
|
||||
<div className={`setup-step${s.vorhanden ? ' da' : ''}`} key={s.setting}>
|
||||
<span className="setup-step-name">
|
||||
{s.art === 'rolle' ? '@' : '#'}{s.name}
|
||||
</span>
|
||||
<span className="setup-step-note">
|
||||
{s.vorhanden ? 'vorhanden — wird verknüpft' : s.privat ? 'neu, privat' : 'neu'}
|
||||
</span>
|
||||
<span className="setup-step-for">{s.label}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="settings-actions">
|
||||
<button className="btn btn-save" disabled={setupRunning} onClick={runSetup}>
|
||||
{setupRunning ? 'Legt an …' : 'Jetzt anlegen'}
|
||||
</button>
|
||||
<button className="btn btn-ghost" disabled={setupRunning} onClick={() => setPlan(null)}>
|
||||
Abbrechen
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{setupResult && (
|
||||
<div className="setup-plan">
|
||||
<p className="setup-plan-intro">Ergebnis:</p>
|
||||
<div className="setup-plan-list">
|
||||
{setupResult.map((r) => (
|
||||
<div className={`setup-step${r.ok ? '' : ' fehler'}`} key={r.setting}>
|
||||
<span className="setup-step-name">{r.name ?? r.label}</span>
|
||||
<span className="setup-step-note">
|
||||
{r.ok
|
||||
? (r.wiederverwendet ? 'verknüpft' : 'angelegt')
|
||||
: r.fehler}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="settings-actions">
|
||||
<button className="btn btn-ghost" onClick={() => setSetupResult(null)}>Schließen</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="key-list">
|
||||
{unfinished.map((m) => (
|
||||
<div className="key-row" key={m.id}>
|
||||
|
||||
Reference in New Issue
Block a user