diff --git a/consignes_fonctions.php b/consignes_fonctions.php index 28d202f..3f841f9 100644 --- a/consignes_fonctions.php +++ b/consignes_fonctions.php @@ -22,6 +22,14 @@ * retourne alors ce même type de tableau (valeurs mises à jour, mêmes clés), * un Test retourne toujours un simple bool. Voir * consigne_completer_code_postal() ci-dessous pour un exemple. + * - Test à UN champ avec accès base (8 septembre 2026) : une fonction de Test + * qui a besoin d'interroger la base (ex. vérifier qu'une valeur n'est pas + * déjà utilisée ailleurs) peut déclarer deux paramètres supplémentaires, + * dans cet ordre : (string $valeur, PDO $pdo, ?int $idExclusion): bool — le + * moteur les fournit systématiquement à tout Test à un champ (voir + * moteur_consignes.php), $idExclusion étant l'identifiant de la fiche en + * cours de modification (null en Création). Voir consigne_mail_unique() et + * consigne_portable_unique() ci-dessous, premier cas réel. * - Jumeau "_seraitDeclenchee" (7 septembre 2026, soir) : toute fonction de * Traitement qui interroge un service externe (IA) et porte * FON_VisuelAttenteIA = 'O' DOIT avoir, en plus, une fonction nommée @@ -34,15 +42,20 @@ * qu'interroge consignes_apercu.php pour piloter le badge IA générique côté * écran, voir ce fichier et moteur_consignes.php * (auMoinsUneConsigneIaSeraitDeclenchee()). - * Fichier volontairement sans dépendance à la session ni à la base de - * données : chaque fonction ne fait que transformer ou tester une ou - * plusieurs valeurs qu'on lui passe. Exception depuis le 7 septembre 2026 - * (soir) : une fonction peut interroger un service externe (API d'IA, voir - * consigne_completer_code_postal()) quand la consigne elle-même l'exige - * explicitement — jamais la session ni la base ASPIC, seulement un appel - * réseau sortant, et toujours avec un repli silencieux (valeurs inchangées) - * en cas d'échec : un Traitement ne doit jamais faire échouer un - * enregistrement à cause d'un service tiers indisponible. + * Fichier volontairement sans dépendance à la session : chaque fonction ne + * fait que transformer ou tester une ou plusieurs valeurs qu'on lui passe (et, + * depuis le 8 septembre 2026, éventuellement interroger $pdo — voir ci-dessus + * — mais jamais $_SESSION). Deux exceptions à la dépendance base/réseau : + * - Depuis le 7 septembre 2026 (soir) : une fonction de Traitement peut + * interroger un service externe (API d'IA, voir + * consigne_completer_code_postal()) quand la consigne elle-même l'exige + * explicitement, toujours avec un repli silencieux (valeurs inchangées) en + * cas d'échec : un Traitement ne doit jamais faire échouer un + * enregistrement à cause d'un service tiers indisponible. + * - Depuis le 8 septembre 2026 : une fonction de Test à un champ peut + * interroger $pdo, reçu directement du moteur (voir "Test à UN champ avec + * accès base" ci-dessus) — jamais de connexion ouverte ici, toujours celle + * déjà établie par l'appelant (db.php). */ // ASP_Fonctions.FON_Nom = 'MAJUSCULE' (Nature=Traitement) @@ -104,6 +117,21 @@ function consigne_mail_valide(string $valeur): bool return filter_var($valeur, FILTER_VALIDATE_EMAIL) !== false; } +// ASP_Fonctions.FON_Nom = 'RETIRER_POINTS' (Nature=Traitement), 8 septembre +// 2026. Retire tous les points (".") d'une valeur, sans autre transformation +// — demandée pour un numéro de téléphone saisi avec des points comme +// séparateurs (ex. "02.98.12.34.56" -> "0298123456"). Générique et +// réutilisable tel quel sur n'importe quel champ texte, même principe que +// SANS_CHIFFRE ci-dessus : aucune fonction par champ, juste une ligne dans +// ASP_Consignes par champ concerné (CNT_Tel et/ou CNT_Portable). Volontairement +// limitée aux points, sur la demande telle que formulée par Jean-Marc — à +// étendre (espaces, tirets...) le jour où un besoin réel et concret se +// présentera, pas par anticipation. +function consigne_retirer_points(string $valeur): string +{ + return str_replace('.', '', $valeur); +} + // ASP_Fonctions.FON_Nom = 'SANS_CHIFFRE' (Nature=Test), 7 septembre 2026. // Vrai si la valeur ne contient aucun chiffre, faux sinon — champ facultatif // (une valeur vide est conforme, même principe que consigne_mail_valide() : @@ -140,10 +168,15 @@ function consigne_sans_chiffre(string $valeur): bool // (ex. arrondissements de Paris/Lyon/Marseille) doit renvoyer null plutôt que // de deviner — c'est écrit explicitement dans la consigne envoyée au modèle. // -// Toute défaillance (clé absente, réseau, quota, réponse inattendue) se -// traduit par un retour silencieux des valeurs inchangées : un Traitement ne -// doit jamais bloquer l'enregistrement d'un contact à cause d'un service -// externe (voir commentaire d'en-tête de moteur_consignes.php) — contrairement +// Toute défaillance (clé absente, réseau, quota, réponse inattendue) laisse +// toujours CNT_AdrCP inchangé — un Traitement ne doit jamais bloquer +// l'enregistrement d'un contact à cause d'un service externe (voir +// commentaire d'en-tête de moteur_consignes.php) — mais n'est PLUS silencieuse +// pour autant depuis le 8 septembre 2026 (correctif demandé par Jean-Marc, +// constat d'un échec resté invisible sur une ville inconnue) : chaque cas +// d'échec pose désormais un "_avertissement" pour l'écran, et un +// "_realisation" quand un appel a réellement été tenté (voir le corps de la +// fonction ci-dessous) — contrairement // à un Test, dont l'échec est lui affiché à l'utilisateur. // Jumeau "_seraitDeclenchee" (voir en-tête de fichier) : réplique à elle // seule la condition de déclenchement de consigne_completer_code_postal() @@ -169,6 +202,17 @@ function consigne_completer_code_postal(array $valeurs): array $apiKey = trim((string) getenv('OPENAI_API_KEY')); if (!$apiKey) { + // Panne de configuration (8 septembre 2026) : jusqu'ici un retour + // totalement silencieux — repéré par Jean-Marc comme le même défaut + // que celui déjà corrigé le 7 septembre pour une ville ambiguë + // ("un constat d'échec doit toujours être visible, jamais un simple + // silence"). Pas de "_realisation" ici : aucun appel réseau n'a + // réellement eu lieu, donc rien à journaliser comme un coût engagé + // (voir en-tête de moteur_consignes.php) — seulement l'avertissement + // écran, pour que l'utilisateur ne reste pas devant un champ vide + // sans explication. + $valeurs['_avertissement'] = "Code postal non déterminé pour « $ville » " + . "(service indisponible). Saisissez le code postal vous-même."; return $valeurs; } @@ -226,6 +270,18 @@ function consigne_completer_code_postal(array $valeurs): array curl_close($ch); if ($reponseBrute === false || $codeHttp !== 200) { + // Échec réseau/HTTP (8 septembre 2026) : contrairement au cas + // "clé API absente" ci-dessus, un appel a bien été tenté (et peut + // avoir un coût, selon où l'échec survient côté fournisseur) — donc + // journalisé comme le cas "ville ambiguë" plus bas, sur le même + // principe posé le 7 septembre par Jean-Marc ("un appel fait à l'IA + // [doit laisser] une trace dans les réalisations"). + $valeurs['_realisation'] = [ + 'action' => 'IA_CP', + 'commentaire' => "Échec technique de l'appel IA pour la ville « $ville » (HTTP $codeHttp).", + ]; + $valeurs['_avertissement'] = "Code postal non déterminé pour « $ville » " + . "(service temporairement indisponible). Saisissez le code postal vous-même."; return $valeurs; } @@ -289,3 +345,64 @@ function consigne_completer_code_postal(array $valeurs): array return $valeurs; } + +// ASP_Fonctions.FON_Nom = 'MAIL_UNIQUE' (Nature=Test), 8 septembre 2026. +// Vrai si l'adresse mail n'est pas déjà utilisée par un AUTRE contact actif, +// faux sinon. Champ facultatif, même principe que consigne_mail_valide() +// ci-dessus : une valeur vide est toujours conforme (deux fiches sans mail ne +// sont pas en conflit) — le caractère obligatoire reste la responsabilité +// d'une consigne NON_VIDE séparée, combinable sur le même champ. +// +// Fonction dédiée à CNT_Mail plutôt qu'un test générique "valeur unique" +// paramétrable par nom de champ (demande explicite de Jean-Marc, 8 septembre +// 2026) : contrairement à SANS_CHIFFRE ci-dessus, volontairement générique +// parce que ça a du sens sur Nom/Prénom/Ville indifféremment, une contrainte +// d'unicité n'a de sens QUE sur certains champs précis — l'ouvrir à un +// paramétrage libre depuis ASP_Consignes exposerait un futur opérateur du +// catalogue à l'appliquer par erreur à un champ comme Prénom, où deux +// contacts homonymes sont parfaitement normaux. Voir consigne_portable_unique() +// ci-dessous, jumelle sur CNT_Portable — toutes deux appuyées sur le même +// utilitaire privé valeurDejaUtilisee() pour ne pas dupliquer la requête SQL. +function consigne_mail_unique(string $valeur, PDO $pdo, ?int $idExclusion = null): bool +{ + return !valeurDejaUtilisee($pdo, 'CNT_Mail', $valeur, $idExclusion); +} + +// ASP_Fonctions.FON_Nom = 'PORTABLE_UNIQUE' (Nature=Test), 8 septembre 2026. +// Même principe que consigne_mail_unique() ci-dessus, appliqué à +// CNT_Portable — fonction séparée plutôt qu'un simple paramétrage de la même +// fonction sur un autre champ, pour la même raison (voir commentaire de +// consigne_mail_unique()). +function consigne_portable_unique(string $valeur, PDO $pdo, ?int $idExclusion = null): bool +{ + return !valeurDejaUtilisee($pdo, 'CNT_Portable', $valeur, $idExclusion); +} + +// Utilitaire privé partagé par consigne_mail_unique() et +// consigne_portable_unique() ci-dessus — pas une fonction du catalogue +// (aucun ASP_Fonctions.FON_NomPHP ne pointe dessus, jamais appelée depuis +// ASP_Consignes) : factorise uniquement la requête SQL commune aux deux. +// $champ vient toujours d'une constante écrite en dur dans l'une des deux +// fonctions ci-dessus, jamais d'une donnée utilisateur — pas de risque +// d'injection SQL malgré l'interpolation dans le nom de colonne. +// $idExclusion (identifiant du contact en cours de modification, voir +// moteur_consignes.php) permet à une fiche de conserver sa propre valeur sans +// se bloquer elle-même ; toujours null en Création, où rien n'existe encore à +// exclure. +function valeurDejaUtilisee(PDO $pdo, string $champ, string $valeur, ?int $idExclusion): bool +{ + $valeur = trim($valeur); + if ($valeur === '') return false; + + $sql = "SELECT 1 FROM ASP_Contacts WHERE $champ = ? AND CNT_Actif = 1"; + $params = [$valeur]; + if ($idExclusion !== null) { + $sql .= ' AND CNT_Incr != ?'; + $params[] = $idExclusion; + } + $sql .= ' LIMIT 1'; + + $stmt = $pdo->prepare($sql); + $stmt->execute($params); + return (bool)$stmt->fetchColumn(); +} diff --git a/contacts.php b/contacts.php index 0230885..078c942 100644 --- a/contacts.php +++ b/contacts.php @@ -321,7 +321,15 @@ try { } $data = json_decode(file_get_contents('php://input'), true) ?? []; - $erreurConsigne = appliquerConsignes($pdo, 'Contacts', 'Modification', $data, $agentConsigne, $journalisationsIA, $avertissementsIA); + // Dernier paramètre (8 septembre 2026) : identifiant du contact + // en cours de modification, transmis pour qu'une consigne + // d'unicité (ex. mail/portable déjà utilisé, voir + // consignes_fonctions.php) puisse exclure cette fiche elle-même + // de sa recherche — sans quoi ré-enregistrer un contact sans + // changer son mail se bloquerait lui-même. Absent en POST + // ci-dessus : en Création, aucun contact n'existe encore à + // exclure. + $erreurConsigne = appliquerConsignes($pdo, 'Contacts', 'Modification', $data, $agentConsigne, $journalisationsIA, $avertissementsIA, (int)$_GET['id']); if ($erreurConsigne !== null) { http_response_code(400); echo json_encode(['error' => $erreurConsigne, 'agent' => $agentConsigne]); diff --git a/ecran_fond.html b/ecran_fond.html index 05cd8f6..b5532d9 100644 --- a/ecran_fond.html +++ b/ecran_fond.html @@ -651,9 +651,10 @@ Créé par Mevelia Solutions"> // inchangés (silhouette identique, seule la position verticale a bougé). const MICRO_ZONE_X1 = 478, MICRO_ZONE_X2 = 549; const MICRO_ZONE_Y1 = 271, MICRO_ZONE_Y2 = 393; - // X décalé légèrement à droite le 8 septembre 2026 (soir), retour de - // Jean-Marc — Y inchangé (remonté juste avant, jugé bon depuis). - const ICONE_CENTRE_X = 522, ICONE_CENTRE_Y = 288; + // X ramené vers la gauche le 8 septembre 2026 (retour de Jean-Marc après + // essai réel : le décalage à droite du 8 septembre précédent allait trop + // loin) — Y inchangé (remonté juste avant, jugé bon depuis). + const ICONE_CENTRE_X = 515, ICONE_CENTRE_Y = 288; const ICONE_TAILLE_FRAC = 22 / IMG_W; // Pastille "nombre de contacts" — remesurée le 8 septembre 2026 sur le // classeur Contacts du nouvel avant-plan (2ᵉ classeur, labelCx=101). diff --git a/journal/ASPIC - Journal. Index.md b/journal/ASPIC - Journal. Index.md index 0d8850e..f2a8bc2 100644 --- a/journal/ASPIC - Journal. Index.md +++ b/journal/ASPIC - Journal. Index.md @@ -59,7 +59,7 @@ Pour les trois documents de ce projet (ce journal — résumé/index —, les R - 29. Interprétation vocale des contacts par LLM - 30. Chantier des consignes — moteur générique -*(Numérotation et titres repris du découpage fait par Jean-Marc le 31 août 2026, légèrement reformulés par lui à cette occasion — se référer aux fichiers eux-mêmes pour le libellé exact. Section 27 (Pilotage vocal) validée de bout en bout et son fichier définitif déposé dans `/journal` le 2 septembre 2026. Section 28 (Écran de fond) ouverte le 2 septembre 2026, toujours **en cours** — passée d'une maquette graphique exploratoire à un premier essai fonctionnel réel, puis (soirée du 2 au 3 septembre) à une pastille de comptage sur le classeur Contacts et à un premier affichage réel des post-its personnels sur le tableau de liège, puis (8 septembre 2026, matin) à un bureau à trois fonds interchangeables (liège actuel, futur écran assistants, futur tableau de bord) navigables par deux flèches, avant-plan désormais séparé du code des fonds — fichier de section mis à jour ce jour-là, à déposer par Jean-Marc, voir Statut actuel. Section 29 (Interprétation vocale des contacts par LLM) ouverte et son fichier définitif déposé le 4 septembre 2026 après validation de bout en bout sur un premier corpus réel ; fichiers de section à déposer par Jean-Marc, voir Statut actuel. Section 30 (Chantier des consignes — moteur générique) ouverte le 6 septembre 2026, fichier définitif rédigé le soir même après validation de bout en bout du mécanisme sur des cas réels ; **substantiellement complétée le 7 septembre 2026** (hiérarchisation cohérence/Tests/IA, traçabilité de l'agent, `REA_NomAffiche`, premier appel IA réel dans une consigne avec avertissement bloquant) — fichier de section mis à jour à déposer par Jean-Marc, voir Statut actuel.)* +*(Numérotation et titres repris du découpage fait par Jean-Marc le 31 août 2026, légèrement reformulés par lui à cette occasion — se référer aux fichiers eux-mêmes pour le libellé exact. Section 27 (Pilotage vocal) validée de bout en bout et son fichier définitif déposé dans `/journal` le 2 septembre 2026. Section 28 (Écran de fond) ouverte le 2 septembre 2026, toujours **en cours** — passée d'une maquette graphique exploratoire à un premier essai fonctionnel réel, puis (soirée du 2 au 3 septembre) à une pastille de comptage sur le classeur Contacts et à un premier affichage réel des post-its personnels sur le tableau de liège, puis (8 septembre 2026, matin) à un bureau à trois fonds interchangeables (liège actuel, futur écran assistants, futur tableau de bord) navigables par deux flèches, avant-plan désormais séparé du code des fonds — fichier de section mis à jour ce jour-là, à déposer par Jean-Marc, voir Statut actuel. Section 29 (Interprétation vocale des contacts par LLM) ouverte et son fichier définitif déposé le 4 septembre 2026 après validation de bout en bout sur un premier corpus réel ; fichiers de section à déposer par Jean-Marc, voir Statut actuel. Section 30 (Chantier des consignes — moteur générique) ouverte le 6 septembre 2026, fichier définitif rédigé le soir même après validation de bout en bout du mécanisme sur des cas réels ; **substantiellement complétée le 7 septembre 2026** (hiérarchisation cohérence/Tests/IA, traçabilité de l'agent, `REA_NomAffiche`, premier appel IA réel dans une consigne avec avertissement bloquant) ; **complétée à nouveau le 8 septembre 2026** (unicité mail/portable, retrait des points sur les numéros, échecs de `consigne_completer_code_postal()` rendus non-silencieux) — fichier de section mis à jour à déposer par Jean-Marc, voir Statut actuel.)* ## Statut actuel (2-8 septembre 2026) @@ -107,12 +107,26 @@ Version incrémentée une seule fois pour l'ensemble de cette session (pastille **8 septembre 2026 (matin) : écran de fond — trois fonds interchangeables derrière un avant-plan désormais séparé du décor (V1.16.18).** Détail complet en section 28 du journal. Résumé : Jean-Marc a constaté que l'écran de fond, limité à un seul décor (le tableau de liège), ne suffira pas à tout gérer, et a posé le principe de trois fonds navigables par deux flèches, derrière un même avant-plan (bureau, classeurs, micro, carnet, téléphone) qui ne change jamais — l'écran 1 restant l'actuel tableau de liège, l'écran 2 un futur écran "assistants" (fond en tasseaux de bois gris, présentation encore à concevoir), l'écran 3 un futur tableau de bord (fond tableau blanc). Décisions actées avant codage : code de l'avant-plan et code des fonds séparés ; les trois fonds codés en dur plutôt qu'en liste extensible ("il faut s'autoriser à coder ça en dur") ; animation de glissement au changement de fond, dont le sens dépend de la flèche pressée (l'écran n+1 entre par la droite en chassant l'écran actuel vers la gauche, et inversement) ; l'avant-plan reste visible sur les trois écrans, y compris le futur tableau de bord ; aucune mémorisation du fond choisi d'une session à l'autre (toujours l'écran 1 à l'ouverture) ; flèches posées sur le bureau à droite. Point corrigé en cours de route : les post-its, d'abord traités par erreur comme un élément fixe de l'avant-plan, doivent en réalité rester solidaires du panneau de liège et disparaître/réapparaître avec lui — résolu par une technique de réattachement du même nœud DOM de post-its à la bonne "diapositive" de fond, sans dupliquer sa logique de position. Le nouvel avant-plan fourni par Jean-Marc étant un dessin recomposé (pas la même photo que l'ancienne), l'ensemble des repères (classeurs, micro, pastille, zone de post-its) a dû être entièrement remesuré par analyse de pixels plutôt qu'ajusté par simple décalage — remesure reconduite une seconde fois après qu'une version corrigée de l'avant-plan (traces blanches et halo du micro retouchés) s'est révélée légèrement décalée par rapport à la première. Nombreux réglages fins en fin de session à partir des retours directs de Jean-Marc sur son propre écran (taille et position des flèches, position de l'icône du micro, position de la pastille de comptage des contacts). Version incrémentée une seule fois pour cette session : **V1.16.18** (`` avancé dès le début à la demande de Jean-Marc, `` inchangé — évolution jugée comme une continuation du chantier de l'écran de fond, pas une nouvelle section des Réflexions stratégiques). +**8 septembre 2026 (après-midi) : petits chantiers de finition — position du micro, unicité mail/portable, retrait des points, robustesse du code postal auto, toast bloquant sans bouton (V1.17.18).** Aucune réflexion stratégique nouvelle (`` inchangé, pas de nouvelle section des Réflexions stratégiques) ; `` avancé dès le début de session à la demande de Jean-Marc. Résumé, dans l'ordre traité : + +- *Position de l'icône micro sur l'écran de fond.* Le dernier réglage du 8 septembre au matin (`ICONE_CENTRE_X = 522`) allait trop loin vers la droite — repéré par Jean-Marc en testant. Ramené à `515` dans `ecran_fond.html` après essai réel. L'icône en état "écoute" reste masquée par un panneau superposé sur l'écran de test actuel de Jean-Marc — signalé, volontairement non traité (pas prioritaire). +- *Unicité mail/portable (chantier des consignes, section 30).* Deux nouveaux Tests dans `ASP_Fonctions`/`consignes_fonctions.php` : `MAIL_UNIQUE` et `PORTABLE_UNIQUE`, deux fonctions dédiées (`consigne_mail_unique()`, `consigne_portable_unique()`) plutôt qu'une seule fonction générique paramétrable par champ — décision explicite de Jean-Marc pour qu'un futur opérateur du catalogue ne puisse pas appliquer par erreur une contrainte d'unicité à un champ où elle n'a pas de sens (ex. Prénom, où deux homonymes sont normaux). Les deux s'appuient en interne sur un même utilitaire privé (`valeurDejaUtilisee()`, hors catalogue) pour ne pas dupliquer la requête SQL. A nécessité une première extension du contrat des Tests à un champ dans `moteur_consignes.php` : ces fonctions peuvent désormais recevoir deux paramètres de plus, `$pdo` et `$idExclusion` (identifiant de la fiche en cours de modification, pour qu'une consigne d'unicité ne se bloque jamais elle-même en retrouvant sa propre valeur déjà enregistrée — null en Création) — sans effet sur les Tests existants, qui ignorent simplement ces arguments en trop. `contacts.php` (PUT) transmet désormais cet identifiant au moteur. +- *Retrait des points dans les numéros de téléphone.* Nouveau Traitement générique `RETIRER_POINTS` (`consigne_retirer_points()`), sur le même principe que `SANS_CHIFFRE` (7 septembre) : une seule fonction réutilisable, appliquée par Jean-Marc à `CNT_Portable` et `CNT_Tel` via deux lignes dans `ASP_Consignes`, sans aucune fonction par champ. +- *Bug corrigé : échecs silencieux de `consigne_completer_code_postal()`.* Jean-Marc a repéré qu'une ville inconnue de l'IA n'aboutissait à aucune trace dans les réalisations, contrairement au cas "ville ambiguë" (type Paris) déjà traité le 7 septembre. Diagnostic : deux sorties anticipées de la fonction (clé API absente ; échec réseau/HTTP) renvoyaient les valeurs inchangées sans jamais poser ni `_realisation` ni `_avertissement`. Corrigé : l'échec réseau/HTTP pose désormais les deux (un appel a réellement été tenté, donc journalisé comme le cas "ville ambiguë") ; la clé API absente pose seulement l'avertissement écran (aucun appel réellement tenté, rien à journaliser comme coût engagé). +- *Toast bloquant du code postal : bouton "Valider" retiré, Échap seul.* Le bouton du toast bloquant (avertissement IA, 7 septembre) fonctionnait, mais Échap sur ce même toast fermait EN PLUS le panneau détail en dessous — le gestionnaire clavier global ne savait pas qu'un toast bloquant était ouvert. Corrigé dans `liste_contacts.html` : bouton retiré, le toast mémorise son rappel de fermeture (`toastValidationEnCours`) et le gestionnaire Échap global le vérifie désormais EN TOUT PREMIER, avant le menu contextuel du panneau détail — Échap referme alors le toast seul. +- *Ménage à prévoir sur `ASP_Consignes`.* Jean-Marc a repéré des champs qui lui semblent redondants dans la structure de cette table (`CNS_TexteDefinition`/`CNS_TexteAffiche` en particulier) — à examiner ensemble, rien fait ce jour. +- *Confusion résolue en cours de session :* un doute initial de Jean-Marc sur la persistance du correctif "Échap" (le bouton "Valider" restait visible après un premier essai) provenait du cache navigateur, pas d'un problème de déploiement — confirmé après rechargement forcé. + +Version incrémentée une seule fois pour l'ensemble de cette session (uniquement ``) : **V1.17.18**. + +**Prochain chantier annoncé par Jean-Marc pour la session suivante : écran 2 ("assistants") — gérer les attributions de Matilda.** À rapprocher des points déjà notés plus bas dans "Reste à faire" (discriminant humain/agent sur `ASP_ToDoList`, répartition dynamique des 70% par charge de tâches, `AGN_Couleur`) — rien de plus précisé à ce stade sur le périmètre exact. + **Reste à faire, noté au passage :** - Décider s'il faut renforcer le prompt d'extraction pour neutraliser les mots parasites/formules de politesse dictés autour d'une adresse mail (bug identifié le 4 septembre sur le corpus réel, correction non encore actée par Jean-Marc). - Clarifier l'origine de la coupure de fin de dictée observée sur un essai du 4 septembre (minuteur de silence à 3,5s vs interruption réelle) avant de décider si `DUREE_SILENCE_ARRET_MS` (`contact_ia_panel.js`) doit être allongée. - Concevoir la séparation entre actions utilisables par l'utilisateur humain (menu/clavier) et actions utilisables par le LLM de saisie vocale, avec "création du contact" formalisée comme action à part entière toujours classée en premier et connue des deux voies d'accès — réflexion actée par Jean-Marc le 4 septembre, non conçue, non codée (voir section 29). - Vérifier concrètement que le `.htaccess` du dossier `/journal` bloque bien l'accès direct (`curl -I` sur un fichier du dossier doit répondre `403`, pas `200` — point en suspens depuis le 31 août). -- Confirmer le déploiement du correctif `.htaccess` de cache livré le 3 septembre (fichier `htaccess-racine-aspic.txt`, à renommer et déposer à la racine de l'application). +- Confirmer le déploiement du correctif `.htaccess` de cache livré le 3 septembre (fichier `htaccess-racine-aspic.txt`, à renommer et déposer à la racine de l'application) — distinct du cache navigateur simple rencontré le 8 septembre (résolu par un rechargement forcé), toujours en suspens. - Laisser Jean-Marc constituer un vrai volume de dictées sur "Gestion des Contacts" (nouvel outil, voir ci-dessus) avant toute analyse ; puis concevoir le catalogue d'actions et le mécanisme LLM à sortie structurée correspondant (voir réflexions stratégiques, section 12, et journal section 27). - Récupérer les résultats de la campagne de tests d'adresses mail que Jean-Marc prévoit de mener le 4 septembre (mode adresse de `micro_test.js`) pour mesurer un vrai taux de réussite, sur un échantillon plus significatif que les 4 essais du 3 septembre. - Retirer le bloc de diagnostic temporaire (`DIAGNOSTIC_ACTIF`/`logDiag`) de `micro_test.js` une fois son comportement confirmé stable sur cette campagne. @@ -123,11 +137,11 @@ Version incrémentée une seule fois pour l'ensemble de cette session (pastille - Étendre `MARQUEUR_FIN_LIGNE` dans `micro.js` pour accepter "puis" en plus de "ensuite" (retour d'usage de Jean-Marc, 3 septembre) — sans urgence. - Continuer à observer les agents actifs (Viktor, Matilda, Léon, Nikita) en usage réel ; ajuster `AGN_Variantes` en base si de nouvelles confusions apparaissent. - Documentation utilisateur : le panneau de détail du contact, la ToDoList et le pilotage vocal restent à documenter (mise à jour du 3 septembre limitée à l'écran d'accueil et à la liste des contacts) — l'outil de banque de dictées et le chantier des consignes n'ont volontairement pas vocation à y figurer pour l'instant, ce sont des chantiers internes, pas des modules utilisateur finalisés. -- Écran de fond : contenu de présentation de l'écran 2 ("assistants") et contenu réel de l'écran 3 (tableaux de bord) restent entièrement à concevoir, seuls les fonds existent à ce stade (8 septembre) ; décider si les autres objets de l'avant-plan (carnet, téléphone) doivent devenir cliquables ; trancher un jour le sort de la piste "maquette liège + post-its par agent" (Claude Design, mise de côté pour cet essai, pas explicitement abandonnée). +- **Écran de fond, écran 2 ("assistants") : prochain chantier annoncé par Jean-Marc — gérer les attributions de Matilda** (voir plus haut). Contenu réel de l'écran 3 (tableau de bord) toujours entièrement à concevoir ; décider si les autres objets de l'avant-plan (carnet, téléphone) doivent devenir cliquables ; trancher un jour le sort de la piste "maquette liège + post-its par agent" (Claude Design, mise de côté pour cet essai, pas explicitement abandonnée). - Section 28 du journal détaillé : combler, si l'occasion se présente, un trou de documentation relevé le 8 septembre — le passage d'un classeur unique ("Contacts") à neuf classeurs (Consignes, Contacts, Catalogue, Documents, Prospection, Réseaux, En-cours, Factures, Journalier) a eu lieu dans une session intermédiaire (aux alentours du 7 septembre) jamais consignée dans le fichier de section correspondant. - Concevoir les deux signes graphiques distincts pour les post-its "à corriger"/"à valider" (réflexions stratégiques, section 12) — le premier acté (point d'interrogation), le second encore à choisir. -- Tableau de liège : coder la répartition dynamique des 70% entre assistants par charge de tâches (formule actée, voir Statut actuel) — bloqué tant que la ToDoList des assistants n'existe pas réellement. -- Étendre `ASP_ToDoList`/`todolist.php` pour qu'un assistant puisse être attributaire d'une tâche (discriminant humain/agent à définir) — préalable à la répartition dynamique et aux couleurs de post-it par assistant (`AGN_Couleur`, à ajouter). +- Tableau de liège : coder la répartition dynamique des 70% entre assistants par charge de tâches (formule actée, voir Statut actuel) — bloqué tant que la ToDoList des assistants n'existe pas réellement. **À rapprocher du prochain chantier (écran 2, attributions de Matilda).** +- Étendre `ASP_ToDoList`/`todolist.php` pour qu'un assistant puisse être attributaire d'une tâche (discriminant humain/agent à définir) — préalable à la répartition dynamique et aux couleurs de post-it par assistant (`AGN_Couleur`, à ajouter). **À rapprocher du prochain chantier (écran 2, attributions de Matilda).** - Écran "journalier" (trace des réalisations, humain + assistants) — donnée déjà quasi disponible (`TDL_RealDate`/`TDL_RealUser`/`TDL_RealText`), écran de consultation à construire. - Revoir les règles de calcul des pastilles rouge/orange/vert (`calculerGroupe()`) pour éviter un tableau trop chargé en rouge/orange maintenant que le vert quitte le tableau immédiatement. - Décider du comportement du clic sur un post-it (aujourd'hui inerte) : ouvrir `liste_todolist.html` sur la tâche précise, ou afficher un détail directement sur l'écran de fond. @@ -139,4 +153,4 @@ Version incrémentée une seule fois pour l'ensemble de cette session (pastille - Gestion des consignes transverses à toute la fiche (`CNS_Champ` NULL, ex. contrôle de doublon) — non traitée par le moteur de consignes actuel. - Distinction Saisie/Validation réellement exploitée un jour dans le moteur de consignes (temps réel), si le besoin redevient assez fort pour justifier la complexité écartée le 6 septembre. - Ouvrir le chantier du dialogue simulé avec Claude intégré à ASPIC (bulles de dialogue façon SMS, mission dédiée, clé API Anthropic) — faisabilité confirmée le 8 septembre 2026 (réflexions stratégiques, section 18), rien conçu ni codé à ce stade. - +- **Ménage dans la structure de `ASP_Consignes`** (8 septembre 2026) : Jean-Marc a repéré des champs qui lui semblent redondants (`CNS_TexteDefinition`/`CNS_TexteAffiche` en particulier) — à examiner ensemble avant de toucher au schéma, pour ne pas supprimer un champ qui sert ailleurs sans qu'on l'ait vérifié. diff --git a/liste_contacts.html b/liste_contacts.html index 8c20208..df38ff9 100644 --- a/liste_contacts.html +++ b/liste_contacts.html @@ -755,19 +755,17 @@ #toast .toast-agent{ margin-top:6px;font-style:italic;color:rgba(255,255,255,0.85); } - /* Bouton de validation d'un toast bloquant (7 septembre 2026, avertissement - IA) : ce toast-là ne disparaît pas tout seul (voir afficherToast()) — il - faut un clic explicite de l'utilisateur pour le refermer, ce qui fait - alors repasser la fiche du mode édition au mode vue (voir - validerFormulaireContact()). Fond blanc/texte rouge plutôt qu'un simple - texte cliquable, pour bien marquer que c'est une action et pas une - information de plus. */ - #toast .toast-bouton{ - display:block;margin:12px auto 0;padding:6px 18px; - background:#fff;color:var(--rouge-suppression); - border:none;border-radius:4px;font-size:13px;font-weight:600; - cursor:pointer; - } + /* Toast bloquant (7 septembre 2026, avertissement IA) : ce toast-là ne + disparaît pas tout seul (voir afficherToast()) — il faut une action + explicite de l'utilisateur pour le refermer, ce qui fait alors repasser + la fiche du mode édition au mode vue (voir validerFormulaireContact()). + Portait initialement un bouton "Valider" cliquable (règle CSS + #toast .toast-bouton, retirée le 8 septembre 2026) — remplacé par Échap + seul (Jean-Marc : le bouton était accessible, mais Échap, plus naturel + ici, refermait AUSSI le panneau détail en dessous au lieu du seul toast). + Voir le gestionnaire keydown global (toastValidationEnCours, vérifié en + tout premier, avant le menu contextuel du panneau détail) et + fermerToastValidation(). */ @@ -1370,6 +1368,17 @@ document.querySelectorAll(".col.sortable").forEach(col=>{ // ---- Navigation clavier + Échap ---- document.addEventListener("keydown", (e)=>{ if(e.key === "Escape"){ + // Toast bloquant en attente de fermeture (8 septembre 2026), vérifié + // AVANT TOUT LE RESTE — y compris le menu contextuel du panneau détail + // ci-dessous : sans cette priorité, Échap fermait le panneau détail (ou + // le menu) EN PLUS du toast au lieu du toast seul (bug signalé par + // Jean-Marc — le bouton "Valider" du toast a été retiré au profit + // d'Échap seul, voir afficherToast()/fermerToastValidation()). + if(toastValidationEnCours){ + e.preventDefault(); + fermerToastValidation(); + return; + } // Menu contextuel du panneau détail vérifié EN PREMIER (27 août 2026, // correction d'un bug signalé par Jean-Marc) : ce menu ne s'ouvre que // panneau détail déjà ouvert, donc panneauDetailOuvert() est également @@ -1511,6 +1520,11 @@ document.getElementById("menu-filtrer").addEventListener("click", ()=>{ // ---- Toast ---- let toastTimer = null; +// Rappel à déclencher quand l'utilisateur referme (Échap) un toast bloquant +// actuellement affiché — null tant qu'aucun n'est ouvert (8 septembre 2026, +// voir afficherToast()/fermerToastValidation() et le gestionnaire keydown +// global, qui le vérifie en tout premier). +let toastValidationEnCours = null; // Troisième paramètre "agent" (7 septembre 2026) : nom libre (CNS_Agent, // voir moteur_consignes.php) de l'agent rattaché à la consigne en échec, // remonté par contacts.php dans data.agent — affiché en italique sur une @@ -1540,24 +1554,31 @@ function afficherToast(message, erreur, agent, onValider){ if(onValider){ // Toast bloquant (7 septembre 2026, avertissement IA — Jean-Marc : "je // souhaite que le message s'affiche et reste en place [...] et que - // lorsque l'utilisateur clique, la fiche soit validée") : pas de - // disparition automatique ici, l'utilisateur referme lui-même via ce - // bouton, qui déclenche alors onValider (bascule en mode vue). - const bouton = document.createElement("button"); - bouton.type = "button"; - bouton.className = "toast-bouton"; - bouton.textContent = "Valider"; - bouton.onclick = () => { - toast.style.display = "none"; - toast.classList.remove("toast-erreur"); - onValider(); - }; - toast.appendChild(bouton); + // lorsque l'utilisateur [le referme], la fiche soit validée") : pas de + // disparition automatique ici. Fermeture uniquement via Échap depuis le + // 8 septembre 2026 (le bouton "Valider" initial est retiré — voir + // fermerToastValidation() et le gestionnaire keydown global) : on se + // contente de mémoriser le rappel à déclencher à la fermeture, rien + // d'autre à faire ici. + toastValidationEnCours = onValider; } else { + toastValidationEnCours = null; toastTimer = setTimeout(()=>{ toast.style.display = "none"; }, 2600); } } +// Referme le toast bloquant en cours (voir afficherToast(), paramètre +// onValider) et déclenche son rappel — appelée uniquement depuis le +// gestionnaire keydown global sur Échap (8 septembre 2026, voir plus bas). +function fermerToastValidation(){ + const toast = document.getElementById("toast"); + toast.style.display = "none"; + toast.classList.remove("toast-erreur"); + const onValider = toastValidationEnCours; + toastValidationEnCours = null; + if(onValider) onValider(); +} + // ---- Bouton micro (pilotage vocal) ---- // Comportement réel (reconnaissance vocale) désormais dans micro.js, // partagé par tous les écrans — voir chargement en fin de fichier et le @@ -2535,12 +2556,13 @@ async function validerFormulaireContact(){ // déduit) qui appelle une action de l'utilisateur. // Toast bloquant (7 septembre 2026, demande explicite de Jean-Marc : // "je souhaite que le message s'affiche et reste en place [...] et que - // lorsque l'utilisateur clique, la fiche soit validée") : pas de + // lorsque l'utilisateur [le referme], la fiche soit validée") : pas de // disparition automatique — la fiche reste visuellement "en attente" - // (mode édition) tant que l'utilisateur n'a pas cliqué sur "Valider" - // dans le toast, ce qui ne fait alors que rebasculer l'affichage en mode - // vue (le contact est déjà bel et bien enregistré depuis plus haut, - // avertissement ou non — ce bouton ne redéclenche aucun enregistrement). + // (mode édition) tant que l'utilisateur n'a pas fermé le toast (Échap + // seul depuis le 8 septembre 2026, voir afficherToast()), ce qui ne fait + // alors que rebasculer l'affichage en mode vue (le contact est déjà bel + // et bien enregistré depuis plus haut, avertissement ou non — cette + // fermeture ne redéclenche aucun enregistrement). if(avertissements.length){ afficherToast(avertissements.join("\n"), true, undefined, () => { basculerModeDetail("vue"); diff --git a/moteur_consignes.php b/moteur_consignes.php index 53a3e12..0180d05 100644 --- a/moteur_consignes.php +++ b/moteur_consignes.php @@ -106,6 +106,23 @@ * d'un code postal déduit par l'IA) verrait la valeur D'AVANT cet appel IA, * puisque les Tests s'exécutent avant l'étape 3 — aucune consigne actuelle * n'est dans ce cas. + * + * Test à un champ avec accès base (8 septembre 2026) : première consigne de + * cette nature (unicité du mail/portable, voir consigne_mail_unique() et + * consigne_portable_unique() dans consignes_fonctions.php). Le contrat de + * base d'un Test à un champ reste (string $valeur): bool (voir en-tête de + * consignes_fonctions.php), mais le moteur appelle désormais TOUJOURS ces + * fonctions avec deux arguments supplémentaires, $pdo et $idExclusion — sans + * effet sur les fonctions existantes qui ne déclarent qu'un seul paramètre + * (PHP ignore silencieusement les arguments en trop), et exploitable par une + * nouvelle fonction qui déclare ces deux paramètres en plus. $idExclusion est + * l'identifiant (ex. CNT_Incr) de la fiche en cours de modification — null en + * Création (rien à exclure), ou en Modification si l'appelant ne le fournit + * pas — pour qu'une consigne d'unicité ne se bloque jamais elle-même en + * retrouvant la propre valeur déjà enregistrée de la fiche qu'on modifie. + * Fourni par l'appelant (voir $idExclusion du paramètre d'appliquerConsignes() + * ci-dessous) : ce fichier n'a connaissance d'aucun identifiant métier propre + * à un contexte précis, volontairement générique. */ /** @@ -280,12 +297,21 @@ function executerTraitements(array $consignesTraitement, array &$data, array &$j * l'appelant (ex. un toast par message), * indépendamment de $journalisations qui * couvre l'historique, pas l'écran. + * @param int|null $idExclusion Identifiant de la fiche en cours de + * modification (ex. CNT_Incr), à exclure + * d'une éventuelle vérification + * d'unicité — null en Création, ou si + * aucune consigne du contexte n'en a + * besoin (8 septembre 2026, voir + * commentaire d'en-tête). Simplement + * retransmis tel quel aux fonctions de + * Test à un champ qui le demandent. * @return string|null Message d'erreur du premier test en * échec (déjà le texte à renvoyer tel * quel au front-end), ou null si tout est * passé. */ -function appliquerConsignes(PDO $pdo, string $contexte, string $portee, array &$data, ?string &$agentEchec = null, ?array &$journalisations = null, ?array &$avertissements = null): ?string +function appliquerConsignes(PDO $pdo, string $contexte, string $portee, array &$data, ?string &$agentEchec = null, ?array &$journalisations = null, ?array &$avertissements = null, ?int $idExclusion = null): ?string { // Type nullable en entrée (comme $agentEchec ci-dessus) : un appelant qui // passe une variable non initialisée par référence la fait exister à @@ -317,7 +343,11 @@ function appliquerConsignes(PDO $pdo, string $contexte, string $portee, array &$ $champs = array_map('trim', explode(',', $c['CNS_Champ'])); if (count($champs) === 1) { $valeur = (string)($data[$champs[0]] ?? ''); - $ok = call_user_func($c['FON_NomPHP'], $valeur); + // $pdo et $idExclusion toujours passés (voir commentaire + // d'en-tête, 8 septembre 2026) : sans effet pour une fonction qui + // ne déclare que (string $valeur), exploités par une fonction qui + // a besoin d'interroger la base (ex. unicité mail/portable). + $ok = call_user_func($c['FON_NomPHP'], $valeur, $pdo, $idExclusion); } else { $valeurs = []; foreach ($champs as $champ) { diff --git a/version.php b/version.php index 4259ab5..c04256d 100644 --- a/version.php +++ b/version.php @@ -50,7 +50,7 @@ * capacité à détecter un déploiement resté en retard. */ -const ASPIC_VERSION = 'V1.16.18'; +const ASPIC_VERSION = 'V1.17.18'; if (basename($_SERVER['SCRIPT_NAME']) === 'version.php') { header('Content-Type: application/json; charset=utf-8');