Lumi/plugins/moderation/views/tos-bans.ejs
2026-05-30 20:37:42 +02:00

253 lines
8.2 KiB
Plaintext

<%- include("../../../src/web/views/partials/layout-top", { title }) %>
<style>
.pill {
padding: 4px 10px;
border-radius: 999px;
background: var(--surface-3);
border: 1px solid var(--border);
font-size: 0.82rem;
font-weight: 600;
}
.pill.ban {
color: #ff7a7a;
border-color: rgba(255, 122, 122, 0.4);
background: rgba(255, 122, 122, 0.12);
}
.pill.timeout {
color: #f1b765;
border-color: rgba(241, 183, 101, 0.4);
background: rgba(241, 183, 101, 0.12);
}
.pill.kick {
color: #9aa1ad;
border-color: rgba(154, 161, 173, 0.4);
background: rgba(154, 161, 173, 0.12);
}
.inline-actions {
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
}
.duration-inputs {
display: grid;
grid-template-columns: 1fr 140px;
gap: 8px;
}
.duration-inputs input,
.duration-inputs select {
width: 100%;
}
.evidence-list {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
.evidence-link {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 4px 10px;
border-radius: 999px;
border: 1px solid var(--border);
background: var(--surface-3);
font-size: 0.75rem;
text-decoration: none;
color: inherit;
}
</style>
<section class="card">
<div class="section-header">
<div>
<h1>TOs & Bans</h1>
<p class="command-subtitle">Monitor active sanctions and moderation history.</p>
</div>
</div>
</section>
<section class="card">
<h2>Current Timeouts & Bans</h2>
<% if (!activeSanctions.length) { %>
<p>No active bans or timeouts.</p>
<% } else { %>
<table class="table">
<thead>
<tr>
<th>User</th>
<th>Type</th>
<th>Reason</th>
<th>Expires</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<% activeSanctions.forEach((sanction) => { %>
<tr>
<td><%= sanction.display_name || sanction.subject_id %></td>
<td>
<span class="pill <%= sanction.action_type %>"><%= sanction.action_type %></span>
</td>
<td><%= sanction.reason_short %></td>
<td><%= sanction.expires_at ? new Date(sanction.expires_at).toLocaleString() : "Permanent" %></td>
<td>
<div class="inline-actions">
<% if (sanction.action_type === 'timeout') { %>
<form method="post" action="/plugins/moderation/actions/<%= sanction.id %>/update-timeout" class="inline-form" data-duration-group>
<% if (!isAdmin) { %>
<select name="duration_preset" data-duration-field>
<% presets.forEach((preset) => { %>
<option value="<%= preset.seconds %>"><%= preset.label %></option>
<% }) %>
</select>
<% } %>
<% if (isAdmin) { %>
<div class="duration-inputs">
<input name="duration_value" placeholder="Custom" data-duration-field />
<select name="duration_unit" data-duration-field>
<option value="hours">Hours</option>
<option value="days">Days</option>
<option value="weeks">Weeks</option>
<option value="months">Months</option>
<option value="years">Years</option>
</select>
</div>
<% } %>
<label class="switch">
<input type="checkbox" class="switch-input" name="permanent" data-duration-permanent />
<span class="switch-track" aria-hidden="true"></span>
<span class="switch-text">Permanent</span>
</label>
<button type="submit" class="button subtle">Update</button>
</form>
<% } %>
<form method="post" action="/plugins/moderation/actions/<%= sanction.id %>/revoke" class="inline-form">
<button type="submit" class="button danger">Revoke</button>
</form>
</div>
</td>
</tr>
<% }) %>
</tbody>
</table>
<% } %>
</section>
<section class="card">
<details>
<summary>
<strong>History</strong>
<span class="hint">Searchable log of every moderation action.</span>
</summary>
<div class="table-tools" style="margin-top:12px;">
<input
class="table-search"
type="search"
placeholder="Search moderation actions"
aria-label="Search moderation actions"
data-table-filter="moderation-actions"
/>
<div class="table-controls">
<label class="table-page-size">
Show
<select data-table-size="moderation-actions">
<option value="25">25</option>
<option value="50">50</option>
<option value="100">100</option>
<option value="250">250</option>
</select>
</label>
</div>
</div>
<% if (!actions.length) { %>
<p>No actions recorded.</p>
<% } else { %>
<div class="table-wrap">
<table
class="table"
data-table="moderation-actions"
data-pageable="true"
data-page-size="25"
data-page-sizes="25,50,100,250"
>
<thead>
<tr>
<th data-sort="user">User</th>
<th data-sort="type">Type</th>
<th data-sort="platform">Platform</th>
<th data-sort="reason">Reason</th>
<th data-sort="by">By</th>
<th>Evidence</th>
<th data-sort="date">Date</th>
</tr>
</thead>
<tbody>
<% actions.forEach((action) => { %>
<% const evidence = actionEvidence[action.id] || []; %>
<% const displayName = action.display_name || action.subject_id; %>
<% const byName = action.created_by_name || action.source || 'Staff'; %>
<% const evidenceNames = evidence.map((item) => item.name).join(' '); %>
<tr
data-search="<%= `${displayName} ${action.action_type} ${action.platform || ''} ${action.reason_short || ''} ${action.reason_detail || ''} ${byName} ${evidenceNames}`.toLowerCase() %>"
data-user="<%= displayName %>"
data-type="<%= action.action_type %>"
data-platform="<%= action.platform || 'global' %>"
data-reason="<%= action.reason_short || '' %>"
data-by="<%= byName %>"
data-date="<%= action.created_at %>"
>
<td><%= displayName %></td>
<td><span class="pill <%= action.action_type %>"><%= action.action_type %></span></td>
<td><%= action.platform || 'global' %></td>
<td><%= action.reason_short %></td>
<td><%= byName %></td>
<td>
<% if (!evidence.length) { %>
<span class="hint">None</span>
<% } else { %>
<div class="evidence-list">
<% evidence.forEach((item) => { %>
<a class="evidence-link" href="/plugins/moderation/evidence/<%= item.id %>" target="_blank" rel="noopener"> <%= item.name %> </a>
<% }) %>
</div>
<% } %>
</td>
<td><%= new Date(action.created_at).toLocaleString() %></td>
</tr>
<% }) %>
</tbody>
</table>
</div>
<div class="table-pagination" data-table-pagination="moderation-actions">
<button type="button" class="button subtle" data-page-prev>Previous</button>
<span class="table-page-label" data-page-label>Page 1 of 1</span>
<button type="button" class="button subtle" data-page-next>Next</button>
</div>
<% } %>
</details>
</section>
<script>
(() => {
const attachDurationToggle = (group) => {
const toggle = group.querySelector("[data-duration-permanent]");
const fields = group.querySelectorAll("[data-duration-field]");
if (!toggle || !fields.length) {
return;
}
const sync = () => {
const disabled = toggle.checked;
fields.forEach((field) => {
field.disabled = disabled;
});
};
toggle.addEventListener("change", sync);
sync();
};
document.querySelectorAll("[data-duration-group]").forEach(attachDurationToggle);
})();
</script>
<%- include("../../../src/web/views/partials/layout-bottom") %>