Files
f0ckm/views/admin/emojis.html
2026-06-03 12:50:21 +02:00

229 lines
11 KiB
HTML

@include(snippets/header)
<div class="pagewrapper">
<div id="main" class="admin-container">
<div class="container">
<h2>Custom Emojis</h2>
<div class="admin-form-container"
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>
<div style="display: flex; gap: 10px; flex-wrap: wrap; align-items: flex-end;">
<div>
<label style="display: block; font-size: 0.8em; margin-bottom: 5px; opacity: 0.7;">Name</label>
<input type="text" id="emoji-name" placeholder="" style="background: var(--bg); border: 1px solid var(--black); padding: 5px; color: var(--white);">
</div>
<div>
<label style="display: block; font-size: 0.8em; margin-bottom: 5px; opacity: 0.7;">Image File</label>
<input type="file" id="emoji-file" style="background: var(--bg); border: 1px solid var(--black); padding: 4px; color: var(--white);">
</div>
<button id="add-emoji" class="btn-upload" style="width: auto; padding: 7px 20px; border: 1px solid var(--nav-border-color); background: var(--bg); color: var(--white); cursor: pointer;">Add</button>
</div>
</div>
<div id="emoji-list" class="emoji-grid">
<!-- Populated by JS -->
</div>
</div>
<!-- Edit Emoji Modal -->
<div id="edit-emoji-modal" class="modal-overlay" style="display: none;">
<div class="modal-content" style="max-width: 460px; background: var(--dropdown-bg, #222); border: 1px solid var(--nav-border-color, #444); border-radius: 8px; padding: 20px;">
<h3 style="margin-top: 0; margin-bottom: 15px; border-bottom: 1px solid var(--nav-border-color, rgba(255,255,255,0.1)); padding-bottom: 10px; color: var(--white);">Edit Emoji</h3>
<input type="hidden" id="edit-emoji-id">
<div style="display: flex; flex-direction: column; gap: 12px; text-align: left;">
<div style="text-align: center;">
<img id="edit-emoji-preview" src="" alt="" style="height: 64px; width: 64px; object-fit: contain; border-radius: 4px; background: rgba(0,0,0,0.3);">
</div>
<div>
<label style="display: block; font-size: 0.85em; margin-bottom: 4px; color: rgba(255,255,255,0.7);">Name (lowercase a-z, 0-9, _, - only)</label>
<input type="text" id="edit-emoji-name" style="width: 100%; background: var(--bg, #111); border: 1px solid var(--black, #000); padding: 8px; color: var(--white); border-radius: 4px; box-sizing: border-box;">
</div>
<div>
<label style="display: block; font-size: 0.85em; margin-bottom: 4px; color: rgba(255,255,255,0.7);">Replace Image — Upload New File</label>
<input type="file" id="edit-emoji-file" accept="image/*" style="width: 100%; background: var(--bg, #111); border: 1px solid var(--black, #000); padding: 8px; color: var(--white); border-radius: 4px; box-sizing: border-box;">
</div>
</div>
<div class="modal-actions" style="margin-top: 20px; display: flex; justify-content: flex-end; gap: 10px;">
<button onclick="window.emojiAdmin.closeEditModal()" class="btn-cancel" style="padding: 8px 16px; background: rgba(255,255,255,0.1); color: var(--white); border: 1px solid rgba(255,255,255,0.2); border-radius: 4px; cursor: pointer;">Cancel</button>
<button onclick="window.emojiAdmin.saveEmoji()" class="btn-save" style="padding: 8px 16px; background: #28a745; color: white; border: none; border-radius: 4px; cursor: pointer;">Save Changes</button>
</div>
</div>
</div>
<script>
(() => {
var i18n = window.f0ckI18n || {};
const esc = (s) => (s || '').toString().replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#039;');
const loadEmojis = async () => {
try {
const res = await fetch('/api/v2/emojis');
const data = await res.json();
if (data.success) {
window.emojiAdmin.emojis = data.emojis;
const grid = document.getElementById('emoji-list');
if (!grid) return;
grid.innerHTML = data.emojis.map(e =>
'<div class="emoji-card">' +
'<button class="emoji-delete" onclick="window.emojiAdmin.deleteEmoji(' + e.id + ')" title="Delete">✕</button>' +
'<img class="emoji-preview" src="' + e.url + '" alt=":' + esc(e.name) + ':">' +
'<span class="emoji-label">:' + esc(e.name) + ':</span>' +
'<button onclick="window.emojiAdmin.openEditModal(' + e.id + ')" style="margin-top: 6px; width: 100%; padding: 4px 0; font-size: 0.75em; background: #28a745; color: white; border: none; border-radius: 3px; cursor: pointer;">Edit</button>' +
'</div>'
).join('');
}
} catch (err) { console.error('[EMOJI_ADMIN] Load Error:', err); }
};
const addEmoji = async (e) => {
if (e) e.preventDefault();
const name = document.getElementById('emoji-name').value;
const fileInput = document.getElementById('emoji-file');
if (!name || !fileInput.files[0]) return alert('Fill Name and select a File');
const btn = document.getElementById('add-emoji');
const oldText = btn.textContent;
btn.disabled = true;
btn.textContent = i18n.uploading || 'Uploading...';
const formData = new FormData();
formData.append('name', name);
if (fileInput.files[0]) {
formData.append('file', fileInput.files[0]);
}
try {
const headers = { 'X-Requested-With': 'XMLHttpRequest' };
const csrf = '{{ csrf_token }}';
if (csrf) headers['X-CSRF-Token'] = csrf;
const res = await fetch('/api/v2/admin/emojis', {
method: 'POST',
headers: headers,
body: formData
});
const data = await res.json();
if (data.success) {
document.getElementById('emoji-name').value = '';
document.getElementById('emoji-file').value = '';
loadEmojis();
} else {
alert('Failed: ' + (data.message || data.msg || 'Unknown error'));
}
} catch (e) {
console.error('[EMOJI_ADMIN] Add Error:', e);
alert('Error: ' + e.message);
} finally {
btn.disabled = false;
btn.textContent = oldText;
}
};
const deleteEmoji = async (id) => {
if (!confirm('Delete this emoji?')) return;
try {
const res = await fetch('/api/v2/admin/emojis/' + id + '/delete', {
method: 'POST',
headers: { 'X-CSRF-Token': '{{ csrf_token }}' }
});
const data = await res.json();
if (data.success) {
loadEmojis();
} else {
alert('Delete failed');
}
} catch (e) { console.error(e); }
};
const openEditModal = (id) => {
const emoji = (window.emojiAdmin.emojis || []).find(e => e.id === id);
if (!emoji) return;
document.getElementById('edit-emoji-id').value = emoji.id;
document.getElementById('edit-emoji-name').value = emoji.name;
document.getElementById('edit-emoji-file').value = '';
const preview = document.getElementById('edit-emoji-preview');
preview.src = emoji.url;
preview.alt = ':' + emoji.name + ':';
const modal = document.getElementById('edit-emoji-modal');
if (modal) modal.style.display = 'flex';
};
const closeEditModal = () => {
const modal = document.getElementById('edit-emoji-modal');
if (modal) modal.style.display = 'none';
document.getElementById('edit-emoji-file').value = '';
};
const saveEmoji = async () => {
const id = document.getElementById('edit-emoji-id').value;
const name = document.getElementById('edit-emoji-name').value.trim().toLowerCase();
const fileInput = document.getElementById('edit-emoji-file');
if (!name) return alert('Emoji name is required');
if (!/^[a-z0-9_-]+$/.test(name)) return alert('Invalid name. Use lowercase a-z, 0-9, _, - only.');
const btn = document.querySelector('#edit-emoji-modal .btn-save');
const oldText = btn.textContent;
btn.disabled = true;
btn.textContent = 'Saving...';
const formData = new FormData();
formData.append('name', name);
if (fileInput.files[0]) {
formData.append('file', fileInput.files[0]);
}
try {
const headers = { 'X-Requested-With': 'XMLHttpRequest' };
const csrf = '{{ csrf_token }}';
if (csrf) headers['X-CSRF-Token'] = csrf;
const res = await fetch('/api/v2/admin/emojis/' + id + '/edit', {
method: 'POST',
headers: headers,
body: formData
});
const data = await res.json();
if (data.success) {
closeEditModal();
loadEmojis();
} else {
alert('Save failed: ' + (data.message || data.msg || 'Unknown error'));
}
} catch (e) {
console.error('[EMOJI_ADMIN] Edit Error:', e);
alert('Save failed: ' + e.message);
} finally {
btn.disabled = false;
btn.textContent = oldText;
}
};
// Global scope for onclick handlers
window.emojiAdmin = { deleteEmoji, openEditModal, closeEditModal, saveEmoji, emojis: [] };
const btnAddEmoji = document.getElementById('add-emoji');
if (btnAddEmoji) btnAddEmoji.addEventListener('click', addEmoji);
// Live Update Listener (SSE dispatched via f0ckm.js)
document.addEventListener('f0ck:emojis_updated', loadEmojis);
loadEmojis();
})();
</script>
</div>
</div>
@include(snippets/footer)