Emerging – Planning & Suivi Marketing
EMERGING Planning & Suivi Marketing
📋 Planning collaboratif : Cliquez sur une tâche pour voir ses détails. L'auteur et Sandrine peuvent modifier la tâche tant qu'elle n'est pas validée.
Juillet 2026
LunMarMerJeuVenSamDim
Aujourd'hui
Liens utiles & Ressources
CTC - CREE TA COM
${task.collaborator} ${isUrgent ? `🔴 Urgent` : ''} ${statusInfo.label} ${task.type || 'Autres'}
`; taskDiv.addEventListener('click', function(e) { const id = parseInt(this.dataset.id, 10); openDetail(id); }); tasksContainer.appendChild(taskDiv); }); } group.appendChild(tasksContainer); agendaDayList.appendChild(group); } } // ===== RENDU GLOBAL ===== function renderView() { if (isMobile()) { // On cache la grille, on affiche l'agenda document.querySelector('.monthly-view').style.display = 'none'; agendaView.style.display = 'block'; renderAgenda(); } else { document.querySelector('.monthly-view').style.display = 'block'; agendaView.style.display = 'none'; if (viewMode === 'weekly') { renderWeekly(currentWeekStart); } else { renderMonthly(currentDate.getFullYear(), currentDate.getMonth()); } } } // ===== OUVERTURE DÉTAIL ===== (inchangé) function openDetail(taskId) { const task = tasks.find(t => t.id === taskId); if (!task) return; currentDetailTaskId = task.id; isEditing = false; correctionModeActive = false; detailTitle.textContent = task.title; detailTitleValue.textContent = task.title; const debut = task.date_debut || task.date; const fin = task.date_fin || task.date; const options = { year: 'numeric', month: 'long', day: 'numeric' }; const d = new Date(debut + 'T00:00:00'); const f = new Date(fin + 'T00:00:00'); detailPeriode.textContent = 'Du ' + d.toLocaleDateString('fr-FR', options) + ' au ' + f.toLocaleDateString('fr-FR', options); detailCollaborator.textContent = task.collaborator || '—'; detailType.textContent = task.type || '—'; detailDescription.textContent = task.description || '(Aucune remarque)'; if (task.url) { detailUrl.innerHTML = `${task.url}`; } else { detailUrl.textContent = 'Aucun lien'; } const status = task.status || 'not_started'; const info = statusMap[status] || statusMap['not_started']; detailStatus.innerHTML = `${info.label}`; const priority = task.priority || 'normal'; const prioInfo = priorityMap[priority] || priorityMap['normal']; detailPriority.innerHTML = `${prioInfo.label}`; if (task.validated_by) { detailValidatedBy.innerHTML = `Validé par : ${task.validated_by}`; } else { detailValidatedBy.innerHTML = ''; } editTitle.value = task.title || ''; editPriority.value = task.priority || 'normal'; editDateDebut.value = task.date_debut || task.date || ''; editDateFin.value = task.date_fin || task.date || ''; editCollaborator.value = task.collaborator || 'Lounis'; editType.value = task.type || 'Veille'; editDescription.value = task.description || ''; editUrl.value = task.url || ''; pendingNote.value = task.pending_note || ''; pendingUrl.value = task.pending_url || ''; correctionNotes.value = task.correction_notes || ''; detailCorrectionNotes.textContent = task.correction_notes || '(Aucune consigne)'; pendingUpdateSection.style.display = (status === 'pending') ? 'block' : 'none'; pendingInfoSection.style.display = (status === 'pending' && (task.pending_note || task.pending_url)) ? 'block' : 'none'; correctionsSection.style.display = (status === 'corrections') ? 'block' : 'none'; if (status === 'pending' && (task.pending_note || task.pending_url)) { detailPendingNote.textContent = task.pending_note || '(aucune remarque)'; detailPendingUrl.innerHTML = task.pending_url ? `${task.pending_url}` : 'Aucun lien'; } const isSandrine = currentUser === 'Sandrine'; const isAdmin = isSandrine; const isAuthor = currentUser === task.collaborator; const isEditable = (isAuthor || isAdmin) && status !== 'validated'; if (isSandrine) { statusContainer.style.display = 'none'; } else { statusContainer.style.display = 'block'; statusSelect.innerHTML = ''; const labels = { 'not_started': 'Pas effectué', 'in_progress': 'En cours', 'pending': 'En attente de validation', 'validated': '✅ Validé', 'corrections': '✗ Corrections demandées' }; let allowedStatuses = []; if (isAdmin) { allowedStatuses = ['not_started', 'in_progress', 'pending', 'validated', 'corrections']; } else { allowedStatuses = ['not_started', 'in_progress', 'pending']; } allowedStatuses.forEach(s => { const opt = document.createElement('option'); opt.value = s; opt.textContent = labels[s] || s; if (s === status) opt.selected = true; statusSelect.appendChild(opt); }); } if (isSandrine) { editToggleBtn.style.display = 'none'; sandrineActions.style.display = 'block'; sandrineCorrectionField.style.display = 'none'; sandrineCorrectionNotes.value = task.correction_notes || ''; requestCorrectionsBtn.textContent = '✏️ Demander des modifications'; validateTaskBtn.style.display = 'inline-block'; document.querySelectorAll('.detail-field .edit-input').forEach(el => el.style.display = 'none'); correctionsSection.style.display = 'none'; updateStatusBtn.style.display = 'none'; } else { editToggleBtn.style.display = isEditable ? 'inline-block' : 'none'; sandrineActions.style.display = 'none'; document.querySelectorAll('.detail-field .edit-input').forEach(el => el.style.display = ''); correctionsSection.style.display = (status === 'corrections') ? 'block' : 'none'; updateStatusBtn.style.display = 'inline-block'; } if (!isSandrine) { updateCorrectionFieldDisplay(status, isAdmin); } setEditMode(false); detailModal.classList.add('active'); } function closeDetail() { detailModal.classList.remove('active'); currentDetailTaskId = null; isEditing = false; correctionModeActive = false; setEditMode(false); pendingUpdateSection.style.display = 'none'; pendingInfoSection.style.display = 'none'; correctionsSection.style.display = 'none'; sandrineActions.style.display = 'none'; sandrineCorrectionField.style.display = 'none'; requestCorrectionsBtn.textContent = '✏️ Demander des modifications'; validateTaskBtn.style.display = 'inline-block'; statusContainer.style.display = 'block'; } function setEditMode(editing) { isEditing = editing; document.querySelectorAll('.detail-field').forEach(f => f.classList.toggle('editing', editing)); editToggleBtn.classList.toggle('hidden', editing); saveEditBtn.classList.toggle('hidden', !editing); cancelEditBtn.classList.toggle('hidden', !editing); updateStatusBtn.classList.toggle('hidden', editing); statusSelect.disabled = editing; const label = document.querySelector('label[for="statusSelect"]'); if (label) label.style.display = editing ? 'none' : 'block'; statusSelect.style.display = editing ? 'none' : 'block'; pendingUpdateSection.style.display = 'none'; pendingInfoSection.style.display = 'none'; } function updateCorrectionFieldDisplay(currentStatus, isAdmin) { const showEditable = isAdmin && currentStatus === 'corrections'; if (showEditable) { detailCorrectionNotes.style.display = 'none'; correctionNotes.style.display = 'block'; correctionNotes.disabled = false; } else { detailCorrectionNotes.style.display = 'block'; correctionNotes.style.display = 'none'; } } editToggleBtn.addEventListener('click', function() { setEditMode(true); setTimeout(() => editTitle.focus(), 100); }); cancelEditBtn.addEventListener('click', function() { const task = tasks.find(t => t.id === currentDetailTaskId); if (task) { editTitle.value = task.title || ''; editPriority.value = task.priority || 'normal'; editDateDebut.value = task.date_debut || task.date || ''; editDateFin.value = task.date_fin || task.date || ''; editCollaborator.value = task.collaborator || 'Lounis'; editType.value = task.type || 'Veille'; editDescription.value = task.description || ''; editUrl.value = task.url || ''; correctionNotes.value = task.correction_notes || ''; } setEditMode(false); }); saveEditBtn.addEventListener('click', function() { if (currentDetailTaskId === null) return; const title = editTitle.value.trim(); if (!title) { alert('Le titre est obligatoire.'); return; } const dateDebut = editDateDebut.value; const dateFin = editDateFin.value; if (!dateDebut || !dateFin) { alert('Veuillez renseigner les dates de début et de fin.'); return; } if (dateFin < dateDebut) { alert('La date de fin doit être postérieure ou égale à la date de début.'); return; } const updates = { title: title, priority: editPriority.value, date_debut: dateDebut, date_fin: dateFin, collaborator: editCollaborator.value, type: editType.value, description: editDescription.value.trim() || '', url: editUrl.value.trim() || '', date: dateDebut, correction_notes: correctionNotes.value.trim() || '' }; updateTask(currentDetailTaskId, updates); setEditMode(false); }); validateTaskBtn.addEventListener('click', function() { if (currentDetailTaskId === null) return; updateTask(currentDetailTaskId, { status: 'validated', validated_by: 'Sandrine' }); }); requestCorrectionsBtn.addEventListener('click', function() { if (currentDetailTaskId === null) return; if (!correctionModeActive) { sandrineCorrectionField.style.display = 'block'; correctionModeActive = true; this.textContent = '📤 Envoyer les corrections'; validateTaskBtn.style.display = 'none'; sandrineCorrectionNotes.focus(); } else { const notes = sandrineCorrectionNotes.value.trim(); if (!notes) { alert('Veuillez saisir des consignes de modification.'); return; } updateTask(currentDetailTaskId, { status: 'corrections', correction_notes: notes, validated_by: 'Sandrine' }); correctionModeActive = false; sandrineCorrectionField.style.display = 'none'; this.textContent = '✏️ Demander des modifications'; validateTaskBtn.style.display = 'inline-block'; } }); statusSelect.addEventListener('change', function() { const val = this.value; pendingUpdateSection.style.display = (val === 'pending') ? 'block' : 'none'; correctionsSection.style.display = (val === 'corrections') ? 'block' : 'none'; const isAdmin = currentUser === 'Sandrine'; if (!isAdmin) { updateCorrectionFieldDisplay(val, isAdmin); } }); updateStatusBtn.addEventListener('click', function() { if (currentDetailTaskId === null) return; const newStatus = statusSelect.value; const isAdmin = currentUser === 'Sandrine'; let updates = { status: newStatus }; if (newStatus === 'validated') { updates.validated_by = isAdmin ? 'Sandrine (validation)' : currentUser; } else if (newStatus === 'corrections') { updates.validated_by = isAdmin ? 'Sandrine (corrections)' : currentUser; updates.correction_notes = correctionNotes.value.trim() || ''; } else { updates.validated_by = null; } if (newStatus === 'pending') { updates.pending_note = pendingNote.value.trim() || ''; updates.pending_url = pendingUrl.value.trim() || ''; } updateTask(currentDetailTaskId, updates); }); // ===== MODALE AJOUT ===== function openModal(dateStr) { if (!dateStr) { const today = new Date(); dateStr = formatDate(today); } modalTargetDate = dateStr; const parts = dateStr.split('-'); const d = new Date(parseInt(parts[0]), parseInt(parts[1]) - 1, parseInt(parts[2])); const monthNames = ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin', 'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre' ]; modalDateLabel.textContent = d.getDate() + ' ' + monthNames[d.getMonth()] + ' ' + d.getFullYear(); taskDateDebut.value = dateStr; taskDateFin.value = dateStr; taskTitleInput.value = ''; taskDescInput.value = ''; taskUrlInput.value = ''; taskTypeSelect.value = 'Veille'; if (currentUser && currentUser !== 'Sandrine') { collaboratorSelect.value = currentUser; collaboratorSelect.disabled = true; } else { collaboratorSelect.value = 'Lounis'; collaboratorSelect.disabled = false; } prioritySelect.value = 'normal'; modalOverlay.classList.add('active'); setTimeout(() => taskTitleInput.focus(), 100); } function closeModal() { modalOverlay.classList.remove('active'); modalTargetDate = null; collaboratorSelect.disabled = false; } function addTaskFromModal() { if (!modalTargetDate) return; const title = taskTitleInput.value.trim(); if (!title) { alert('Veuillez saisir un titre.'); return; } const description = taskDescInput.value.trim() || ''; const url = taskUrlInput.value.trim() || ''; const type = taskTypeSelect.value; const collaborator = collaboratorSelect.value; const priority = prioritySelect.value; const dateDebut = taskDateDebut.value; const dateFin = taskDateFin.value; if (!dateDebut || !dateFin) { alert('Veuillez renseigner les dates de début et de fin.'); return; } if (dateFin < dateDebut) { alert('La date de fin doit être postérieure ou égale à la date de début.'); return; } const newTask = { date: dateDebut, date_debut: dateDebut, date_fin: dateFin, title: title, description: description, url: url, type: type, collaborator: collaborator, priority: priority, status: 'not_started', pending_note: '', pending_url: '', correction_notes: '' }; addTask(newTask); closeModal(); } // ===== TOAST ===== function showToast(message) { toast.textContent = message; toast.classList.add('show'); clearTimeout(toastTimeout); toastTimeout = setTimeout(() => { toast.classList.remove('show'); }, 4000); } // ===== NAVIGATION ===== function goToToday() { const now = new Date(); if (isMobile()) { agendaDate = new Date(now.getFullYear(), now.getMonth(), now.getDate()); renderView(); } else { if (viewMode === 'weekly') { currentWeekStart = getMonday(now); } else { currentDate = new Date(now.getFullYear(), now.getMonth(), 1); } renderView(); } } function goPrev() { if (isMobile()) { agendaDate = addDays(agendaDate, -AGENDA_DAYS); renderView(); } else { if (viewMode === 'weekly') { currentWeekStart = addDays(currentWeekStart, -7); } else { const y = currentDate.getFullYear(); const m = currentDate.getMonth(); if (m === 0) currentDate = new Date(y - 1, 11, 1); else currentDate = new Date(y, m - 1, 1); } renderView(); } } function goNext() { if (isMobile()) { agendaDate = addDays(agendaDate, AGENDA_DAYS); renderView(); } else { if (viewMode === 'weekly') { currentWeekStart = addDays(currentWeekStart, 7); } else { const y = currentDate.getFullYear(); const m = currentDate.getMonth(); if (m === 11) currentDate = new Date(y + 1, 0, 1); else currentDate = new Date(y, m + 1, 1); } renderView(); } } function toggleView() { if (viewMode === 'weekly') { viewMode = 'monthly'; viewToggleBtn.textContent = '📅 Vue semaine'; const now = new Date(); currentDate = new Date(now.getFullYear(), now.getMonth(), 1); } else { viewMode = 'weekly'; viewToggleBtn.textContent = '📅 Vue mois'; currentWeekStart = getMonday(new Date()); } renderView(); } // ===== INIT ===== function init() { loginBtn.addEventListener('click', login); loginCode.addEventListener('keydown', function(e) { if (e.key === 'Enter') login(); }); logoutBtn.addEventListener('click', logout); const hasSession = checkSession(); if (hasSession) { fetchTasks(); } const now = new Date(); currentWeekStart = getMonday(now); currentDate = new Date(now.getFullYear(), now.getMonth(), 1); agendaDate = new Date(now.getFullYear(), now.getMonth(), now.getDate()); viewMode = 'monthly'; viewToggleBtn.textContent = '📅 Vue semaine'; prevBtn.addEventListener('click', goPrev); nextBtn.addEventListener('click', goNext); todayBtn.addEventListener('click', goToToday); viewToggleBtn.addEventListener('click', toggleView); // Navigation agenda mobile agendaPrevBtn.addEventListener('click', goPrev); agendaNextBtn.addEventListener('click', goNext); addTaskGlobalBtn.addEventListener('click', function() { openModal(); }); modalAddBtn.addEventListener('click', addTaskFromModal); modalCancelBtn.addEventListener('click', closeModal); modalOverlay.addEventListener('click', function(e) { if (e.target === modalOverlay) closeModal(); }); detailCloseBtn.addEventListener('click', closeDetail); detailModal.addEventListener('click', function(e) { if (e.target === detailModal) closeDetail(); }); taskTitleInput.addEventListener('keydown', function(e) { if (e.key === 'Enter') { e.preventDefault(); addTaskFromModal(); } if (e.key === 'Escape') closeModal(); }); document.addEventListener('keydown', function(e) { if (e.key === 'Escape') { if (detailModal.classList.contains('active')) closeDetail(); else if (modalOverlay.classList.contains('active')) closeModal(); } }); // Recalculer à chaque redimensionnement window.addEventListener('resize', function() { renderView(); }); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();