first commit
This commit is contained in:
@@ -0,0 +1,151 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}Galerie admin — JH Photomaton{% endblock %}
|
||||
|
||||
{% block nav_links %}
|
||||
<a href="/admin">Dashboard</a>
|
||||
<a href="/admin/gallery" class="active">Galerie admin</a>
|
||||
<a href="/admin/print">Impression</a>
|
||||
<a href="/admin/actions">Actions</a>
|
||||
<a href="/gallery">Galerie publique</a>
|
||||
<a href="/admin/logout" style="margin-left:auto;color:var(--text-dim)">Déconnexion</a>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<h1 class="page-title" style="margin:0">Galerie — Administration</h1>
|
||||
<div class="flex gap-1 items-center">
|
||||
<span class="text-sm text-muted" id="photo-count">Chargement…</span>
|
||||
<div class="flex gap-1">
|
||||
<input type="number" id="copies-input" min="1" max="3" value="1" class="form-control" style="width:70px" title="Copies">
|
||||
<button class="btn btn-ghost btn-sm" onclick="refreshPhotos()">↻ Actualiser</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Filtres / navigation pages -->
|
||||
<div class="flex items-center gap-2 mb-2">
|
||||
<button class="btn btn-ghost btn-sm" id="prev-btn" onclick="changePage(-1)" disabled>← Préc.</button>
|
||||
<span class="text-sm text-muted">Page <span id="page-cur">1</span> / <span id="page-total">1</span></span>
|
||||
<button class="btn btn-ghost btn-sm" id="next-btn" onclick="changePage(1)">Suiv. →</button>
|
||||
</div>
|
||||
|
||||
<div class="photo-grid" id="photo-grid">
|
||||
<div class="empty-state"><div class="icon">⏳</div>Chargement…</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Lightbox admin -->
|
||||
<div class="lightbox" id="lightbox" onclick="closeLightbox(event)">
|
||||
<button class="lightbox-close" onclick="closeLightbox()">×</button>
|
||||
<img id="lightbox-img" src="" alt="">
|
||||
<div class="lightbox-actions">
|
||||
<button class="btn btn-success" id="lb-print-btn">🖨 Imprimer</button>
|
||||
<a class="btn btn-ghost" id="lb-download-btn" download>⬇ Télécharger</a>
|
||||
<button class="btn btn-danger" id="lb-delete-btn">🗑 Supprimer</button>
|
||||
</div>
|
||||
<div class="text-sm text-muted" id="lb-filename"></div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script>
|
||||
let currentPage = 1;
|
||||
let totalPages = 1;
|
||||
let currentPhotoId = null;
|
||||
|
||||
async function loadPhotos(page = 1) {
|
||||
try {
|
||||
const data = await api('GET', `/admin/api/gallery/photos?page=${page}&limit=24`);
|
||||
const { photos, total, pages } = data;
|
||||
|
||||
currentPage = page;
|
||||
totalPages = pages;
|
||||
|
||||
document.getElementById('photo-count').textContent = `${total} photo(s)`;
|
||||
document.getElementById('page-cur').textContent = page;
|
||||
document.getElementById('page-total').textContent = pages;
|
||||
document.getElementById('prev-btn').disabled = page <= 1;
|
||||
document.getElementById('next-btn').disabled = page >= pages;
|
||||
|
||||
const grid = document.getElementById('photo-grid');
|
||||
if (!photos.length) {
|
||||
grid.innerHTML = '<div class="empty-state"><div class="icon">📷</div>Aucune photo</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
grid.innerHTML = photos.map(p => {
|
||||
const pid = p.id || p.filename || p.uid || '';
|
||||
return `
|
||||
<div class="photo-card" onclick="openLightbox('${pid}', '${p.full_url}', '${p.thumb_url}')">
|
||||
<img src="${p.thumb_url}" loading="lazy" alt="">
|
||||
<div class="photo-card-actions">
|
||||
<button class="btn btn-success btn-sm" onclick="event.stopPropagation();printPhoto('${pid}')">🖨</button>
|
||||
<button class="btn btn-danger btn-sm" onclick="event.stopPropagation();deletePhoto('${pid}')">🗑</button>
|
||||
<a class="btn btn-ghost btn-sm" href="${p.full_url}" download onclick="event.stopPropagation()">⬇</a>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}).join('');
|
||||
} catch(e) {
|
||||
document.getElementById('photo-grid').innerHTML = '<div class="empty-state"><div class="icon">❌</div>Erreur de chargement</div>';
|
||||
}
|
||||
}
|
||||
|
||||
function changePage(delta) {
|
||||
loadPhotos(currentPage + delta);
|
||||
}
|
||||
|
||||
function refreshPhotos() { loadPhotos(currentPage); }
|
||||
|
||||
// ── Lightbox ──────────────────────────────────────────────────────────────────
|
||||
function openLightbox(pid, fullUrl, thumbUrl) {
|
||||
currentPhotoId = pid;
|
||||
document.getElementById('lightbox-img').src = fullUrl || thumbUrl;
|
||||
document.getElementById('lb-download-btn').href = fullUrl;
|
||||
document.getElementById('lb-filename').textContent = pid;
|
||||
document.getElementById('lb-print-btn').onclick = () => printPhoto(pid);
|
||||
document.getElementById('lb-delete-btn').onclick = () => deletePhoto(pid);
|
||||
document.getElementById('lightbox').classList.add('open');
|
||||
}
|
||||
|
||||
function closeLightbox(e) {
|
||||
if (e && e.target !== document.getElementById('lightbox') && e.type !== 'click') return;
|
||||
document.getElementById('lightbox').classList.remove('open');
|
||||
currentPhotoId = null;
|
||||
}
|
||||
|
||||
document.addEventListener('keydown', e => { if (e.key === 'Escape') closeLightbox(); });
|
||||
|
||||
// ── Actions ───────────────────────────────────────────────────────────────────
|
||||
async function printPhoto(pid) {
|
||||
const copies = parseInt(document.getElementById('copies-input').value) || 1;
|
||||
if (!confirm(`Imprimer ${copies} copie(s) ?`)) return;
|
||||
try {
|
||||
const r = await api('POST', `/admin/api/gallery/print/${pid}?copies=${copies}`);
|
||||
if (r.success) showToast('✅ Impression lancée sur ' + r.printer, 'success');
|
||||
else if (r.ok) showToast('📋 Demande ajoutée à la file', 'info');
|
||||
else showToast('❌ ' + (r.error || 'Erreur'), 'error');
|
||||
} catch(e) { showToast('Erreur impression', 'error'); }
|
||||
}
|
||||
|
||||
async function deletePhoto(pid) {
|
||||
if (!confirm('Supprimer cette photo définitivement ?')) return;
|
||||
try {
|
||||
const r = await api('DELETE', `/admin/api/gallery/${pid}`);
|
||||
showToast(r.ok ? '🗑 Photo supprimée' : '❌ Erreur suppression', r.ok ? 'success' : 'error');
|
||||
if (r.ok) {
|
||||
document.getElementById('lightbox').classList.remove('open');
|
||||
loadPhotos(currentPage);
|
||||
}
|
||||
} catch(e) { showToast('Erreur suppression', 'error'); }
|
||||
}
|
||||
|
||||
function onWsMessage(msg) {
|
||||
if (msg.type === 'photo_deleted') loadPhotos(currentPage);
|
||||
if (msg.type === 'print_result') showToast(msg.result.success ? '✅ Impression OK' : '❌ ' + msg.result.error, msg.result.success ? 'success' : 'error');
|
||||
}
|
||||
|
||||
loadPhotos(1);
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user