@@ -2627,7 +2695,7 @@ if ($isAjaxRequest) {
@@ -2636,7 +2704,7 @@ if ($isAjaxRequest) {
@@ -3127,7 +3195,7 @@ function closeDayContextMenu() {
activeContextMenuDay = null;
}
-// --- Rechtermuisklik Context Menu voor Taken (Release v2.2) ---
+// --- Rechtermuisklik Context Menu voor Taken & Outlook Events (Release v2.2) ---
let activeContextMenuTask = null;
function handleTaskContextMenu(e, taskData) {
@@ -3141,52 +3209,86 @@ function handleTaskContextMenu(e, taskData) {
const titleEl = document.getElementById('taskContextMenuTitle');
const priorityBadge = document.getElementById('taskContextMenuPriorityBadge');
const metaEl = document.getElementById('taskContextMenuMeta');
+ const btnOpen = document.getElementById('taskCtxBtnOpen');
+ const btnEdit = document.getElementById('taskCtxBtnEdit');
+ const btnEditLabel = document.getElementById('taskCtxBtnEditLabel');
+ const btnToggle = document.getElementById('taskCtxBtnToggle');
const toggleLabel = document.getElementById('taskCtxBtnToggleLabel');
+ const btnSubtasks = document.getElementById('taskCtxBtnSubtasks');
const btnOutlook = document.getElementById('taskCtxBtnOutlook');
+ const btnDelete = document.getElementById('taskCtxBtnDelete');
+ const btnDeleteLabel = document.getElementById('taskCtxBtnDeleteLabel');
+ const divider = document.getElementById('taskCtxDivider');
if (!menu) return;
- if (titleEl) titleEl.textContent = taskData.title || 'Taak';
+ const isOutlookEvent = Boolean(taskData && (taskData.is_outlook_event || taskData.source === 'OUTLOOK_LIVE'));
+
+ if (titleEl) titleEl.textContent = (isOutlookEvent ? (taskData.subject || taskData.title) : taskData.title) || 'Item';
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';
+ if (isOutlookEvent) {
+ priorityBadge.textContent = 'AFSPRAAK';
+ priorityBadge.className = 'text-[9px] font-extrabold px-1.5 py-0.5 rounded-md bg-sky-100 text-sky-700 dark:bg-sky-950 dark:text-sky-400 border border-sky-300 dark:border-sky-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';
+ 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)`;
+ meta += `${taskData.start}${taskData.end ? ' - ' + taskData.end : ''}`;
+ if (taskData.duration) meta += ` (${taskData.duration}m)`;
} else if (taskData.duration) {
meta += `${taskData.duration}m`;
}
- if (taskData.source && taskData.source !== 'FOCIZ') {
+ if (isOutlookEvent) {
+ meta += (meta ? ' · ' : '') + 'Outlook';
+ if (taskData.location && taskData.location !== 'Geen locatie') {
+ meta += ` · ${taskData.location}`;
+ }
+ } else 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');
+ // Pas labels en zichtbaarheid aan op basis van type
+ if (isOutlookEvent) {
+ if (btnEditLabel) btnEditLabel.textContent = 'Afspraak bewerken';
+ if (btnDeleteLabel) btnDeleteLabel.textContent = 'Afspraak verwijderen';
+ if (btnToggle) btnToggle.classList.add('hidden');
+ if (btnSubtasks) btnSubtasks.classList.add('hidden');
+ if (btnOutlook) btnOutlook.classList.add('hidden');
+ } else {
+ if (btnEditLabel) btnEditLabel.textContent = 'Taak bewerken';
+ if (btnDeleteLabel) btnDeleteLabel.textContent = 'Taak verwijderen';
+ if (btnToggle) {
+ btnToggle.classList.remove('hidden');
+ if (toggleLabel) {
+ toggleLabel.textContent = taskData.is_completed ? 'Markeren als openstaand' : 'Markeren als voltooid';
+ }
+ }
+ if (btnSubtasks) btnSubtasks.classList.remove('hidden');
+ 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();
@@ -3194,7 +3296,6 @@ function handleTaskContextMenu(e, taskData) {
};
}
- const btnEdit = document.getElementById('taskCtxBtnEdit');
if (btnEdit) {
btnEdit.onclick = () => {
closeTaskContextMenu();
@@ -3202,7 +3303,6 @@ function handleTaskContextMenu(e, taskData) {
};
}
- const btnToggle = document.getElementById('taskCtxBtnToggle');
if (btnToggle) {
btnToggle.onclick = () => {
closeTaskContextMenu();
@@ -3210,7 +3310,6 @@ function handleTaskContextMenu(e, taskData) {
};
}
- const btnSubtasks = document.getElementById('taskCtxBtnSubtasks');
if (btnSubtasks) {
btnSubtasks.onclick = () => {
closeTaskContextMenu();
@@ -3231,11 +3330,14 @@ function handleTaskContextMenu(e, taskData) {
};
}
- const btnDelete = document.getElementById('taskCtxBtnDelete');
if (btnDelete) {
btnDelete.onclick = () => {
closeTaskContextMenu();
- deleteTask(taskData.id);
+ if (isOutlookEvent) {
+ deleteOutlookEvent(taskData.id, taskData.subject || taskData.title);
+ } else {
+ deleteTask(taskData.id);
+ }
};
}
@@ -3291,30 +3393,34 @@ async function openTaskDetailModal(taskDataOrId) {
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 || []);
+ const isOutlookEvent = Boolean(task && (task.is_outlook_event || task.source === 'OUTLOOK_LIVE'));
+
+ // Als het een Outlook event is, hoeven we geen Fociz task ID API aan te roepen
+ if (!isOutlookEvent && taskId) {
+ 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);
}
- } catch (err) {
- console.error('Fout bij ophalen taakdetails:', err);
}
if (window.lucide && lucide.createIcons) {
@@ -3332,56 +3438,85 @@ function populateTaskDetailModal(task, subtasks) {
const timeSlotEl = document.getElementById('taskDetailTimeSlot');
const durationEl = document.getElementById('taskDetailDuration');
const descEl = document.getElementById('taskDetailDescription');
+ const subtasksSection = document.getElementById('taskDetailSubtasksSection');
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 btnEditLabel = document.getElementById('taskDetailBtnEditLabel');
const btnDelete = document.getElementById('taskDetailBtnDelete');
+ const btnDeleteLabel = document.getElementById('taskDetailBtnDeleteLabel');
- if (titleEl) titleEl.textContent = task.title || 'Geen titel';
+ const isOutlookEvent = Boolean(task && (task.is_outlook_event || task.source === 'OUTLOOK_LIVE'));
- // 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';
+ if (titleEl) titleEl.textContent = (isOutlookEvent ? (task.subject || task.title) : task.title) || 'Geen titel';
+
+ // Prioriteit & icoon
+ if (isOutlookEvent) {
+ if (priorityBadge) {
+ priorityBadge.textContent = 'Outlook Afspraak';
+ priorityBadge.className = 'px-2 py-0.5 rounded-full text-[10px] font-extrabold uppercase tracking-wide bg-sky-50 dark:bg-sky-500/15 text-sky-700 dark:text-sky-400 border border-sky-200 dark:border-sky-500/30';
+ }
+ if (priorityIcon) {
+ priorityIcon.className = 'w-10 h-10 rounded-2xl bg-sky-50 dark:bg-sky-500/10 border border-sky-200 dark:border-sky-500/20 text-sky-600 dark:text-sky-400 flex items-center justify-center flex-shrink-0 mt-0.5';
+ priorityIcon.innerHTML = '
';
+ }
+ } else {
+ 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';
+ }
+ }
+ if (priorityIcon) {
+ priorityIcon.innerHTML = '
';
}
}
// 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') {
+ if (isOutlookEvent) {
+ sourceBadge.textContent = 'OUTLOOK 365';
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';
+ 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';
+ if (isOutlookEvent) {
+ statusBadge.textContent = 'Agenda Afspraak';
+ statusBadge.className = 'px-2 py-0.5 rounded-full text-[10px] 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 {
- 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';
+ 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';
+ }
}
}
@@ -3393,38 +3528,81 @@ function populateTaskDetailModal(task, subtasks) {
timeSlotEl.textContent = (task.start) ? `${task.start} - ${task.end || '?'}` : 'Geen tijdblok';
}
if (durationEl) {
- durationEl.textContent = `${task.duration || 30} minuten`;
+ let dur = task.duration;
+ if (!dur && task.start && task.end) {
+ const sm = parseTimeToMinutes(task.start);
+ const em = parseTimeToMinutes(task.end);
+ if (sm !== null && em !== null && em > sm) dur = em - sm;
+ }
+ durationEl.textContent = `${dur || 30} minuten`;
}
- // Beschrijving
+ // Beschrijving / Locatie
if (descEl) {
- if (task.description && task.description.trim()) {
- descEl.textContent = task.description;
- descEl.classList.remove('italic', 'text-slate-400');
+ if (isOutlookEvent) {
+ let descParts = [];
+ if (task.location && task.location !== 'Geen locatie') {
+ descParts.push(`Locatie: ${task.location}`);
+ }
+ if (task.description && task.description.trim()) {
+ descParts.push(task.description.trim());
+ }
+ if (descParts.length > 0) {
+ descEl.textContent = descParts.join('\n\n');
+ descEl.classList.remove('italic', 'text-slate-400');
+ } else {
+ descEl.textContent = 'Geen extra notities of locatie voor deze Outlook-afspraak.';
+ descEl.classList.add('italic', 'text-slate-400');
+ }
} else {
- descEl.textContent = 'Geen beschrijving of notities opgegeven voor deze taak.';
- descEl.classList.add('italic', 'text-slate-400');
+ 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 || []);
+ // Subtaken: verbergen voor Outlook afspraken
+ if (subtasksSection) {
+ if (isOutlookEvent) {
+ subtasksSection.classList.add('hidden');
+ } else {
+ subtasksSection.classList.remove('hidden');
+ if (subtasksList) {
+ renderSubtasksInDetailModal(task.id, subtasks || []);
+ }
+ }
}
// Externe link
if (extBlock && extLink && extLabel) {
- if (task.external_url) {
+ const extUrl = isOutlookEvent ? (task.meeting_url || task.web_link || task.external_url) : task.external_url;
+ if (extUrl) {
extBlock.classList.remove('hidden');
- extLink.href = task.external_url;
- if (task.source === 'JIRA') {
+ extLink.href = extUrl;
+ if (isOutlookEvent) {
+ if (task.meeting_url && task.meeting_url.includes('teams.microsoft.com')) {
+ extLabel.textContent = 'Deelnemen via Microsoft Teams';
+ extLink.querySelector('span') && (extLink.querySelector('span').textContent = 'Deelnemen');
+ } else {
+ extLabel.textContent = 'Gekoppeld in Microsoft Outlook Agenda';
+ extLink.querySelector('span') && (extLink.querySelector('span').textContent = 'In Outlook');
+ }
+ } else if (task.source === 'JIRA') {
extLabel.textContent = `Gekoppeld Jira Ticket: ${task.external_id || 'Jira'}`;
+ extLink.querySelector('span') && (extLink.querySelector('span').textContent = 'Openen');
} else if (task.source === 'OUTLOOK' || task.source === 'MICROSOFT') {
extLabel.textContent = 'Gekoppeld in Microsoft Outlook Agenda';
+ extLink.querySelector('span') && (extLink.querySelector('span').textContent = 'Openen');
} else if (task.source === 'OUTLOOK_MAIL') {
extLabel.textContent = 'Afkomstig uit Outlook E-mail';
+ extLink.querySelector('span') && (extLink.querySelector('span').textContent = 'Openen');
} else {
extLabel.textContent = 'Externe koppeling';
+ extLink.querySelector('span') && (extLink.querySelector('span').textContent = 'Openen');
}
} else {
extBlock.classList.add('hidden');
@@ -3432,6 +3610,13 @@ function populateTaskDetailModal(task, subtasks) {
}
// Buttons
+ if (btnEditLabel) {
+ btnEditLabel.textContent = isOutlookEvent ? 'Afspraak Bewerken' : 'Taak Bewerken';
+ }
+ if (btnDeleteLabel) {
+ btnDeleteLabel.textContent = isOutlookEvent ? 'Afspraak Verwijderen' : 'Verwijderen';
+ }
+
if (btnEdit) {
btnEdit.onclick = () => {
closeTaskDetailModal();
@@ -3441,7 +3626,11 @@ function populateTaskDetailModal(task, subtasks) {
if (btnDelete) {
btnDelete.onclick = () => {
closeTaskDetailModal();
- deleteTask(task.id);
+ if (isOutlookEvent) {
+ deleteOutlookEvent(task.id, task.subject || task.title);
+ } else {
+ deleteTask(task.id);
+ }
};
}
}
@@ -4002,19 +4191,19 @@ function toggleWidgetCollapse(wId) {
function toggleDaySelectionCollapse() {
const strip = document.getElementById('weekDaysStripContainer');
const icon = document.getElementById('iconToggleDaySelection');
- const label = document.getElementById('labelToggleDaySelection');
+ const btn = document.getElementById('btnToggleDaySelection');
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';
+ if (btn) btn.title = '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';
+ if (btn) btn.title = 'Dagen tonen';
try { localStorage.setItem('fociz_days_selection_collapsed', '1'); } catch(e) {}
}
if (typeof lucide !== 'undefined' && lucide.createIcons) {
@@ -4027,10 +4216,10 @@ function initPlannerDaySelectionCollapse() {
if (localStorage.getItem('fociz_days_selection_collapsed') === '1') {
const strip = document.getElementById('weekDaysStripContainer');
const icon = document.getElementById('iconToggleDaySelection');
- const label = document.getElementById('labelToggleDaySelection');
+ const btn = document.getElementById('btnToggleDaySelection');
if (strip) strip.classList.add('hidden');
if (icon) icon.classList.add('rotate-180');
- if (label) label.textContent = 'Dagen tonen';
+ if (btn) btn.title = 'Dagen tonen';
}
} catch(e) {}
}
@@ -4407,6 +4596,7 @@ plannerJiraAssigned.forEach(iss => {
if (iss && iss.key && !plannerTicketStore.has(iss.key)) plannerTicketStore.set(iss.key, iss);
});
+const plannedExternalIdsSet = new Set(= json_encode(array_keys($plannedExternalIds)) ?>);
let activePlannerJiraQueue = ''; // '': Alle, 'my': Mijn, 'DX': DX Queue, 'SD': SD Queue
function isIssueInQueue(iss, qCode) {
@@ -4508,6 +4698,27 @@ function filterPlannerJira() {
? '
DX'
: '
SD';
+ const isPlanned = plannedExternalIdsSet.has(issue.key);
+ const planBtn = isPlanned
+ ? `
`
+ : `
`;
+
return `
@@ -4525,14 +4736,7 @@ function filterPlannerJira() {
-
+ ${planBtn}
`;
}).join('');
@@ -4608,6 +4812,18 @@ function openJiraTicketDetail(issueKey) {
linkEl.classList.add('hidden');
}
+ // Knop inplannen / ingepland status
+ const detailPlanBtn = document.getElementById('btnJiraDetailPlan');
+ if (detailPlanBtn) {
+ if (plannedExternalIdsSet.has(issue.key)) {
+ detailPlanBtn.className = "flex items-center space-x-1.5 px-3 py-1.5 rounded-xl bg-emerald-600 hover:bg-emerald-500 text-white font-bold text-xs transition cursor-pointer shadow-xs";
+ detailPlanBtn.innerHTML = '
Ingepland';
+ } else {
+ detailPlanBtn.className = "flex items-center space-x-1.5 px-3 py-1.5 rounded-xl bg-indigo-600 hover:bg-indigo-500 text-white font-bold text-xs transition cursor-pointer";
+ detailPlanBtn.innerHTML = '
Inplannen';
+ }
+ }
+
const modal = document.getElementById('jiraTicketDetailModal');
modal.classList.remove('hidden');
modal.classList.add('flex');
@@ -4960,6 +5176,23 @@ async function submitJiraPlanModal() {
if (data.success) {
closeJiraPlanModal();
showWidgetToast(`
${escapeHtml(issue.key)} ingepland als dagtaak`, 'success');
+
+ // Live update: voeg toe aan plannedExternalIdsSet en transformeer knoppen naar groen 'Ingepland'
+ if (typeof plannedExternalIdsSet !== 'undefined') {
+ plannedExternalIdsSet.add(issue.key);
+ }
+ document.querySelectorAll(`.plan-btn-jira[data-jira-key="${issue.key}"]`).forEach(btn => {
+ btn.className = "plan-btn-jira flex items-center space-x-1 text-[11px] font-bold text-white bg-emerald-600 hover:bg-emerald-500 border border-emerald-600 shadow-xs px-2.5 py-1 rounded-lg transition flex-shrink-0 cursor-pointer";
+ btn.title = "Dit ticket is al ingepland als dagtaak (klik om opnieuw/anders in te plannen)";
+ btn.innerHTML = '
Ingepland';
+ });
+ const detailPlanBtn = document.getElementById('btnJiraDetailPlan');
+ if (detailPlanBtn && typeof _jiraPlanCurrentIssue !== 'undefined' && _jiraPlanCurrentIssue && _jiraPlanCurrentIssue.key === issue.key) {
+ detailPlanBtn.className = "flex items-center space-x-1.5 px-3 py-1.5 rounded-xl bg-emerald-600 hover:bg-emerald-500 text-white font-bold text-xs transition cursor-pointer shadow-xs";
+ detailPlanBtn.innerHTML = '
Ingepland';
+ }
+ if (typeof lucide !== 'undefined' && lucide.createIcons) lucide.createIcons();
+
if (typeof switchPlannerDate === 'function') {
switchPlannerDate(planDate, null, false);
}
@@ -5263,6 +5496,20 @@ async function submitPlanEmailDirect() {
if (data.success) {
closePlanEmailDirectModal();
showWidgetToast(`
E-mail succesvol ingepland als dagtaak`, 'success');
+
+ // Live update: voeg toe aan plannedExternalIdsSet en transformeer knoppen naar groen 'Ingepland'
+ if (mailId) {
+ if (typeof plannedExternalIdsSet !== 'undefined') {
+ plannedExternalIdsSet.add(mailId);
+ }
+ document.querySelectorAll(`.plan-btn-mail[data-mail-id="${mailId}"]`).forEach(btn => {
+ btn.className = "plan-btn-mail flex items-center space-x-1 text-[11px] font-bold text-white bg-emerald-600 hover:bg-emerald-500 border border-emerald-600 shadow-xs px-2.5 py-1 rounded-lg transition cursor-pointer";
+ btn.title = "Deze e-mail is al ingepland als dagtaak (klik om opnieuw/anders in te plannen)";
+ btn.innerHTML = '
Ingepland';
+ });
+ if (typeof lucide !== 'undefined' && lucide.createIcons) lucide.createIcons();
+ }
+
if (typeof switchPlannerDate === 'function') {
switchPlannerDate(planDate, null, false);
}
@@ -6122,8 +6369,94 @@ function setSelectedEditTaskDuration(mins) {
if (durationDisplay) durationDisplay.innerText = `${mins}m`;
}
+// --- Taak & Tijdblok Bewerken Modal Logica ---
+function openEditTaskModal(task) {
+ if (!task) return;
+ const isOutlook = Boolean(task.is_outlook_event || task.source === 'OUTLOOK_LIVE');
+
+ document.getElementById('editTaskId').value = task.id;
+ const isOutlookInput = document.getElementById('editTaskIsOutlook');
+ if (isOutlookInput) isOutlookInput.value = isOutlook ? '1' : '0';
+
+ const headerTitle = document.getElementById('editTaskModalHeaderTitle');
+ const headerSubtitle = document.getElementById('editTaskModalHeaderSubtitle');
+ const titleLabel = document.getElementById('editTaskTitleLabel');
+ const priorityContainer = document.getElementById('editTaskPriorityContainer');
+ const saveBtn = document.getElementById('btnSaveEditTask');
+
+ if (isOutlook) {
+ if (headerTitle) headerTitle.textContent = 'Outlook Afspraak Bewerken';
+ if (headerSubtitle) headerSubtitle.textContent = 'Pas onderwerp, tijdblok en datum van je agenda-afspraak aan';
+ if (titleLabel) titleLabel.textContent = 'Onderwerp';
+ if (priorityContainer) priorityContainer.classList.add('hidden');
+ if (saveBtn) saveBtn.textContent = 'Afspraak Opslaan';
+ } else {
+ if (headerTitle) headerTitle.textContent = 'Taak & Tijdblok Bewerken';
+ if (headerSubtitle) headerSubtitle.textContent = 'Pas titel, start-/eindtijd en tijdsduur vrij aan';
+ if (titleLabel) titleLabel.textContent = 'Taaktitel';
+ if (priorityContainer) priorityContainer.classList.remove('hidden');
+ if (saveBtn) saveBtn.textContent = 'Wijzigingen Opslaan';
+ }
+
+ document.getElementById('editTaskTitle').value = (isOutlook ? (task.subject || task.title) : 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 || '';
+
+ // Datum
+ const dateInput = document.getElementById('editTaskDate');
+ if (dateInput) {
+ dateInput.value = task.date || (typeof activePlannerDate !== 'undefined' ? activePlannerDate : new Date().toISOString().slice(0, 10));
+ }
+
+ // Reset statusbericht van open blok
+ const notice = document.getElementById('editTaskOpenBlockNotice');
+ if (notice) notice.classList.add('hidden');
+
+ let dur = task.duration;
+ if (!dur && task.start && task.end) {
+ const sm = parseTimeToMinutes(task.start);
+ const em = parseTimeToMinutes(task.end);
+ if (sm !== null && em !== null && em > sm) dur = em - sm;
+ }
+ dur = dur || 30;
+ setSelectedEditTaskDuration(dur);
+
+ const outlookNotice = document.getElementById('editTaskOutlookNotice');
+ if (outlookNotice) {
+ if (isOutlook) {
+ outlookNotice.classList.remove('hidden');
+ outlookNotice.innerHTML = '
Wijzigingen worden direct gesynchroniseerd naar Microsoft Outlook 365.';
+ } else if (task.source === 'OUTLOOK' || task.external_id) {
+ outlookNotice.classList.remove('hidden');
+ outlookNotice.innerHTML = '
Deze taak is gekoppeld aan Outlook 365. Wijzigingen worden automatisch in uw Outlook agenda gesynchroniseerd.';
+ } else {
+ outlookNotice.classList.add('hidden');
+ }
+ }
+
+ const modal = document.getElementById('editTaskModal');
+ if (modal) {
+ modal.classList.remove('hidden');
+ modal.classList.add('flex');
+ if (typeof lucide !== 'undefined') lucide.createIcons();
+ document.getElementById('editTaskTitle')?.focus();
+ }
+}
+
+function closeEditTaskModal() {
+ const modal = document.getElementById('editTaskModal');
+ if (modal) {
+ modal.classList.add('hidden');
+ modal.classList.remove('flex');
+ }
+}
+
async function submitEditTask() {
- const taskId = parseInt(document.getElementById('editTaskId').value, 10);
+ const isOutlook = document.getElementById('editTaskIsOutlook')?.value === '1';
+ const taskId = document.getElementById('editTaskId').value;
const title = document.getElementById('editTaskTitle').value.trim();
const priority = document.getElementById('editTaskPriority').value;
const dateInput = document.getElementById('editTaskDate');
@@ -6134,7 +6467,7 @@ async function submitEditTask() {
const saveBtn = document.getElementById('btnSaveEditTask');
if (!title) {
- alert('Vul een taaktitel in.');
+ alert(isOutlook ? 'Vul een onderwerp in voor de afspraak.' : 'Vul een taaktitel in.');
return;
}
@@ -6149,7 +6482,7 @@ async function submitEditTask() {
if (answer === 'NEE') {
// Automatisch naar volgende werkdag met eerder gezette voorwaarden
try {
- const nextSlot = await findSlotOnNextWorkday(duration, taskId);
+ const nextSlot = await findSlotOnNextWorkday(duration, isOutlook ? 0 : parseInt(taskId, 10));
taskDate = nextSlot.date;
startTime = nextSlot.start;
endTime = nextSlot.end;
@@ -6163,52 +6496,109 @@ async function submitEditTask() {
}
if (saveBtn) {
- saveBtn.innerText = 'Opslaan...';
+ saveBtn.innerText = isOutlook ? 'Afspraak Opslaan...' : 'Opslaan...';
saveBtn.disabled = true;
}
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,
- duration_minutes: duration,
- date: taskDate
- })
- });
- const data = await res.json();
- if (data.success) {
- closeEditTaskModal();
- const targetDate = data.date || taskDate;
- if (typeof switchPlannerDate === 'function') {
- switchPlannerDate(targetDate, null, false);
- }
- if (typeof showWidgetToast === 'function') {
- showWidgetToast(`Taak
${escapeHtml(title)} succesvol bijgewerkt`, 'success');
+
+ if (isOutlook) {
+ const res = await fetch('/api/planner.php?action=update_outlook_event', {
+ method: 'POST',
+ headers: {'Content-Type': 'application/json'},
+ body: JSON.stringify({
+ event_id: taskId,
+ title: title,
+ description: description,
+ time_block_start: startTime,
+ time_block_end: endTime,
+ date: taskDate
+ })
+ });
+ const data = await res.json();
+ if (data.success) {
+ closeEditTaskModal();
+ const targetDate = data.date || taskDate;
+ if (typeof switchPlannerDate === 'function') {
+ switchPlannerDate(targetDate, null, false);
+ }
+ if (typeof showWidgetToast === 'function') {
+ showWidgetToast(`Outlook afspraak
${escapeHtml(title)} succesvol bijgewerkt`, 'success');
+ }
+ } else {
+ alert(data.error || 'Kon afspraak niet bijwerken in Microsoft Outlook');
+ if (saveBtn) {
+ saveBtn.innerText = 'Afspraak Opslaan';
+ saveBtn.disabled = false;
+ }
}
} else {
- alert(data.error || 'Kon taak niet bijwerken');
- if (saveBtn) {
- saveBtn.innerText = 'Wijzigingen Opslaan';
- saveBtn.disabled = false;
+ const res = await fetch('/api/planner.php?action=update_task', {
+ method: 'POST',
+ headers: {'Content-Type': 'application/json'},
+ body: JSON.stringify({
+ task_id: parseInt(taskId, 10),
+ title: title,
+ description: description,
+ priority: priority,
+ time_block_start: startTime,
+ time_block_end: endTime,
+ duration_minutes: duration,
+ date: taskDate
+ })
+ });
+ const data = await res.json();
+ if (data.success) {
+ closeEditTaskModal();
+ const targetDate = data.date || taskDate;
+ if (typeof switchPlannerDate === 'function') {
+ switchPlannerDate(targetDate, null, false);
+ }
+ if (typeof showWidgetToast === 'function') {
+ showWidgetToast(`Taak
${escapeHtml(title)} succesvol bijgewerkt`, 'success');
+ }
+ } else {
+ alert(data.error || 'Kon taak niet bijwerken');
+ if (saveBtn) {
+ saveBtn.innerText = 'Wijzigingen Opslaan';
+ saveBtn.disabled = false;
+ }
}
}
} catch (e) {
- alert('Netwerkfout bij bijwerken van de taak');
+ alert('Netwerkfout bij bijwerken');
if (saveBtn) {
- saveBtn.innerText = 'Wijzigingen Opslaan';
+ saveBtn.innerText = isOutlook ? 'Afspraak Opslaan' : 'Wijzigingen Opslaan';
saveBtn.disabled = false;
}
}
}
+async function deleteOutlookEvent(eventId, eventSubject) {
+ const displayTitle = eventSubject ? ` "${eventSubject}"` : '';
+ if (!confirm(`Weet je zeker dat je de Outlook afspraak${displayTitle} wilt verwijderen uit je Microsoft Outlook agenda?`)) return;
+
+ try {
+ const res = await fetch('/api/planner.php?action=delete_outlook_event', {
+ method: 'POST',
+ headers: {'Content-Type': 'application/json'},
+ body: JSON.stringify({ event_id: eventId })
+ });
+ const data = await res.json();
+ if (data.success) {
+ if (typeof showWidgetToast === 'function') {
+ showWidgetToast(`Afspraak
${escapeHtml(eventSubject || 'Outlook')} verwijderd`, 'info');
+ }
+ switchPlannerDate(activePlannerDate, null, false);
+ } else {
+ alert(data.error || 'Kon afspraak niet verwijderen uit Outlook');
+ }
+ } catch (err) {
+ alert('Netwerkfout bij verwijderen van Outlook afspraak');
+ }
+}
+
async function syncTaskToOutlook(taskId, btn) {
if (!btn) return;
const originalHtml = btn.innerHTML;