Files
broccolini-bot/settings-site/public/index.html
2026-04-18 11:10:41 +00:00

385 lines
30 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Broccolini Settings</title>
<link rel="stylesheet" href="/css/main.css">
</head>
<body>
<div id="loading" class="loading"><div class="spinner"></div></div>
<div id="toast-container"></div>
<!-- Sidebar -->
<nav class="sidebar">
<div class="logo">Broccolini Settings</div>
<a href="/" class="active">Core</a>
<a href="/channels">Channels</a>
<a href="/categories">Categories</a>
<a href="/gmail">Gmail</a>
<a href="/behavior">Ticket Behavior</a>
<a href="/threads">Staff Threads</a>
<a href="/pins">Pin Messages</a>
<a href="/notifications">Notifications</a>
<a href="/logging">Logging</a>
<a href="/automation">Automation</a>
<a href="/appearance">Appearance</a>
<a href="/staff">Staff</a>
<a href="/advanced">Advanced</a>
</nav>
<!-- Top bar -->
<div class="topbar">
<h1>Settings</h1>
<div class="status">
<span class="dot" id="bot-status-dot"></span>
<span id="bot-status-text">Checking...</span>
</div>
<div class="actions">
<button type="button" id="logout-btn">Logout</button>
</div>
</div>
<!-- Main content -->
<div class="main">
<!-- 1. Core -->
<div class="section" id="s-core">
<div class="section-header"><h2>Core</h2><p>Discord bot credentials and guild</p><span class="chevron">&#9660;</span></div>
<div class="section-body"><div class="field-grid">
<div class="field"><label>Discord Token</label><input type="password" data-key="DISCORD_TOKEN" placeholder="Bot token"></div>
<div class="field"><label>Application ID</label><input type="text" data-key="DISCORD_APPLICATION_ID"></div>
<div class="field"><label>Guild ID</label><input type="text" data-key="DISCORD_GUILD_ID"></div>
</div></div>
</div>
<!-- 2. Channels -->
<div class="section" id="s-channels">
<div class="section-header"><h2>Channels</h2><p>Channel assignments for logging, transcripts, and alerts</p><span class="chevron">&#9660;</span></div>
<div class="section-body"><div class="field-grid">
<div class="field"><label>Transcript Channel</label><input type="text" data-key="TRANSCRIPT_CHANNEL_ID" data-smart="channel"></div>
<div class="field"><label>Logging Channel</label><input type="text" data-key="LOGGING_CHANNEL_ID" data-smart="channel"></div>
<div class="field"><label>Debugging Channel</label><input type="text" data-key="DEBUGGING_CHANNEL_ID" data-smart="channel"></div>
<div class="field"><label>Backup/Export Channel</label><input type="text" data-key="BACKUP_EXPORT_CHANNEL_ID" data-smart="channel"></div>
<div class="field"><label>Account Info Channel</label><input type="text" data-key="ACCOUNT_INFO_CHANNEL_ID" data-smart="channel"></div>
<div class="field"><label>Gmail Log Channel</label><input type="text" data-key="GMAIL_LOG_CHANNEL_ID" data-smart="channel"></div>
<div class="field"><label>Automation Log Channel</label><input type="text" data-key="AUTOMATION_LOG_CHANNEL_ID" data-smart="channel"></div>
<div class="field"><label>Rename Log Channel</label><input type="text" data-key="RENAME_LOG_CHANNEL_ID" data-smart="channel"></div>
<div class="field"><label>Security Log Channel</label><input type="text" data-key="SECURITY_LOG_CHANNEL_ID" data-smart="channel"></div>
<div class="field"><label>System Log Channel</label><input type="text" data-key="SYSTEM_LOG_CHANNEL_ID" data-smart="channel"></div>
<div class="field"><label>All Staff Channel</label><input type="text" data-key="ALL_STAFF_CHANNEL_ID" data-smart="channel"></div>
<div class="field"><label>Chat Alert Channel</label><input type="text" data-key="ALL_STAFF_CHAT_ALERT_CHANNEL_ID" data-smart="channel"></div>
<div class="field"><label>User Patterns Channel</label><input type="text" data-key="USER_PATTERNS_CHANNEL_ID" data-smart="channel"></div>
<div class="field"><label>Game Patterns Channel</label><input type="text" data-key="GAME_PATTERNS_CHANNEL_ID" data-smart="channel"></div>
<div class="field"><label>Tag Patterns Channel</label><input type="text" data-key="TAG_PATTERNS_CHANNEL_ID" data-smart="channel"></div>
<div class="field"><label>Escalation Patterns Channel</label><input type="text" data-key="ESCALATION_PATTERNS_CHANNEL_ID" data-smart="channel"></div>
<div class="field"><label>Staff Patterns Channel</label><input type="text" data-key="STAFF_PATTERNS_CHANNEL_ID" data-smart="channel"></div>
<div class="field"><label>Combined Patterns Channel</label><input type="text" data-key="COMBINED_PATTERNS_CHANNEL_ID" data-smart="channel"></div>
</div></div>
</div>
<!-- 3. Categories -->
<div class="section" id="s-categories">
<div class="section-header"><h2>Categories</h2><p>Ticket category assignments and escalation targets</p><span class="chevron">&#9660;</span></div>
<div class="section-body"><div class="field-grid">
<div class="field"><label>Email Ticket Category</label><input type="text" data-key="TICKET_CATEGORY_ID" data-smart="category"></div>
<div class="field"><label>Discord Ticket Category</label><input type="text" data-key="DISCORD_TICKET_CATEGORY_ID" data-smart="category"></div>
<div class="field"><label>Email T2 Category</label><input type="text" data-key="EMAIL_ESCALATED2_CHANNEL_ID" data-smart="category"></div>
<div class="field"><label>Discord T2 Category</label><input type="text" data-key="DISCORD_ESCALATED2_CHANNEL_ID" data-smart="category"></div>
<div class="field"><label>Email T3 Category</label><input type="text" data-key="EMAIL_ESCALATED3_CHANNEL_ID" data-smart="category"></div>
<div class="field"><label>Discord T3 Category</label><input type="text" data-key="DISCORD_ESCALATED3_CHANNEL_ID" data-smart="category"></div>
<div class="field"><label>Staff Notification Category</label><input type="text" data-key="STAFF_NOTIFICATION_CATEGORY_ID" data-smart="category"></div>
<div class="field"><label>Category Name</label><input type="text" data-key="TICKET_CATEGORY_NAME"></div>
<div class="field"><label>T2 Category Name</label><input type="text" data-key="TICKET_T2_CATEGORY_NAME"></div>
<div class="field"><label>T3 Category Name</label><input type="text" data-key="TICKET_T3_CATEGORY_NAME"></div>
<div class="field"><label>Discord Thread Channel</label><input type="text" data-key="DISCORD_THREAD_CHANNEL_ID" data-smart="channel"></div>
<div class="field"><label>Email Thread Channel</label><input type="text" data-key="EMAIL_THREAD_CHANNEL_ID" data-smart="channel"></div>
</div></div>
</div>
<!-- 4. Gmail -->
<div class="section" id="s-gmail">
<div class="section-header"><h2>Gmail</h2><p>Google OAuth credentials and email settings</p><span class="chevron">&#9660;</span></div>
<div class="section-body"><div class="field-grid">
<div class="field"><label>Google Client ID</label><input type="text" data-key="GOOGLE_CLIENT_ID"></div>
<div class="field"><label>Google Client Secret</label><input type="password" data-key="GOOGLE_CLIENT_SECRET"></div>
<div class="field"><label>Refresh Token</label><input type="password" data-key="REFRESH_TOKEN"></div>
<div class="field"><label>Support Email</label><input type="email" data-key="MY_EMAIL"></div>
</div></div>
</div>
<!-- 5. Ticket Behavior -->
<div class="section" id="s-behavior">
<div class="section-header"><h2>Ticket Behavior</h2><p>Automation, limits, and messages</p><span class="chevron">&#9660;</span></div>
<div class="section-body"><div class="field-grid">
<div class="field"><label>Auto-Close</label><div class="toggle-wrap"><label class="toggle"><input type="checkbox" data-key="AUTO_CLOSE_ENABLED"><span class="slider"></span></label><span>Enabled</span></div></div>
<div class="field"><label>Auto-Close Hours</label><input type="number" data-key="AUTO_CLOSE_AFTER_HOURS"></div>
<div class="field"><label>Reminders</label><div class="toggle-wrap"><label class="toggle"><input type="checkbox" data-key="REMINDER_ENABLED"><span class="slider"></span></label><span>Enabled</span></div></div>
<div class="field"><label>Reminder Hours</label><input type="number" data-key="REMINDER_AFTER_HOURS"></div>
<div class="field"><label>Priority System</label><div class="toggle-wrap"><label class="toggle"><input type="checkbox" data-key="PRIORITY_ENABLED"><span class="slider"></span></label><span>Enabled</span></div></div>
<div class="field"><label>Claim Timeout</label><div class="toggle-wrap"><label class="toggle"><input type="checkbox" data-key="CLAIM_TIMEOUT_ENABLED"><span class="slider"></span></label><span>Enabled</span></div></div>
<div class="field"><label>Claim Timeout Hours</label><input type="number" data-key="CLAIM_TIMEOUT_HOURS"></div>
<div class="field"><label>Auto-Unclaim</label><div class="toggle-wrap"><label class="toggle"><input type="checkbox" data-key="AUTO_UNCLAIM_ENABLED"><span class="slider"></span></label><span>Enabled</span></div></div>
<div class="field"><label>Auto-Unclaim Hours</label><input type="number" data-key="AUTO_UNCLAIM_AFTER_HOURS"></div>
<div class="field"><label>Allow Claim Overwrite</label><div class="toggle-wrap"><label class="toggle"><input type="checkbox" data-key="ALLOW_CLAIM_OVERWRITE"><span class="slider"></span></label><span>Enabled</span></div></div>
<div class="field"><label>Global Ticket Limit</label><input type="number" data-key="GLOBAL_TICKET_LIMIT"></div>
<div class="field"><label>Rate Limit (per user)</label><input type="number" data-key="RATE_LIMIT_TICKETS_PER_USER"></div>
<div class="field"><label>Rate Limit Window (min)</label><input type="number" data-key="RATE_LIMIT_WINDOW_MINUTES"></div>
<div class="field"><label>Role to Ping</label><input type="text" data-key="ROLE_ID_TO_PING" data-smart="role"></div>
<div class="field full-width"><label>Welcome Message</label><textarea data-key="TICKET_WELCOME_MESSAGE" rows="3"></textarea></div>
<div class="field full-width"><label>Claimed Message</label><textarea data-key="TICKET_CLAIMED_MESSAGE" rows="2"></textarea><div class="hint">Variables: {staff_mention}, {staff_name}</div></div>
<div class="field full-width"><label>Unclaimed Message</label><textarea data-key="TICKET_UNCLAIMED_MESSAGE" rows="2"></textarea></div>
<div class="field full-width"><label>Escalation Message</label><textarea data-key="ESCALATION_MESSAGE" rows="3"></textarea><div class="hint">Variables: {support_name}</div></div>
<div class="field full-width"><label>Reminder Message</label><textarea data-key="REMINDER_MESSAGE" rows="2"></textarea><div class="hint">Variables: {ping}, {hours}</div></div>
</div></div>
</div>
<!-- 6. Staff Threads -->
<div class="section" id="s-threads">
<div class="section-header"><h2>Staff Threads</h2><p>Private staff discussion threads on ticket channels</p><span class="chevron">&#9660;</span></div>
<div class="section-body"><div class="field-grid">
<div class="field"><label>Enabled</label><div class="toggle-wrap"><label class="toggle"><input type="checkbox" data-key="STAFF_THREAD_ENABLED"><span class="slider"></span></label><span>Enabled</span></div></div>
<div class="field"><label>Thread Name</label><input type="text" data-key="STAFF_THREAD_NAME"></div>
<div class="field"><label>Auto-Add Role</label><div class="toggle-wrap"><label class="toggle"><input type="checkbox" data-key="STAFF_THREAD_AUTO_ADD_ROLE"><span class="slider"></span></label><span>Enabled</span></div></div>
<div class="field"><label>Staff Thread Role</label><input type="text" data-key="STAFF_THREAD_ROLE_ID" data-smart="role"></div>
</div></div>
</div>
<!-- 7. Pin Messages -->
<div class="section" id="s-pins">
<div class="section-header"><h2>Pin Messages</h2><p>Auto-pin welcome and escalation messages</p><span class="chevron">&#9660;</span></div>
<div class="section-body"><div class="field-grid">
<div class="field"><label>Pin Initial Message</label><div class="toggle-wrap"><label class="toggle"><input type="checkbox" data-key="PIN_INITIAL_MESSAGE_ENABLED"><span class="slider"></span></label><span>Enabled</span></div></div>
<div class="field"><label>Pin Escalation Message</label><div class="toggle-wrap"><label class="toggle"><input type="checkbox" data-key="PIN_ESCALATION_MESSAGE_ENABLED"><span class="slider"></span></label><span>Enabled</span></div></div>
<div class="field"><label>Suppress Pin Notice</label><div class="toggle-wrap"><label class="toggle"><input type="checkbox" data-key="PIN_SUPPRESS_SYSTEM_MESSAGE"><span class="slider"></span></label><span>Enabled</span></div></div>
</div></div>
</div>
<!-- 8. Notifications -->
<div class="section" id="s-notifications">
<div class="section-header"><h2>Notifications</h2><p>Threshold milestones and trigger conditions by alert category</p><span class="chevron">&#9660;</span></div>
<div class="section-body">
<input type="hidden" data-key="NOTIFICATION_THRESHOLDS_JSON">
<div class="notif-tabs" role="tablist" aria-label="Notification categories">
<button type="button" class="notif-tab-btn active" data-notif-tab="surge">Surge</button>
<button type="button" class="notif-tab-btn" data-notif-tab="patterns">Patterns</button>
<button type="button" class="notif-tab-btn" data-notif-tab="unclaimed">Unclaimed</button>
<button type="button" class="notif-tab-btn" data-notif-tab="chat">Chat</button>
</div>
<div class="notif-panel" data-notif-panel="surge">
<p class="hint">Surge alerts fire when active ticket conditions cross thresholds — high volume, unclaimed queues, no staff online. Each alert escalates through its threshold list, spacing out pings as the condition persists. The counter resets when the condition clears.</p>
<div class="notif-editor">
<div class="field"><label>Alert key</label><select class="notif-alert-select" data-notif-category="surge"></select></div>
<div class="hint notif-alert-description" data-notif-description="surge"></div>
<div class="notif-chips" data-notif-chips="surge"></div>
<div class="notif-input-row">
<input type="text" class="notif-threshold-input" data-notif-input="surge" placeholder="15m, 1h, 1d6h, 2d6h, 5">
<button type="button" class="notif-add-btn" data-notif-add="surge">Add</button>
</div>
<div class="notif-presets" data-notif-presets="surge"></div>
</div>
<details class="notif-trigger">
<summary>Trigger conditions</summary>
<div class="field-grid">
<div class="field"><label>Surge Role</label><input type="text" data-key="SURGE_ROLE_ID" data-smart="role"></div>
<div class="field"><label>Ticket Surge Count</label><input type="number" data-key="SURGE_TICKET_COUNT"></div>
<div class="field"><label>Ticket Window (min)</label><input type="number" data-key="SURGE_TICKET_WINDOW_MINUTES"></div>
<div class="field"><label>Game Surge Count</label><input type="number" data-key="SURGE_GAME_TICKET_COUNT"></div>
<div class="field"><label>Game Window (min)</label><input type="number" data-key="SURGE_GAME_TICKET_WINDOW_MINUTES"></div>
<div class="field"><label>Stale Count</label><input type="number" data-key="SURGE_STALE_COUNT"></div>
<div class="field"><label>Stale Hours</label><input type="number" data-key="SURGE_STALE_HOURS"></div>
<div class="field"><label>Needs Response Count</label><input type="number" data-key="SURGE_NEEDS_RESPONSE_COUNT"></div>
<div class="field"><label>Needs Response Hours</label><input type="number" data-key="SURGE_NEEDS_RESPONSE_HOURS"></div>
<div class="field"><label>Unclaimed Count</label><input type="number" data-key="SURGE_UNCLAIMED_COUNT"></div>
<div class="field"><label>Unclaimed Minutes</label><input type="number" data-key="SURGE_UNCLAIMED_MINUTES"></div>
<div class="field"><label>Tier 3 Unclaimed (min)</label><input type="number" data-key="SURGE_TIER3_UNCLAIMED_MINUTES"></div>
<div class="field"><label>Cooldown (min)</label><input type="number" data-key="SURGE_COOLDOWN_MINUTES"></div>
<div class="field"><label>DND = Available</label><div class="toggle-wrap"><label class="toggle"><input type="checkbox" data-key="STAFF_DND_COUNTS_AS_AVAILABLE"><span class="slider"></span></label><span>Enabled</span></div></div>
<div class="field"><label>No-Staff Cooldown (min)</label><input type="number" data-key="SURGE_NO_STAFF_COOLDOWN_MINUTES"></div>
<div class="field"><label>No-Staff Ticket Threshold</label><input type="number" data-key="SURGE_NO_STAFF_OPEN_TICKET_THRESHOLD"></div>
</div>
</details>
</div>
<div class="notif-panel hidden" data-notif-panel="patterns">
<p class="hint">Pattern alerts detect trends over time — surges by game, escalation rates, staff behavior. Each alert fires once per threshold crossed within its window (daily/weekly/monthly) and won't repeat until the next window resets.</p>
<div class="notif-editor">
<div class="field"><label>Alert key</label><select class="notif-alert-select" data-notif-category="patterns"></select></div>
<div class="hint notif-alert-description" data-notif-description="patterns"></div>
<div class="notif-chips" data-notif-chips="patterns"></div>
<div class="notif-input-row">
<input type="text" class="notif-threshold-input" data-notif-input="patterns" placeholder="15m, 1h, 1d6h, 2d6h, 5">
<button type="button" class="notif-add-btn" data-notif-add="patterns">Add</button>
</div>
<div class="notif-presets" data-notif-presets="patterns"></div>
</div>
<details class="notif-trigger">
<summary>Trigger conditions</summary>
<div class="field-grid">
<div class="field"><label>Check Interval (min)</label><input type="number" data-key="PATTERN_CHECK_INTERVAL_MINUTES"></div>
<div class="field"><label>User Ticket Threshold</label><input type="number" data-key="PATTERN_USER_TICKET_THRESHOLD"></div>
<div class="field"><label>Game Ticket Threshold</label><input type="number" data-key="PATTERN_GAME_TICKET_THRESHOLD"></div>
<div class="field"><label>Staff Stale Ping Threshold</label><input type="number" data-key="PATTERN_STAFF_STALE_PING_THRESHOLD"></div>
<div class="field"><label>Escalation Threshold</label><input type="number" data-key="PATTERN_ESCALATION_THRESHOLD"></div>
<div class="field"><label>Rapid Close Seconds</label><input type="number" data-key="PATTERN_RAPID_CLOSE_SECONDS"></div>
<div class="field"><label>Unclaimed Hours</label><input type="number" data-key="PATTERN_UNCLAIMED_HOURS"></div>
</div>
</details>
</div>
<div class="notif-panel hidden" data-notif-panel="unclaimed">
<p class="hint">Per-ticket reminders sent to staff notification channels when a ticket remains unclaimed. Each threshold fires once per ticket. Escalating a ticket resets the threshold list so reminders restart for the new tier.</p>
<div class="notif-editor">
<div class="field"><label>Alert key</label><select class="notif-alert-select" data-notif-category="unclaimed"></select></div>
<div class="hint notif-alert-description" data-notif-description="unclaimed"></div>
<div class="notif-chips" data-notif-chips="unclaimed"></div>
<div class="notif-input-row">
<input type="text" class="notif-threshold-input" data-notif-input="unclaimed" placeholder="15m, 1h, 1d6h, 2d6h, 5">
<button type="button" class="notif-add-btn" data-notif-add="unclaimed">Add</button>
</div>
<div class="notif-presets" data-notif-presets="unclaimed"></div>
</div>
<details class="notif-trigger">
<summary>Trigger conditions</summary>
<div class="field-grid">
<div class="field full-width"><p class="hint">Unclaimed notifications use threshold milestones only.</p></div>
</div>
</details>
</div>
<div class="notif-panel hidden" data-notif-panel="chat">
<p class="hint">Monitors configured chat channels for unresponded user messages. Fires at escalating intervals while the condition persists. Resets when a staff member responds.</p>
<div class="notif-editor">
<div class="field"><label>Alert key</label><select class="notif-alert-select" data-notif-category="chat"></select></div>
<div class="hint notif-alert-description" data-notif-description="chat"></div>
<div class="notif-chips" data-notif-chips="chat"></div>
<div class="notif-input-row">
<input type="text" class="notif-threshold-input" data-notif-input="chat" placeholder="15m, 1h, 1d6h, 2d6h, 5">
<button type="button" class="notif-add-btn" data-notif-add="chat">Add</button>
</div>
<div class="notif-presets" data-notif-presets="chat"></div>
</div>
<details class="notif-trigger">
<summary>Trigger conditions</summary>
<div class="field-grid">
<div class="field"><label>Chat Alert Message Count</label><input type="number" data-key="CHAT_ALERT_MESSAGE_COUNT"></div>
<div class="field"><label>Chat No-Response Hours</label><input type="number" data-key="CHAT_ALERT_HOURS_WITHOUT_RESPONSE"></div>
<div class="field"><label>Chat Alert Cooldown (min)</label><input type="number" data-key="CHAT_ALERT_COOLDOWN_MINUTES"></div>
</div>
</details>
</div>
</div>
</div>
<!-- 10. Logging -->
<div class="section" id="s-logging">
<div class="section-header"><h2>Logging</h2><p>Log channel configuration (channels set in Channels section)</p><span class="chevron">&#9660;</span></div>
<div class="section-body"><div class="field-grid">
<div class="field full-width"><p class="logging-hint">Log channels are configured in the <a href="/channels">Channels</a> section. This section shows which logs are active based on configured channels.</p></div>
</div></div>
</div>
<!-- 11. Automation -->
<div class="section" id="s-automation">
<div class="section-header"><h2>Automation</h2><p>Polling intervals and timer durations</p><span class="chevron">&#9660;</span></div>
<div class="section-body"><div class="field-grid">
<div class="field"><label>Gmail Poll Interval (sec)</label><select data-key="GMAIL_POLL_INTERVAL_SECONDS">
<option value="5">5s</option><option value="10">10s</option><option value="30">30s</option><option value="45">45s</option>
<option value="60">1m</option><option value="120">2m</option><option value="180">3m</option><option value="240">4m</option>
<option value="300">5m</option><option value="600">10m</option>
</select></div>
<div class="field"><label>Force-Close Timer (sec)</label><select data-key="FORCE_CLOSE_TIMER_SECONDS">
<option value="5">5s</option><option value="10">10s</option><option value="30">30s</option><option value="45">45s</option>
<option value="60">1m</option><option value="120">2m</option><option value="180">3m</option><option value="240">4m</option>
<option value="300">5m</option><option value="600">10m</option>
</select></div>
</div></div>
</div>
<!-- 12. Appearance -->
<div class="section" id="s-appearance">
<div class="section-header"><h2>Appearance</h2><p>Embed colors, button labels, and emojis</p><span class="chevron">&#9660;</span></div>
<div class="section-body"><div class="field-grid">
<div class="field"><label>Open Color</label><div class="color-field"><input type="color" data-key="EMBED_COLOR_OPEN"><span>Open tickets</span></div></div>
<div class="field"><label>Closed Color</label><div class="color-field"><input type="color" data-key="EMBED_COLOR_CLOSED"><span>Closed tickets</span></div></div>
<div class="field"><label>Claimed Color</label><div class="color-field"><input type="color" data-key="EMBED_COLOR_CLAIMED"><span>Claimed tickets</span></div></div>
<div class="field"><label>Escalated Color</label><div class="color-field"><input type="color" data-key="EMBED_COLOR_ESCALATED"><span>Escalated tickets</span></div></div>
<div class="field"><label>Info Color</label><div class="color-field"><input type="color" data-key="EMBED_COLOR_INFO"><span>Info embeds</span></div></div>
<div class="field"><label>Close Button Label</label><input type="text" data-key="BUTTON_LABEL_CLOSE"></div>
<div class="field"><label>Claim Button Label</label><input type="text" data-key="BUTTON_LABEL_CLAIM"></div>
<div class="field"><label>Unclaim Button Label</label><input type="text" data-key="BUTTON_LABEL_UNCLAIM"></div>
<div class="field"><label>Close Emoji</label><input type="text" data-key="BUTTON_EMOJI_CLOSE"></div>
<div class="field"><label>Claim Emoji</label><input type="text" data-key="BUTTON_EMOJI_CLAIM"></div>
<div class="field"><label>Unclaim Emoji</label><input type="text" data-key="BUTTON_EMOJI_UNCLAIM"></div>
<div class="field"><label>High Priority Emoji</label><input type="text" data-key="PRIORITY_HIGH_EMOJI"></div>
<div class="field"><label>Medium Priority Emoji</label><input type="text" data-key="PRIORITY_MEDIUM_EMOJI"></div>
<div class="field"><label>Low Priority Emoji</label><input type="text" data-key="PRIORITY_LOW_EMOJI"></div>
<div class="field"><label>Claimer Emoji Fallback</label><input type="text" data-key="CLAIMER_EMOJI_FALLBACK"></div>
</div></div>
</div>
<!-- 13. Staff -->
<div class="section" id="s-staff">
<div class="section-header"><h2>Staff</h2><p>Staff IDs, emojis, and admin settings</p><span class="chevron">&#9660;</span></div>
<div class="section-body"><div class="field-grid">
<div class="field full-width"><label>Staff IDs (comma-separated)</label><input type="text" data-key="STAFF_IDS" data-smart="multi-member"></div>
<div class="field"><label>Admin ID</label><input type="text" data-key="ADMIN_ID" data-smart="member"></div>
<div class="field full-width"><label>Staff Emojis (userId:emoji, comma-separated)</label><input type="text" data-key="STAFF_EMOJIS"><div class="hint">Format: 123456:emoji,789012:emoji</div></div>
<div class="field full-width"><label>Additional Staff Roles (comma-separated)</label><input type="text" data-key="ADDITIONAL_STAFF_ROLES"><div class="hint">Role IDs with staff permissions</div></div>
<div class="field full-width"><label>Blacklisted Roles (comma-separated)</label><input type="text" data-key="BLACKLISTED_ROLES"><div class="hint">Role IDs that cannot open tickets</div></div>
<div class="field full-width"><label>Unclaimed Reminder Thresholds (hours, comma-separated)</label><input type="text" data-key="UNCLAIMED_REMINDER_THRESHOLDS"><div class="hint">e.g. 1,2,4</div></div>
</div></div>
</div>
<!-- 14. Advanced -->
<div class="section" id="s-advanced">
<div class="section-header"><h2>Advanced</h2><p>Ports, URLs, game list, branding</p><span class="chevron">&#9660;</span></div>
<div class="section-body"><div class="field-grid">
<div class="field"><label>Bot Port</label><input type="number" data-key="DISCORD_ONLY_PORT"></div>
<div class="field"><label>Healthcheck Host</label><input type="text" data-key="HEALTHCHECK_HOST" placeholder="leave empty for all interfaces"></div>
<div class="field"><label>Settings Port</label><input type="number" data-key="SETTINGS_PORT"></div>
<div class="field"><label>Settings Domain</label><input type="text" data-key="SETTINGS_DOMAIN"></div>
<div class="field"><label>Internal API Port</label><input type="number" data-key="INTERNAL_API_PORT"></div>
<div class="field"><label>Support Name</label><input type="text" data-key="SUPPORT_NAME"></div>
<div class="field"><label>Logo URL</label><input type="text" data-key="LOGO_URL"></div>
<div class="field full-width"><label>Game List (comma-separated)</label><textarea data-key="GAME_LIST" rows="3"></textarea></div>
<div class="field full-width"><label>Email Signature (HTML, use \n for breaks)</label><textarea data-key="EMAIL_SIGNATURE" rows="3"></textarea></div>
<div class="field full-width"><label>Close Subject Prefix</label><input type="text" data-key="TICKET_CLOSE_SUBJECT_PREFIX"></div>
<div class="field full-width"><label>Close Message (email body)</label><textarea data-key="TICKET_CLOSE_MESSAGE" rows="2"></textarea></div>
<div class="field full-width"><label>Discord Close Message</label><textarea data-key="DISCORD_CLOSE_MESSAGE" rows="2"></textarea></div>
<div class="field full-width"><label>Transcript Message</label><textarea data-key="DISCORD_TRANSCRIPT_MESSAGE" rows="2"></textarea><div class="hint">Variables: {channel_name}, {email}, {date_opened}, {date_closed}</div></div>
<div class="field full-width"><label>Auto-Close Message</label><textarea data-key="DISCORD_AUTO_CLOSE_MESSAGE" rows="2"></textarea></div>
</div></div>
</div>
</div>
<!-- Save bar -->
<div id="save-bar" class="save-bar">
<span id="change-count">0 unsaved changes</span>
<div class="save-actions">
<button type="button" id="save-btn">Save</button>
<button type="button" id="save-restart-btn" class="danger">Save &amp; Restart Now</button>
<button type="button" id="schedule-restart-btn" class="secondary">Schedule restart...</button>
</div>
</div>
<!-- Schedule modal -->
<div id="schedule-modal" class="modal hidden">
<div class="modal-card">
<h3>Schedule restart</h3>
<input type="datetime-local" id="schedule-datetime">
<div class="modal-actions">
<button type="button" id="schedule-confirm-btn">Schedule</button>
<button type="button" id="schedule-cancel-btn" class="secondary">Cancel</button>
</div>
</div>
</div>
<script src="/js/discord.js"></script>
<script src="/js/app.js"></script>
</body>
</html>