// GET /api/tasks/upcoming — Top 3 upcoming tasks per user (for Dashboard) #include "cJSON.h" #include "esp_http_server.h" #include "todo.hpp" #include "types.hpp" internal esp_err_t api_tasks_upcoming_handler(httpd_req_t *req) { httpd_resp_set_type(req, "application/json"); httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); cJSON *root = cJSON_CreateObject(); cJSON *users_arr = cJSON_AddArrayToObject(root, "users"); for (int u = 0; u < MAX_USERS; u++) { if (!g_Users[u].active) continue; // Collect incomplete tasks for this user task_t *user_tasks[MAX_TASKS]; int count = 0; for (int t = 0; t < MAX_TASKS; t++) { if (g_Tasks[t].active && g_Tasks[t].user_id == g_Users[u].id && !g_Tasks[t].completed) { user_tasks[count++] = &g_Tasks[t]; } } // Sort by due_date ascending sort_tasks_by_due_date(user_tasks, count); // Build user object with top 3 cJSON *user_obj = cJSON_CreateObject(); cJSON_AddNumberToObject(user_obj, "id", g_Users[u].id); cJSON_AddStringToObject(user_obj, "name", g_Users[u].name); cJSON *tasks_arr = cJSON_AddArrayToObject(user_obj, "tasks"); int limit = count < 3 ? count : 3; for (int i = 0; i < limit; i++) { cJSON *t_obj = cJSON_CreateObject(); cJSON_AddNumberToObject(t_obj, "id", user_tasks[i]->id); cJSON_AddStringToObject(t_obj, "title", user_tasks[i]->title); cJSON_AddNumberToObject(t_obj, "due_date", (double)user_tasks[i]->due_date); cJSON_AddBoolToObject(t_obj, "completed", user_tasks[i]->completed); cJSON_AddItemToArray(tasks_arr, t_obj); } cJSON_AddItemToArray(users_arr, user_obj); } const char *json = cJSON_PrintUnformatted(root); httpd_resp_sendstr(req, json); free((void *)json); cJSON_Delete(root); return ESP_OK; } internal const httpd_uri_t api_tasks_upcoming_uri = { .uri = "/api/tasks/upcoming", .method = HTTP_GET, .handler = api_tasks_upcoming_handler, .user_ctx = NULL};