Simple todo

This commit is contained in:
2026-10-05 10:32:30 +02:00
parent d4e4b69e02
commit ef116be9b8
3 changed files with 1334 additions and 16 deletions
+413
View File
@@ -87,6 +87,419 @@ try {
exit;
}
// 1d. Snelle Bullet Notities ophalen
if ($action === 'get_bullets') {
$date = $_GET['date'] ?? $today;
$stmt = $db->prepare("
SELECT b.*, t.time_block_start as live_start, t.time_block_end as live_end, t.status as task_status
FROM dashboard_bullets b
LEFT JOIN tasks t ON b.scheduled_task_id = t.id
WHERE b.user_id = ? AND b.plan_date = ?
ORDER BY b.position ASC, b.id ASC
");
$stmt->execute([$user['id'], $date]);
$bullets = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Tel openstaande items van eerdere dagen
$stmtPast = $db->prepare("
SELECT COUNT(*) FROM dashboard_bullets
WHERE user_id = ? AND plan_date < ? AND is_completed = 0
");
$stmtPast->execute([$user['id'], $date]);
$pastOpenCount = (int)$stmtPast->fetchColumn();
echo json_encode([
'success' => true,
'bullets' => $bullets,
'past_open_count' => $pastOpenCount
]);
exit;
}
// 1e. Bullet notitie toevoegen
if ($action === 'add_bullet') {
$data = json_decode(file_get_contents('php://input'), true);
$date = !empty($data['date']) ? trim($data['date']) : $today;
$content = trim($data['content'] ?? '');
if ($content === '') {
echo json_encode(['success' => false, 'error' => 'Notitie mag niet leeg zijn']);
exit;
}
// Bepaal positie
$stmtPos = $db->prepare("SELECT COALESCE(MAX(position), 0) + 1 FROM dashboard_bullets WHERE user_id = ? AND plan_date = ?");
$stmtPos->execute([$user['id'], $date]);
$pos = (int)$stmtPos->fetchColumn();
$stmt = $db->prepare("
INSERT INTO dashboard_bullets (user_id, plan_date, content, position)
VALUES (?, ?, ?, ?)
");
$stmt->execute([$user['id'], $date, $content, $pos]);
$bulletId = (int)$db->lastInsertId();
$stmtFetch = $db->prepare("SELECT * FROM dashboard_bullets WHERE id = ?");
$stmtFetch->execute([$bulletId]);
$newBullet = $stmtFetch->fetch(PDO::FETCH_ASSOC);
echo json_encode([
'success' => true,
'bullet' => $newBullet
]);
exit;
}
// 1f. Bullet notitie status wisselen (afvinken / openen)
if ($action === 'toggle_bullet') {
$data = json_decode(file_get_contents('php://input'), true);
$bulletId = (int)($data['bullet_id'] ?? 0);
$isCompleted = !empty($data['is_completed']) ? 1 : 0;
$stmt = $db->prepare("UPDATE dashboard_bullets SET is_completed = ? WHERE id = ? AND user_id = ?");
$stmt->execute([$isCompleted, $bulletId, $user['id']]);
// Als er een gekoppelde taak is, werk de status daarvan ook bij
$stmtCheck = $db->prepare("SELECT scheduled_task_id FROM dashboard_bullets WHERE id = ? AND user_id = ?");
$stmtCheck->execute([$bulletId, $user['id']]);
$taskId = (int)$stmtCheck->fetchColumn();
if ($taskId > 0) {
$taskStatus = $isCompleted ? 'DONE' : 'TODO';
$stmtTask = $db->prepare("UPDATE tasks SET is_completed = ?, status = ? WHERE id = ? AND user_id = ?");
$stmtTask->execute([$isCompleted, $taskStatus, $taskId, $user['id']]);
}
echo json_encode(['success' => true, 'is_completed' => $isCompleted]);
exit;
}
// 1g. Bullet notitie bewerken
if ($action === 'update_bullet') {
$data = json_decode(file_get_contents('php://input'), true);
$bulletId = (int)($data['bullet_id'] ?? 0);
$content = trim($data['content'] ?? '');
if ($content === '') {
echo json_encode(['success' => false, 'error' => 'Inhoud mag niet leeg zijn']);
exit;
}
$stmt = $db->prepare("UPDATE dashboard_bullets SET content = ? WHERE id = ? AND user_id = ?");
$stmt->execute([$content, $bulletId, $user['id']]);
// Indien gekoppeld aan taak, werk ook taaktitel bij
$stmtCheck = $db->prepare("SELECT scheduled_task_id FROM dashboard_bullets WHERE id = ? AND user_id = ?");
$stmtCheck->execute([$bulletId, $user['id']]);
$taskId = (int)$stmtCheck->fetchColumn();
if ($taskId > 0) {
$stmtTask = $db->prepare("UPDATE tasks SET title = ? WHERE id = ? AND user_id = ?");
$stmtTask->execute([$content, $taskId, $user['id']]);
}
echo json_encode(['success' => true]);
exit;
}
// 1h. Bullet notitie verwijderen
if ($action === 'delete_bullet') {
$data = json_decode(file_get_contents('php://input'), true);
$bulletId = (int)($data['bullet_id'] ?? 0);
$deleteTask = !empty($data['delete_task']);
// Check voor gekoppelde taak
$stmtCheck = $db->prepare("SELECT scheduled_task_id FROM dashboard_bullets WHERE id = ? AND user_id = ?");
$stmtCheck->execute([$bulletId, $user['id']]);
$taskId = (int)$stmtCheck->fetchColumn();
if ($taskId > 0 && $deleteTask) {
// Verwijder eventuele Outlook afspraak
$stmtTask = $db->prepare("SELECT external_id FROM tasks WHERE id = ? AND user_id = ?");
$stmtTask->execute([$taskId, $user['id']]);
$externalId = $stmtTask->fetchColumn();
if (!empty($externalId) && function_exists('microsoft_delete_event')) {
try {
microsoft_delete_event($user['id'], $externalId);
} catch (Throwable $e) {}
}
$stmtDelTask = $db->prepare("DELETE FROM tasks WHERE id = ? AND user_id = ?");
$stmtDelTask->execute([$taskId, $user['id']]);
}
$stmt = $db->prepare("DELETE FROM dashboard_bullets WHERE id = ? AND user_id = ?");
$stmt->execute([$bulletId, $user['id']]);
echo json_encode(['success' => true]);
exit;
}
// 1i. Bullet inplannen in agenda / vrij blok
if ($action === 'schedule_bullet') {
$data = json_decode(file_get_contents('php://input'), true);
$bulletId = (int)($data['bullet_id'] ?? 0);
$date = !empty($data['date']) ? trim($data['date']) : $today;
$title = !empty($data['title']) ? trim($data['title']) : '';
$startTime = !empty($data['start_time']) ? trim($data['start_time']) : '';
$endTime = !empty($data['end_time']) ? trim($data['end_time']) : '';
$duration = max(5, (int)($data['duration_minutes'] ?? 30));
$priority = in_array($data['priority'] ?? '', ['P1', 'P2', 'P3']) ? $data['priority'] : 'P2';
$syncOutlook = !empty($data['sync_to_outlook']);
// Haal bullet op
$stmtB = $db->prepare("SELECT * FROM dashboard_bullets WHERE id = ? AND user_id = ?");
$stmtB->execute([$bulletId, $user['id']]);
$bullet = $stmtB->fetch(PDO::FETCH_ASSOC);
if (!$bullet) {
echo json_encode(['success' => false, 'error' => 'Notitie niet gevonden']);
exit;
}
if (empty($title)) {
$title = $bullet['content'];
}
if (empty($startTime)) {
echo json_encode(['success' => false, 'error' => 'Kies een starttijd voor het agendablok']);
exit;
}
if (empty($endTime)) {
$dtEnd = new DateTime("{$date} {$startTime}:00", new DateTimeZone('Europe/Amsterdam'));
$dtEnd->modify("+{$duration} minutes");
$endTime = $dtEnd->format('H:i');
}
// Zorg dat daily_plan bestaat
$stmtPlan = $db->prepare("
INSERT INTO daily_plans (user_id, plan_date)
VALUES (?, ?)
ON DUPLICATE KEY UPDATE id=LAST_INSERT_ID(id)
");
$stmtPlan->execute([$user['id'], $date]);
$planId = (int)$db->lastInsertId();
$source = 'FOCIZ';
$externalId = null;
$externalUrl = null;
$outlookSyncMsg = null;
// Microsoft Outlook synchronisatie
if ($syncOutlook) {
require_once __DIR__ . '/../includes/integrations/microsoft.php';
try {
$msResult = microsoft_create_event($user['id'], [
'subject' => $title,
'description' => "Ingepland vanuit Snelle Notities / Bullet Todo's",
'date' => $date,
'start_time' => $startTime,
'end_time' => $endTime,
'duration_minutes' => $duration,
]);
if (!empty($msResult['success']) && !empty($msResult['event_id'])) {
$source = 'OUTLOOK';
$externalId = $msResult['event_id'];
$externalUrl = $msResult['web_link'] ?? null;
$outlookSyncMsg = 'Succesvol gesynchroniseerd met Microsoft Outlook agenda.';
}
} catch (Throwable $e) {}
}
$taskId = (int)($bullet['scheduled_task_id'] ?? 0);
if ($taskId > 0) {
// Update bestaande taak
$stmtUpdateTask = $db->prepare("
UPDATE tasks
SET daily_plan_id = ?, title = ?, priority = ?, time_block_start = ?, time_block_end = ?, duration_minutes = ?, source = ?, external_id = COALESCE(?, external_id), external_url = COALESCE(?, external_url)
WHERE id = ? AND user_id = ?
");
$stmtUpdateTask->execute([$planId, $title, $priority, $startTime, $endTime, $duration, $source, $externalId, $externalUrl, $taskId, $user['id']]);
} else {
// Nieuwe taak aanmaken
$stmtInsertTask = $db->prepare("
INSERT INTO tasks (user_id, daily_plan_id, title, description, priority, time_block_start, time_block_end, duration_minutes, status, source, external_id, external_url)
VALUES (?, ?, ?, 'Ingepland vanuit Snelle Notities', ?, ?, ?, ?, 'TODO', ?, ?, ?)
");
$stmtInsertTask->execute([$user['id'], $planId, $title, $priority, $startTime, $endTime, $duration, $source, $externalId, $externalUrl]);
$taskId = (int)$db->lastInsertId();
}
// Update de bullet record
$stmtUpdateB = $db->prepare("
UPDATE dashboard_bullets
SET scheduled_task_id = ?, scheduled_date = ?, scheduled_start_time = ?, scheduled_end_time = ?
WHERE id = ? AND user_id = ?
");
$stmtUpdateB->execute([$taskId, $date, $startTime, $endTime, $bulletId, $user['id']]);
echo json_encode([
'success' => true,
'task_id' => $taskId,
'scheduled_date' => $date,
'scheduled_start_time' => $startTime,
'scheduled_end_time' => $endTime,
'outlook_message' => $outlookSyncMsg
]);
exit;
}
// 1j. Bullet uit agenda halen
if ($action === 'unschedule_bullet') {
$data = json_decode(file_get_contents('php://input'), true);
$bulletId = (int)($data['bullet_id'] ?? 0);
$stmtB = $db->prepare("SELECT scheduled_task_id FROM dashboard_bullets WHERE id = ? AND user_id = ?");
$stmtB->execute([$bulletId, $user['id']]);
$taskId = (int)$stmtB->fetchColumn();
if ($taskId > 0) {
$stmtTask = $db->prepare("SELECT external_id FROM tasks WHERE id = ? AND user_id = ?");
$stmtTask->execute([$taskId, $user['id']]);
$externalId = $stmtTask->fetchColumn();
if (!empty($externalId) && function_exists('microsoft_delete_event')) {
try {
microsoft_delete_event($user['id'], $externalId);
} catch (Throwable $e) {}
}
$stmtDel = $db->prepare("DELETE FROM tasks WHERE id = ? AND user_id = ?");
$stmtDel->execute([$taskId, $user['id']]);
}
$stmtUpdate = $db->prepare("
UPDATE dashboard_bullets
SET scheduled_task_id = NULL, scheduled_date = NULL, scheduled_start_time = NULL, scheduled_end_time = NULL
WHERE id = ? AND user_id = ?
");
$stmtUpdate->execute([$bulletId, $user['id']]);
echo json_encode(['success' => true]);
exit;
}
// 1k. Openstaande bullets van eerdere dagen overnemen naar geselecteerde dag
if ($action === 'carryover_bullets') {
$data = json_decode(file_get_contents('php://input'), true);
$targetDate = !empty($data['target_date']) ? trim($data['target_date']) : $today;
// Verplaats alle openstaande notities van eerdere dagen naar targetDate
$stmt = $db->prepare("
UPDATE dashboard_bullets
SET plan_date = ?
WHERE user_id = ? AND plan_date < ? AND is_completed = 0
");
$stmt->execute([$targetDate, $user['id'], $targetDate]);
$movedCount = $stmt->rowCount();
echo json_encode(['success' => true, 'moved_count' => $movedCount]);
exit;
}
// 1l. Haal alle beschikbare vrije agendablokken op voor een specifieke dag
if ($action === 'get_day_free_slots') {
$targetDate = $_GET['date'] ?? $today;
$duration = max(5, (int)($_GET['duration_minutes'] ?? 30));
$workdaySettings = get_workday_settings();
$workdayStartStr = $workdaySettings['workday_start'] ?? '08:30';
$workdayEndStr = $workdaySettings['workday_end'] ?? '17:00';
$lunchStartStr = $workdaySettings['lunch_start'] ?? '12:30';
$lunchEndStr = $workdaySettings['lunch_end'] ?? '13:00';
$toMins = function(string $timeStr): int {
$parts = explode(':', trim($timeStr));
return ((int)($parts[0] ?? 0) * 60) + (int)($parts[1] ?? 0);
};
$toTimeStr = function(int $mins): string {
$h = (int)floor($mins / 60);
$m = $mins % 60;
return sprintf('%02d:%02d', $h, $m);
};
$workdayStartMin = $toMins($workdayStartStr);
$workdayEndMin = $toMins($workdayEndStr);
$lunchStartMin = $toMins($lunchStartStr);
$lunchEndMin = $toMins($lunchEndStr);
$tz = new DateTimeZone('Europe/Amsterdam');
$now = new DateTime('now', $tz);
$isToday = ($targetDate === $now->format('Y-m-d'));
// Starttijd afronden naar kwartier
$earliestMin = $workdayStartMin;
if ($isToday) {
$nowMin = ((int)$now->format('G') * 60) + (int)$now->format('i');
if ($nowMin % 15 !== 0) {
$nowMin = (int)(ceil($nowMin / 15) * 15);
}
$earliestMin = max($workdayStartMin, $nowMin);
}
$busyIntervals = [];
if ($lunchEndMin > $lunchStartMin) {
$busyIntervals[] = [$lunchStartMin, $lunchEndMin];
}
// Taken op deze dag
$stmtTasks = $db->prepare("
SELECT time_block_start, time_block_end, duration_minutes
FROM tasks t
INNER JOIN daily_plans dp ON t.daily_plan_id = dp.id
WHERE t.user_id = ? AND dp.plan_date = ? AND t.time_block_start IS NOT NULL AND t.time_block_start != '' AND t.status != 'CANCELLED'
");
$stmtTasks->execute([$user['id'], $targetDate]);
while ($t = $stmtTasks->fetch()) {
$tStart = $toMins($t['time_block_start']);
$tDur = (int)($t['duration_minutes'] ?: 30);
$tEnd = !empty($t['time_block_end']) ? $toMins($t['time_block_end']) : ($tStart + $tDur);
if ($tEnd > $tStart) {
$busyIntervals[] = [$tStart, $tEnd];
}
}
// Outlook afspraken
if (function_exists('microsoft_fetch_events')) {
try {
$outlookEvents = microsoft_fetch_events($user['id'], $targetDate, false);
foreach ($outlookEvents as $oe) {
if (!empty($oe['is_all_day'])) continue;
if (empty($oe['start']) || empty($oe['end'])) continue;
$oStart = $toMins($oe['start']);
$oEnd = $toMins($oe['end']);
if ($oEnd > $oStart) {
$busyIntervals[] = [$oStart, $oEnd];
}
}
} catch (Throwable $e) {}
}
$freeSlots = [];
for ($slotStart = $earliestMin; ($slotStart + $duration) <= $workdayEndMin; $slotStart += 15) {
$slotEnd = $slotStart + $duration;
$collision = false;
foreach ($busyIntervals as [$bStart, $bEnd]) {
if ($slotStart < $bEnd && $slotEnd > $bStart) {
$collision = true;
break;
}
}
if (!$collision) {
$freeSlots[] = [
'start' => $toTimeStr($slotStart),
'end' => $toTimeStr($slotEnd),
'label' => $toTimeStr($slotStart) . ' - ' . $toTimeStr($slotEnd)
];
}
}
echo json_encode([
'success' => true,
'date' => $targetDate,
'duration_minutes' => $duration,
'free_slots' => $freeSlots
]);
exit;
}
// 2. Taak toevoegen
if ($action === 'add_task') {
$data = json_decode(file_get_contents('php://input'), true);