0) ? date('Y-m-d', $ts) : null; } function parseNaturalDatetime(string $text): ?string { $text = trim($text); if (!$text) return null; $ts = strtotime($text); return ($ts !== false && $ts > 0) ? date('Y-m-d H:i:s', $ts) : null; } // ── Route ───────────────────────────────────────────────────────────────────── // $action from api.php: tasks | appointments | today | done | summary switch ($action) { // ── Tasks ───────────────────────────────────────────────────────────────── case 'tasks': if ($method === 'GET') { $status = $_GET['status'] ?? ''; $category = $_GET['category'] ?? ''; $where = '1=1'; $params = []; if ($status) { $where .= ' AND status=?'; $params[] = $status; } if ($category) { $where .= ' AND category=?'; $params[] = $category; } else { $where .= " AND status NOT IN ('done','cancelled')"; } $rows = JarvisDB::query( "SELECT * FROM tasks WHERE {$where} ORDER BY FIELD(priority,'urgent','high','normal','low'), due_date ASC, created_at DESC", $params ) ?? []; echo json_encode(['tasks' => $rows]); } elseif ($method === 'POST') { $id = (int)($data['id'] ?? 0); $title = trim($data['title'] ?? ''); $notes = trim($data['notes'] ?? ''); $category = $data['category'] ?? 'personal'; $priority = $data['priority'] ?? 'normal'; $status = $data['status'] ?? 'pending'; $due_date = parseNaturalDate($data['due_date'] ?? ''); $due_time = !empty($data['due_time']) ? $data['due_time'] : null; if (!$title) { echo json_encode(['error' => 'Title required']); exit; } if ($id) { JarvisDB::execute( 'UPDATE tasks SET title=?,notes=?,category=?,priority=?,status=?,due_date=?,due_time=?,updated_at=NOW() WHERE id=?', [$title,$notes,$category,$priority,$status,$due_date,$due_time,$id] ); echo json_encode(['success' => true, 'id' => $id]); } else { $newId = JarvisDB::insert( 'INSERT INTO tasks (title,notes,category,priority,due_date,due_time) VALUES (?,?,?,?,?,?)', [$title,$notes,$category,$priority,$due_date,$due_time] ); echo json_encode(['success' => true, 'id' => $newId]); } } elseif ($method === 'DELETE') { $id = (int)($_GET['id'] ?? 0); if ($id) { JarvisDB::execute('DELETE FROM tasks WHERE id=?', [$id]); } echo json_encode(['success' => true]); } break; // ── Mark task done ──────────────────────────────────────────────────────── case 'done': $id = (int)($data['id'] ?? $_GET['id'] ?? 0); if ($id) { JarvisDB::execute( "UPDATE tasks SET status='done', completed_at=NOW() WHERE id=?", [$id] ); } echo json_encode(['success' => true]); break; // ── Appointments ────────────────────────────────────────────────────────── case 'appointments': if ($method === 'GET') { $from = $_GET['from'] ?? date('Y-m-d'); $to = $_GET['to'] ?? date('Y-m-d', strtotime('+90 days')); $rows = JarvisDB::query( "SELECT * FROM appointments WHERE DATE(start_at) BETWEEN ? AND ? ORDER BY start_at ASC", [$from, $to] ) ?? []; echo json_encode(['appointments' => $rows]); } elseif ($method === 'POST') { $id = (int)($data['id'] ?? 0); $title = trim($data['title'] ?? ''); $description = trim($data['description'] ?? ''); $category = $data['category'] ?? 'personal'; $location = trim($data['location'] ?? ''); $all_day = (int)($data['all_day'] ?? 0); $reminder = (int)($data['reminder_min'] ?? 30); $start_raw = trim($data['start_at'] ?? ''); $end_raw = trim($data['end_at'] ?? ''); if (!$title || !$start_raw) { echo json_encode(['error' => 'Title and start time required']); exit; } $start_at = parseNaturalDatetime($start_raw) ?? date('Y-m-d H:i:s', strtotime($start_raw)); $end_at = $end_raw ? (parseNaturalDatetime($end_raw) ?? null) : null; if ($id) { JarvisDB::execute( 'UPDATE appointments SET title=?,description=?,category=?,start_at=?,end_at=?,location=?,all_day=?,reminder_min=?,alerted=0,updated_at=NOW() WHERE id=?', [$title,$description,$category,$start_at,$end_at,$location,$all_day,$reminder,$id] ); echo json_encode(['success' => true, 'id' => $id]); } else { $newId = JarvisDB::insert( 'INSERT INTO appointments (title,description,category,start_at,end_at,location,all_day,reminder_min) VALUES (?,?,?,?,?,?,?,?)', [$title,$description,$category,$start_at,$end_at,$location,$all_day,$reminder] ); echo json_encode(['success' => true, 'id' => $newId]); } } elseif ($method === 'DELETE') { $id = (int)($_GET['id'] ?? 0); if ($id) { JarvisDB::execute('DELETE FROM appointments WHERE id=?', [$id]); } echo json_encode(['success' => true]); } break; // ── Today / briefing summary ────────────────────────────────────────────── case 'today': default: $today = date('Y-m-d'); $tomorrow = date('Y-m-d', strtotime('+1 day')); $tasks_today = JarvisDB::query( "SELECT * FROM tasks WHERE due_date=? AND status NOT IN ('done','cancelled') ORDER BY FIELD(priority,'urgent','high','normal','low')", [$today] ) ?? []; $tasks_overdue = JarvisDB::query( "SELECT * FROM tasks WHERE due_date < ? AND status NOT IN ('done','cancelled') ORDER BY due_date ASC", [$today] ) ?? []; $tasks_pending = JarvisDB::query( "SELECT COUNT(*) cnt FROM tasks WHERE status='pending' AND (due_date IS NULL OR due_date >= ?)", [$today] ); $appts_today = JarvisDB::query( "SELECT * FROM appointments WHERE DATE(start_at)=? ORDER BY start_at ASC", [$today] ) ?? []; $appts_upcoming = JarvisDB::query( "SELECT * FROM appointments WHERE start_at > NOW() ORDER BY start_at ASC LIMIT 5", [] ) ?? []; echo json_encode([ 'date' => $today, 'tasks_today' => $tasks_today, 'tasks_overdue' => $tasks_overdue, 'pending_count' => (int)($tasks_pending[0]['cnt'] ?? 0), 'appts_today' => $appts_today, 'appts_upcoming' => $appts_upcoming, ]); break; }