369 lines
14 KiB
PHP
369 lines
14 KiB
PHP
<?php
|
|
/**
|
|
* AJAX API voor Dagplanning & Taken
|
|
*/
|
|
|
|
require_once __DIR__ . '/../includes/auth.php';
|
|
require_once __DIR__ . '/../includes/integrations/microsoft.php';
|
|
$user = require_auth();
|
|
$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;
|
|
}
|
|
|
|
// 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;
|
|
$durationMinutes = (int)($data['duration_minutes'] ?? 30);
|
|
if ($durationMinutes <= 0) $durationMinutes = 30;
|
|
$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;
|
|
}
|
|
|
|
// 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;
|
|
$timeBlockEnd = null;
|
|
$outlookSyncError = null;
|
|
|
|
// Als synchronisatie naar Outlook gewenst is (of source is OUTLOOK) en nog geen extern ID heeft
|
|
if (($syncOutlook || $source === 'OUTLOOK') && empty($externalId)) {
|
|
// Als er geen starttijd is ingevuld maar wel sync gewenst is, gebruik 09:00 als veilige standaard
|
|
if (empty($timeBlockStart)) {
|
|
$timeBlockStart = '09:00';
|
|
}
|
|
|
|
$msResult = microsoft_create_event($user['id'], [
|
|
'subject' => $title,
|
|
'description' => "Gepland via Fociz Dagplanning ({$priority})",
|
|
'date' => $date,
|
|
'start_time' => $timeBlockStart,
|
|
'duration_minutes' => $durationMinutes,
|
|
]);
|
|
|
|
if (!empty($msResult['success']) && !empty($msResult['event_id'])) {
|
|
$source = 'OUTLOOK';
|
|
$externalId = $msResult['event_id'];
|
|
$externalUrl = $msResult['web_link'] ?? 'https://outlook.office.com';
|
|
$timeBlockStart = $msResult['start_time'] ?? $timeBlockStart;
|
|
$timeBlockEnd = $msResult['end_time'] ?? null;
|
|
} else {
|
|
$outlookSyncError = $msResult['error'] ?? 'Kon afspraak niet aanmaken in Microsoft Outlook';
|
|
}
|
|
}
|
|
|
|
// Bereken timeBlockEnd indien nog niet gezet maar wel starttijd bekend
|
|
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
|
|
}
|
|
}
|
|
|
|
$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;
|
|
}
|
|
|
|
// 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');
|
|
$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,
|
|
'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;
|
|
}
|
|
|
|
echo json_encode(['error' => 'Onbekende actie']);
|
|
} catch (Exception $e) {
|
|
http_response_code(500);
|
|
echo json_encode(['error' => $e->getMessage()]);
|
|
}
|
|
|