@@ -2872,16 +3127,451 @@ function closeDayContextMenu() {
activeContextMenuDay = null;
}
+// --- Rechtermuisklik Context Menu voor Taken (Release v2.2) ---
+let activeContextMenuTask = null;
+
+function handleTaskContextMenu(e, taskData) {
+ e.preventDefault();
+ e.stopPropagation();
+
+ closeDayContextMenu();
+ activeContextMenuTask = taskData;
+
+ const menu = document.getElementById('taskContextMenu');
+ const titleEl = document.getElementById('taskContextMenuTitle');
+ const priorityBadge = document.getElementById('taskContextMenuPriorityBadge');
+ const metaEl = document.getElementById('taskContextMenuMeta');
+ const toggleLabel = document.getElementById('taskCtxBtnToggleLabel');
+ const btnOutlook = document.getElementById('taskCtxBtnOutlook');
+
+ if (!menu) return;
+
+ if (titleEl) titleEl.textContent = taskData.title || 'Taak';
+
+ if (priorityBadge) {
+ const p = taskData.priority || 'P2';
+ priorityBadge.textContent = p;
+ if (p === 'P1') {
+ priorityBadge.className = 'text-[9px] font-extrabold px-1.5 py-0.5 rounded-md bg-rose-100 text-rose-700 dark:bg-rose-950 dark:text-rose-400 border border-rose-300 dark:border-rose-800';
+ } else if (p === 'P2') {
+ priorityBadge.className = 'text-[9px] font-extrabold px-1.5 py-0.5 rounded-md bg-amber-100 text-amber-700 dark:bg-amber-950 dark:text-amber-400 border border-amber-300 dark:border-amber-800';
+ } else {
+ priorityBadge.className = 'text-[9px] font-extrabold px-1.5 py-0.5 rounded-md bg-slate-100 text-slate-700 dark:bg-slate-800 dark:text-slate-300 border border-slate-300 dark:border-slate-700';
+ }
+ }
+
+ if (metaEl) {
+ let meta = '';
+ if (taskData.start) {
+ meta += `${taskData.start}${taskData.end ? ' - ' + taskData.end : ''} (${taskData.duration || 30}m)`;
+ } else if (taskData.duration) {
+ meta += `${taskData.duration}m`;
+ }
+ if (taskData.source && taskData.source !== 'FOCIZ') {
+ meta += (meta ? ' · ' : '') + taskData.source;
+ }
+ metaEl.textContent = meta || (taskData.date || 'Vandaag');
+ }
+
+ if (toggleLabel) {
+ toggleLabel.textContent = taskData.is_completed ? 'Markeren als openstaand' : 'Markeren als voltooid';
+ }
+
+ if (btnOutlook) {
+ if (window.isOutlookConnected && !taskData.external_id && !taskData.is_completed) {
+ btnOutlook.classList.remove('hidden');
+ } else {
+ btnOutlook.classList.add('hidden');
+ }
+ }
+
+ // Koppel actieknoppen
+ const btnOpen = document.getElementById('taskCtxBtnOpen');
+ if (btnOpen) {
+ btnOpen.onclick = () => {
+ closeTaskContextMenu();
+ openTaskDetailModal(taskData);
+ };
+ }
+
+ const btnEdit = document.getElementById('taskCtxBtnEdit');
+ if (btnEdit) {
+ btnEdit.onclick = () => {
+ closeTaskContextMenu();
+ openEditTaskModal(taskData);
+ };
+ }
+
+ const btnToggle = document.getElementById('taskCtxBtnToggle');
+ if (btnToggle) {
+ btnToggle.onclick = () => {
+ closeTaskContextMenu();
+ toggleTask(taskData.id, taskData.is_completed ? 0 : 1);
+ };
+ }
+
+ const btnSubtasks = document.getElementById('taskCtxBtnSubtasks');
+ if (btnSubtasks) {
+ btnSubtasks.onclick = () => {
+ closeTaskContextMenu();
+ if (typeof toggleSubtaskPanel === 'function') {
+ toggleSubtaskPanel(taskData.id);
+ } else {
+ openTaskDetailModal(taskData);
+ }
+ };
+ }
+
+ if (btnOutlook) {
+ btnOutlook.onclick = () => {
+ closeTaskContextMenu();
+ if (typeof syncTaskToOutlook === 'function') {
+ syncTaskToOutlook(taskData.id);
+ }
+ };
+ }
+
+ const btnDelete = document.getElementById('taskCtxBtnDelete');
+ if (btnDelete) {
+ btnDelete.onclick = () => {
+ closeTaskContextMenu();
+ deleteTask(taskData.id);
+ };
+ }
+
+ if (window.lucide && lucide.createIcons) {
+ lucide.createIcons();
+ }
+
+ menu.classList.remove('hidden');
+
+ const menuWidth = 240;
+ const menuHeight = menu.offsetHeight || 220;
+ let posX = Math.max(12, Math.min(window.innerWidth - menuWidth - 16, e.clientX));
+ let posY = Math.max(12, Math.min(window.innerHeight - menuHeight - 16, e.clientY));
+
+ menu.style.left = `${posX}px`;
+ menu.style.top = `${posY}px`;
+}
+
+function closeTaskContextMenu() {
+ const menu = document.getElementById('taskContextMenu');
+ if (menu) {
+ menu.classList.add('hidden');
+ }
+ activeContextMenuTask = null;
+}
+
+// --- Task Detail Modal (Volledige inhoud bekijken) ---
+let currentDetailTaskId = null;
+let currentDetailTaskData = null;
+
+async function openTaskDetailModal(taskDataOrId) {
+ let task = null;
+ let taskId = 0;
+
+ if (typeof taskDataOrId === 'object' && taskDataOrId !== null) {
+ task = taskDataOrId;
+ taskId = task.id;
+ } else {
+ taskId = parseInt(taskDataOrId, 10);
+ }
+
+ currentDetailTaskId = taskId;
+ currentDetailTaskData = task;
+
+ const modal = document.getElementById('taskDetailModal');
+ if (!modal) return;
+
+ // Toon direct modal met basisgegevens
+ if (task) {
+ populateTaskDetailModal(task, _subtaskCache[taskId] || []);
+ }
+
+ modal.classList.remove('hidden');
+ modal.classList.add('flex');
+
+ // Haal live alle volledige details op inclusief beschrijving en subtaken
+ try {
+ const resp = await fetch(`/api/planner.php?action=get_task_details&task_id=${taskId}`);
+ const data = await resp.json();
+ if (data.success && data.task) {
+ currentDetailTaskData = {
+ id: data.task.id,
+ title: data.task.title,
+ description: data.task.description || '',
+ priority: data.task.priority || 'P2',
+ start: data.task.time_block_start || '',
+ end: data.task.time_block_end || '',
+ duration: parseInt(data.task.duration_minutes, 10) || 30,
+ is_completed: parseInt(data.task.is_completed, 10) === 1,
+ source: data.task.source || 'FOCIZ',
+ external_id: data.task.external_id || '',
+ external_url: data.task.external_url || '',
+ date: data.task.plan_date || (task ? task.date : '')
+ };
+ _subtaskCache[taskId] = data.subtasks || [];
+ populateTaskDetailModal(currentDetailTaskData, data.subtasks || []);
+ }
+ } catch (err) {
+ console.error('Fout bij ophalen taakdetails:', err);
+ }
+
+ if (window.lucide && lucide.createIcons) {
+ lucide.createIcons();
+ }
+}
+
+function populateTaskDetailModal(task, subtasks) {
+ const titleEl = document.getElementById('taskDetailTitle');
+ const priorityBadge = document.getElementById('taskDetailPriorityBadge');
+ const priorityIcon = document.getElementById('taskDetailPriorityIcon');
+ const sourceBadge = document.getElementById('taskDetailSourceBadge');
+ const statusBadge = document.getElementById('taskDetailStatusBadge');
+ const dateEl = document.getElementById('taskDetailDate');
+ const timeSlotEl = document.getElementById('taskDetailTimeSlot');
+ const durationEl = document.getElementById('taskDetailDuration');
+ const descEl = document.getElementById('taskDetailDescription');
+ const subtasksList = document.getElementById('taskDetailSubtasksList');
+ const extBlock = document.getElementById('taskDetailExternalBlock');
+ const extLabel = document.getElementById('taskDetailExternalLabel');
+ const extLink = document.getElementById('taskDetailExternalLink');
+ const btnEdit = document.getElementById('taskDetailBtnEdit');
+ const btnDelete = document.getElementById('taskDetailBtnDelete');
+
+ if (titleEl) titleEl.textContent = task.title || 'Geen titel';
+
+ // Prioriteit
+ const p = task.priority || 'P2';
+ if (priorityBadge) {
+ priorityBadge.textContent = p === 'P1' ? 'P1 · Hoge Prioriteit' : (p === 'P2' ? 'P2 · Belangrijk' : 'P3 · Regulier');
+ if (p === 'P1') {
+ priorityBadge.className = 'px-2 py-0.5 rounded-full text-[10px] font-extrabold uppercase tracking-wide bg-rose-50 dark:bg-rose-500/15 text-rose-700 dark:text-rose-400 border border-rose-200 dark:border-rose-500/30';
+ if (priorityIcon) priorityIcon.className = 'w-10 h-10 rounded-2xl bg-rose-50 dark:bg-rose-500/10 border border-rose-200 dark:border-rose-500/20 text-rose-600 dark:text-rose-400 flex items-center justify-center flex-shrink-0 mt-0.5';
+ } else if (p === 'P2') {
+ priorityBadge.className = 'px-2 py-0.5 rounded-full text-[10px] font-extrabold uppercase tracking-wide bg-amber-50 dark:bg-amber-500/15 text-amber-700 dark:text-amber-400 border border-amber-200 dark:border-amber-500/30';
+ if (priorityIcon) priorityIcon.className = 'w-10 h-10 rounded-2xl bg-amber-50 dark:bg-amber-500/10 border border-amber-200 dark:border-amber-500/20 text-amber-600 dark:text-amber-400 flex items-center justify-center flex-shrink-0 mt-0.5';
+ } else {
+ priorityBadge.className = 'px-2 py-0.5 rounded-full text-[10px] font-extrabold uppercase tracking-wide bg-slate-100 dark:bg-slate-800 text-slate-700 dark:text-slate-300 border border-slate-200 dark:border-slate-700';
+ if (priorityIcon) priorityIcon.className = 'w-10 h-10 rounded-2xl bg-slate-100 dark:bg-slate-800 border border-slate-200 dark:border-slate-700 text-slate-600 dark:text-slate-400 flex items-center justify-center flex-shrink-0 mt-0.5';
+ }
+ }
+
+ // Source
+ if (sourceBadge) {
+ const src = (task.source || 'FOCIZ').toUpperCase();
+ sourceBadge.textContent = src;
+ if (src === 'JIRA') {
+ sourceBadge.className = 'px-2 py-0.5 rounded-full text-[10px] font-mono font-bold bg-indigo-50 dark:bg-indigo-500/15 text-indigo-700 dark:text-indigo-400 border border-indigo-200 dark:border-indigo-500/30';
+ } else if (src === 'OUTLOOK' || src === 'MICROSOFT') {
+ sourceBadge.className = 'px-2 py-0.5 rounded-full text-[10px] font-mono font-bold bg-sky-50 dark:bg-sky-500/15 text-sky-700 dark:text-sky-400 border border-sky-200 dark:border-sky-500/30';
+ } else if (src === 'OUTLOOK_MAIL' || src === 'MAIL') {
+ sourceBadge.className = 'px-2 py-0.5 rounded-full text-[10px] font-mono font-bold bg-violet-50 dark:bg-violet-500/15 text-violet-700 dark:text-violet-400 border border-violet-200 dark:border-violet-500/30';
+ } else if (src === 'BRAINSTORM') {
+ sourceBadge.className = 'px-2 py-0.5 rounded-full text-[10px] font-mono font-bold bg-amber-50 dark:bg-amber-500/15 text-amber-700 dark:text-amber-400 border border-amber-200 dark:border-amber-500/30';
+ } else {
+ sourceBadge.className = 'px-2 py-0.5 rounded-full text-[10px] font-mono font-bold bg-slate-100 dark:bg-slate-800 text-slate-600 dark:text-slate-400 border border-slate-200 dark:border-slate-700';
+ }
+ }
+
+ // Status
+ if (statusBadge) {
+ if (task.is_completed) {
+ statusBadge.textContent = 'Voltooid';
+ statusBadge.className = 'px-2 py-0.5 rounded-full text-[10px] font-bold bg-emerald-50 dark:bg-emerald-500/15 text-emerald-700 dark:text-emerald-400 border border-emerald-200 dark:border-emerald-500/30';
+ } else {
+ statusBadge.textContent = 'Openstaand';
+ statusBadge.className = 'px-2 py-0.5 rounded-full text-[10px] font-bold bg-sky-50 dark:bg-sky-500/15 text-sky-700 dark:text-sky-400 border border-sky-200 dark:border-sky-500/30';
+ }
+ }
+
+ // Datum & Tijd
+ if (dateEl) {
+ dateEl.textContent = task.date || (typeof activePlannerDate !== 'undefined' ? activePlannerDate : 'Vandaag');
+ }
+ if (timeSlotEl) {
+ timeSlotEl.textContent = (task.start) ? `${task.start} - ${task.end || '?'}` : 'Geen tijdblok';
+ }
+ if (durationEl) {
+ durationEl.textContent = `${task.duration || 30} minuten`;
+ }
+
+ // Beschrijving
+ if (descEl) {
+ if (task.description && task.description.trim()) {
+ descEl.textContent = task.description;
+ descEl.classList.remove('italic', 'text-slate-400');
+ } else {
+ descEl.textContent = 'Geen beschrijving of notities opgegeven voor deze taak.';
+ descEl.classList.add('italic', 'text-slate-400');
+ }
+ }
+
+ // Subtaken
+ if (subtasksList) {
+ renderSubtasksInDetailModal(task.id, subtasks || []);
+ }
+
+ // Externe link
+ if (extBlock && extLink && extLabel) {
+ if (task.external_url) {
+ extBlock.classList.remove('hidden');
+ extLink.href = task.external_url;
+ if (task.source === 'JIRA') {
+ extLabel.textContent = `Gekoppeld Jira Ticket: ${task.external_id || 'Jira'}`;
+ } else if (task.source === 'OUTLOOK' || task.source === 'MICROSOFT') {
+ extLabel.textContent = 'Gekoppeld in Microsoft Outlook Agenda';
+ } else if (task.source === 'OUTLOOK_MAIL') {
+ extLabel.textContent = 'Afkomstig uit Outlook E-mail';
+ } else {
+ extLabel.textContent = 'Externe koppeling';
+ }
+ } else {
+ extBlock.classList.add('hidden');
+ }
+ }
+
+ // Buttons
+ if (btnEdit) {
+ btnEdit.onclick = () => {
+ closeTaskDetailModal();
+ openEditTaskModal(task);
+ };
+ }
+ if (btnDelete) {
+ btnDelete.onclick = () => {
+ closeTaskDetailModal();
+ deleteTask(task.id);
+ };
+ }
+}
+
+function renderSubtasksInDetailModal(taskId, subtasks) {
+ const list = document.getElementById('taskDetailSubtasksList');
+ const countEl = document.getElementById('taskDetailSubtasksCount');
+ if (!list) return;
+
+ if (!subtasks || subtasks.length === 0) {
+ list.innerHTML = '
Nog geen subtaken of checklist items.
';
+ if (countEl) countEl.textContent = '0 items';
+ return;
+ }
+
+ const completedCount = subtasks.filter(s => parseInt(s.is_completed, 10) === 1).length;
+ if (countEl) countEl.textContent = `${completedCount} van ${subtasks.length} gereed`;
+
+ list.innerHTML = subtasks.map(s => {
+ const isDone = parseInt(s.is_completed, 10) === 1;
+ return `
+
+
+
+
+ `;
+ }).join('');
+
+ if (window.lucide && lucide.createIcons) {
+ lucide.createIcons();
+ }
+}
+
+async function toggleSubtaskFromDetailModal(taskId, subtaskId, isChecked) {
+ try {
+ await fetch('/api/planner.php?action=toggle_subtask', {
+ method: 'POST',
+ headers: {'Content-Type': 'application/json'},
+ body: JSON.stringify({ subtask_id: subtaskId, is_completed: isChecked ? 1 : 0 })
+ });
+ const resp = await fetch(`/api/planner.php?action=get_subtasks&task_id=${taskId}`);
+ const data = await resp.json();
+ if (data.success) {
+ _subtaskCache[taskId] = data.subtasks || [];
+ renderSubtasksInDetailModal(taskId, data.subtasks || []);
+ if (typeof renderSubtasks === 'function') renderSubtasks(taskId);
+ }
+ } catch (e) {
+ console.error('Fout bij afvinken subtaak:', e);
+ }
+}
+
+async function deleteSubtaskFromDetailModal(taskId, subtaskId) {
+ try {
+ await fetch('/api/planner.php?action=delete_subtask', {
+ method: 'POST',
+ headers: {'Content-Type': 'application/json'},
+ body: JSON.stringify({ subtask_id: subtaskId })
+ });
+ const resp = await fetch(`/api/planner.php?action=get_subtasks&task_id=${taskId}`);
+ const data = await resp.json();
+ if (data.success) {
+ _subtaskCache[taskId] = data.subtasks || [];
+ renderSubtasksInDetailModal(taskId, data.subtasks || []);
+ if (typeof renderSubtasks === 'function') renderSubtasks(taskId);
+ }
+ } catch (e) {
+ console.error('Fout bij verwijderen subtaak:', e);
+ }
+}
+
+async function addSubtaskFromDetailModal() {
+ const input = document.getElementById('taskDetailNewSubtaskInput');
+ if (!input || !currentDetailTaskId) return;
+ const title = input.value.trim();
+ if (!title) return;
+
+ try {
+ const resp = await fetch('/api/planner.php?action=add_subtask', {
+ method: 'POST',
+ headers: {'Content-Type': 'application/json'},
+ body: JSON.stringify({ task_id: currentDetailTaskId, title: title })
+ });
+ const data = await resp.json();
+ if (data.success) {
+ input.value = '';
+ const subResp = await fetch(`/api/planner.php?action=get_subtasks&task_id=${currentDetailTaskId}`);
+ const subData = await subResp.json();
+ if (subData.success) {
+ _subtaskCache[currentDetailTaskId] = subData.subtasks || [];
+ renderSubtasksInDetailModal(currentDetailTaskId, subData.subtasks || []);
+ if (typeof renderSubtasks === 'function') renderSubtasks(currentDetailTaskId);
+ }
+ }
+ } catch (e) {
+ console.error('Fout bij toevoegen subtaak:', e);
+ }
+}
+
+function closeTaskDetailModal() {
+ const modal = document.getElementById('taskDetailModal');
+ if (modal) {
+ modal.classList.add('hidden');
+ modal.classList.remove('flex');
+ }
+ currentDetailTaskId = null;
+ currentDetailTaskData = null;
+}
+
document.addEventListener('click', function(e) {
- const menu = document.getElementById('dayContextMenu');
- if (menu && !menu.contains(e.target)) {
+ const dayMenu = document.getElementById('dayContextMenu');
+ if (dayMenu && !dayMenu.contains(e.target)) {
closeDayContextMenu();
}
+ const taskMenu = document.getElementById('taskContextMenu');
+ if (taskMenu && !taskMenu.contains(e.target)) {
+ closeTaskContextMenu();
+ }
});
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape') {
closeDayContextMenu();
+ closeTaskContextMenu();
+ closeTaskDetailModal();
}
});
@@ -3308,6 +3998,43 @@ function toggleWidgetCollapse(wId) {
} catch (e) {}
}
+// --- Dagenselectie (Weekstrook) Inklappen / Uitklappen ---
+function toggleDaySelectionCollapse() {
+ const strip = document.getElementById('weekDaysStripContainer');
+ const icon = document.getElementById('iconToggleDaySelection');
+ const label = document.getElementById('labelToggleDaySelection');
+ if (!strip) return;
+
+ const isCurrentlyHidden = strip.classList.contains('hidden');
+ if (isCurrentlyHidden) {
+ strip.classList.remove('hidden');
+ if (icon) icon.classList.remove('rotate-180');
+ if (label) label.textContent = 'Dagen inklappen';
+ try { localStorage.setItem('fociz_days_selection_collapsed', '0'); } catch(e) {}
+ } else {
+ strip.classList.add('hidden');
+ if (icon) icon.classList.add('rotate-180');
+ if (label) label.textContent = 'Dagen tonen';
+ try { localStorage.setItem('fociz_days_selection_collapsed', '1'); } catch(e) {}
+ }
+ if (typeof lucide !== 'undefined' && lucide.createIcons) {
+ lucide.createIcons();
+ }
+}
+
+function initPlannerDaySelectionCollapse() {
+ try {
+ if (localStorage.getItem('fociz_days_selection_collapsed') === '1') {
+ const strip = document.getElementById('weekDaysStripContainer');
+ const icon = document.getElementById('iconToggleDaySelection');
+ const label = document.getElementById('labelToggleDaySelection');
+ if (strip) strip.classList.add('hidden');
+ if (icon) icon.classList.add('rotate-180');
+ if (label) label.textContent = 'Dagen tonen';
+ }
+ } catch(e) {}
+}
+
// --- Widget Verbergen van Scherm met Undo Toast ---
function hideWidgetOnScreen(wId) {
document.getElementById(`widgetSettingsMenu_${wId}`)?.classList.add('hidden');
@@ -3361,6 +4088,9 @@ function initPlannerWidgets() {
const grid = document.getElementById('focizWidgetGrid');
if (!grid) return;
+ // Herstel dagenselectie inklapstatus
+ initPlannerDaySelectionCollapse();
+
// Herstel eventuele ingeklapte widgets vanuit localStorage
document.querySelectorAll('.fociz-widget-wrapper').forEach(wrapper => {
const wId = wrapper.getAttribute('data-widget-id');
@@ -4004,6 +4734,13 @@ function openJiraPlanModal(issueKey) {
if (notice) notice.classList.add('hidden');
updateJiraPlanEndTimeDisplay();
+ // Reset submit button state
+ const submitBtn = document.getElementById('btnSubmitJiraPlan');
+ if (submitBtn) {
+ submitBtn.disabled = false;
+ submitBtn.innerHTML = '
Inplannen';
+ }
+
const modal = document.getElementById('jiraPlanModal');
modal.classList.remove('hidden');
modal.classList.add('flex');
@@ -4014,6 +4751,11 @@ function closeJiraPlanModal() {
const modal = document.getElementById('jiraPlanModal');
modal.classList.add('hidden');
modal.classList.remove('flex');
+ const submitBtn = document.getElementById('btnSubmitJiraPlan');
+ if (submitBtn) {
+ submitBtn.disabled = false;
+ submitBtn.innerHTML = '
Inplannen';
+ }
_jiraPlanCurrentIssue = null;
}
@@ -4044,6 +4786,14 @@ function updateJiraPlanEndTimeDisplay() {
badge.textContent = `(tot ${endTimeStr})`;
}
+function clearJiraPlanStartTime() {
+ const startInput = document.getElementById('jiraPlanStartTime');
+ if (startInput) {
+ startInput.value = '';
+ updateJiraPlanEndTimeDisplay();
+ }
+}
+
async function planJiraInNextOpenBlock() {
const durationInput = document.getElementById('jiraPlanDuration');
const duration = durationInput ? (parseInt(durationInput.value, 10) || 15) : 15;
@@ -4215,16 +4965,14 @@ async function submitJiraPlanModal() {
}
} else {
alert(data.error || 'Kon ticket niet inplannen');
- if (btn) {
- btn.innerHTML = origHtml;
- btn.disabled = false;
- }
}
} catch(e) {
alert('Netwerkfout bij inplannen');
+ } finally {
if (btn) {
- btn.innerHTML = origHtml;
+ btn.innerHTML = origHtml || '
Inplannen';
btn.disabled = false;
+ if (typeof lucide !== 'undefined') lucide.createIcons();
}
}
}
@@ -4236,17 +4984,91 @@ async function quickImportJiraByKey(btn, issueKey) {
// --- Direct E-mail Inplannen als Dagtaak ---
-function openPlanEmailDirect(mailId, subject, sender, webLink) {
- document.getElementById('planEmailId').value = mailId;
- document.getElementById('planEmailTitle').value = subject ? `[Mail] ${subject}` : '[Mail] E-mail opvolgen';
- document.getElementById('planEmailSender').value = sender || 'Outlook Mail';
+let _emailPlanCurrentData = null;
+
+function toggleEmailPlanPreview() {
+ const wrapper = document.getElementById('emailPlanDescWrapper');
+ const chevron = document.getElementById('emailPlanDescChevron');
+ const label = document.getElementById('emailPlanDescToggleLabel');
+ if (!wrapper) return;
+ const isHidden = wrapper.classList.contains('hidden');
+ if (isHidden) {
+ wrapper.classList.remove('hidden');
+ if (chevron) chevron.classList.add('rotate-180');
+ if (label) label.textContent = 'Inhoud verbergen';
+ } else {
+ wrapper.classList.add('hidden');
+ if (chevron) chevron.classList.remove('rotate-180');
+ if (label) label.textContent = 'Inhoud van het bericht bekijken';
+ }
+ if (typeof lucide !== 'undefined') lucide.createIcons();
+}
+
+function openPlanEmailDirect(mailId, subject, sender, webLink, bodyPreview = '') {
+ _emailPlanCurrentData = { mailId, subject, sender, webLink, bodyPreview };
+
+ document.getElementById('planEmailId').value = mailId || '';
document.getElementById('planEmailWebLink').value = webLink || '';
+ document.getElementById('planEmailSender').value = sender || 'Outlook Mail';
+ document.getElementById('planEmailModalSenderBadge').textContent = sender || 'Outlook Mail';
+ document.getElementById('planEmailModalSenderName').textContent = sender || 'Outlook Mail';
+ document.getElementById('planEmailModalSubject').textContent = subject || '(Geen onderwerp)';
+
+ // Titel input
+ const titleInput = document.getElementById('planEmailTitle');
+ if (titleInput) {
+ titleInput.value = subject ? `[Mail] ${subject}` : '[Mail] E-mail opvolgen';
+ }
+
+ // Body preview
+ const descEl = document.getElementById('emailPlanModalDescription');
+ if (descEl) {
+ descEl.textContent = (bodyPreview && bodyPreview.trim()) ? bodyPreview.trim() : 'Geen berichtvoorbeeld beschikbaar.';
+ }
+ const descWrapper = document.getElementById('emailPlanDescWrapper');
+ const descChevron = document.getElementById('emailPlanDescChevron');
+ const descLabel = document.getElementById('emailPlanDescToggleLabel');
+ if (descWrapper) descWrapper.classList.add('hidden');
+ if (descChevron) descChevron.classList.remove('rotate-180');
+ if (descLabel) descLabel.textContent = 'Inhoud van het bericht bekijken';
+
+ // Datum: actieve planner datum of vandaag
+ const dateInput = document.getElementById('planEmailDate');
+ const targetDate = (typeof activePlannerDate !== 'undefined' && activePlannerDate) ? activePlannerDate : new Date().toISOString().slice(0, 10);
+ if (dateInput) {
+ dateInput.value = targetDate;
+ }
+
+ // Duur: standaard 30 minuten
+ setEmailPlanDuration(30);
+
+ // Prioriteit: standaard P2
+ const prioEl = document.getElementById('planEmailPriority');
+ if (prioEl) prioEl.value = 'P2';
+
+ // Starttijd: suggestie
+ const now = new Date();
+ const todayStr = now.toISOString().slice(0, 10);
+ const suggestTime = targetDate === todayStr ? suggestNextFreeSlot() : '09:00';
+ document.getElementById('planEmailStartTime').value = suggestTime;
+
+ const notice = document.getElementById('emailPlanOpenBlockNotice');
+ if (notice) notice.classList.add('hidden');
+ updatePlanEmailEndTimeDisplay();
+
+ // Reset submit button state
+ const submitBtn = document.getElementById('btnSubmitPlanEmailDirect');
+ if (submitBtn) {
+ submitBtn.disabled = false;
+ submitBtn.innerHTML = '
Inplannen';
+ }
const modal = document.getElementById('planEmailDirectModal');
if (modal) {
modal.classList.remove('hidden');
modal.classList.add('flex');
}
+ if (typeof lucide !== 'undefined') lucide.createIcons();
}
function closePlanEmailDirectModal() {
@@ -4255,6 +5077,113 @@ function closePlanEmailDirectModal() {
modal.classList.add('hidden');
modal.classList.remove('flex');
}
+ const submitBtn = document.getElementById('btnSubmitPlanEmailDirect');
+ if (submitBtn) {
+ submitBtn.disabled = false;
+ submitBtn.innerHTML = '
Inplannen';
+ }
+ _emailPlanCurrentData = null;
+}
+
+function setEmailPlanDuration(minutes) {
+ const durInput = document.getElementById('planEmailDuration');
+ if (durInput) durInput.value = minutes;
+ document.querySelectorAll('.email-plan-dur-btn').forEach(btn => {
+ const dur = parseInt(btn.getAttribute('data-dur'), 10);
+ if (dur === minutes) {
+ btn.className = 'email-plan-dur-btn px-2 py-1.5 rounded-xl border text-[11px] font-bold transition cursor-pointer bg-indigo-600 border-indigo-600 text-white shadow-xs';
+ } else {
+ btn.className = 'email-plan-dur-btn px-2 py-1.5 rounded-xl border text-[11px] font-bold transition cursor-pointer bg-white dark:bg-slate-900 border-slate-200 dark:border-slate-800 text-slate-700 dark:text-slate-300 hover:border-indigo-400';
+ }
+ });
+ updatePlanEmailEndTimeDisplay();
+}
+
+function clearPlanEmailStartTime() {
+ const startInput = document.getElementById('planEmailStartTime');
+ if (startInput) {
+ startInput.value = '';
+ updatePlanEmailEndTimeDisplay();
+ }
+}
+
+function updatePlanEmailEndTimeDisplay() {
+ const startTimeInput = document.getElementById('planEmailStartTime');
+ const badge = document.getElementById('planEmailEndTimeBadge');
+ const durationInput = document.getElementById('planEmailDuration');
+ if (!badge) return;
+
+ const startTime = startTimeInput ? startTimeInput.value.trim() : '';
+ const duration = durationInput ? (parseInt(durationInput.value, 10) || 30) : 30;
+
+ if (!startTime) {
+ badge.textContent = '';
+ return;
+ }
+
+ const parts = startTime.split(':').map(Number);
+ if (parts.length < 2 || isNaN(parts[0]) || isNaN(parts[1])) {
+ badge.textContent = '';
+ return;
+ }
+
+ const endTotalMin = parts[0] * 60 + parts[1] + duration;
+ const endH = Math.floor(endTotalMin / 60);
+ const endM = endTotalMin % 60;
+ const endTimeStr = `${String(endH).padStart(2,'0')}:${String(endM).padStart(2,'0')}`;
+ badge.textContent = `(tot ${endTimeStr})`;
+}
+
+async function planEmailInNextOpenBlock() {
+ const durationInput = document.getElementById('planEmailDuration');
+ const duration = durationInput ? (parseInt(durationInput.value, 10) || 30) : 30;
+ const btn = document.getElementById('btnEmailPlanOpenBlock');
+ const notice = document.getElementById('emailPlanOpenBlockNotice');
+ const noticeText = document.getElementById('emailPlanOpenBlockNoticeText');
+
+ let origBtnHtml = '';
+ if (btn) {
+ origBtnHtml = btn.innerHTML;
+ btn.innerHTML = '
Zoeken...';
+ btn.disabled = true;
+ }
+
+ try {
+ const res = await fetch('/api/planner.php?action=find_next_free_slot', {
+ method: 'POST',
+ headers: {'Content-Type': 'application/json'},
+ body: JSON.stringify({
+ duration_minutes: duration
+ })
+ });
+ const data = await res.json();
+
+ if (data.success && data.slot) {
+ const slot = data.slot;
+ const dateInput = document.getElementById('planEmailDate');
+ if (dateInput) dateInput.value = slot.date;
+
+ const startInput = document.getElementById('planEmailStartTime');
+ if (startInput) startInput.value = slot.start;
+
+ updatePlanEmailEndTimeDisplay();
+
+ if (notice && noticeText) {
+ noticeText.textContent = `Open blok gevonden: ${slot.formatted_date} van ${slot.start} tot ${slot.end} (${slot.duration_minutes}m)`;
+ notice.classList.remove('hidden');
+ }
+ } else {
+ alert(data.error || 'Geen vrij blok gevonden binnen de gezette werktijden.');
+ }
+ } catch (err) {
+ alert('Netwerkfout bij zoeken naar een open blok.');
+ } finally {
+ if (btn) {
+ btn.innerHTML = origBtnHtml;
+ btn.disabled = false;
+ }
+ if (typeof lucide !== 'undefined') lucide.createIcons();
+ }
}
async function submitPlanEmailDirect() {
@@ -4262,16 +5191,19 @@ async function submitPlanEmailDirect() {
const title = document.getElementById('planEmailTitle').value.trim();
const priority = document.getElementById('planEmailPriority').value;
const duration = parseInt(document.getElementById('planEmailDuration').value, 10) || 30;
- const startTime = document.getElementById('planEmailStartTime').value.trim();
- const syncOutlook = document.getElementById('planEmailSyncOutlook').checked;
+ let startTime = document.getElementById('planEmailStartTime').value.trim();
+ const syncOutlook = document.getElementById('planEmailSyncOutlook') ? document.getElementById('planEmailSyncOutlook').checked : false;
const webLink = document.getElementById('planEmailWebLink').value;
+ const sender = document.getElementById('planEmailSender').value;
+
+ const dateInput = document.getElementById('planEmailDate');
+ let planDate = (dateInput && dateInput.value) ? dateInput.value : ((typeof activePlannerDate !== 'undefined' && activePlannerDate) ? activePlannerDate : new Date().toISOString().slice(0, 10));
if (!title) {
alert('Vul een taaktitel in');
return;
}
- let planDate = activePlannerDate;
if (startTime) {
const [hh, mm] = startTime.split(':').map(Number);
const endMin = hh * 60 + mm + duration;
@@ -4297,20 +5229,32 @@ async function submitPlanEmailDirect() {
}
const btn = document.getElementById('btnSubmitPlanEmailDirect');
- const origHtml = btn.innerHTML;
- btn.innerText = 'Inplannen...';
- btn.disabled = true;
+ const origHtml = btn ? btn.innerHTML : '';
+ if (btn) {
+ btn.innerHTML = '
Inplannen...';
+ btn.disabled = true;
+ }
try {
+ let timeBlockEnd = null;
+ if (startTime) {
+ const [hh, mm] = startTime.split(':').map(Number);
+ const endMin = hh * 60 + mm + duration;
+ timeBlockEnd = `${String(Math.floor(endMin / 60)).padStart(2,'0')}:${String(endMin % 60).padStart(2,'0')}`;
+ }
+
const res = await fetch('/api/mail.php?action=plan_email', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
mail_id: mailId,
title: title,
+ sender: sender,
+ web_link: webLink,
priority: priority,
duration_minutes: duration,
time_block_start: startTime,
+ time_block_end: timeBlockEnd,
sync_outlook: syncOutlook,
date: planDate
})
@@ -4318,16 +5262,21 @@ async function submitPlanEmailDirect() {
const data = await res.json();
if (data.success) {
closePlanEmailDirectModal();
- switchPlannerDate(planDate, null, false);
+ showWidgetToast(`
E-mail succesvol ingepland als dagtaak`, 'success');
+ if (typeof switchPlannerDate === 'function') {
+ switchPlannerDate(planDate, null, false);
+ }
} else {
alert(data.error || 'Kon e-mail niet inplannen');
}
} catch(err) {
alert('Netwerkfout bij inplannen van e-mail');
} finally {
- btn.innerHTML = origHtml;
- btn.disabled = false;
- if (typeof lucide !== 'undefined') lucide.createIcons();
+ if (btn) {
+ btn.innerHTML = origHtml || '
Inplannen';
+ btn.disabled = false;
+ if (typeof lucide !== 'undefined') lucide.createIcons();
+ }
}
}
@@ -4959,6 +5908,8 @@ function openEditTaskModal(task) {
if (!task) return;
document.getElementById('editTaskId').value = task.id;
document.getElementById('editTaskTitle').value = task.title || '';
+ const descEl = document.getElementById('editTaskDescription');
+ if (descEl) descEl.value = task.description || '';
document.getElementById('editTaskPriority').value = task.priority || 'P2';
document.getElementById('editTaskStartTime').value = task.start || '';
document.getElementById('editTaskEndTime').value = task.end || '';
@@ -5217,12 +6168,14 @@ async function submitEditTask() {
}
try {
+ const description = document.getElementById('editTaskDescription')?.value.trim() || '';
const res = await fetch('/api/planner.php?action=update_task', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
task_id: taskId,
title: title,
+ description: description,
priority: priority,
time_block_start: startTime,
time_block_end: endTime,