// public/message.js - VERSION DEBUG & UX CORRIGÉE
function $(selector, root = document) { return root.querySelector(selector); }
function showToast(message, type = 'success') {
const container = $('#toast');
if (!container) return;
const el = document.createElement('div');
el.className = `toast ${type}`;
el.textContent = message;
container.appendChild(el);
requestAnimationFrame(() => el.classList.add('show'));
setTimeout(() => { el.classList.remove('show'); el.addEventListener('transitionend', () => el.remove(), { once: true }); }, 4000);
}
function escapeHtml(unsafe) { return String(unsafe).replace(/[&<>"']/g, c => ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c])); }
function formatDate(ts) { return new Date(ts * 1000).toLocaleString('fr-FR', { day:'2-digit', month:'2-digit', hour:'2-digit', minute:'2-digit' }); }
// --- Config ---
const SLUG = window.location.pathname.slice(1);
const MAX_FILES = 3, MAX_SIZE = 15 * 1024 * 1024, MAX_CHARS = 1000;
const ALLOWED_TYPES = ['image/jpeg', 'image/png', 'image/webp', 'image/gif'];
// --- State ---
let selectedFiles = [];
// --- DOM ---
const slugDisplay = $('#slugDisplay');
const msgForm = $('#msgForm');
const contentEl = $('#content');
const charCount = $('#charCount');
const dropZone = $('#dropZone');
const fileInput = $('#fileInput');
const previews = $('#previews');
const dropText = $('#dropText');
const btnSend = $('#btnSend');
const legalCheck = $('#legalCheck');
const messagesList = $('#messagesList');
const imgModal = $('#imgModal');
const imgModalImg = $('#imgModal img');
const imgModalClose = $('#imgModal .close-btn');
// --- Init ---
function init() {
slugDisplay.textContent = SLUG;
document.title = `Message pour /${SLUG}`;
setupForm();
setupDropZone();
setupImageModal();
loadMessages();
}
// --- Form ---
function setupForm() {
contentEl.addEventListener('input', () => {
const len = contentEl.value.length;
charCount.textContent = len;
charCount.classList.toggle('warning', len > MAX_CHARS * 0.8);
charCount.classList.toggle('danger', len >= MAX_CHARS);
checkFormValidity();
});
legalCheck.addEventListener('change', checkFormValidity);
msgForm.addEventListener('submit', async (e) => {
e.preventDefault();
// --- DEBUG : Affiche l'état exact avant envoi ---
const content = contentEl.value.trim();
const hasText = content.length > 0;
const hasFiles = selectedFiles.length > 0;
const legal = legalCheck.checked;
console.log('[DEBUG SUBMIT]', { hasText, hasFiles, legal, contentLen: content.length, filesCount: selectedFiles.length });
// 1. Validation Contenu (Texte OU Image)
if (!hasText && !hasFiles) {
showToast('❌ Écrivez un message OU ajoutez une image', 'error');
return;
}
if (content.length > MAX_CHARS) return showToast(`Trop long (max ${MAX_CHARS})`, 'error');
if (selectedFiles.length > MAX_FILES) return showToast(`Max ${MAX_FILES} images`, 'error');
// 2. Validation Légale (Double check)
if (!legal) {
showToast('❌ Vous devez cocher la case d\'attestation légale', 'error');
legalCheck.focus();
return;
}
// 3. Envoi
btnSend.disabled = true;
btnSend.textContent = 'Envoi...';
const formData = new FormData();
formData.append('content', content);
formData.append('legal_accepted', 'true'); // String "true" attendue par le backend
selectedFiles.forEach(f => formData.append('images', f));
// DEBUG: Voir ce qui part dans FormData
for (let pair of formData.entries()) { console.log('[FORMDATA]', pair[0], pair[1] instanceof File ? `(File: ${pair[1].name})` : pair[1]); }
try {
const res = await fetch(`/api/public/${SLUG}/message`, { method: 'POST', body: formData, credentials: 'include' });
const data = await res.json().catch(() => ({}));
if (!res.ok) throw new Error(data.error || `Erreur ${res.status}`);
showToast('✅ Message envoyé !', 'success');
msgForm.reset();
selectedFiles = [];
updatePreviews();
charCount.textContent = '0';
charCount.classList.remove('warning', 'danger');
loadMessages();
} catch (err) {
console.error('Send Error:', err);
showToast(err.message, 'error');
} finally {
btnSend.disabled = false;
checkFormValidity();
}
});
}
function checkFormValidity() {
const hasText = contentEl.value.trim().length > 0;
const hasFiles = selectedFiles.length > 0;
const legal = legalCheck.checked;
const ready = legal && (hasText || hasFiles);
btnSend.disabled = !ready;
// UX : Change le texte du bouton pour expliquer pourquoi c'est grisé
if (!legal) btnSend.textContent = '⚠️ Cochez la case légale pour activer';
else if (!hasText && !hasFiles) btnSend.textContent = '📝 Écrivez ou ajoutez une image';
else btnSend.textContent = '✅ Valider et Envoyer';
}
// --- Dropzone & Files (Identique, robuste) ---
function setupDropZone() {
['dragenter','dragover'].forEach(ev => dropZone.addEventListener(ev, e => { e.preventDefault(); e.stopPropagation(); dropZone.classList.add('drag'); }));
['dragleave','drop'].forEach(ev => dropZone.addEventListener(ev, e => { e.preventDefault(); e.stopPropagation(); dropZone.classList.remove('drag'); }));
dropZone.addEventListener('drop', e => handleFiles(e.dataTransfer.files));
dropZone.addEventListener('click', e => {
// Si on clique directement sur l'input file, on laisse le navigateur faire nativement
if (e.target === fileInput) return;
if (!e.target.closest('.preview-item button')) fileInput.click();
});
dropZone.addEventListener('keydown', e => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); fileInput.click(); } });
fileInput.addEventListener('change', e => { handleFiles(e.target.files); fileInput.value = ''; });
}
function handleFiles(files) {
const arr = [...files];
const slots = MAX_FILES - selectedFiles.length;
if (slots <= 0) return showToast(`Max ${MAX_FILES} images`, 'error');
const valid = arr.filter(f => {
if (!ALLOWED_TYPES.includes(f.type)) { showToast(`${f.name}: Format non supporté`, 'error'); return false; }
if (f.size > MAX_SIZE) { showToast(`${f.name}: > 15 Mo`, 'error'); return false; }
return true;
}).slice(0, slots);
if (valid.length < arr.length && arr.length > slots) showToast(`Max ${MAX_FILES} images`, 'error');
selectedFiles.push(...valid);
updatePreviews();
checkFormValidity();
}
function updatePreviews() {
previews.innerHTML = '';
selectedFiles.forEach((file, i) => {
const url = URL.createObjectURL(file);
const div = document.createElement('div');
div.className = 'preview-item';
div.innerHTML = ``;
div.querySelector('button').onclick = e => { e.stopPropagation(); URL.revokeObjectURL(url); selectedFiles.splice(i, 1); updatePreviews(); checkFormValidity(); };
div.querySelector('img').onclick = () => openImgModal(url);
previews.appendChild(div);
});
dropText.textContent = selectedFiles.length ? `${selectedFiles.length}/${MAX_FILES} image(s)` : 'Cliquez ou glissez-déposez vos images ici';
}
// --- Messages ---
async function loadMessages() {
try {
const res = await fetch(`/api/public/${SLUG}`, { credentials: 'include' });
if (!res.ok) throw new Error(res.status === 404 ? 'Lien introuvable' : 'Erreur chargement');
const { link, messages } = await res.json();
slugDisplay.textContent = link.slug;
document.title = `Messages pour /${link.slug}`;
renderMessages(messages);
} catch (e) { messagesList.innerHTML = `