mirror of
https://github.com/JMP-MS/ASPIC.git
synced 2026-10-27 14:57:09 +00:00
319 lines
14 KiB
PHP
319 lines
14 KiB
PHP
<?php
|
|
/**
|
|
* ASPIC — todolist.php
|
|
* API CRUD sur ASP_ToDoList (GET liste triée par urgence + fiche / POST
|
|
* création / PUT modification / DELETE suppression logique).
|
|
*
|
|
* Affichage volontairement non filtré par utilisateur (voir journal de
|
|
* développement) : un agent IA pourrait ajouter une tâche pour un autre
|
|
* utilisateur, même en usage mono-utilisateur — toutes les tâches actives
|
|
* sont donc renvoyées, quel que soit TDL_ToDoUser.
|
|
*
|
|
* Tri par urgence (calculé ici plutôt qu'en SQL, pour rester lisible — voir
|
|
* journal) : trois groupes, dans cet ordre —
|
|
* 0 = rouge : non réalisée, sans date de réalisation souhaitée OU date
|
|
* dépassée ("à faire tout de suite" et "en retard" partagent
|
|
* le même groupe — une tâche sans échéance est jugée aussi
|
|
* urgente qu'une tâche en retard, décision de Jean-Marc).
|
|
* 1 = orange : non réalisée, date de réalisation souhaitée renseignée et
|
|
* pas encore dépassée.
|
|
* 2 = vert : réalisée (TDL_RealDate renseignée), qu'elle ait été faite
|
|
* en retard ou non.
|
|
* Sous-tri : dans le groupe rouge, TDL_ToDoDateSouhaitée croissante avec les
|
|
* tâches sans date en tête (NULL trié avant toute date, comportement natif
|
|
* de la comparaison PHP utilisé ici), puis TDL_ToDoDate croissante en cas
|
|
* d'égalité (le cas le plus courant : deux tâches "à faire tout de suite",
|
|
* donc sans date souhaitée toutes les deux — la plus anciennement notée
|
|
* remonte en premier) ; dans le groupe orange, échéance la plus proche en
|
|
* premier, TDL_ToDoDate en second critère ; dans le groupe vert,
|
|
* réalisation la plus récente en premier.
|
|
*
|
|
* GET ?id=<TDL_Incr> : 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=<TDL_Incr> : 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=<TDL_Incr> : 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()]);
|
|
}
|