Ai aanpassingen en slepen in planner blokken
This commit is contained in:
+172
-4
@@ -1240,8 +1240,12 @@ for ($h = $startHour; $h <= $endHour; $h++) {
|
||||
return !empty($t['time_block_start']) && (int)explode(':', $t['time_block_start'])[0] === $hourInt;
|
||||
});
|
||||
?>
|
||||
<div class="flex items-start group min-h-[48px] border-b border-slate-100 dark:border-slate-800/40 pb-2.5 last:border-0 min-w-0">
|
||||
<div class="w-12 sm:w-14 text-xs font-mono font-semibold text-slate-500 dark:text-slate-400 pt-1.5 flex-shrink-0">
|
||||
<div class="timeline-hour-slot flex items-start group min-h-[48px] border-b border-slate-100 dark:border-slate-800/40 pb-2.5 last:border-0 min-w-0 transition-all duration-150 rounded-xl p-1"
|
||||
data-hour="<?= $hour ?>"
|
||||
ondragover="handleTimelineSlotDragOver(event)"
|
||||
ondragleave="handleTimelineSlotDragLeave(event)"
|
||||
ondrop="handleTimelineSlotDrop(event, '<?= $hour ?>')">
|
||||
<div class="w-12 sm:w-14 text-xs font-mono font-semibold text-slate-500 dark:text-slate-400 pt-1.5 flex-shrink-0 select-none">
|
||||
<?= $hour ?>
|
||||
</div>
|
||||
|
||||
@@ -1278,7 +1282,13 @@ for ($h = $startHour; $h <= $endHour; $h++) {
|
||||
'is_outlook_event' => true,
|
||||
];
|
||||
?>
|
||||
<div oncontextmenu="handleTaskContextMenu(event, <?= htmlspecialchars(json_encode($evtJson), ENT_QUOTES, 'UTF-8') ?>)" title="Rechtermuisklik voor opties (openen, bewerken, verwijderen)" class="p-3 rounded-xl bg-sky-500/[0.07] dark:bg-sky-500/[0.12] border-l-4 border-l-sky-500 border border-sky-500/20 text-sky-950 dark:text-sky-100 text-xs shadow-2xs min-w-0 overflow-hidden transition-all hover:bg-sky-500/[0.10] dark:hover:bg-sky-500/[0.16] cursor-pointer group/evt">
|
||||
<div id="timeline-evt-<?= htmlspecialchars(preg_replace('/[^a-zA-Z0-9_-]/', '', $evt['id'] ?? '')) ?>"
|
||||
draggable="true"
|
||||
ondragstart="handleTimelineItemDragStart(event, 'outlook', <?= htmlspecialchars(json_encode($evtJson), ENT_QUOTES, 'UTF-8') ?>)"
|
||||
ondragend="handleTimelineItemDragEnd(event)"
|
||||
oncontextmenu="handleTaskContextMenu(event, <?= htmlspecialchars(json_encode($evtJson), ENT_QUOTES, 'UTF-8') ?>)"
|
||||
title="Sleep naar ander tijdblok, of rechtermuisklik voor opties"
|
||||
class="p-3 rounded-xl bg-sky-500/[0.07] dark:bg-sky-500/[0.12] border-l-4 border-l-sky-500 border border-sky-500/20 text-sky-950 dark:text-sky-100 text-xs shadow-2xs min-w-0 overflow-hidden transition-all hover:bg-sky-500/[0.10] dark:hover:bg-sky-500/[0.16] cursor-grab active:cursor-grabbing group/evt">
|
||||
<div class="flex items-start justify-between gap-2.5 min-w-0">
|
||||
<div class="flex items-start space-x-2 min-w-0 flex-1" onclick="openTaskDetailModal(<?= htmlspecialchars(json_encode($evtJson), ENT_QUOTES, 'UTF-8') ?>)">
|
||||
<span class="px-2 py-0.5 rounded-md bg-sky-500/15 text-sky-800 dark:text-sky-300 font-mono text-[10px] font-bold flex-shrink-0 whitespace-nowrap mt-0.5">
|
||||
@@ -1322,7 +1332,13 @@ for ($h = $startHour; $h <= $endHour; $h++) {
|
||||
'date' => $selectedDate
|
||||
];
|
||||
?>
|
||||
<div id="timeline-task-<?= $t['id'] ?>" oncontextmenu="handleTaskContextMenu(event, <?= htmlspecialchars(json_encode($slotTaskJson), ENT_QUOTES, 'UTF-8') ?>)" title="Rechtermuisklik voor opties (openen, bewerken, verwijderen)" class="p-3 rounded-xl border-l-4 <?= $t['priority'] === 'P1' ? 'border-l-rose-500' : ($t['priority'] === 'P2' ? 'border-l-amber-500' : 'border-l-indigo-400') ?> <?= $t['is_completed'] ? 'bg-slate-100/60 dark:bg-slate-900/40 border border-slate-200 dark:border-slate-800 text-slate-400 dark:text-slate-500 line-through' : 'bg-white/80 dark:bg-[#0c0f17]/70 border border-slate-200/90 dark:border-slate-800 text-slate-900 dark:text-slate-100 shadow-2xs hover:shadow-xs' ?> text-xs min-w-0 overflow-hidden transition-all group/task">
|
||||
<div id="timeline-task-<?= $t['id'] ?>"
|
||||
draggable="true"
|
||||
ondragstart="handleTimelineItemDragStart(event, 'task', <?= htmlspecialchars(json_encode($slotTaskJson), ENT_QUOTES, 'UTF-8') ?>)"
|
||||
ondragend="handleTimelineItemDragEnd(event)"
|
||||
oncontextmenu="handleTaskContextMenu(event, <?= htmlspecialchars(json_encode($slotTaskJson), ENT_QUOTES, 'UTF-8') ?>)"
|
||||
title="Sleep naar ander tijdblok, of rechtermuisklik voor opties"
|
||||
class="p-3 rounded-xl border-l-4 <?= $t['priority'] === 'P1' ? 'border-l-rose-500' : ($t['priority'] === 'P2' ? 'border-l-amber-500' : 'border-l-indigo-400') ?> <?= $t['is_completed'] ? 'bg-slate-100/60 dark:bg-slate-900/40 border border-slate-200 dark:border-slate-800 text-slate-400 dark:text-slate-500 line-through' : 'bg-white/80 dark:bg-[#0c0f17]/70 border border-slate-200/90 dark:border-slate-800 text-slate-900 dark:text-slate-100 shadow-2xs hover:shadow-xs' ?> text-xs min-w-0 overflow-hidden transition-all cursor-grab active:cursor-grabbing group/task">
|
||||
<div class="flex items-start justify-between gap-2.5 min-w-0">
|
||||
<div class="flex items-start space-x-2 min-w-0 flex-1 cursor-pointer" onclick="openTaskDetailModal(<?= htmlspecialchars(json_encode($slotTaskJson), ENT_QUOTES, 'UTF-8') ?>)">
|
||||
<span class="px-2 py-0.5 rounded-md bg-indigo-50 dark:bg-indigo-500/10 text-indigo-700 dark:text-indigo-300 border border-indigo-200 dark:border-indigo-500/20 font-mono text-[10px] font-bold flex-shrink-0 whitespace-nowrap mt-0.5">
|
||||
@@ -6873,6 +6889,158 @@ async function aiSmartSchedule(btn) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- Drag & Drop van Taken & Afspraken op Tijdlijn ---
|
||||
let draggedTimelineItem = null;
|
||||
|
||||
function handleTimelineItemDragStart(e, type, item) {
|
||||
draggedTimelineItem = { type, item };
|
||||
try {
|
||||
e.dataTransfer.effectAllowed = 'move';
|
||||
e.dataTransfer.setData('text/plain', JSON.stringify({ type, id: item.id }));
|
||||
} catch (err) {}
|
||||
|
||||
// Voeg visuele feedback toe aan het versleepte element
|
||||
setTimeout(() => {
|
||||
if (e.target) {
|
||||
e.target.classList.add('opacity-40', 'scale-[0.98]');
|
||||
}
|
||||
}, 0);
|
||||
}
|
||||
|
||||
function handleTimelineItemDragEnd(e) {
|
||||
if (e.target) {
|
||||
e.target.classList.remove('opacity-40', 'scale-[0.98]');
|
||||
}
|
||||
// Verwijder eventuele achtergebleven drag-over styles van slots
|
||||
document.querySelectorAll('.timeline-hour-slot').forEach(slot => {
|
||||
slot.classList.remove('bg-indigo-50/80', 'dark:bg-indigo-950/40', 'ring-2', 'ring-indigo-400', 'dark:ring-indigo-500', 'ring-inset');
|
||||
});
|
||||
}
|
||||
|
||||
function handleTimelineSlotDragOver(e) {
|
||||
if (!draggedTimelineItem) return;
|
||||
e.preventDefault();
|
||||
e.dataTransfer.dropEffect = 'move';
|
||||
const slot = e.currentTarget;
|
||||
if (slot && !slot.classList.contains('ring-2')) {
|
||||
slot.classList.add('bg-indigo-50/80', 'dark:bg-indigo-950/40', 'ring-2', 'ring-indigo-400', 'dark:ring-indigo-500', 'ring-inset');
|
||||
}
|
||||
}
|
||||
|
||||
function handleTimelineSlotDragLeave(e) {
|
||||
const slot = e.currentTarget;
|
||||
if (slot && !slot.contains(e.relatedTarget)) {
|
||||
slot.classList.remove('bg-indigo-50/80', 'dark:bg-indigo-950/40', 'ring-2', 'ring-indigo-400', 'dark:ring-indigo-500', 'ring-inset');
|
||||
}
|
||||
}
|
||||
|
||||
async function handleTimelineSlotDrop(e, targetHour) {
|
||||
e.preventDefault();
|
||||
const slot = e.currentTarget;
|
||||
if (slot) {
|
||||
slot.classList.remove('bg-indigo-50/80', 'dark:bg-indigo-950/40', 'ring-2', 'ring-indigo-400', 'dark:ring-indigo-500', 'ring-inset');
|
||||
}
|
||||
|
||||
if (!draggedTimelineItem) return;
|
||||
const { type, item } = draggedTimelineItem;
|
||||
draggedTimelineItem = null;
|
||||
|
||||
// Bepaal de oorspronkelijke tijdsduur van het item in minuten
|
||||
let durationMinutes = 30;
|
||||
if (item.duration && parseInt(item.duration, 10) > 0) {
|
||||
durationMinutes = parseInt(item.duration, 10);
|
||||
} else if (item.start && item.end) {
|
||||
const sMins = parseTimeToMinutes(item.start);
|
||||
const eMins = parseTimeToMinutes(item.end);
|
||||
if (sMins !== null && eMins !== null && eMins > sMins) {
|
||||
durationMinutes = eMins - sMins;
|
||||
}
|
||||
}
|
||||
|
||||
// Nieuwe starttijd is het drop-uurslot (bijv. "14:00")
|
||||
const newStart = targetHour;
|
||||
const startMins = parseTimeToMinutes(newStart);
|
||||
let newEnd = '';
|
||||
if (startMins !== null) {
|
||||
newEnd = formatMinutesToTime(startMins + durationMinutes);
|
||||
}
|
||||
|
||||
const itemTitle = (type === 'outlook' ? (item.subject || item.title) : item.title) || 'Item';
|
||||
const planDate = item.date || (typeof activePlannerDate !== 'undefined' ? activePlannerDate : new Date().toISOString().slice(0, 10));
|
||||
|
||||
if (typeof showWidgetToast === 'function') {
|
||||
showWidgetToast(`Verplaatsen naar <strong>${newStart} - ${newEnd}</strong>...`, 'info');
|
||||
}
|
||||
|
||||
try {
|
||||
if (type === 'outlook') {
|
||||
const res = await fetch('/api/planner.php?action=update_outlook_event', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
event_id: item.id,
|
||||
title: itemTitle,
|
||||
description: item.description || '',
|
||||
time_block_start: newStart,
|
||||
time_block_end: newEnd,
|
||||
date: planDate
|
||||
})
|
||||
});
|
||||
const data = await res.json();
|
||||
if (data.success) {
|
||||
if (typeof showWidgetToast === 'function') {
|
||||
showWidgetToast(`Outlook afspraak <strong>${escapeHtml(itemTitle)}</strong> verplaatst naar ${newStart} - ${newEnd}`, 'success');
|
||||
}
|
||||
if (typeof switchPlannerDate === 'function') {
|
||||
switchPlannerDate(planDate, null, false);
|
||||
}
|
||||
} else {
|
||||
alert(data.error || 'Kon afspraak niet verplaatsen in Outlook');
|
||||
if (typeof switchPlannerDate === 'function') {
|
||||
switchPlannerDate(planDate, null, false);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Reguliere Fociz taak
|
||||
const res = await fetch('/api/planner.php?action=update_task', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
task_id: parseInt(item.id, 10),
|
||||
title: itemTitle,
|
||||
description: item.description || '',
|
||||
priority: item.priority || 'P2',
|
||||
time_block_start: newStart,
|
||||
time_block_end: newEnd,
|
||||
duration_minutes: durationMinutes,
|
||||
date: planDate
|
||||
})
|
||||
});
|
||||
const data = await res.json();
|
||||
if (data.success) {
|
||||
if (typeof showWidgetToast === 'function') {
|
||||
showWidgetToast(`Taak <strong>${escapeHtml(itemTitle)}</strong> verplaatst naar ${newStart} - ${newEnd}`, 'success');
|
||||
}
|
||||
if (typeof switchPlannerDate === 'function') {
|
||||
switchPlannerDate(planDate, null, false);
|
||||
}
|
||||
} else {
|
||||
alert(data.error || 'Kon taak niet verplaatsen');
|
||||
if (typeof switchPlannerDate === 'function') {
|
||||
switchPlannerDate(planDate, null, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Drag and drop fout:', err);
|
||||
alert('Netwerkfout bij verplaatsen van item');
|
||||
if (typeof switchPlannerDate === 'function') {
|
||||
switchPlannerDate(planDate, null, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- Outlook 365 Auto-Sync & Achtergrond Controle ---
|
||||
const isOutlookConnected = <?= $isOutlookConnected ? 'true' : 'false' ?>;
|
||||
const syncIntervalSeconds = <?= (int)($syncIntervalSeconds ?? 30) ?>;
|
||||
|
||||
Reference in New Issue
Block a user