feat: Implement comments, notifications, and custom emojis with new API routes, UI components, and database migrations.

This commit is contained in:
x
2026-01-25 03:48:24 +01:00
parent 595118c2c8
commit d903ce8b98
18 changed files with 1900 additions and 44 deletions

96
views/admin/emojis.html Normal file
View File

@@ -0,0 +1,96 @@
@include(snippets/header)
<div class="container" style="padding-top: 20px;">
<h2>Custom Emojis</h2>
<div class="upload-form"
style="margin-bottom: 20px; text-align: left; background: var(--dropdown-bg); padding: 15px; border: 1px solid var(--nav-border-color);">
<h4>Add New Emoji</h4>
<input type="text" id="emoji-name" placeholder="Name (e.g. pingu)"
style="background: var(--bg); border: 1px solid var(--black); padding: 5px; color: var(--white);">
<input type="text" id="emoji-url" placeholder="URL (e.g. /s/img/pingu.gif)"
style="background: var(--bg); border: 1px solid var(--black); padding: 5px; color: var(--white); width: 300px;">
<button id="add-emoji" class="btn-upload"
style="width: auto; padding: 5px 15px; border: 1px solid var(--nav-border-color); background: var(--bg); color: var(--white); cursor: pointer;">Add</button>
</div>
<div class="upload-form" style="overflow-x: auto;">
<table style="width: 100%; border-collapse: collapse; color: var(--white);">
<thead>
<tr style="border-bottom: 1px solid var(--nav-border-color); text-align: left;">
<th style="padding: 10px;">Preview</th>
<th style="padding: 10px;">Name</th>
<th style="padding: 10px;">URL</th>
<th style="padding: 10px;">Actions</th>
</tr>
</thead>
<tbody id="emoji-list">
<!-- Populated by JS -->
</tbody>
</table>
</div>
</div>
<script>
const loadEmojis = async () => {
try {
const res = await fetch('/api/v2/emojis');
const data = await res.json();
if (data.success) {
const tbody = document.getElementById('emoji-list');
tbody.innerHTML = data.emojis.map(e =>
'<tr style="border-bottom: 1px solid rgba(255,255,255,0.05);">' +
'<td style="padding: 10px;"><img src="' + e.url + '" style="height: 30px; object-fit: contain;"></td>' +
'<td style="padding: 10px; font-family: monospace; font-size: 1.1em; color: var(--accent);">:' + e.name + ':</td>' +
'<td style="padding: 10px; opacity: 0.7;">' + e.url + '</td>' +
'<td style="padding: 10px;">' +
'<button onclick="deleteEmoji(' + e.id + ')" class="btn-remove" style="padding: 5px 10px; font-size: 0.8em; background: #c00; color: white; border: none; cursor: pointer;">Delete</button>' +
'</td>' +
'</tr>'
).join('');
}
} catch (err) { console.error(err); }
};
const addEmoji = async () => {
const name = document.getElementById('emoji-name').value;
const url = document.getElementById('emoji-url').value;
if (!name || !url) return alert('Fill both fields');
try {
const res = await fetch('/api/v2/admin/emojis', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, // Body parsing middleware uses this or JSON? Typically form-encoded in this stack
body: new URLSearchParams({ name, url })
});
const data = await res.json();
if (data.success) {
document.getElementById('emoji-name').value = '';
document.getElementById('emoji-url').value = '';
loadEmojis();
} else {
alert('Failed: ' + data.message);
}
} catch (e) {
alert('Error: ' + e.message);
}
};
const deleteEmoji = async (id) => {
if (!confirm('Delete this emoji?')) return;
try {
const res = await fetch('/api/v2/admin/emojis/' + id + '/delete', { method: 'POST' });
const data = await res.json();
if (data.success) {
loadEmojis();
} else {
alert('Failed');
}
} catch (e) { alert(e); }
};
document.getElementById('add-emoji').addEventListener('click', addEmoji);
loadEmojis();
</script>
@include(snippets/footer)