1170 lines
48 KiB
PHP
1170 lines
48 KiB
PHP
<?php
|
|
/**
|
|
* AJAX API voor Dagplanning & Taken
|
|
*/
|
|
|
|
require_once __DIR__ . '/../includes/auth.php';
|
|
require_once __DIR__ . '/../includes/settings.php';
|
|
require_once __DIR__ . '/../includes/integrations/microsoft.php';
|
|
$user = require_auth(true);
|
|
$db = get_db_connection();
|
|
|
|
header('Content-Type: application/json');
|
|
|
|
$action = $_GET['action'] ?? ($_POST['action'] ?? '');
|
|
$today = date('Y-m-d');
|
|
|
|
try {
|
|
// 1. Dagdoel of notities opslaan
|
|
if ($action === 'save_goal') {
|
|
$data = json_decode(file_get_contents('php://input'), true);
|
|
$date = $data['date'] ?? $today;
|
|
$focusGoal = $data['focus_goal'] ?? '';
|
|
$notes = $data['notes'] ?? '';
|
|
|
|
$stmt = $db->prepare("
|
|
INSERT INTO daily_plans (user_id, plan_date, focus_goal, notes)
|
|
VALUES (?, ?, ?, ?)
|
|
ON DUPLICATE KEY UPDATE focus_goal = VALUES(focus_goal), notes = VALUES(notes)
|
|
");
|
|
$stmt->execute([$user['id'], $date, $focusGoal, $notes]);
|
|
|
|
echo json_encode(['success' => true]);
|
|
exit;
|
|
}
|
|
|
|
// 1b. Widget layout opslaan
|
|
if ($action === 'save_widget_layout') {
|
|
$data = json_decode(file_get_contents('php://input'), true);
|
|
$layout = $data['layout'] ?? null;
|
|
|
|
if (!is_array($layout)) {
|
|
echo json_encode(['success' => false, 'error' => 'Ongeldig layout formaat']);
|
|
exit;
|
|
}
|
|
|
|
// Valideer en sanitize layout items inclusief optionele custom widget titel
|
|
$cleanedLayout = [];
|
|
foreach ($layout as $item) {
|
|
if (!is_array($item) || empty($item['id'])) continue;
|
|
$cleaned = [
|
|
'id' => substr(trim((string)$item['id']), 0, 64),
|
|
'enabled' => !empty($item['enabled']),
|
|
'col_span' => in_array((int)($item['col_span'] ?? 6), [4, 5, 6, 7, 8, 12]) ? (int)$item['col_span'] : 6,
|
|
'order' => (int)($item['order'] ?? 1),
|
|
];
|
|
if (!empty($item['custom_title'])) {
|
|
$customTitle = trim(strip_tags((string)$item['custom_title']));
|
|
if ($customTitle !== '') {
|
|
$cleaned['custom_title'] = substr($customTitle, 0, 50);
|
|
}
|
|
}
|
|
$cleanedLayout[] = $cleaned;
|
|
}
|
|
|
|
$stmt = $db->prepare("UPDATE users SET fociz_widget_layout = ? WHERE id = ?");
|
|
$stmt->execute([json_encode($cleanedLayout, JSON_UNESCAPED_UNICODE), $user['id']]);
|
|
|
|
echo json_encode(['success' => true, 'message' => 'Widget indeling succesvol opgeslagen']);
|
|
exit;
|
|
}
|
|
|
|
// 1c. Scratchpad snelle notities opslaan
|
|
if ($action === 'save_scratchpad') {
|
|
$data = json_decode(file_get_contents('php://input'), true);
|
|
$date = $data['date'] ?? $today;
|
|
$notes = $data['notes'] ?? '';
|
|
|
|
$stmt = $db->prepare("
|
|
INSERT INTO daily_plans (user_id, plan_date, notes)
|
|
VALUES (?, ?, ?)
|
|
ON DUPLICATE KEY UPDATE notes = VALUES(notes)
|
|
");
|
|
$stmt->execute([$user['id'], $date, $notes]);
|
|
|
|
echo json_encode(['success' => true]);
|
|
exit;
|
|
}
|
|
|
|
// 2. Taak toevoegen
|
|
if ($action === 'add_task') {
|
|
$data = json_decode(file_get_contents('php://input'), true);
|
|
$title = trim($data['title'] ?? '');
|
|
$priority = in_array($data['priority'] ?? '', ['P1', 'P2', 'P3']) ? $data['priority'] : 'P2';
|
|
$timeBlockStart = !empty($data['time_block_start']) ? trim($data['time_block_start']) : null;
|
|
$timeBlockEnd = !empty($data['time_block_end']) ? trim($data['time_block_end']) : null;
|
|
$durationMinutes = (int)($data['duration_minutes'] ?? 0);
|
|
$date = !empty($data['date']) ? trim($data['date']) : $today;
|
|
$syncOutlook = !empty($data['sync_to_outlook']);
|
|
|
|
if (empty($title)) {
|
|
echo json_encode(['error' => 'Titel is verplicht']);
|
|
exit;
|
|
}
|
|
|
|
// Als zowel start- als eindtijd zijn opgegeven, bereken exact de minuten
|
|
if (!empty($timeBlockStart) && !empty($timeBlockEnd)) {
|
|
$t1 = strtotime("2000-01-01 {$timeBlockStart}");
|
|
$t2 = strtotime("2000-01-01 {$timeBlockEnd}");
|
|
if ($t1 !== false && $t2 !== false && $t2 > $t1) {
|
|
$durationMinutes = (int)(($t2 - $t1) / 60);
|
|
}
|
|
}
|
|
|
|
if ($durationMinutes <= 0) {
|
|
$durationMinutes = 30;
|
|
}
|
|
|
|
// Als starttijd bekend is maar eindtijd nog niet, bereken eindtijd
|
|
if (!empty($timeBlockStart) && empty($timeBlockEnd)) {
|
|
try {
|
|
$dtEnd = new DateTime("{$date} {$timeBlockStart}:00", new DateTimeZone('Europe/Amsterdam'));
|
|
$dtEnd->modify("+{$durationMinutes} minutes");
|
|
$timeBlockEnd = $dtEnd->format('H:i');
|
|
} catch (Throwable $e) {
|
|
// negeer
|
|
}
|
|
}
|
|
// Haal of maak daily plan op
|
|
$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 = $db->lastInsertId();
|
|
|
|
$source = !empty($data['source']) ? substr(trim($data['source']), 0, 50) : 'FOCIZ';
|
|
$externalId = !empty($data['external_id']) ? substr(trim($data['external_id']), 0, 255) : null;
|
|
$externalUrl = !empty($data['external_url']) ? trim($data['external_url']) : null;
|
|
$outlookSyncError = null;
|
|
|
|
// Controleer of de gebruiker Outlook 365 heeft gekoppeld
|
|
$userHasOutlook = false;
|
|
if (function_exists('microsoft_create_event')) {
|
|
$stmtChk = $db->prepare("SELECT id FROM integrations WHERE user_id = ? AND provider = 'OUTLOOK' AND is_enabled = 1 AND access_token IS NOT NULL LIMIT 1");
|
|
$stmtChk->execute([$user['id']]);
|
|
$userHasOutlook = (bool)$stmtChk->fetchColumn();
|
|
}
|
|
|
|
// Automatisch synchroniseren naar Outlook indien gevraagd OF indien gebruiker Outlook heeft gekoppeld
|
|
if (($syncOutlook || $source === 'OUTLOOK' || $userHasOutlook) && empty($externalId)) {
|
|
$syncStartTime = !empty($timeBlockStart) ? $timeBlockStart : '09:00';
|
|
|
|
$itemDesc = !empty($data['description']) ? trim((string)$data['description']) : "Gepland via Fociz Dagplanning ({$priority})";
|
|
if ($source === 'JIRA' && !empty($data['external_url'])) {
|
|
$itemDesc .= "\n\nJira: " . $data['external_url'];
|
|
}
|
|
|
|
$msResult = microsoft_create_event($user['id'], [
|
|
'subject' => $title,
|
|
'description' => $itemDesc,
|
|
'date' => $date,
|
|
'start_time' => $syncStartTime,
|
|
'end_time' => $timeBlockEnd,
|
|
'duration_minutes' => $durationMinutes,
|
|
]);
|
|
|
|
if (!empty($msResult['success']) && !empty($msResult['event_id'])) {
|
|
$source = 'OUTLOOK';
|
|
$externalId = $msResult['event_id'];
|
|
$externalUrl = $msResult['web_link'] ?? ($externalUrl ?: 'https://outlook.office.com');
|
|
$timeBlockStart = $msResult['start_time'] ?? $syncStartTime;
|
|
$timeBlockEnd = $msResult['end_time'] ?? $timeBlockEnd;
|
|
} else {
|
|
$outlookSyncError = $msResult['error'] ?? 'Kon afspraak niet aanmaken in Microsoft Outlook';
|
|
}
|
|
}
|
|
$stmt = $db->prepare("
|
|
INSERT INTO tasks (user_id, daily_plan_id, title, priority, time_block_start, time_block_end, duration_minutes, status, source, external_id, external_url)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, 'TODO', ?, ?, ?)
|
|
");
|
|
$stmt->execute([$user['id'], $planId, $title, $priority, $timeBlockStart, $timeBlockEnd, $durationMinutes, $source, $externalId, $externalUrl]);
|
|
$taskId = (int)$db->lastInsertId();
|
|
|
|
echo json_encode([
|
|
'success' => true,
|
|
'task' => [
|
|
'id' => $taskId,
|
|
'title' => $title,
|
|
'priority' => $priority,
|
|
'time_block_start' => $timeBlockStart,
|
|
'time_block_end' => $timeBlockEnd,
|
|
'duration_minutes' => $durationMinutes,
|
|
'is_completed' => 0,
|
|
'status' => 'TODO',
|
|
'source' => $source,
|
|
'external_id' => $externalId,
|
|
'external_url' => $externalUrl,
|
|
],
|
|
'outlook_synced' => ($source === 'OUTLOOK' && !empty($externalId)),
|
|
'outlook_error' => $outlookSyncError,
|
|
]);
|
|
exit;
|
|
}
|
|
|
|
// 2b. Taak bewerken (titel, prioriteit, starttijd, eindtijd, duur)
|
|
if ($action === 'update_task') {
|
|
$data = json_decode(file_get_contents('php://input'), true);
|
|
$taskId = (int)($data['task_id'] ?? 0);
|
|
$title = trim($data['title'] ?? '');
|
|
$priority = in_array($data['priority'] ?? '', ['P1', 'P2', 'P3']) ? $data['priority'] : 'P2';
|
|
$timeBlockStart = !empty($data['time_block_start']) ? trim($data['time_block_start']) : null;
|
|
$timeBlockEnd = !empty($data['time_block_end']) ? trim($data['time_block_end']) : null;
|
|
$durationMinutes = (int)($data['duration_minutes'] ?? 0);
|
|
|
|
if (empty($title)) {
|
|
echo json_encode(['error' => 'Titel mag niet leeg zijn']);
|
|
exit;
|
|
}
|
|
|
|
// Haal bestaande taak op
|
|
$stmtTask = $db->prepare("SELECT * FROM tasks WHERE id = ? AND user_id = ?");
|
|
$stmtTask->execute([$taskId, $user['id']]);
|
|
$existingTask = $stmtTask->fetch();
|
|
|
|
if (!$existingTask) {
|
|
echo json_encode(['error' => 'Taak niet gevonden']);
|
|
exit;
|
|
}
|
|
|
|
if (!empty($timeBlockStart) && !empty($timeBlockEnd)) {
|
|
$t1 = strtotime("2000-01-01 {$timeBlockStart}");
|
|
$t2 = strtotime("2000-01-01 {$timeBlockEnd}");
|
|
if ($t1 !== false && $t2 !== false && $t2 > $t1) {
|
|
$durationMinutes = (int)(($t2 - $t1) / 60);
|
|
}
|
|
}
|
|
|
|
if ($durationMinutes <= 0) {
|
|
$durationMinutes = (int)($existingTask['duration_minutes'] ?: 30);
|
|
}
|
|
|
|
if (!empty($timeBlockStart) && empty($timeBlockEnd)) {
|
|
try {
|
|
$dtEnd = new DateTime("2000-01-01 {$timeBlockStart}:00", new DateTimeZone('Europe/Amsterdam'));
|
|
$dtEnd->modify("+{$durationMinutes} minutes");
|
|
$timeBlockEnd = $dtEnd->format('H:i');
|
|
} catch (Throwable $e) {}
|
|
}
|
|
|
|
$targetDate = !empty($data['date']) ? trim($data['date']) : null;
|
|
$planDate = null;
|
|
$newPlanId = null;
|
|
|
|
if ($targetDate && preg_match('/^\d{4}-\d{2}-\d{2}$/', $targetDate)) {
|
|
$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'], $targetDate]);
|
|
$newPlanId = (int)$db->lastInsertId();
|
|
$planDate = $targetDate;
|
|
} else {
|
|
$stmtPlan = $db->prepare("SELECT plan_date FROM daily_plans WHERE id = ?");
|
|
$stmtPlan->execute([$existingTask['daily_plan_id']]);
|
|
$planDate = $stmtPlan->fetchColumn() ?: date('Y-m-d');
|
|
}
|
|
|
|
// Controleer of de gebruiker Outlook 365 heeft gekoppeld
|
|
$userHasOutlook = false;
|
|
if (function_exists('microsoft_create_event')) {
|
|
$stmtChk = $db->prepare("SELECT id FROM integrations WHERE user_id = ? AND provider = 'OUTLOOK' AND is_enabled = 1 AND access_token IS NOT NULL LIMIT 1");
|
|
$stmtChk->execute([$user['id']]);
|
|
$userHasOutlook = (bool)$stmtChk->fetchColumn();
|
|
}
|
|
|
|
$source = $existingTask['source'] ?? 'FOCIZ';
|
|
$externalId = $existingTask['external_id'] ?? null;
|
|
$externalUrl = $existingTask['external_url'] ?? null;
|
|
|
|
// Indien gekoppeld aan Outlook, werk afspraak bij via Graph API
|
|
if (!empty($externalId) && $source === 'OUTLOOK') {
|
|
try {
|
|
microsoft_update_event($user['id'], $externalId, [
|
|
'subject' => $title,
|
|
'date' => $planDate,
|
|
'start_time' => $timeBlockStart ?: '09:00',
|
|
'end_time' => $timeBlockEnd ?: '09:30',
|
|
]);
|
|
} catch (Throwable $t) {
|
|
error_log("Fout bij bijwerken Outlook event: " . $t->getMessage());
|
|
}
|
|
} elseif ($userHasOutlook && empty($externalId) && function_exists('microsoft_create_event')) {
|
|
// Nog niet in Outlook gesynchroniseerd, maar gebruiker heeft Outlook: maak afspraak aan
|
|
try {
|
|
$syncStartTime = !empty($timeBlockStart) ? $timeBlockStart : '09:00';
|
|
$itemDesc = !empty($existingTask['description']) ? trim((string)$existingTask['description']) : "Gepland via Fociz Dagplanning ({$priority})";
|
|
if ($source === 'JIRA' && !empty($externalUrl)) {
|
|
$itemDesc .= "\n\nJira: " . $externalUrl;
|
|
}
|
|
|
|
$msResult = microsoft_create_event($user['id'], [
|
|
'subject' => $title,
|
|
'description' => $itemDesc,
|
|
'date' => $planDate,
|
|
'start_time' => $syncStartTime,
|
|
'end_time' => $timeBlockEnd,
|
|
'duration_minutes' => $durationMinutes,
|
|
]);
|
|
|
|
if (!empty($msResult['success']) && !empty($msResult['event_id'])) {
|
|
$source = 'OUTLOOK';
|
|
$externalId = $msResult['event_id'];
|
|
$externalUrl = $msResult['web_link'] ?? ($externalUrl ?: 'https://outlook.office.com');
|
|
}
|
|
} catch (Throwable $t) {
|
|
error_log("Fout bij auto-create Outlook event in update_task: " . $t->getMessage());
|
|
}
|
|
}
|
|
|
|
$description = isset($data['description']) ? trim((string)$data['description']) : ($existingTask['description'] ?? null);
|
|
|
|
if ($newPlanId) {
|
|
$stmtUpdate = $db->prepare("
|
|
UPDATE tasks
|
|
SET title = ?, description = ?, priority = ?, time_block_start = ?, time_block_end = ?, duration_minutes = ?, source = ?, external_id = ?, external_url = ?, daily_plan_id = ?
|
|
WHERE id = ? AND user_id = ?
|
|
");
|
|
$stmtUpdate->execute([$title, $description, $priority, $timeBlockStart, $timeBlockEnd, $durationMinutes, $source, $externalId, $externalUrl, $newPlanId, $taskId, $user['id']]);
|
|
} else {
|
|
$stmtUpdate = $db->prepare("
|
|
UPDATE tasks
|
|
SET title = ?, description = ?, priority = ?, time_block_start = ?, time_block_end = ?, duration_minutes = ?, source = ?, external_id = ?, external_url = ?
|
|
WHERE id = ? AND user_id = ?
|
|
");
|
|
$stmtUpdate->execute([$title, $description, $priority, $timeBlockStart, $timeBlockEnd, $durationMinutes, $source, $externalId, $externalUrl, $taskId, $user['id']]);
|
|
}
|
|
|
|
echo json_encode([
|
|
'success' => true,
|
|
'time_block_start' => $timeBlockStart,
|
|
'time_block_end' => $timeBlockEnd,
|
|
'duration_minutes' => $durationMinutes,
|
|
'date' => $planDate
|
|
]);
|
|
exit;
|
|
}
|
|
|
|
// 3. Bestaande taak handmatig naar Microsoft Outlook synchroniseren
|
|
if ($action === 'sync_task_to_outlook') {
|
|
$data = json_decode(file_get_contents('php://input'), true);
|
|
$taskId = (int)($data['task_id'] ?? 0);
|
|
|
|
if (!$taskId) {
|
|
echo json_encode(['error' => 'Geen taak-ID opgegeven']);
|
|
exit;
|
|
}
|
|
|
|
// Haal taak inclusief plan datum op
|
|
$stmtTask = $db->prepare("
|
|
SELECT t.*, dp.plan_date
|
|
FROM tasks t
|
|
LEFT JOIN daily_plans dp ON t.daily_plan_id = dp.id
|
|
WHERE t.id = ? AND t.user_id = ?
|
|
");
|
|
$stmtTask->execute([$taskId, $user['id']]);
|
|
$task = $stmtTask->fetch();
|
|
|
|
if (!$task) {
|
|
echo json_encode(['error' => 'Taak niet gevonden']);
|
|
exit;
|
|
}
|
|
|
|
// Als al gesynchroniseerd
|
|
if (!empty($task['external_id']) && $task['source'] === 'OUTLOOK') {
|
|
echo json_encode([
|
|
'success' => true,
|
|
'already_synced' => true,
|
|
'external_id' => $task['external_id'],
|
|
'external_url' => $task['external_url']
|
|
]);
|
|
exit;
|
|
}
|
|
|
|
$planDate = $task['plan_date'] ?: $today;
|
|
$startTime = !empty($task['time_block_start']) ? $task['time_block_start'] : ($data['time_block_start'] ?? '09:00');
|
|
$endTime = !empty($task['time_block_end']) ? $task['time_block_end'] : null;
|
|
$duration = (int)($task['duration_minutes'] ?: 30);
|
|
|
|
$msResult = microsoft_create_event($user['id'], [
|
|
'subject' => $task['title'],
|
|
'description' => "Gepland via Fociz Dagplanning (" . $task['priority'] . ")",
|
|
'date' => $planDate,
|
|
'start_time' => $startTime,
|
|
'end_time' => $endTime,
|
|
'duration_minutes' => $duration,
|
|
]);
|
|
|
|
if (empty($msResult['success']) || empty($msResult['event_id'])) {
|
|
echo json_encode(['error' => $msResult['error'] ?? 'Kon afspraak niet aanmaken in Microsoft Outlook']);
|
|
exit;
|
|
}
|
|
|
|
// Sla Outlook ID en bron op in de taak
|
|
$stmtUp = $db->prepare("
|
|
UPDATE tasks
|
|
SET source = 'OUTLOOK',
|
|
external_id = ?,
|
|
external_url = ?,
|
|
time_block_start = COALESCE(time_block_start, ?),
|
|
time_block_end = ?
|
|
WHERE id = ? AND user_id = ?
|
|
");
|
|
$stmtUp->execute([
|
|
$msResult['event_id'],
|
|
$msResult['web_link'] ?? 'https://outlook.office.com',
|
|
$msResult['start_time'],
|
|
$msResult['end_time'],
|
|
$taskId,
|
|
$user['id']
|
|
]);
|
|
|
|
echo json_encode([
|
|
'success' => true,
|
|
'external_id' => $msResult['event_id'],
|
|
'external_url' => $msResult['web_link'] ?? 'https://outlook.office.com',
|
|
'start_time' => $msResult['start_time'],
|
|
'end_time' => $msResult['end_time'],
|
|
]);
|
|
exit;
|
|
}
|
|
|
|
// 4. Taak status toggelen (voltooid / open)
|
|
if ($action === 'toggle_task') {
|
|
$data = json_decode(file_get_contents('php://input'), true);
|
|
$taskId = (int)($data['task_id'] ?? 0);
|
|
$isCompleted = !empty($data['is_completed']) ? 1 : 0;
|
|
$status = $isCompleted ? 'DONE' : 'TODO';
|
|
|
|
$stmt = $db->prepare("
|
|
UPDATE tasks SET is_completed = ?, status = ?
|
|
WHERE id = ? AND user_id = ?
|
|
");
|
|
$stmt->execute([$isCompleted, $status, $taskId, $user['id']]);
|
|
|
|
echo json_encode(['success' => true]);
|
|
exit;
|
|
}
|
|
|
|
// 5. Taak verwijderen (verwijder ook eventueel gekoppelde Outlook afspraak)
|
|
if ($action === 'delete_task') {
|
|
$data = json_decode(file_get_contents('php://input'), true);
|
|
$taskId = (int)($data['task_id'] ?? 0);
|
|
|
|
// Haal eerst op of deze taak aan Outlook was gekoppeld
|
|
$stmtCheck = $db->prepare("SELECT external_id, source FROM tasks WHERE id = ? AND user_id = ?");
|
|
$stmtCheck->execute([$taskId, $user['id']]);
|
|
$taskToDelete = $stmtCheck->fetch();
|
|
|
|
if ($taskToDelete && !empty($taskToDelete['external_id']) && $taskToDelete['source'] === 'OUTLOOK') {
|
|
try {
|
|
microsoft_delete_event($user['id'], $taskToDelete['external_id']);
|
|
} catch (Throwable $t) {
|
|
error_log("Fout bij verwijderen Outlook afspraak voor taak {$taskId}: " . $t->getMessage());
|
|
}
|
|
}
|
|
|
|
$stmt = $db->prepare("DELETE FROM tasks WHERE id = ? AND user_id = ?");
|
|
$stmt->execute([$taskId, $user['id']]);
|
|
|
|
echo json_encode(['success' => true]);
|
|
exit;
|
|
}
|
|
|
|
// 6. Realtime Outlook synchronisatie & controle op gewijzigde/gewiste items
|
|
if ($action === 'check_outlook_sync') {
|
|
$rawInput = file_get_contents('php://input');
|
|
$data = !empty($rawInput) ? (json_decode($rawInput, true) ?: []) : [];
|
|
if (empty($data)) {
|
|
$data = $_GET;
|
|
}
|
|
|
|
$planDate = !empty($data['plan_date']) ? trim($data['plan_date']) : $today;
|
|
$weekStart = !empty($data['week_start']) ? trim($data['week_start']) : date('Y-m-d', strtotime('monday this week', strtotime($planDate)));
|
|
$weekEnd = !empty($data['week_end']) ? trim($data['week_end']) : date('Y-m-d', strtotime('sunday this week', strtotime($planDate)));
|
|
$lastHash = !empty($data['last_hash']) ? trim($data['last_hash']) : '';
|
|
|
|
// Controleer of Outlook gekoppeld is
|
|
$stmtInt = $db->prepare("SELECT * FROM integrations WHERE user_id = ? AND provider = 'OUTLOOK' AND is_enabled = 1");
|
|
$stmtInt->execute([$user['id']]);
|
|
$integration = $stmtInt->fetch();
|
|
|
|
if (!$integration || empty($integration['access_token']) || !empty($integration['is_demo_mode'])) {
|
|
echo json_encode([
|
|
'success' => true,
|
|
'connected' => false,
|
|
'has_changes' => false,
|
|
'last_sync' => date('H:i:s'),
|
|
]);
|
|
exit;
|
|
}
|
|
|
|
// Haal live afspraken op voor de hele week
|
|
$weekLiveEvents = microsoft_fetch_events_range($user['id'], $weekStart, $weekEnd);
|
|
|
|
// Voer reconciliatie uit met de Fociz tasks tabel
|
|
$reconcileRes = microsoft_reconcile_tasks_with_outlook($user['id'], $weekStart, $weekEnd, $weekLiveEvents);
|
|
|
|
// Haal de geactualiseerde taken voor deze dag op
|
|
$stmtPlan = $db->prepare("SELECT id FROM daily_plans WHERE user_id = ? AND plan_date = ?");
|
|
$stmtPlan->execute([$user['id'], $planDate]);
|
|
$planId = $stmtPlan->fetchColumn();
|
|
|
|
$tasks = [];
|
|
if ($planId) {
|
|
$stmtTasks = $db->prepare("
|
|
SELECT id, title, priority, time_block_start, time_block_end, duration_minutes, is_completed, status, source, external_id
|
|
FROM tasks
|
|
WHERE user_id = ? AND (daily_plan_id = ? " . ($planDate === $today ? "OR daily_plan_id IS NULL" : "") . ")
|
|
ORDER BY is_completed ASC, priority ASC, time_block_start ASC, created_at DESC
|
|
");
|
|
$stmtTasks->execute([$user['id'], $planId]);
|
|
$tasks = $stmtTasks->fetchAll();
|
|
}
|
|
|
|
// Filter events voor de geselecteerde dag die geen gesynchroniseerde Fociz taak zijn
|
|
$syncedIds = [];
|
|
foreach ($tasks as $t) {
|
|
if (!empty($t['external_id'])) {
|
|
$syncedIds[$t['external_id']] = true;
|
|
}
|
|
}
|
|
|
|
$dayEvents = [];
|
|
foreach ($weekLiveEvents as $e) {
|
|
if (($e['date'] ?? '') === $planDate) {
|
|
if (!empty($e['id']) && isset($syncedIds[$e['id']])) {
|
|
continue;
|
|
}
|
|
$dayEvents[] = [
|
|
'id' => $e['id'],
|
|
'start' => $e['start'],
|
|
'end' => $e['end'],
|
|
'subject' => $e['subject'],
|
|
];
|
|
}
|
|
}
|
|
|
|
// Bereken status hash voor de dag
|
|
$hashData = [
|
|
'events' => $dayEvents,
|
|
'tasks' => array_map(function($t) {
|
|
return [
|
|
'id' => $t['id'],
|
|
'title' => $t['title'],
|
|
'time_block_start' => $t['time_block_start'],
|
|
'time_block_end' => $t['time_block_end'],
|
|
'is_completed' => $t['is_completed'],
|
|
];
|
|
}, $tasks)
|
|
];
|
|
$currentHash = md5(json_encode($hashData));
|
|
|
|
$hasChanges = ($reconcileRes['deleted'] > 0 || $reconcileRes['updated'] > 0);
|
|
if (!empty($lastHash) && $lastHash !== $currentHash) {
|
|
$hasChanges = true;
|
|
}
|
|
|
|
echo json_encode([
|
|
'success' => true,
|
|
'connected' => true,
|
|
'has_changes' => $hasChanges,
|
|
'hash' => $currentHash,
|
|
'deleted_count' => $reconcileRes['deleted'],
|
|
'updated_count' => $reconcileRes['updated'],
|
|
'events_count' => count($dayEvents),
|
|
'tasks_count' => count($tasks),
|
|
'last_sync' => date('H:i:s'),
|
|
]);
|
|
exit;
|
|
}
|
|
|
|
// --- V2.0: Subtaken & Checklists ---
|
|
|
|
// 7. Subtaak toevoegen aan een taak
|
|
if ($action === 'add_subtask') {
|
|
$data = json_decode(file_get_contents('php://input'), true);
|
|
$taskId = (int)($data['task_id'] ?? 0);
|
|
$title = trim($data['title'] ?? '');
|
|
|
|
if (!$taskId || empty($title)) {
|
|
echo json_encode(['error' => 'Taak-ID en titel zijn verplicht']);
|
|
exit;
|
|
}
|
|
|
|
// Controleer of de taak van deze gebruiker is
|
|
$stmtCheck = $db->prepare("SELECT id FROM tasks WHERE id = ? AND user_id = ?");
|
|
$stmtCheck->execute([$taskId, $user['id']]);
|
|
if (!$stmtCheck->fetchColumn()) {
|
|
echo json_encode(['error' => 'Taak niet gevonden']);
|
|
exit;
|
|
}
|
|
|
|
// Bereken positie (achteraan)
|
|
$stmtPos = $db->prepare("SELECT COALESCE(MAX(position), -1) + 1 FROM task_subtasks WHERE task_id = ?");
|
|
$stmtPos->execute([$taskId]);
|
|
$position = (int)$stmtPos->fetchColumn();
|
|
|
|
$stmt = $db->prepare("
|
|
INSERT INTO task_subtasks (task_id, user_id, title, is_completed, position)
|
|
VALUES (?, ?, ?, 0, ?)
|
|
");
|
|
$stmt->execute([$taskId, $user['id'], substr($title, 0, 255), $position]);
|
|
$subtaskId = (int)$db->lastInsertId();
|
|
|
|
echo json_encode([
|
|
'success' => true,
|
|
'subtask' => [
|
|
'id' => $subtaskId,
|
|
'task_id' => $taskId,
|
|
'title' => $title,
|
|
'is_completed' => 0,
|
|
'position' => $position,
|
|
]
|
|
]);
|
|
exit;
|
|
}
|
|
|
|
// 8. Subtaak afvinken / openzetten
|
|
if ($action === 'toggle_subtask') {
|
|
$data = json_decode(file_get_contents('php://input'), true);
|
|
$subtaskId = (int)($data['subtask_id'] ?? 0);
|
|
$isCompleted = !empty($data['is_completed']) ? 1 : 0;
|
|
|
|
if (!$subtaskId) {
|
|
echo json_encode(['error' => 'Subtaak-ID ontbreekt']);
|
|
exit;
|
|
}
|
|
|
|
$stmt = $db->prepare("
|
|
UPDATE task_subtasks SET is_completed = ?
|
|
WHERE id = ? AND user_id = ?
|
|
");
|
|
$stmt->execute([$isCompleted, $subtaskId, $user['id']]);
|
|
|
|
echo json_encode(['success' => true, 'is_completed' => $isCompleted]);
|
|
exit;
|
|
}
|
|
|
|
// 9. Subtaak verwijderen
|
|
if ($action === 'delete_subtask') {
|
|
$data = json_decode(file_get_contents('php://input'), true);
|
|
$subtaskId = (int)($data['subtask_id'] ?? 0);
|
|
|
|
if (!$subtaskId) {
|
|
echo json_encode(['error' => 'Subtaak-ID ontbreekt']);
|
|
exit;
|
|
}
|
|
|
|
$stmt = $db->prepare("DELETE FROM task_subtasks WHERE id = ? AND user_id = ?");
|
|
$stmt->execute([$subtaskId, $user['id']]);
|
|
|
|
echo json_encode(['success' => true]);
|
|
exit;
|
|
}
|
|
|
|
// 10. Subtaken ophalen voor een taak
|
|
if ($action === 'get_subtasks') {
|
|
$taskId = (int)($_GET['task_id'] ?? 0);
|
|
|
|
if (!$taskId) {
|
|
echo json_encode(['error' => 'Taak-ID ontbreekt']);
|
|
exit;
|
|
}
|
|
|
|
// Controleer eigenaarschap
|
|
$stmtCheck = $db->prepare("SELECT id FROM tasks WHERE id = ? AND user_id = ?");
|
|
$stmtCheck->execute([$taskId, $user['id']]);
|
|
if (!$stmtCheck->fetchColumn()) {
|
|
echo json_encode(['error' => 'Taak niet gevonden']);
|
|
exit;
|
|
}
|
|
|
|
$stmt = $db->prepare("
|
|
SELECT id, task_id, title, is_completed, position
|
|
FROM task_subtasks
|
|
WHERE task_id = ? AND user_id = ?
|
|
ORDER BY position ASC, created_at ASC
|
|
");
|
|
$stmt->execute([$taskId, $user['id']]);
|
|
$subtasks = $stmt->fetchAll();
|
|
|
|
echo json_encode(['success' => true, 'subtasks' => $subtasks]);
|
|
exit;
|
|
}
|
|
|
|
// 10b. Volledige taakdetails ophalen inclusief subtaken en dagplan informatie
|
|
if ($action === 'get_task_details') {
|
|
$taskId = (int)($_GET['task_id'] ?? ($_POST['task_id'] ?? 0));
|
|
|
|
if (!$taskId) {
|
|
echo json_encode(['error' => 'Taak-ID ontbreekt']);
|
|
exit;
|
|
}
|
|
|
|
$stmt = $db->prepare("
|
|
SELECT t.*, dp.plan_date, dp.focus_goal
|
|
FROM tasks t
|
|
LEFT JOIN daily_plans dp ON t.daily_plan_id = dp.id
|
|
WHERE t.id = ? AND t.user_id = ?
|
|
");
|
|
$stmt->execute([$taskId, $user['id']]);
|
|
$task = $stmt->fetch();
|
|
|
|
if (!$task) {
|
|
echo json_encode(['error' => 'Taak niet gevonden']);
|
|
exit;
|
|
}
|
|
|
|
// Subtaken ophalen
|
|
$stmtSub = $db->prepare("
|
|
SELECT id, task_id, title, is_completed, position, created_at
|
|
FROM task_subtasks
|
|
WHERE task_id = ? AND user_id = ?
|
|
ORDER BY position ASC, created_at ASC
|
|
");
|
|
$stmtSub->execute([$taskId, $user['id']]);
|
|
$subtasks = $stmtSub->fetchAll();
|
|
|
|
echo json_encode([
|
|
'success' => true,
|
|
'task' => $task,
|
|
'subtasks' => $subtasks,
|
|
]);
|
|
exit;
|
|
}
|
|
|
|
// 11. Zoek eerstvolgende vrije open blok binnen werktijden (Release v2.1)
|
|
if ($action === 'find_next_free_slot') {
|
|
$rawInput = json_decode(file_get_contents('php://input'), true);
|
|
$data = is_array($rawInput) ? $rawInput : $_GET;
|
|
$duration = max(5, (int)($data['duration_minutes'] ?? 30));
|
|
$excludeTaskId = (int)($data['task_id'] ?? 0);
|
|
$startFromNextWorkday = !empty($data['start_from_next_workday']);
|
|
$targetUserId = !empty($data['target_user_id']) ? (int)$data['target_user_id'] : (int)$user['id'];
|
|
|
|
$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';
|
|
$activeWorkdays = $workdaySettings['workdays'] ?? [1, 2, 3, 4, 5];
|
|
if (!is_array($activeWorkdays) || empty($activeWorkdays)) {
|
|
$activeWorkdays = [1, 2, 3, 4, 5];
|
|
}
|
|
|
|
$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);
|
|
|
|
// Huidige tijd afronden naar hele kwartieren
|
|
$nowH = (int)$now->format('G');
|
|
$nowM = (int)$now->format('i');
|
|
if ($nowM % 15 !== 0) {
|
|
$nowM = (int)(ceil($nowM / 15) * 15);
|
|
if ($nowM >= 60) {
|
|
$nowH += (int)floor($nowM / 60);
|
|
$nowM = $nowM % 60;
|
|
}
|
|
}
|
|
$currentQuarterMin = $nowH * 60 + $nowM;
|
|
|
|
$dutchDays = [1 => 'Maandag', 2 => 'Dinsdag', 3 => 'Woensdag', 4 => 'Donderdag', 5 => 'Vrijdag', 6 => 'Zaterdag', 7 => 'Zondag'];
|
|
$dutchMonths = ['', 'jan', 'feb', 'mrt', 'apr', 'mei', 'jun', 'jul', 'aug', 'sep', 'okt', 'nov', 'dec'];
|
|
|
|
$checkDate = clone $now;
|
|
if ($startFromNextWorkday) {
|
|
$checkDate->modify('+1 day');
|
|
while (!in_array((int)$checkDate->format('N'), $activeWorkdays, true)) {
|
|
$checkDate->modify('+1 day');
|
|
}
|
|
}
|
|
$foundSlot = null;
|
|
$maxDays = 14;
|
|
$checkedDays = 0;
|
|
|
|
// Bepaal het totale zoekvenster voor batch-ophalen (O(1) database & Graph API schaalbaarheid)
|
|
$rangeStartDate = $checkDate->format('Y-m-d');
|
|
$rangeEndObj = clone $checkDate;
|
|
$rangeEndObj->modify('+21 days'); // ruim venster incl. weekenden
|
|
$rangeEndDate = $rangeEndObj->format('Y-m-d');
|
|
|
|
// 1. Batch SQL: haal alle bestaande taken van de doelcollega in 1 geoptimaliseerde query op
|
|
$tasksByDate = [];
|
|
$stmtTasks = $db->prepare("
|
|
SELECT t.id, dp.plan_date, t.time_block_start, t.time_block_end, t.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 BETWEEN ? AND ?
|
|
AND t.time_block_start IS NOT NULL
|
|
AND t.time_block_start != ''
|
|
AND t.status != 'CANCELLED'
|
|
");
|
|
$stmtTasks->execute([$targetUserId, $rangeStartDate, $rangeEndDate]);
|
|
while ($t = $stmtTasks->fetch()) {
|
|
if ($excludeTaskId > 0 && (int)$t['id'] === $excludeTaskId) {
|
|
continue;
|
|
}
|
|
$tasksByDate[$t['plan_date']][] = $t;
|
|
}
|
|
|
|
// 2. Batch Microsoft Graph: haal alle Outlook events in 1 API call op voor het gehele venster van de doelcollega
|
|
$outlookByDate = [];
|
|
if (function_exists('microsoft_fetch_events_range')) {
|
|
try {
|
|
$allOutlookEvts = microsoft_fetch_events_range($targetUserId, $rangeStartDate, $rangeEndDate, false);
|
|
foreach ($allOutlookEvts as $oe) {
|
|
if (!empty($oe['date'])) {
|
|
$outlookByDate[$oe['date']][] = $oe;
|
|
}
|
|
}
|
|
} catch (Throwable $e) {}
|
|
}
|
|
|
|
// 3. Batch Calendar Blocks: haal verlof en geblokkeerde vakantiedagen op voor dit venster van de doelcollega
|
|
$blockedDates = [];
|
|
try {
|
|
$stmtBlk = $db->prepare("SELECT start_date, end_date FROM calendar_blocks WHERE user_id = ? AND start_date <= ? AND end_date >= ?");
|
|
$stmtBlk->execute([$targetUserId, $rangeEndDate, $rangeStartDate]);
|
|
while ($b = $stmtBlk->fetch()) {
|
|
$bStart = strtotime($b['start_date']);
|
|
$bEnd = strtotime($b['end_date']);
|
|
for ($d = $bStart; $d <= $bEnd; $d += 86400) {
|
|
$blockedDates[date('Y-m-d', $d)] = true;
|
|
}
|
|
}
|
|
} catch (Throwable $e) {}
|
|
|
|
while ($checkedDays < $maxDays) {
|
|
$dayOfWeek = (int)$checkDate->format('N'); // 1 = Ma, 7 = Zo
|
|
$dateStr = $checkDate->format('Y-m-d');
|
|
|
|
// Niet-werkdag / weekend overslaan op basis van organisatie-instellingen
|
|
if (!in_array($dayOfWeek, $activeWorkdays, true)) {
|
|
$checkDate->modify('+1 day');
|
|
continue;
|
|
}
|
|
|
|
// Geblokkeerde dagen (bijv. vakantie, verlof) overslaan
|
|
if (!empty($blockedDates[$dateStr])) {
|
|
$checkDate->modify('+1 day');
|
|
continue;
|
|
}
|
|
$isToday = ($dateStr === $now->format('Y-m-d')) && !$startFromNextWorkday;
|
|
|
|
// Bepaal vroegste startminuut voor deze dag (op hele kwartieren)
|
|
if ($isToday) {
|
|
$earliestMin = max($workdayStartMin, $currentQuarterMin);
|
|
if ($earliestMin % 15 !== 0) {
|
|
$earliestMin = (int)(ceil($earliestMin / 15) * 15);
|
|
}
|
|
} else {
|
|
$earliestMin = $workdayStartMin;
|
|
if ($earliestMin % 15 !== 0) {
|
|
$earliestMin = (int)(ceil($earliestMin / 15) * 15);
|
|
}
|
|
}
|
|
|
|
// Alleen zoeken als er binnen de werkdag nog voldoende tijd is voor duration
|
|
if ($earliestMin + $duration <= $workdayEndMin) {
|
|
$busyIntervals = [];
|
|
|
|
// Lunchpauze
|
|
if ($lunchEndMin > $lunchStartMin) {
|
|
$busyIntervals[] = [$lunchStartMin, $lunchEndMin];
|
|
}
|
|
|
|
// Bestaande taken voor deze datum uit het geheugen
|
|
$dayTasks = $tasksByDate[$dateStr] ?? [];
|
|
foreach ($dayTasks as $t) {
|
|
$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 voor deze datum uit het geheugen
|
|
$dayOutlook = $outlookByDate[$dateStr] ?? [];
|
|
foreach ($dayOutlook 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];
|
|
}
|
|
}
|
|
|
|
// Doorzoek kwartierslots
|
|
for ($slotStart = $earliestMin; ($slotStart + $duration) <= $workdayEndMin; $slotStart += 15) {
|
|
$slotEnd = $slotStart + $duration;
|
|
$hasCollision = false;
|
|
|
|
foreach ($busyIntervals as [$bStart, $bEnd]) {
|
|
if ($slotStart < $bEnd && $slotEnd > $bStart) {
|
|
$hasCollision = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!$hasCollision) {
|
|
$foundSlot = [
|
|
'date' => $dateStr,
|
|
'start' => $toTimeStr($slotStart),
|
|
'end' => $toTimeStr($slotEnd),
|
|
'duration_minutes' => $duration,
|
|
'is_today' => $isToday,
|
|
'formatted_date' => $isToday
|
|
? 'Vandaag (' . date('j', strtotime($dateStr)) . ' ' . $dutchMonths[(int)date('n', strtotime($dateStr))] . ')'
|
|
: $dutchDays[(int)$checkDate->format('N')] . ' ' . date('j', strtotime($dateStr)) . ' ' . $dutchMonths[(int)date('n', strtotime($dateStr))],
|
|
];
|
|
break 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
$checkDate->modify('+1 day');
|
|
$checkedDays++;
|
|
}
|
|
|
|
if ($foundSlot) {
|
|
echo json_encode([
|
|
'success' => true,
|
|
'slot' => $foundSlot,
|
|
'workday_start' => $workdayStartStr,
|
|
'workday_end' => $workdayEndStr,
|
|
]);
|
|
} else {
|
|
echo json_encode([
|
|
'success' => false,
|
|
'workday_start' => $workdayStartStr,
|
|
'workday_end' => $workdayEndStr,
|
|
'error' => "Geen vrij blok van {$duration} minuten gevonden binnen de gezette werktijden ({$workdayStartStr} - {$workdayEndStr}) voor de komende {$maxDays} werkdagen.",
|
|
]);
|
|
}
|
|
exit;
|
|
}
|
|
|
|
// 12. Kalenderblokkades ophalen (Vakanties / Verlof / Afwezigheid)
|
|
if ($action === 'get_calendar_blocks') {
|
|
$stmt = $db->prepare("
|
|
SELECT * FROM calendar_blocks
|
|
WHERE user_id = ?
|
|
ORDER BY start_date DESC
|
|
LIMIT 100
|
|
");
|
|
$stmt->execute([$user['id']]);
|
|
$blocks = $stmt->fetchAll() ?: [];
|
|
echo json_encode(['success' => true, 'blocks' => $blocks]);
|
|
exit;
|
|
}
|
|
|
|
// 13. Kalenderblokkade opslaan (Vakantie / Verlof / Dag(en) blokkeren)
|
|
if ($action === 'save_calendar_block') {
|
|
$rawInput = json_decode(file_get_contents('php://input'), true);
|
|
$data = is_array($rawInput) ? $rawInput : $_POST;
|
|
|
|
$blockId = (int)($data['id'] ?? 0);
|
|
$title = trim($data['title'] ?? '');
|
|
$blockType = trim($data['block_type'] ?? 'VACATION');
|
|
$startDate = trim($data['start_date'] ?? $today);
|
|
$endDate = trim($data['end_date'] ?? $startDate);
|
|
$notes = trim($data['notes'] ?? '');
|
|
$syncToOutlook = !empty($data['sync_to_outlook']);
|
|
|
|
if (empty($title)) {
|
|
echo json_encode(['success' => false, 'error' => 'Geef een titel op voor de blokkade (bijv. Vakantie of Vrije dag)']);
|
|
exit;
|
|
}
|
|
|
|
if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $startDate) || !preg_match('/^\d{4}-\d{2}-\d{2}$/', $endDate)) {
|
|
echo json_encode(['success' => false, 'error' => 'Ongeldige datumnotatie']);
|
|
exit;
|
|
}
|
|
|
|
if ($endDate < $startDate) {
|
|
$temp = $startDate;
|
|
$startDate = $endDate;
|
|
$endDate = $temp;
|
|
}
|
|
|
|
$outlookEventId = null;
|
|
|
|
// Indien bewerken en er bestond al een Outlook event, haal het event ID op
|
|
if ($blockId > 0) {
|
|
$stmtEx = $db->prepare("SELECT outlook_event_id FROM calendar_blocks WHERE id = ? AND user_id = ?");
|
|
$stmtEx->execute([$blockId, $user['id']]);
|
|
$outlookEventId = $stmtEx->fetchColumn() ?: null;
|
|
}
|
|
|
|
// Optioneel synchroniseren naar Outlook 365
|
|
if ($syncToOutlook && function_exists('microsoft_create_event')) {
|
|
try {
|
|
if (!empty($outlookEventId) && function_exists('microsoft_delete_event')) {
|
|
microsoft_delete_event($user['id'], $outlookEventId);
|
|
}
|
|
|
|
$eventDesc = !empty($notes) ? $notes : "Geblokkeerd via Fociz Agenda ({$blockType})";
|
|
$msRes = microsoft_create_event($user['id'], [
|
|
'subject' => $title,
|
|
'description' => $eventDesc,
|
|
'date' => $startDate,
|
|
'end_date' => $endDate,
|
|
'is_all_day' => true,
|
|
'show_as' => ($blockType === 'FOCUS') ? 'busy' : 'oof',
|
|
]);
|
|
if (!empty($msRes['success']) && !empty($msRes['event_id'])) {
|
|
$outlookEventId = $msRes['event_id'];
|
|
}
|
|
} catch (Throwable $e) {
|
|
error_log("Fout bij sync calendar_block naar Outlook: " . $e->getMessage());
|
|
}
|
|
}
|
|
|
|
if ($blockId > 0) {
|
|
$stmt = $db->prepare("
|
|
UPDATE calendar_blocks
|
|
SET title = ?, block_type = ?, start_date = ?, end_date = ?, notes = ?, outlook_event_id = ?, updated_at = NOW()
|
|
WHERE id = ? AND user_id = ?
|
|
");
|
|
$stmt->execute([$title, $blockType, $startDate, $endDate, $notes, $outlookEventId, $blockId, $user['id']]);
|
|
} else {
|
|
$stmt = $db->prepare("
|
|
INSERT INTO calendar_blocks (user_id, start_date, end_date, title, block_type, notes, outlook_event_id, created_at, updated_at)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, NOW(), NOW())
|
|
");
|
|
$stmt->execute([$user['id'], $startDate, $endDate, $title, $blockType, $notes, $outlookEventId]);
|
|
$blockId = (int)$db->lastInsertId();
|
|
}
|
|
|
|
echo json_encode([
|
|
'success' => true,
|
|
'message' => 'Agenda-blokkade succesvol opgeslagen',
|
|
'block_id' => $blockId,
|
|
'start_date'=> $startDate,
|
|
'end_date' => $endDate,
|
|
'title' => $title
|
|
]);
|
|
exit;
|
|
}
|
|
|
|
// 14. Kalenderblokkade verwijderen / opheffen
|
|
if ($action === 'delete_calendar_block') {
|
|
$rawInput = json_decode(file_get_contents('php://input'), true);
|
|
$data = is_array($rawInput) ? $rawInput : $_POST;
|
|
$blockId = (int)($data['id'] ?? 0);
|
|
|
|
if ($blockId <= 0) {
|
|
echo json_encode(['success' => false, 'error' => 'Ongeldig blok ID']);
|
|
exit;
|
|
}
|
|
|
|
$stmtEx = $db->prepare("SELECT outlook_event_id FROM calendar_blocks WHERE id = ? AND user_id = ?");
|
|
$stmtEx->execute([$blockId, $user['id']]);
|
|
$outlookEventId = $stmtEx->fetchColumn();
|
|
|
|
if (!empty($outlookEventId) && function_exists('microsoft_delete_event')) {
|
|
try {
|
|
microsoft_delete_event($user['id'], $outlookEventId);
|
|
} catch (Throwable $e) {}
|
|
}
|
|
|
|
$stmtDel = $db->prepare("DELETE FROM calendar_blocks WHERE id = ? AND user_id = ?");
|
|
$stmtDel->execute([$blockId, $user['id']]);
|
|
|
|
echo json_encode(['success' => true, 'message' => 'Blokkade succesvol opgeheven']);
|
|
exit;
|
|
}
|
|
|
|
// 15. Outlook Agenda-afspraak verwijderen
|
|
if ($action === 'delete_outlook_event') {
|
|
$rawInput = json_decode(file_get_contents('php://input'), true);
|
|
$data = is_array($rawInput) ? $rawInput : $_POST;
|
|
$eventId = trim($data['event_id'] ?? '');
|
|
|
|
if (empty($eventId)) {
|
|
echo json_encode(['success' => false, 'error' => 'Geen event ID opgegeven']);
|
|
exit;
|
|
}
|
|
|
|
if (!function_exists('microsoft_delete_event')) {
|
|
echo json_encode(['success' => false, 'error' => 'Microsoft integratie niet beschikbaar']);
|
|
exit;
|
|
}
|
|
|
|
$ok = microsoft_delete_event((int)$user['id'], $eventId);
|
|
if ($ok) {
|
|
echo json_encode(['success' => true, 'message' => 'Afspraak succesvol verwijderd uit Outlook']);
|
|
} else {
|
|
echo json_encode(['success' => false, 'error' => 'Kon afspraak niet verwijderen uit Outlook (mogelijk al verwijderd of geen bevoegdheid)']);
|
|
}
|
|
exit;
|
|
}
|
|
|
|
// 16. Outlook Agenda-afspraak bijwerken
|
|
if ($action === 'update_outlook_event') {
|
|
$rawInput = json_decode(file_get_contents('php://input'), true);
|
|
$data = is_array($rawInput) ? $rawInput : $_POST;
|
|
$eventId = trim($data['event_id'] ?? '');
|
|
$subject = trim($data['title'] ?? ($data['subject'] ?? ''));
|
|
$date = trim($data['date'] ?? date('Y-m-d'));
|
|
$startTime = trim($data['time_block_start'] ?? ($data['start_time'] ?? '09:00'));
|
|
$endTime = trim($data['time_block_end'] ?? ($data['end_time'] ?? '09:30'));
|
|
$description = trim($data['description'] ?? '');
|
|
|
|
if (empty($eventId) || empty($subject)) {
|
|
echo json_encode(['success' => false, 'error' => 'Titel of event ID ontbreekt']);
|
|
exit;
|
|
}
|
|
|
|
if (!function_exists('microsoft_update_event')) {
|
|
echo json_encode(['success' => false, 'error' => 'Microsoft integratie niet beschikbaar']);
|
|
exit;
|
|
}
|
|
|
|
$updateData = [
|
|
'subject' => $subject,
|
|
'date' => $date,
|
|
'start_time' => $startTime,
|
|
'end_time' => $endTime,
|
|
];
|
|
if ($description !== '') {
|
|
$updateData['description'] = $description;
|
|
}
|
|
|
|
$ok = microsoft_update_event((int)$user['id'], $eventId, $updateData);
|
|
if ($ok) {
|
|
echo json_encode(['success' => true, 'date' => $date, 'message' => 'Afspraak succesvol bijgewerkt in Outlook']);
|
|
} else {
|
|
echo json_encode(['success' => false, 'error' => 'Kon afspraak niet bijwerken in Microsoft Outlook']);
|
|
}
|
|
exit;
|
|
}
|
|
|
|
echo json_encode(['error' => 'Onbekende actie']);
|
|
} catch (Exception $e) {
|
|
http_response_code(500);
|
|
echo json_encode(['error' => $e->getMessage()]);
|
|
}
|
|
|