mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-02 02:43:44 +00:00
Guild chat per guild (/chat_log/guild/<id>, from the guild's card), team chat by team (/chat_log/teams) and a character's whispers by conversation (/characters/<id>/whispers, chat_dms; opening one is audited), paged on the server. The Chat Log filters by account and time range, shows the conversation around a message, links to its history and marks flagged messages; messages can be picked (shift-click for a range) to flag. The character page links to all of them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
158 lines
7.4 KiB
Django/Jinja
158 lines
7.4 KiB
Django/Jinja
{% extends "base.jinja2" %}
|
|
|
|
{% block title %}Guilds - DarkflameServer{% endblock %}
|
|
|
|
{% block content %}
|
|
<h2 class="mb-1">Guilds</h2>
|
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
<p class="text-body-secondary mb-0">Every guild. A name the chat filter doesn't allow waits for review; other players see no guild name until it is approved. Rejecting a name renames the guild “Guild <id>”.</p>
|
|
<div class="form-check form-switch">
|
|
<input class="form-check-input" type="checkbox" id="pendingOnly" data-pref="guilds.pendingOnly">
|
|
<label class="form-check-label" for="pendingOnly">Pending only</label>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card mb-4">
|
|
<div class="card-body">
|
|
<table id="guildsTable" class="table table-striped table-hover table-stack">
|
|
<thead>
|
|
<tr>
|
|
<th>ID</th>
|
|
<th>Name</th>
|
|
<th>Status</th>
|
|
<th>Members</th>
|
|
<th>Leader</th>
|
|
<th>Created</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card d-none" id="guildDetail">
|
|
<div class="card-header d-flex justify-content-between align-items-center">
|
|
<h5 class="mb-0" id="guildTitle"></h5>
|
|
<div>
|
|
{% if can.chat_private %}<a class="btn btn-sm btn-outline-secondary me-1" id="guildChat" href="#">Guild chat</a>{% endif %}
|
|
<button class="btn btn-sm btn-outline-primary me-1" id="guildRename">Rename</button>
|
|
<button class="btn btn-sm btn-outline-danger" id="guildDisband">Disband</button>
|
|
</div>
|
|
</div>
|
|
<div class="card-body">
|
|
<p class="small text-body-secondary" id="guildInfo"></p>
|
|
<div class="row">
|
|
<div class="col-lg-6">
|
|
<h6>Members</h6>
|
|
<table class="table table-sm table-stack">
|
|
<thead><tr><th>Character</th><th>Rank</th><th>Joined</th><th></th></tr></thead>
|
|
<tbody id="guildMembers"></tbody>
|
|
</table>
|
|
</div>
|
|
<div class="col-lg-6">
|
|
<h6>History</h6>
|
|
<table class="table table-sm table-stack">
|
|
<thead><tr><th>When</th><th>What</th><th>Who</th><th>By</th><th>Detail</th></tr></thead>
|
|
<tbody id="guildEvents"></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block scripts %}
|
|
<script>
|
|
$(function () {
|
|
var ranks = { 1: 'Leader', 2: 'Officer', 3: 'Veteran', 4: 'Recruit' };
|
|
var pending = document.getElementById('pendingOnly');
|
|
var shown = null;
|
|
|
|
var table = serverTable('#guildsTable', '/api/tables/guilds', [
|
|
{ data: 'id', orderable: false },
|
|
{ data: 'name', orderable: false },
|
|
{ data: 'name_status', orderable: false, render: function (d) { return d === 'approved' ? fmt.badge('Approved', 'success') : fmt.badge('Pending', 'warning'); } },
|
|
{ data: 'member_count', orderable: false },
|
|
{ data: 'leader_id', orderable: false, render: function (d, t, row) { return d ? fmt.character(d, row.leader_name) : '<span class="text-body-secondary small">-</span>'; } },
|
|
{ data: 'created_at', orderable: false, render: function (d) { return fmt.unix(d); } },
|
|
{ data: null, orderable: false, render: function (d) {
|
|
var buttons = '<button class="btn btn-sm btn-outline-secondary me-1" data-guild-action="view" data-id="' + esc(d.id) + '">View</button>';
|
|
if (d.name_status !== 'approved') {
|
|
buttons += '<button class="btn btn-sm btn-outline-success me-1" data-guild-action="approve" data-id="' + esc(d.id) + '">Approve</button>' +
|
|
'<button class="btn btn-sm btn-outline-danger" data-guild-action="reject" data-id="' + esc(d.id) + '" data-name="' + esc(d.name) + '">Reject</button>';
|
|
}
|
|
return buttons;
|
|
}}
|
|
], { liveTable: 'guilds', dataTable: { order: [] }, extra: function () { return { pending: pending.checked }; } });
|
|
pending.addEventListener('change', function () { table.ajax.reload(); });
|
|
|
|
function show(id) {
|
|
api.get('/api/guilds/' + id).then(function (d) {
|
|
var guild = d.guild;
|
|
shown = guild;
|
|
document.getElementById('guildDetail').classList.remove('d-none');
|
|
document.getElementById('guildTitle').textContent = guild.name + ' (' + guild.id + ')';
|
|
var chatLink = document.getElementById('guildChat');
|
|
if (chatLink) chatLink.href = '/chat_log/guild/' + encodeURIComponent(guild.id);
|
|
document.getElementById('guildInfo').innerHTML = 'Name ' + esc(guild.name_status) + '. Founded ' + esc(fmt.unix(guild.created_at)) +
|
|
(guild.founder_name ? ' by ' + fmt.character(guild.founder_id, guild.founder_name) : '') + '.';
|
|
document.getElementById('guildMembers').innerHTML = guild.members.map(function (m) {
|
|
return '<tr><td>' + fmt.character(m.id, m.name || m.id) + '</td><td>' + esc(ranks[m.rank] || m.rank) + '</td><td>' + esc(fmt.unix(m.joined_at)) +
|
|
'</td><td><button class="btn btn-sm btn-outline-danger" data-member="' + esc(m.id) + '" data-name="' + esc(m.name) + '">Remove</button></td></tr>';
|
|
}).join('') || '<tr><td colspan="4" class="text-body-secondary">No members</td></tr>';
|
|
document.getElementById('guildEvents').innerHTML = guild.events.map(function (e) {
|
|
return '<tr><td>' + esc(fmt.unix(e.time)) + '</td><td>' + esc(e.kind) + '</td><td>' + (e.character_id !== '0' ? fmt.character(e.character_id, e.character_name || e.character_id) : '') +
|
|
'</td><td>' + esc(e.actor) + '</td><td>' + esc(e.detail) + '</td></tr>';
|
|
}).join('');
|
|
}).catch(function () {});
|
|
}
|
|
|
|
function refresh() {
|
|
table.ajax.reload(null, false);
|
|
if (shown) show(shown.id);
|
|
}
|
|
|
|
$('#guildsTable').on('click', '[data-guild-action]', function () {
|
|
var action = this.getAttribute('data-guild-action');
|
|
var id = this.getAttribute('data-id');
|
|
if (action === 'view') return show(id);
|
|
if (action === 'approve') { api.action('/api/guilds/' + id + '/approve', {}, 'Approved').then(refresh).catch(function () {}); return; }
|
|
var reason = prompt('Reject the guild name “' + this.getAttribute('data-name') + '”? The guild is renamed “Guild ' + id + '”. Reason (optional, kept in its history):', '');
|
|
if (reason === null) return;
|
|
api.action('/api/guilds/' + id + '/reject', { reason: reason }, 'Rejected').then(refresh).catch(function () {});
|
|
});
|
|
|
|
// #guild=<id> opens that guild (links from its chat history)
|
|
var fromHash = (location.hash.match(/guild=(\d+)/) || [])[1];
|
|
if (fromHash) show(fromHash);
|
|
|
|
document.getElementById('guildRename').addEventListener('click', function () {
|
|
if (!shown) return;
|
|
var name = prompt('New name for ' + shown.name + ' (3 to 30 letters, digits, spaces, \' - and .):', shown.name);
|
|
if (name === null || name === shown.name) return;
|
|
api.action('/api/guilds/' + shown.id + '/rename', { name: name }, 'Renamed').then(refresh).catch(function () {});
|
|
});
|
|
|
|
document.getElementById('guildDisband').addEventListener('click', function () {
|
|
if (!shown) return;
|
|
var typed = prompt('This removes every member of ' + shown.name + ' and deletes the guild (its history stays). Type the guild ID (' + shown.id + ') to confirm:');
|
|
if (typed !== shown.id) return;
|
|
var id = shown.id;
|
|
api.action('/api/guilds/' + id + '/disband', {}, 'Disbanded').then(function () {
|
|
shown = null;
|
|
document.getElementById('guildDetail').classList.add('d-none');
|
|
table.ajax.reload(null, false);
|
|
}).catch(function () {});
|
|
});
|
|
|
|
document.getElementById('guildMembers').addEventListener('click', function (event) {
|
|
var button = event.target.closest('[data-member]');
|
|
if (!button || !shown) return;
|
|
if (!confirm('Remove ' + button.getAttribute('data-name') + ' from ' + shown.name + '?')) return;
|
|
api.action('/api/guilds/' + shown.id + '/members/' + button.getAttribute('data-member') + '/remove', {}, 'Removed').then(refresh).catch(function () {});
|
|
});
|
|
});
|
|
</script>
|
|
{% endblock %}
|