: une tâche active. * GET (sans id) : toutes les tâches actives, triées par urgence. * POST : création. Tous les champs sont saisissables dès la * création, y compris les champs de réalisation (TDL_RealDate/ * TDL_RealUser/TDL_RealText) — permet de journaliser une tâche déjà * faite, pour en garder une trace (demande explicite de Jean-Marc). * TDL_ToDoDate est initialisée au jour de saisie par défaut (colonne * DEFAULT CURRENT_TIMESTAMP, laissée à MySQL si absente du corps de la * requête), mais reste éditable : Jean-Marc doit pouvoir noter * aujourd'hui une tâche oubliée en la datant rétroactivement à son vrai * jour d'apparition, pour qu'elle se classe correctement (voir le tri du * groupe rouge ci-dessous). TDL_ToDoUser est saisi par le client mais * retombe sur l'utilisateur de la session si absent. * PUT ?id= : modification, tous les champs sont modifiables * (y compris TDL_ToDoDate et les champs de réalisation — c'est ainsi * qu'une tâche se marque "réalisée", pas d'action contextuelle séparée * pour ça). TDL_ToDoDate n'est écrasée que si explicitement fournie * (jamais remise à vide). * DELETE ?id= : suppression logique (TDL_Actif = 0). Contrairement * à la suppression d'un contact, aucun commentaire n'est demandé ici : * ASP_ToDoList n'a pas d'équivalent d'ASP_Realisations pour journaliser * un commentaire de suppression avec horodatage/auteur — l'ajouter * obligerait à inventer une table d'audit dédiée pour ce seul usage, * non demandé à ce stade (voir journal, principe "pas de * sur-ingénierie"). * * Session utilisateur : accès réservé aux utilisateurs identifiés, même * principe que contacts.php/realisations.php. */ session_start(); header('Content-Type: application/json; charset=utf-8'); require __DIR__ . '/db.php'; if (empty($_SESSION['USR_Code'])) { http_response_code(401); echo json_encode(['error' => 'Non identifié — veuillez vous connecter.']); exit; } $method = $_SERVER['REQUEST_METHOD']; try { $pdo = getDbConnection(); } catch (PDOException $e) { http_response_code(500); echo json_encode(['error' => 'Connexion base de données impossible']); exit; } /** * Calcule le groupe d'urgence (0 rouge / 1 orange / 2 vert) d'une tâche. */ function calculerGroupe(array $t, string $aujourdhui): int { if ($t['TDL_RealDate'] !== null) { return 2; } if ($t['TDL_ToDoDateSouhaitée'] === null || $t['TDL_ToDoDateSouhaitée'] < $aujourdhui) { return 0; } return 1; } /** * Compare deux dates (chaînes "YYYY-MM-DD" ou null), null se classant avant * toute date — utilisé pour le sous-tri du groupe rouge (une tâche sans * échéance passe avant une tâche en retard avec échéance connue). */ function comparerDateNullEnPremier(?string $a, ?string $b): int { if ($a === $b) return 0; if ($a === null) return -1; if ($b === null) return 1; return strcmp($a, $b); } /** * Capacité maximale de post-its "en attente" affichables sur le tableau de * liège de l'écran de fond, par attributaire (voir journal de développement, * section 28 — "bureau à l'ancienne"). Ajoutée le 2 septembre 2026, en amont * du code réel du tableau : Jean-Marc a posé le principe avant d'avoir les * pixels pour le calculer précisément ("mets-le où tu veux, il faudra le * faire évoluer") — valeur ronde provisoire, à corriger dès que la taille * réelle de la zone et celle d'un post-it seront posées à l'écran. * * Choix de principe explicite de Jean-Marc : PAS d'ascenseur/défilement sur * le tableau, même si ça permettrait d'accepter plus de post-its — un vrai * bureau a une capacité finie, et c'est justement cette contrainte physique * qui pousse à traiter ses tâches plutôt qu'à les empiler indéfiniment. Une * fois cette capacité atteinte, la création est refusée avec un message * clair (voir POST ci-dessous) plutôt que de trouver un moyen d'entasser * plus de post-its ou de faire défiler le tableau. */ const CAPACITE_TABLEAU_PAR_UTILISATEUR = 20; try { switch ($method) { case 'GET': if (isset($_GET['id'])) { $stmt = $pdo->prepare('SELECT * FROM ASP_ToDoList WHERE TDL_Incr = ? AND TDL_Actif = 1'); $stmt->execute([$_GET['id']]); $tache = $stmt->fetch(); if (!$tache) { http_response_code(404); echo json_encode(['error' => 'Tâche introuvable']); exit; } echo json_encode($tache); exit; } $stmt = $pdo->query('SELECT * FROM ASP_ToDoList WHERE TDL_Actif = 1'); $taches = $stmt->fetchAll(); $aujourdhui = date('Y-m-d'); foreach ($taches as &$t) { $t['TDL_Groupe'] = calculerGroupe($t, $aujourdhui); } unset($t); usort($taches, function ($a, $b) { if ($a['TDL_Groupe'] !== $b['TDL_Groupe']) { return $a['TDL_Groupe'] <=> $b['TDL_Groupe']; } if ($a['TDL_Groupe'] === 0) { $cmp = comparerDateNullEnPremier($a['TDL_ToDoDateSouhaitée'], $b['TDL_ToDoDateSouhaitée']); if ($cmp !== 0) return $cmp; // À échéance égale (souvent : aucune échéance des deux // côtés), la tâche notée le plus tôt remonte en premier. return strcmp((string)$a['TDL_ToDoDate'], (string)$b['TDL_ToDoDate']); } if ($a['TDL_Groupe'] === 1) { $cmp = strcmp((string)$a['TDL_ToDoDateSouhaitée'], (string)$b['TDL_ToDoDateSouhaitée']); if ($cmp !== 0) return $cmp; return strcmp((string)$a['TDL_ToDoDate'], (string)$b['TDL_ToDoDate']); } // Groupe 2 (vert) : réalisation la plus récente en premier. return strcmp((string)$b['TDL_RealDate'], (string)$a['TDL_RealDate']); }); echo json_encode(['taches' => $taches, 'total' => count($taches)]); break; case 'POST': $data = json_decode(file_get_contents('php://input'), true) ?? []; $texte = trim($data['TDL_ToDoTxt'] ?? ''); if ($texte === '') { http_response_code(400); echo json_encode(['error' => 'Le texte de la tâche est obligatoire']); exit; } $todoUser = trim($data['TDL_ToDoUser'] ?? ''); if ($todoUser === '') { $todoUser = $_SESSION['USR_Code']; } // Capacité du tableau (voir CAPACITE_TABLEAU_PAR_UTILISATEUR // ci-dessus) : ne s'applique qu'aux tâches qui vont réellement // occuper un post-it "en attente" sur le bureau de $todoUser. // Une tâche créée déjà réalisée (TDL_RealDate fourni dès la // création, voir doc d'en-tête — permet de journaliser une tâche // déjà faite) n'occupe jamais de place sur le tableau, donc // n'est jamais bloquée par cette limite. if (empty($data['TDL_RealDate'])) { $stmtCompte = $pdo->prepare( 'SELECT COUNT(*) FROM ASP_ToDoList WHERE TDL_Actif = 1 AND TDL_ToDoUser = ? AND TDL_RealDate IS NULL' ); $stmtCompte->execute([$todoUser]); if ((int)$stmtCompte->fetchColumn() >= CAPACITE_TABLEAU_PAR_UTILISATEUR) { http_response_code(409); echo json_encode(['error' => 'Ton tableau est plein, tu ne peux plus ajouter de post-it !']); exit; } } // TDL_ToDoDate : incluse dans l'INSERT seulement si fournie par le // client (permet de dater rétroactivement une tâche oubliée) — // sinon on laisse MySQL appliquer DEFAULT CURRENT_TIMESTAMP // (aujourd'hui), plutôt que de dupliquer cette valeur par défaut // ici en PHP. $colonnes = ['TDL_ToDoUser', 'TDL_ToDoTxt', 'TDL_ToDoDateSouhaitée', 'TDL_RealDate', 'TDL_RealUser', 'TDL_RealText']; $params = [ $todoUser, $texte, $data['TDL_ToDoDateSouhaitée'] ?? null ?: null, $data['TDL_RealDate'] ?? null ?: null, $data['TDL_RealUser'] ?? null ?: null, $data['TDL_RealText'] ?? null ?: null, ]; if (!empty($data['TDL_ToDoDate'])) { $colonnes[] = 'TDL_ToDoDate'; $params[] = $data['TDL_ToDoDate']; } $placeholders = array_fill(0, count($colonnes), '?'); $stmt = $pdo->prepare( 'INSERT INTO ASP_ToDoList (' . implode(', ', $colonnes) . ') VALUES (' . implode(', ', $placeholders) . ')' ); $stmt->execute($params); echo json_encode(['id' => (int) $pdo->lastInsertId()]); break; case 'PUT': if (!isset($_GET['id'])) { http_response_code(400); echo json_encode(['error' => 'id manquant']); exit; } $data = json_decode(file_get_contents('php://input'), true) ?? []; $texte = trim($data['TDL_ToDoTxt'] ?? ''); if ($texte === '') { http_response_code(400); echo json_encode(['error' => 'Le texte de la tâche est obligatoire']); exit; } // Vérification d'existence séparée de l'UPDATE (même précaution // que contacts.php : rowCount() ne reflète que les lignes // réellement modifiées, pas les lignes trouvées par le WHERE). $verif = $pdo->prepare('SELECT TDL_Incr FROM ASP_ToDoList WHERE TDL_Incr = ? AND TDL_Actif = 1'); $verif->execute([$_GET['id']]); if (!$verif->fetch()) { http_response_code(404); echo json_encode(['error' => 'Tâche introuvable ou déjà désactivée']); exit; } $todoUser = trim($data['TDL_ToDoUser'] ?? ''); if ($todoUser === '') { $todoUser = $_SESSION['USR_Code']; } $assignations = ['TDL_ToDoUser = ?', 'TDL_ToDoTxt = ?', 'TDL_ToDoDateSouhaitée = ?', 'TDL_RealDate = ?', 'TDL_RealUser = ?', 'TDL_RealText = ?']; $params = [ $todoUser, $texte, $data['TDL_ToDoDateSouhaitée'] ?? null ?: null, $data['TDL_RealDate'] ?? null ?: null, $data['TDL_RealUser'] ?? null ?: null, $data['TDL_RealText'] ?? null ?: null, ]; // TDL_ToDoDate : n'est écrasée que si explicitement fournie — // jamais remise à vide (contrairement aux autres champs, une date // de création n'a pas de sens "effacée"). if (!empty($data['TDL_ToDoDate'])) { $assignations[] = 'TDL_ToDoDate = ?'; $params[] = $data['TDL_ToDoDate']; } $params[] = $_GET['id']; $stmt = $pdo->prepare( 'UPDATE ASP_ToDoList SET ' . implode(', ', $assignations) . ' WHERE TDL_Incr = ? AND TDL_Actif = 1' ); $stmt->execute($params); echo json_encode(['success' => true]); break; case 'DELETE': if (!isset($_GET['id'])) { http_response_code(400); echo json_encode(['error' => 'id manquant']); exit; } $stmt = $pdo->prepare('UPDATE ASP_ToDoList SET TDL_Actif = 0 WHERE TDL_Incr = ? AND TDL_Actif = 1'); $stmt->execute([$_GET['id']]); if ($stmt->rowCount() === 0) { http_response_code(404); echo json_encode(['error' => 'Tâche introuvable ou déjà désactivée']); exit; } echo json_encode(['success' => true]); break; default: http_response_code(405); echo json_encode(['error' => 'Méthode non autorisée']); } } catch (PDOException $e) { http_response_code(500); echo json_encode(['error' => 'Erreur base de données : ' . $e->getMessage()]); }