288 lines
16 KiB
HTML
288 lines
16 KiB
HTML
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
<title>Discord Bot Dashboard</title>
|
|
<link rel="stylesheet" href="styles.css" />
|
|
</head>
|
|
<body>
|
|
<div class="app-shell">
|
|
<header class="navbar">
|
|
<div class="navbar__brand">
|
|
<div class="brand-mark">DB</div>
|
|
<div>
|
|
<p class="muted">Discord Bot Dashboard</p>
|
|
<strong id="userDisplay">Loading session...</strong>
|
|
<p class="muted" id="sessionExpiry"></p>
|
|
</div>
|
|
</div>
|
|
<div class="navbar__actions">
|
|
<a class="button button--ghost" href="/">Home</a>
|
|
<button class="button button--primary" id="logoutButton">Log out</button>
|
|
</div>
|
|
</header>
|
|
|
|
<div class="error-banner" id="errorBanner" hidden>
|
|
<div>
|
|
<strong>Something went wrong.</strong>
|
|
<p data-role="error-message"></p>
|
|
</div>
|
|
<button class="button button--ghost" data-role="error-copy">Copy error details</button>
|
|
</div>
|
|
|
|
<div class="toast-container" id="toastContainer"></div>
|
|
|
|
<div class="layout">
|
|
<aside class="sidebar">
|
|
<div class="sidebar__header">
|
|
<div>
|
|
<h2>Guilds</h2>
|
|
<p class="muted">Pick a server to manage.</p>
|
|
</div>
|
|
<input type="search" id="guildSearch" placeholder="Search guilds" />
|
|
</div>
|
|
|
|
<div class="guild-list" id="guildList"></div>
|
|
<div class="skeleton-list" id="guildListSkeleton" hidden>
|
|
<div class="skeleton-line"></div>
|
|
<div class="skeleton-line"></div>
|
|
<div class="skeleton-line"></div>
|
|
<div class="skeleton-line"></div>
|
|
</div>
|
|
<div class="empty-state" id="guildEmpty" hidden>
|
|
No manageable guilds found. Make sure you have Manage Server permissions.
|
|
</div>
|
|
</aside>
|
|
|
|
<main class="content">
|
|
<section class="guild-header">
|
|
<div class="guild-header__info">
|
|
<img id="guildHeaderIcon" alt="" />
|
|
<div>
|
|
<h1 id="guildHeaderName">Select a guild</h1>
|
|
<div class="guild-header__status">
|
|
<span class="status-badge" id="guildStatusBadge"></span>
|
|
<span class="muted" id="guildStatusNote"></span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="guild-header__actions">
|
|
<button class="button button--primary" id="inviteButton" hidden>Invite bot</button>
|
|
<button class="button button--ghost" id="refreshBotButton" hidden>Refresh status</button>
|
|
</div>
|
|
</section>
|
|
|
|
<div class="tabs">
|
|
<button class="tab active" data-tab="settings">Settings</button>
|
|
<button class="tab" data-tab="permissions">Permissions</button>
|
|
<button class="tab" data-tab="warnings">Warnings</button>
|
|
</div>
|
|
|
|
<section class="panel" id="settingsPanel">
|
|
<div class="panel__header">
|
|
<div>
|
|
<h2>Admin access</h2>
|
|
<p class="muted">Control who can edit bot configuration and permissions.</p>
|
|
</div>
|
|
</div>
|
|
<div class="callout" id="botInactiveCallout" hidden>
|
|
<strong>Bot not added yet.</strong>
|
|
<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">
|
|
<span>
|
|
Admin roles
|
|
<span class="tooltip" title="Members with these roles can edit dashboard settings.">?</span>
|
|
</span>
|
|
<div id="adminRolesPicker"></div>
|
|
</div>
|
|
<div class="field">
|
|
<span>
|
|
Admin user IDs
|
|
<span class="tooltip" title="Paste one or more user IDs separated by commas or spaces.">?</span>
|
|
</span>
|
|
<div class="input-row">
|
|
<input type="text" id="adminUserInput" placeholder="Paste user IDs" />
|
|
<button class="button" id="addAdminUserButton">Add</button>
|
|
</div>
|
|
<div class="chip-list" id="adminUserChips"></div>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="panel" id="permissionsPanel" hidden>
|
|
<div class="panel__header panel__header--split">
|
|
<div>
|
|
<h2>Command permissions</h2>
|
|
<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
|
|
<span class="tooltip" title="Create new command overrides for custom commands.">?</span>
|
|
</span>
|
|
<div class="input-row">
|
|
<input type="text" id="newCommandInput" placeholder="command-name" />
|
|
<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 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">
|
|
<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 at least 2 characters" />
|
|
<span class="muted" id="warningsSearchStatus">Start typing to search this guild.</span>
|
|
</div>
|
|
<div class="member-list" id="warningsUserResults"></div>
|
|
<div class="field">
|
|
<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">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">
|
|
<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>
|
|
<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="Describe what happened and why the warning is being issued."
|
|
maxlength="500"
|
|
></textarea>
|
|
<div class="field__meta">
|
|
<span class="muted" id="warningCharCount">0/500</span>
|
|
<span class="muted" id="warningStatus"></span>
|
|
</div>
|
|
</div>
|
|
<button class="button button--primary" id="addWarningButton">Add warning</button>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<div class="save-bar" id="saveBar" hidden>
|
|
<span class="save-indicator" id="dirtyIndicator">Unsaved changes</span>
|
|
<div class="button-row">
|
|
<button class="button button--ghost" id="discardChangesButton">Discard</button>
|
|
<button class="button button--primary" id="savePermissionsButton">Save changes</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="panel skeleton-panel" id="contentSkeleton" hidden>
|
|
<div class="skeleton-line"></div>
|
|
<div class="skeleton-line"></div>
|
|
<div class="skeleton-line"></div>
|
|
</div>
|
|
</main>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="modal" id="copyModal" hidden>
|
|
<div class="modal__content">
|
|
<h3>Copy permissions</h3>
|
|
<p class="muted">Select another guild to copy permissions from.</p>
|
|
<select id="copyGuildSelect"></select>
|
|
<div class="button-row">
|
|
<button class="button button--ghost" id="cancelCopyButton">Cancel</button>
|
|
<button class="button button--primary" id="confirmCopyButton">Copy</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script src="app.js"></script>
|
|
<script src="dashboard.js"></script>
|
|
</body>
|
|
</html>
|