Add /ban-role moderation command and improve module loading

This commit is contained in:
2026-09-27 01:04:44 +02:00
parent 65a281dbcb
commit a7f8263931
13 changed files with 959 additions and 56 deletions
+87 -17
View File
@@ -88,7 +88,7 @@
</div>
<div class="callout" id="botInactiveCallout" hidden>
<strong>Bot not added yet.</strong>
<p class="muted">The bot isn’t in this server yet. Invite it to configure settings.</p>
<p class="muted">The bot is not in this server yet. Invite it to configure settings.</p>
<p class="muted">You can review saved settings, but changes will stay inactive until the bot joins.</p>
</div>
<div class="field">
@@ -115,13 +115,53 @@
<div class="panel__header panel__header--split">
<div>
<h2>Command permissions</h2>
<p class="muted">Restrict commands to roles or specific users.</p>
<p class="muted">Review and edit restrictions faster with search, filters, and live summaries.</p>
</div>
<div class="button-row">
<button class="button button--ghost" id="clearAllCommands">Clear all restrictions</button>
<button class="button" id="copyFromGuild">Copy from another guild</button>
</div>
</div>
<div class="stats-grid">
<article class="stat-card">
<span class="stat-card__label">Commands</span>
<strong class="stat-card__value" id="permissionTotalCount">0</strong>
<span class="muted">Available moderation commands</span>
</article>
<article class="stat-card">
<span class="stat-card__label">Restricted</span>
<strong class="stat-card__value" id="permissionRestrictedCount">0</strong>
<span class="muted">Commands limited to specific roles or users</span>
</article>
<article class="stat-card">
<span class="stat-card__label">Open</span>
<strong class="stat-card__value" id="permissionOpenCount">0</strong>
<span class="muted">Commands currently available to everyone</span>
</article>
<article class="stat-card">
<span class="stat-card__label">Unsaved edits</span>
<strong class="stat-card__value" id="permissionChangedCount">0</strong>
<span class="muted">Cards changed since the last save</span>
</article>
</div>
<div class="toolbar">
<div class="field toolbar__field">
<label for="commandSearch">Find command</label>
<input type="search" id="commandSearch" placeholder="Search by command name" />
</div>
<div class="field toolbar__field">
<label for="commandFilter">Show</label>
<select id="commandFilter">
<option value="all">All commands</option>
<option value="restricted">Restricted only</option>
<option value="open">Open only</option>
<option value="changed">Changed only</option>
</select>
</div>
</div>
<div class="field">
<span>
Add command
@@ -132,48 +172,78 @@
<button class="button" id="addCommandButton">Add</button>
</div>
</div>
<div class="command-list" id="commandsList"></div>
<div class="empty-state" id="commandsEmpty" hidden>
No commands configured yet. Add one or invite the bot to sync commands.
No commands match the current filter. Try clearing the search or add a custom command override.
</div>
</section>
<section class="panel" id="warningsPanel" hidden>
<div class="warnings-layout">
<div class="warnings-pane">
<h2>User lookup</h2>
<p class="muted">Search members or paste an ID.</p>
<div class="panel__header">
<div>
<h2>User lookup</h2>
<p class="muted">Search members or paste an ID, then manage all warnings from one target card.</p>
</div>
</div>
<div class="field">
<label for="warningsUserSearch">Find member</label>
<input type="search" id="warningsUserSearch" placeholder="Type a username" />
<input type="search" id="warningsUserSearch" placeholder="Type at least 2 characters" />
<span class="muted" id="warningsSearchStatus">Start typing to search this guild.</span>
</div>
<div class="member-list" id="warningsUserResults"></div>
<p class="muted" id="warningsSelectedUser">No user selected.</p>
<div class="field">
<label for="warningsUserInput">User ID</label>
<label for="warningsUserInput">Manual user ID</label>
<input type="text" id="warningsUserInput" placeholder="123456789" />
</div>
<div class="selected-user-card" id="warningsSelectedCard">
<span class="selected-user-card__label">Current target</span>
<strong id="warningsSelectedUser">No user selected.</strong>
<span class="muted" id="warningsSelectedHint">Pick a member from search results or paste a Discord user ID.</span>
</div>
<div class="button-row">
<button class="button" id="fetchWarningsButton">Fetch warnings</button>
<button class="button" id="fetchWarningsButton">Load history</button>
<button class="button button--ghost" id="clearWarningsButton">Clear warnings</button>
</div>
<div class="spinner" id="warningsSpinner" hidden></div>
</div>
<div class="warnings-pane">
<h2>Warnings</h2>
<div class="empty-state" id="warningsEmpty" hidden>No warnings found.</div>
<div class="panel__header panel__header--split">
<div>
<h2>Warnings</h2>
<p class="muted">See warning history and add new entries for the selected target.</p>
</div>
<div class="warning-status-pill" id="warningsHistoryState">No history loaded</div>
</div>
<div class="stats-grid stats-grid--compact">
<article class="stat-card stat-card--compact">
<span class="stat-card__label">Total warnings</span>
<strong class="stat-card__value" id="warningsTotalCount">0</strong>
</article>
<article class="stat-card stat-card--compact">
<span class="stat-card__label">Latest warning</span>
<strong class="stat-card__value stat-card__value--small" id="warningsLatestDate">None</strong>
</article>
</div>
<div class="empty-state" id="warningsEmpty" hidden>No warnings found for this user.</div>
<ul id="warningsList"></ul>
<div class="divider"></div>
<h3>Add warning</h3>
<div class="field">
<label for="newWarningUserInput">User ID</label>
<input type="text" id="newWarningUserInput" placeholder="123456789" />
</div>
<p class="muted">The warning will be added to the currently selected target.</p>
<div class="field">
<label for="warningReasonInput">Reason</label>
<textarea id="warningReasonInput" placeholder="Reason for warning" maxlength="200"></textarea>
<textarea
id="warningReasonInput"
placeholder="Describe what happened and why the warning is being issued."
maxlength="500"
></textarea>
<div class="field__meta">
<span class="muted" id="warningCharCount">0/200</span>
<span class="muted" id="warningCharCount">0/500</span>
<span class="muted" id="warningStatus"></span>
</div>
</div>
+129
View File
@@ -552,6 +552,61 @@ textarea:focus-visible,
flex-wrap: wrap;
}
.toolbar {
display: grid;
grid-template-columns: minmax(240px, 1fr) 220px;
gap: 14px;
margin-bottom: 18px;
}
.toolbar__field {
margin-bottom: 0;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
gap: 12px;
margin-bottom: 18px;
}
.stats-grid--compact {
margin-top: 4px;
margin-bottom: 16px;
}
.stat-card {
padding: 14px;
border-radius: 14px;
border: 1px solid var(--border);
background: linear-gradient(180deg, rgba(21, 31, 39, 0.96), rgba(17, 26, 34, 0.9));
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.03);
}
.stat-card--compact {
padding: 12px;
}
.stat-card__label {
display: block;
margin-bottom: 8px;
color: var(--muted);
font-size: 0.82rem;
text-transform: uppercase;
letter-spacing: 0.06em;
}
.stat-card__value {
display: block;
font-size: 1.8rem;
line-height: 1.1;
margin-bottom: 8px;
}
.stat-card__value--small {
font-size: 1rem;
}
.command-list {
display: flex;
flex-direction: column;
@@ -578,6 +633,46 @@ textarea:focus-visible,
gap: 12px;
}
.command-card__title {
display: flex;
flex-direction: column;
gap: 6px;
}
.command-card__summary {
display: inline-flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
}
.mini-badge {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 4px 9px;
border-radius: 999px;
border: 1px solid var(--border);
color: var(--muted);
font-size: 0.76rem;
background: rgba(11, 17, 19, 0.25);
}
.mini-badge--accent {
border-color: rgba(44, 185, 198, 0.45);
color: #ccf7fb;
}
.mini-badge--warn {
border-color: rgba(244, 184, 96, 0.45);
color: #ffe8b1;
}
.mini-badge--ok {
border-color: rgba(51, 209, 122, 0.45);
color: #d6f5de;
}
.command-card__body {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
@@ -673,6 +768,7 @@ textarea:focus-visible,
border: 1px solid var(--border);
padding: 10px;
margin-bottom: 10px;
list-style: none;
}
.warning-item__meta {
@@ -697,6 +793,35 @@ textarea:focus-visible,
padding: 14px;
}
.selected-user-card {
display: flex;
flex-direction: column;
gap: 8px;
padding: 14px;
border-radius: 14px;
border: 1px solid rgba(44, 185, 198, 0.25);
background: linear-gradient(180deg, rgba(44, 185, 198, 0.08), rgba(17, 26, 34, 0.9));
margin-bottom: 16px;
}
.selected-user-card__label {
color: var(--muted);
font-size: 0.78rem;
text-transform: uppercase;
letter-spacing: 0.06em;
}
.warning-status-pill {
display: inline-flex;
align-items: center;
padding: 6px 10px;
border-radius: 999px;
border: 1px solid var(--border);
color: var(--muted);
background: rgba(17, 26, 34, 0.8);
font-size: 0.8rem;
}
.divider {
height: 1px;
background: var(--border);
@@ -869,4 +994,8 @@ strong {
flex-direction: column;
align-items: flex-start;
}
.toolbar {
grid-template-columns: 1fr;
}
}