Dashboard UX cleanup: fix German typos, add loading spinner, fix popstate, create static dir
Some checks failed
Deploy Discord Bot / deploy (push) Has been cancelled
Some checks failed
Deploy Discord Bot / deploy (push) Has been cancelled
This commit is contained in:
@@ -98,9 +98,11 @@ router.get('/', (req, res) => {
|
||||
<div class="layout">
|
||||
${sidebar}
|
||||
<div class="content">
|
||||
<h1>Waehle eine Guild aus</h1>
|
||||
<div class="muted">Nur Guilds, auf denen der Bot ist.</div>
|
||||
<main id="guildGrid"></main>
|
||||
<h1>Wähle einen Server aus</h1>
|
||||
<div class="muted">Nur Server, auf denen der Bot ist.</div>
|
||||
<main id="guildGrid">
|
||||
<div class="loading-wrap"><div class="spinner"></div><span>Lade Server...</span></div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
@@ -273,6 +275,9 @@ router.get('/', (req, res) => {
|
||||
@media (max-width: 1100px) {
|
||||
.tickets-grid { grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }
|
||||
}
|
||||
.spinner { width:28px; height:28px; border:3px solid rgba(255,255,255,0.12); border-top-color:var(--accent); border-radius:50%; animation:spin .6s linear infinite; margin:12px auto; }
|
||||
@keyframes spin { to { transform:rotate(360deg); } }
|
||||
.loading-wrap { display:flex; flex-direction:column; align-items:center; gap:6px; padding:24px; color:var(--muted); font-size:13px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -331,10 +336,10 @@ router.get('/', (req, res) => {
|
||||
<div class="card" style="display:flex; gap:10px; flex-wrap:wrap; align-items:center; justify-content:space-between;">
|
||||
<div>
|
||||
<p class="section-title">Tickets</p>
|
||||
<p class="section-sub">bersicht, Pipeline, SLA, Automationen, Knowledge-Base.</p>
|
||||
<p class="section-sub">Übersicht, Pipeline, SLA, Automationen, Knowledge-Base.</p>
|
||||
</div>
|
||||
<div class="row" style="gap:8px; flex-wrap:wrap;">
|
||||
<button class="secondary-btn ticket-tab-btn active" data-tab="overview">bersicht</button>
|
||||
<button class="secondary-btn ticket-tab-btn active" data-tab="overview">Übersicht</button>
|
||||
<button class="secondary-btn ticket-tab-btn" data-tab="pipeline">Pipeline</button>
|
||||
<button class="secondary-btn ticket-tab-btn" data-tab="sla">SLA</button>
|
||||
<button class="secondary-btn ticket-tab-btn" data-tab="automations">Automationen</button>
|
||||
@@ -347,7 +352,7 @@ router.get('/', (req, res) => {
|
||||
<div style="display:flex; align-items:center; justify-content:space-between; gap:12px;">
|
||||
<div>
|
||||
<p class="section-title">Ticketliste</p>
|
||||
<p class="section-sub">Links auswhlen, Details im Modal. Plus ffnet Panel-Erstellung.</p>
|
||||
<p class="section-sub">Links auswählen, Details im Modal. Plus öffnet Panel-Erstellung.</p>
|
||||
</div>
|
||||
<div class="row" style="gap:10px;">
|
||||
<button class="secondary-btn" id="openSupportLogin" type="button">Support-Login Einstellungen</button>
|
||||
@@ -382,7 +387,7 @@ router.get('/', (req, res) => {
|
||||
<div style="display:flex; justify-content:space-between; align-items:center; gap:12px;">
|
||||
<div>
|
||||
<p class="section-title">Status-Pipeline</p>
|
||||
<p class="section-sub">Tickets nach Phase. Status per Dropdown ndern.</p>
|
||||
<p class="section-sub">Tickets nach Phase. Status per Dropdown ändern.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid" style="grid-template-columns: repeat(auto-fit, minmax(240px,1fr)); gap:12px;" id="pipelineGrid">
|
||||
@@ -542,7 +547,7 @@ router.get('/', (req, res) => {
|
||||
<div class="form-field">
|
||||
<label class="form-label">Custom Badwords (kommagetrennt oder zeilenweise)</label>
|
||||
<textarea id="automodBadwords" rows="3" placeholder="badword1, badword2"></textarea>
|
||||
<p class="muted">Diese Woerter werden zusaetzlich zum Standard-Filter geblockt.</p>
|
||||
<p class="muted">Diese Wörter werden zusätzlich zum Standard-Filter geblockt.</p>
|
||||
</div>
|
||||
<div class="form-field">
|
||||
<label class="form-label">Whitelist-Rollen (IDs, Kommagetrennt)</label>
|
||||
@@ -627,7 +632,7 @@ router.get('/', (req, res) => {
|
||||
<div style="display:flex; justify-content:space-between; align-items:center; gap:12px; flex-wrap:wrap;">
|
||||
<div>
|
||||
<p class="section-title">Dynamic Voice</p>
|
||||
<p class="section-sub">Lobby waehlen, Channel-Namen & Limits setzen.</p>
|
||||
<p class="section-sub">Lobby wählen, Channel-Namen & Limits setzen.</p>
|
||||
</div>
|
||||
<div class="inline">
|
||||
<span class="form-label">Aktivieren</span>
|
||||
@@ -676,7 +681,7 @@ router.get('/', (req, res) => {
|
||||
<div class="row" style="justify-content:space-between; align-items:flex-start; gap:12px;">
|
||||
<div>
|
||||
<p class="section-title">Birthday</p>
|
||||
<p class="section-sub">Automatische Glueckwuensche je Guild.</p>
|
||||
<p class="section-sub">Automatische Glückwünsche je Server.</p>
|
||||
</div>
|
||||
<div class="row" style="align-items:center; gap:10px; flex-wrap:wrap; justify-content:flex-end;">
|
||||
<label class="form-label">Sendezeit (Stunde)</label>
|
||||
@@ -700,7 +705,7 @@ router.get('/', (req, res) => {
|
||||
<div class="row" style="justify-content:space-between; align-items:center;">
|
||||
<div>
|
||||
<p class="section-title">Gespeicherte Geburtstage</p>
|
||||
<p class="section-sub">Eintraege werden per /birthday angelegt.</p>
|
||||
<p class="section-sub">Einträge werden per /birthday angelegt.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div id="birthdayList" class="module-list"></div>
|
||||
@@ -736,7 +741,7 @@ router.get('/', (req, res) => {
|
||||
<textarea id="reactionRoleDescription" rows="2" placeholder="Kurze Beschreibung fuer das Embed"></textarea>
|
||||
</div>
|
||||
<div class="form-field">
|
||||
<label class="form-label">Eintraege (Emoji | Role ID | Label | Beschreibung)</label>
|
||||
<label class="form-label">Einträge (Emoji | Role ID | Label | Beschreibung)</label>
|
||||
<textarea id="reactionRoleEntries" rows="4" placeholder="😀 | 123456789 | Freunde | Erhaelt Freunde Rolle"></textarea>
|
||||
<p class="muted">Eine Zeile pro Zuordnung. Label/Beschreibung optional.</p>
|
||||
</div>
|
||||
@@ -1446,7 +1451,7 @@ router.get('/', (req, res) => {
|
||||
edit.addEventListener('click', () => editServerStat(item));
|
||||
const del = document.createElement('button');
|
||||
del.className = 'danger-btn';
|
||||
del.textContent = 'Loeschen';
|
||||
del.textContent = 'Löschen';
|
||||
del.addEventListener('click', () => {
|
||||
serverStatsCache.items = (serverStatsCache.items || []).filter((x) => x !== item);
|
||||
renderServerStats();
|
||||
@@ -1999,14 +2004,14 @@ router.get('/', (req, res) => {
|
||||
edit.addEventListener('click', () => fillAutomationForm(r));
|
||||
const del = document.createElement('button');
|
||||
del.className = 'danger-btn';
|
||||
del.textContent = 'Lschen';
|
||||
del.textContent = 'Löschen';
|
||||
del.addEventListener('click', async () => {
|
||||
const res = await fetch('/api/automations/' + r.id, {
|
||||
method: 'DELETE',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ guildId: currentGuild })
|
||||
});
|
||||
showToast(res.ok ? 'Regel gelscht' : 'Lschen fehlgeschlagen', !res.ok);
|
||||
showToast(res.ok ? 'Regel gelöscht' : 'Löschen fehlgeschlagen', !res.ok);
|
||||
if (res.ok) loadAutomations();
|
||||
});
|
||||
actions.appendChild(edit);
|
||||
@@ -2066,14 +2071,14 @@ router.get('/', (req, res) => {
|
||||
edit.addEventListener('click', () => fillKbForm(a));
|
||||
const del = document.createElement('button');
|
||||
del.className = 'danger-btn';
|
||||
del.textContent = 'Lschen';
|
||||
del.textContent = 'Löschen';
|
||||
del.addEventListener('click', async () => {
|
||||
const res = await fetch('/api/kb/' + a.id, {
|
||||
method: 'DELETE',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ guildId: currentGuild })
|
||||
});
|
||||
showToast(res.ok ? 'Artikel gelscht' : 'Lschen fehlgeschlagen', !res.ok);
|
||||
showToast(res.ok ? 'Artikel gelöscht' : 'Löschen fehlgeschlagen', !res.ok);
|
||||
if (res.ok) loadKb();
|
||||
});
|
||||
actions.appendChild(edit);
|
||||
@@ -2319,7 +2324,7 @@ router.get('/', (req, res) => {
|
||||
editBtn.addEventListener('click', () => fillEventForm(ev));
|
||||
const delBtn = document.createElement('button');
|
||||
delBtn.className = 'danger-btn';
|
||||
delBtn.textContent = 'Loeschen';
|
||||
delBtn.textContent = 'Löschen';
|
||||
delBtn.style.padding = '8px 10px';
|
||||
delBtn.addEventListener('click', () => deleteEvent(ev.id));
|
||||
actions.appendChild(editBtn);
|
||||
@@ -2385,7 +2390,7 @@ router.get('/', (req, res) => {
|
||||
async function deleteEvent(id) {
|
||||
if (!currentGuild) return;
|
||||
const res = await fetch('/api/events/' + id, { method:'DELETE', headers:{'Content-Type':'application/json'}, body: JSON.stringify({ guildId: currentGuild }) });
|
||||
showToast(res.ok ? 'Event geloescht' : 'Event loeschen fehlgeschlagen', !res.ok);
|
||||
showToast(res.ok ? 'Event gelöscht' : 'Event löschen fehlgeschlagen', !res.ok);
|
||||
if (res.ok) loadEvents();
|
||||
}
|
||||
|
||||
@@ -2539,7 +2544,7 @@ router.get('/', (req, res) => {
|
||||
delBtn.className = 'danger-btn';
|
||||
delBtn.style.padding = '8px 10px';
|
||||
delBtn.style.fontSize = '12px';
|
||||
delBtn.textContent = 'Loeschen';
|
||||
delBtn.textContent = 'Löschen';
|
||||
delBtn.addEventListener('click', () => deleteReactionRole(set.id));
|
||||
actions.appendChild(editBtn);
|
||||
actions.appendChild(syncBtn);
|
||||
@@ -2569,7 +2574,7 @@ router.get('/', (req, res) => {
|
||||
async function deleteReactionRole(id) {
|
||||
if (!currentGuild) return;
|
||||
const res = await fetch('/api/reactionroles/' + id, { method:'DELETE', headers:{'Content-Type':'application/json'}, body: JSON.stringify({ guildId: currentGuild }) });
|
||||
showToast(res.ok ? 'Geloescht' : 'Fehler beim Loeschen', !res.ok);
|
||||
showToast(res.ok ? 'Gelöscht' : 'Fehler beim Löschen', !res.ok);
|
||||
if (res.ok) {
|
||||
if (editingReactionRole === id) resetReactionRoleForm();
|
||||
loadReactionRoles();
|
||||
@@ -2999,6 +3004,12 @@ router.get('/', (req, res) => {
|
||||
});
|
||||
}
|
||||
|
||||
window.addEventListener('popstate', () => {
|
||||
const section = location.hash.replace('#', '') || 'overview';
|
||||
activateSection(section);
|
||||
if (section === 'admin' && isAdmin) loadAdminAll();
|
||||
});
|
||||
|
||||
const initialSection = location.hash.replace('#', '') || 'overview';
|
||||
activateSection(initialSection);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user