mirror of
https://github.com/JMP-MS/ASPIC.git
synced 2026-10-27 16:07:10 +00:00
V1.19.21 Réflexions stratégiques et gestion de la personnalité des collaborateurs
This commit is contained in:
+634
-1
@@ -539,6 +539,254 @@
|
||||
font-family:"Segoe UI","Arial",sans-serif;
|
||||
pointer-events:none;
|
||||
}
|
||||
|
||||
/* Outils "Construire une identité" / "Tester l'identité" (10 septembre
|
||||
2026, réflexions stratégiques section 20 ; repositionnés et redesignés
|
||||
le 10 septembre 2026 (suite), retour de Jean-Marc après un premier essai
|
||||
réel) — deux boutons juste sous le panneau "L'équipe des collaborateurs",
|
||||
positionnés par JS dans positionner() (comme panneauBureau lui-même :
|
||||
largeur/police recalculées, pas de pourcentage fixe) puisqu'ils doivent
|
||||
suivre la même zone sous les 2 dernières colonnes. */
|
||||
#outils-collaborateurs{
|
||||
position:absolute;
|
||||
display:flex;
|
||||
gap:8px;
|
||||
pointer-events:auto;
|
||||
}
|
||||
.bouton-outil-collab{
|
||||
font:inherit;
|
||||
font-family:"Segoe UI","Arial",sans-serif;
|
||||
font-weight:700;
|
||||
background:#fff;
|
||||
color:#2a2a2a;
|
||||
border:1px solid #999;
|
||||
border-radius:5px;
|
||||
cursor:pointer;
|
||||
box-shadow:2px 3px 6px rgba(0,0,0,.3);
|
||||
white-space:nowrap;
|
||||
}
|
||||
.bouton-outil-collab.actif{
|
||||
background:#2a2a2a;
|
||||
color:#fff;
|
||||
}
|
||||
|
||||
/* Écran plein cadre "Construire une identité" / "Tester l'identité" (refonte
|
||||
du 10 septembre 2026, retour explicite de Jean-Marc) : position:fixed sur
|
||||
TOUT le viewport, au-dessus de tout le reste (fond photo, avant-plan,
|
||||
portraits) — "efface complètement l'écran, y compris l'écran de fond".
|
||||
Volontairement hors de #assistants-layer/#fonds-container : un simple
|
||||
calque opaque par-dessus tout suffit à "effacer" visuellement sans avoir
|
||||
à toucher à l'état des autres calques (rien à restaurer à la fermeture).
|
||||
Element statique du body (jamais créé/attaché dynamiquement) : présent
|
||||
dans le document dès le chargement de la page, contrairement au premier
|
||||
essai (nœuds attachés à #assistants-layer, donc absents du document tant
|
||||
que l'écran des collaborateurs n'avait pas encore été affiché une
|
||||
première fois — cause du bug "écran blanc" remonté par Jean-Marc). */
|
||||
#overlay-collab{
|
||||
position:fixed;
|
||||
inset:0;
|
||||
z-index:500;
|
||||
background:#1c1c1c;
|
||||
display:flex;
|
||||
flex-direction:column;
|
||||
align-items:center;
|
||||
box-sizing:border-box;
|
||||
/* Haut/bas resserrés le 10 septembre 2026 (soir), demande de Jean-Marc :
|
||||
remonter le titre et redescendre les boutons au plus près des bords,
|
||||
pour laisser le plus de hauteur possible aux zones de saisie/dialogue
|
||||
(ni la taille des photos ni celle du texte sous les photos n'y touche,
|
||||
seul l'espacement autour change). */
|
||||
padding:10px 32px;
|
||||
font-family:"Segoe UI","Arial",sans-serif;
|
||||
color:#eee;
|
||||
}
|
||||
/* Bug trouvé en test automatisé le 10 septembre 2026 : l'ID selector
|
||||
ci-dessus (display:flex) l'emporte en spécificité sur la règle UA
|
||||
[hidden]{display:none}, donc SANS cette ligne #overlay-collab reste
|
||||
affiché (et intercepte tous les clics, plein écran) même avec l'attribut
|
||||
hidden posé — l'écran plein cadre bloquait alors silencieusement toute
|
||||
la navigation dès le chargement de la page, hidden ou pas. */
|
||||
#overlay-collab[hidden]{
|
||||
display:none;
|
||||
}
|
||||
#overlay-collab-titre{
|
||||
font-size:16px;
|
||||
font-weight:700;
|
||||
color:#eee;
|
||||
margin-bottom:8px;
|
||||
}
|
||||
.ligne-photos-collab{
|
||||
display:flex;
|
||||
gap:22px;
|
||||
flex-wrap:wrap;
|
||||
justify-content:center;
|
||||
margin-bottom:10px;
|
||||
}
|
||||
.photo-collab-selection{
|
||||
display:flex;
|
||||
flex-direction:column;
|
||||
align-items:center;
|
||||
gap:6px;
|
||||
cursor:pointer;
|
||||
background:none;
|
||||
border:none;
|
||||
padding:6px;
|
||||
border-radius:8px;
|
||||
}
|
||||
.photo-collab-selection:hover{
|
||||
background:rgba(255,255,255,.08);
|
||||
}
|
||||
.photo-collab-selection.selectionne{
|
||||
background:rgba(255,255,255,.16);
|
||||
}
|
||||
.photo-collab-selection img,
|
||||
.photo-collab-selection .repli{
|
||||
width:72px;height:72px;border-radius:8px;object-fit:cover;
|
||||
background:#3a3a3a;box-shadow:1px 2px 6px rgba(0,0,0,.4);
|
||||
}
|
||||
.photo-collab-selection .repli{
|
||||
display:flex;align-items:center;justify-content:center;
|
||||
color:#999;font-weight:700;
|
||||
}
|
||||
.photo-collab-selection .prenom-collab{
|
||||
font-size:13px;font-weight:700;color:#f0f0f0;
|
||||
}
|
||||
.photo-collab-selection .fonction-collab{
|
||||
font-size:11px;font-weight:700;color:#aaa;
|
||||
}
|
||||
|
||||
.overlay-collab-corps{
|
||||
flex:1;
|
||||
width:100%;
|
||||
max-width:760px;
|
||||
display:flex;
|
||||
flex-direction:column;
|
||||
min-height:0;
|
||||
}
|
||||
/* Même correction que #overlay-collab[hidden] ci-dessus, pour la même
|
||||
raison : sans cette règle, corps-identite et corps-test resteraient
|
||||
TOUS LES DEUX affichés en permanence (superposés), l'attribut hidden
|
||||
posé par le script (voir ouvrirConstruireIdentite()/ouvrirTesterIdentite())
|
||||
n'ayant plus aucun effet face à ce display:flex d'origine auteur. */
|
||||
.overlay-collab-corps[hidden]{
|
||||
display:none;
|
||||
}
|
||||
/* Zone de saisie "terminal" (même esprit que les outils de mise au point
|
||||
internes déjà livrés sur ce projet — contact_ia_test.html,
|
||||
dictees_test.php) : fond noir, police mono, ascenseur si le texte
|
||||
dépasse. */
|
||||
#texte-identite{
|
||||
flex:1;
|
||||
min-height:0;
|
||||
resize:none;
|
||||
overflow-y:auto;
|
||||
background:#000;
|
||||
color:#ddd;
|
||||
font-family:"Courier New",Consolas,monospace;
|
||||
font-size:13px;
|
||||
line-height:1.5;
|
||||
border:1px solid #444;
|
||||
border-radius:5px;
|
||||
padding:12px;
|
||||
}
|
||||
.boutons-identite{
|
||||
display:flex;
|
||||
justify-content:flex-end;
|
||||
gap:10px;
|
||||
margin-top:12px;
|
||||
}
|
||||
#compteur-identite{
|
||||
text-align:right;
|
||||
font-size:11px;
|
||||
color:#888;
|
||||
margin-top:4px;
|
||||
}
|
||||
#compteur-identite.avertissement{color:#d9a84e;}
|
||||
#compteur-identite.limite{color:#e14a5c;font-weight:700;}
|
||||
.boutons-identite button{
|
||||
font:inherit;font-size:13px;font-weight:700;border-radius:5px;
|
||||
padding:9px 20px;cursor:pointer;
|
||||
}
|
||||
.boutons-identite button:disabled{opacity:.4;cursor:default;}
|
||||
#btn-abandonner-identite{
|
||||
background:#333;color:#eee;border:1px solid #555;
|
||||
}
|
||||
#btn-valider-identite{
|
||||
background:#2a4d2a;color:#bdf7bd;border:1px solid #3a6d3a;
|
||||
}
|
||||
#btn-fermer-test-identite{
|
||||
background:#333;color:#eee;border:1px solid #555;
|
||||
}
|
||||
#btn-chat-envoyer{
|
||||
background:#2a4d2a;color:#bdf7bd;border:1px solid #3a6d3a;
|
||||
}
|
||||
#message-identite{
|
||||
font-size:12px;margin-top:6px;min-height:14px;
|
||||
}
|
||||
#message-identite.ok{color:#7be07b;}
|
||||
#message-identite.ko{color:#e14a5c;}
|
||||
|
||||
/* Fenêtre "type session Claude" (Tester l'identité) : bulles alternées,
|
||||
couleur du collaborateur (AGN_Couleur) pour ses propres bulles — même
|
||||
teinte que sa carte sur l'écran des collaborateurs et déjà anticipée
|
||||
pour cet usage (réflexions stratégiques, section 18/28). */
|
||||
#chat-messages{
|
||||
flex:1;
|
||||
min-height:0;
|
||||
overflow-y:auto;
|
||||
background:#111;
|
||||
border:1px solid #444;
|
||||
border-radius:5px;
|
||||
padding:14px;
|
||||
display:flex;
|
||||
flex-direction:column;
|
||||
gap:10px;
|
||||
}
|
||||
.bulle-chat{
|
||||
max-width:75%;
|
||||
padding:9px 13px;
|
||||
border-radius:12px;
|
||||
font-size:13px;
|
||||
line-height:1.5;
|
||||
white-space:pre-wrap;
|
||||
}
|
||||
.bulle-chat.moi{
|
||||
align-self:flex-end;
|
||||
background:#f2d94e;
|
||||
color:#2a2a2a;
|
||||
border-bottom-right-radius:3px;
|
||||
}
|
||||
.bulle-chat.collab{
|
||||
align-self:flex-start;
|
||||
color:#1a1a1a;
|
||||
border-bottom-left-radius:3px;
|
||||
}
|
||||
.bulle-chat.systeme{
|
||||
align-self:center;
|
||||
color:#999;
|
||||
font-size:12px;
|
||||
font-style:italic;
|
||||
background:none;
|
||||
}
|
||||
/* Zone de saisie du message — sur sa propre ligne, deux lignes de haut par
|
||||
défaut (attribut rows="2" côté HTML), Envoyer et Fermer regroupés
|
||||
juste en dessous (voir .boutons-identite, réutilisée telle quelle ici :
|
||||
10 septembre 2026 soir, demande de Jean-Marc — même agencement que
|
||||
"Construire une identité", Fermer/Envoyer à la place d'Abandonner/Valider). */
|
||||
#chat-input{
|
||||
width:100%;
|
||||
box-sizing:border-box;
|
||||
resize:none;
|
||||
background:#000;
|
||||
color:#ddd;
|
||||
font:inherit;
|
||||
font-size:13px;
|
||||
border:1px solid #444;
|
||||
border-radius:5px;
|
||||
padding:9px 12px;
|
||||
margin-top:12px;
|
||||
}
|
||||
#btn-chat-envoyer:disabled{opacity:.4;cursor:default;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -592,6 +840,46 @@ Créé par Mevelia Solutions">
|
||||
<img src="fleche_suivante.png" alt="Écran suivant">
|
||||
</button>
|
||||
|
||||
<!-- Écran plein cadre "Construire une identité" / "Tester l'identité"
|
||||
(10 septembre 2026, réflexions stratégiques section 20 — voir commentaire
|
||||
CSS #overlay-collab). Statique ici, dans le body, pour être présent dans
|
||||
le document dès le chargement — voir ce même commentaire pour la raison
|
||||
précise (bug corrigé du 10 septembre). Les deux "corps" (identité /
|
||||
test) partagent l'entête (titre + photos) et se basculent l'un l'autre,
|
||||
jamais affichés en même temps. -->
|
||||
<div id="overlay-collab" hidden>
|
||||
<div id="overlay-collab-titre"></div>
|
||||
<div class="ligne-photos-collab" id="overlay-collab-photos"></div>
|
||||
|
||||
<div id="corps-identite" class="overlay-collab-corps" hidden>
|
||||
<textarea id="texte-identite" spellcheck="false"></textarea>
|
||||
<!-- Indicateur ajouté le 10 septembre 2026 (soir), demande de Jean-Marc :
|
||||
AGN_Identite est une colonne MEDIUMTEXT (élargie le même soir depuis
|
||||
TEXT — voir collaborateurs_identite.php, en-tête — en prévision d'un
|
||||
texte appelé à s'enrichir avec l'usage réel de l'application, pas une
|
||||
biographie figée) — 16 777 215 octets maximum en MySQL, PAS
|
||||
16 777 215 caractères (utf8mb4 : de 1 à 4 octets par caractère selon
|
||||
le caractère). Compte donc en octets (encodage UTF-8, comme MySQL le
|
||||
stocke réellement), seule mesure exacte face à cette limite. Voir
|
||||
mettreAJourCompteurIdentite(). -->
|
||||
<div id="compteur-identite"></div>
|
||||
<div class="boutons-identite">
|
||||
<button type="button" id="btn-abandonner-identite">Abandonner</button>
|
||||
<button type="button" id="btn-valider-identite">Valider</button>
|
||||
</div>
|
||||
<div id="message-identite"></div>
|
||||
</div>
|
||||
|
||||
<div id="corps-test" class="overlay-collab-corps" hidden>
|
||||
<div id="chat-messages"></div>
|
||||
<textarea id="chat-input" rows="2" placeholder="Écris un message…"></textarea>
|
||||
<div class="boutons-identite">
|
||||
<button type="button" id="btn-fermer-test-identite">Fermer</button>
|
||||
<button type="button" id="btn-chat-envoyer">Envoyer</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Référence de l'image : 1024×457, inchangée en dimensions (voir
|
||||
// ecran_fond.jpg — 3ᵉ version le 3 septembre 2026, Jean-Marc a raccourci
|
||||
@@ -1129,11 +1417,30 @@ Créé par Mevelia Solutions">
|
||||
panneauBureau.style.height = panneauHauteur + 'px';
|
||||
panneauBureau.style.borderWidth = matPx + 'px';
|
||||
panneauBureau.style.fontSize = panneauPolicePx + 'px';
|
||||
|
||||
// Boutons "Construire une identité"/"Tester l'identité" (10 septembre
|
||||
// 2026, retour de Jean-Marc) : juste sous panneauBureau, centrés sur
|
||||
// le même axe — translateX(-50%) plutôt qu'une largeur mesurée, la
|
||||
// largeur réelle du groupe de boutons dépendant du rendu du
|
||||
// navigateur (police, padding), pas d'un texte qu'on mesure nous-mêmes
|
||||
// comme pour panneauBureau.
|
||||
outilsCollaborateurs.style.display = 'flex';
|
||||
outilsCollaborateurs.style.fontSize = (panneauPolicePx * 0.8) + 'px';
|
||||
outilsCollaborateurs.style.gap = (8 * scale) + 'px';
|
||||
outilsCollaborateurs.style.top = (zoneCentreY + panneauHauteur / 2 + BUREAU_PANNEAU_GAP * scale) + 'px';
|
||||
outilsCollaborateurs.style.left = zoneCentreX + 'px';
|
||||
outilsCollaborateurs.style.transform = 'translateX(-50%)';
|
||||
outilsCollaborateurs.querySelectorAll('.bouton-outil-collab').forEach(function (b) {
|
||||
b.style.padding = (padV * 0.8) + 'px ' + (padH * 0.8) + 'px';
|
||||
b.style.borderRadius = (5 * scale) + 'px';
|
||||
});
|
||||
} else {
|
||||
panneauBureau.style.display = 'none';
|
||||
outilsCollaborateurs.style.display = 'none';
|
||||
}
|
||||
} else {
|
||||
panneauBureau.style.display = 'none';
|
||||
outilsCollaborateurs.style.display = 'none';
|
||||
}
|
||||
}
|
||||
// -------- Fonds interchangeables (8 septembre 2026) --------
|
||||
@@ -1173,9 +1480,33 @@ Créé par Mevelia Solutions">
|
||||
// recalculée à chaque appel de positionner().
|
||||
const panneauBureau = document.createElement('div');
|
||||
panneauBureau.className = 'bureau-panneau';
|
||||
panneauBureau.textContent = 'Bureau des assistants';
|
||||
panneauBureau.textContent = "L'équipe des collaborateurs";
|
||||
coucheAssistants.appendChild(panneauBureau);
|
||||
|
||||
// -------- Outils "Construire une identité" / "Tester l'identité" --------
|
||||
// (10 septembre 2026, réflexions stratégiques section 20 ; repositionnés
|
||||
// le 10 septembre 2026 (suite), sous panneauBureau.) Les deux boutons
|
||||
// restent propres à l'écran des collaborateurs (déplacés avec
|
||||
// coucheAssistants comme panneauBureau) ; l'écran plein cadre qu'ils
|
||||
// ouvrent, lui, est statique dans le body — voir #overlay-collab, HTML,
|
||||
// et le commentaire CSS associé pour la raison précise.
|
||||
const outilsCollaborateurs = document.createElement('div');
|
||||
outilsCollaborateurs.id = 'outils-collaborateurs';
|
||||
|
||||
const btnConstruireIdentite = document.createElement('button');
|
||||
btnConstruireIdentite.type = 'button';
|
||||
btnConstruireIdentite.className = 'bouton-outil-collab';
|
||||
btnConstruireIdentite.textContent = 'Construire une identité';
|
||||
|
||||
const btnTesterIdentite = document.createElement('button');
|
||||
btnTesterIdentite.type = 'button';
|
||||
btnTesterIdentite.className = 'bouton-outil-collab';
|
||||
btnTesterIdentite.textContent = "Tester l'identité";
|
||||
|
||||
outilsCollaborateurs.appendChild(btnConstruireIdentite);
|
||||
outilsCollaborateurs.appendChild(btnTesterIdentite);
|
||||
coucheAssistants.appendChild(outilsCollaborateurs);
|
||||
|
||||
function creerFondSlide(index) {
|
||||
const slide = document.createElement('div');
|
||||
slide.className = 'fond-slide';
|
||||
@@ -1446,6 +1777,308 @@ Créé par Mevelia Solutions">
|
||||
}
|
||||
chargerAgentsEcranAssistants();
|
||||
|
||||
// -------- "Construire une identité" / "Tester l'identité" --------
|
||||
// (10 septembre 2026, réflexions stratégiques section 20 — redesign du
|
||||
// même jour, après-midi : écran plein cadre partagé entre les deux outils,
|
||||
// #overlay-collab statique dans le body — voir commentaire CSS/HTML plus
|
||||
// haut pour le bug que ce choix corrige au passage.)
|
||||
|
||||
// Ébauches proposées par Claude le 10 septembre 2026 — affichées comme
|
||||
// point de départ dans la zone de saisie UNIQUEMENT tant qu'aucune identité
|
||||
// n'a encore été enregistrée en base pour ce collaborateur (voir
|
||||
// selectionnerCollaborateurIdentite() plus bas). Clé = AGN_Prenom exact.
|
||||
const EBAUCHES_IDENTITE = {
|
||||
'Matilda': "Matilda a fait des études de gestion avant de passer quinze ans comme assistante de direction dans une PME industrielle familiale, où elle a appris à anticiper les besoins de son dirigeant avant même qu'il les formule. Rigoureuse sur les échéances, elle relit toujours deux fois avant d'envoyer et a un sens aigu de la confidentialité — ce qui passe par elle n'en ressort jamais sans son accord. Plutôt directe à l'oral, elle préfère dire clairement ce qui ne va pas plutôt que de laisser traîner un doute.",
|
||||
'Viktor': "Viktor a démarré comme commercial terrain dans la distribution avant de se spécialiser dans la vente B2B pour des PME de services. Énergique et optimiste, il croit qu'un bon relationnel finit toujours par payer — quitte à pousser un peu fort quand il sent une affaire prête à aboutir. Il déteste laisser un prospect refroidir et relance volontiers plus tôt que prévu. Un travers qu'on lui connaît : il a tendance à sous-estimer le temps qu'une négociation de prix peut prendre côté client.",
|
||||
'Léon': "Léon a passé plus de vingt-cinq ans comme contrôleur de gestion dans plusieurs entreprises avant de rejoindre l'équipe sur le tard, avec la conviction qu'une affaire mal chiffrée finit toujours par coûter cher. Posé, mesuré dans ses propos, il prend le temps de vérifier avant de conclure et n'hésite jamais à freiner un enthousiasme collectif s'il juge une marge trop courte. Mieux vaut arriver avec de vrais chiffres quand on lui soumet un dossier.",
|
||||
'Nikita': "Nikita vient du monde de la veille digitale et des réseaux sociaux, où elle a appris à recouper une information avant de s'y fier. Curieuse et sociable, elle aime creuser au-delà du premier résultat et préfère dire honnêtement qu'elle n'a rien trouvé plutôt que d'avancer une réponse rassurante mais bancale. Elle se méfie instinctivement d'un profil qui n'existe nulle part en ligne.",
|
||||
'Sophia': "Sophia a un parcours de communication et de storytelling de marque, passée par une agence avant de rejoindre l'équipe. Créative et spontanée, elle voit dans chaque situation — bonne ou mauvaise — une histoire à raconter, et pense volontiers en termes d'image et de témoignage client plutôt qu'en termes de process. Elle a tendance à s'enthousiasmer vite pour une idée, quitte à devoir être ramenée aux contraintes concrètes du terrain."
|
||||
};
|
||||
|
||||
const overlayCollab = document.getElementById('overlay-collab');
|
||||
const overlayCollabTitre = document.getElementById('overlay-collab-titre');
|
||||
const overlayCollabPhotos = document.getElementById('overlay-collab-photos');
|
||||
const corpsIdentite = document.getElementById('corps-identite');
|
||||
const corpsTest = document.getElementById('corps-test');
|
||||
const texteIdentite = document.getElementById('texte-identite');
|
||||
const messageIdentite = document.getElementById('message-identite');
|
||||
const btnAbandonnerIdentite = document.getElementById('btn-abandonner-identite');
|
||||
const btnValiderIdentite = document.getElementById('btn-valider-identite');
|
||||
const chatMessages = document.getElementById('chat-messages');
|
||||
const chatInput = document.getElementById('chat-input');
|
||||
const btnChatEnvoyer = document.getElementById('btn-chat-envoyer');
|
||||
const btnFermerTestIdentite = document.getElementById('btn-fermer-test-identite');
|
||||
const compteurIdentite = document.getElementById('compteur-identite');
|
||||
|
||||
// AGN_Identite est une colonne MEDIUMTEXT (16 777 215 octets maximum en
|
||||
// MySQL — élargie le 10 septembre 2026 depuis TEXT, à la demande de
|
||||
// Jean-Marc : ce texte est voué à s'enrichir avec l'usage réel de
|
||||
// l'application plutôt qu'à rester une biographie figée écrite une fois —
|
||||
// voir collaborateurs_identite.php, en-tête, et compteur-identite en HTML
|
||||
// plus haut).
|
||||
const TEXTE_IDENTITE_MAX_OCTETS = 16777215;
|
||||
const codeurUtf8 = new TextEncoder();
|
||||
|
||||
function mettreAJourCompteurIdentite() {
|
||||
const octets = codeurUtf8.encode(texteIdentite.value).length;
|
||||
compteurIdentite.textContent = octets.toLocaleString('fr-FR') + ' / ' +
|
||||
TEXTE_IDENTITE_MAX_OCTETS.toLocaleString('fr-FR') + ' octets';
|
||||
const depasse = octets > TEXTE_IDENTITE_MAX_OCTETS;
|
||||
compteurIdentite.className = depasse ? 'limite' : (octets > TEXTE_IDENTITE_MAX_OCTETS * 0.9 ? 'avertissement' : '');
|
||||
btnValiderIdentite.disabled = depasse;
|
||||
return depasse;
|
||||
}
|
||||
|
||||
texteIdentite.addEventListener('input', mettreAJourCompteurIdentite);
|
||||
|
||||
let collaborateursIdentite = null; // rempli au premier chargement réussi de collaborateurs_identite.php
|
||||
let selectionIdentiteIncr = null;
|
||||
let modeOverlayActif = null; // 'identite' | 'test' | null — jamais les deux en même temps
|
||||
let collaborateurTest = null; // collaborateur choisi pour la discussion "Tester l'identité" en cours
|
||||
let historiqueChatTest = []; // [{role:'user'|'assistant', content:'...'}] — API sans état, tout réexpédié à chaque appel
|
||||
|
||||
// Construit la ligne de photos cliquables, partagée entre les deux
|
||||
// écrans (Construire/Tester) — même liste de collaborateurs, seule la
|
||||
// réaction au clic diffère (onClic).
|
||||
function remplirLignePhotos(conteneur, collaborateurs, onClic) {
|
||||
conteneur.innerHTML = '';
|
||||
collaborateurs.forEach(function (collab) {
|
||||
const bouton = document.createElement('button');
|
||||
bouton.type = 'button';
|
||||
bouton.className = 'photo-collab-selection';
|
||||
bouton.dataset.incr = collab.AGN_Incr;
|
||||
|
||||
if (collab.AGN_Photo) {
|
||||
const img = document.createElement('img');
|
||||
img.src = collab.AGN_Photo;
|
||||
img.alt = collab.AGN_Prenom || '';
|
||||
bouton.appendChild(img);
|
||||
} else {
|
||||
const repli = document.createElement('div');
|
||||
repli.className = 'repli';
|
||||
repli.textContent = '?';
|
||||
bouton.appendChild(repli);
|
||||
}
|
||||
|
||||
const prenom = document.createElement('div');
|
||||
prenom.className = 'prenom-collab';
|
||||
prenom.textContent = collab.AGN_Prenom || '';
|
||||
bouton.appendChild(prenom);
|
||||
|
||||
if (collab.AGN_Fonction) {
|
||||
const fonction = document.createElement('div');
|
||||
fonction.className = 'fonction-collab';
|
||||
fonction.textContent = collab.AGN_Fonction;
|
||||
bouton.appendChild(fonction);
|
||||
}
|
||||
|
||||
bouton.addEventListener('click', function () { onClic(collab, bouton); });
|
||||
conteneur.appendChild(bouton);
|
||||
});
|
||||
}
|
||||
|
||||
function marquerPhotoSelectionnee(conteneur, incr) {
|
||||
conteneur.querySelectorAll('.photo-collab-selection').forEach(function (b) {
|
||||
b.classList.toggle('selectionne', Number(b.dataset.incr) === Number(incr));
|
||||
});
|
||||
}
|
||||
|
||||
// Referme l'écran plein cadre quel que soit le mode en cours, et remet les
|
||||
// deux boutons déclencheurs dans leur état au repos.
|
||||
function fermerOverlay() {
|
||||
overlayCollab.hidden = true;
|
||||
corpsIdentite.hidden = true;
|
||||
corpsTest.hidden = true;
|
||||
btnConstruireIdentite.classList.remove('actif');
|
||||
btnConstruireIdentite.textContent = 'Construire une identité';
|
||||
btnTesterIdentite.classList.remove('actif');
|
||||
btnTesterIdentite.textContent = "Tester l'identité";
|
||||
modeOverlayActif = null;
|
||||
selectionIdentiteIncr = null;
|
||||
collaborateurTest = null;
|
||||
}
|
||||
|
||||
// -------- "Construire une identité" --------
|
||||
async function ouvrirConstruireIdentite() {
|
||||
modeOverlayActif = 'identite';
|
||||
overlayCollab.hidden = false;
|
||||
corpsIdentite.hidden = true; // tant qu'aucune photo n'est choisie
|
||||
corpsTest.hidden = true;
|
||||
overlayCollabTitre.textContent = 'Construire une identité — cliquer sur une photo';
|
||||
overlayCollabPhotos.innerHTML = 'Chargement…';
|
||||
btnConstruireIdentite.classList.add('actif');
|
||||
btnConstruireIdentite.textContent = 'Fermer';
|
||||
btnTesterIdentite.classList.remove('actif');
|
||||
btnTesterIdentite.textContent = "Tester l'identité";
|
||||
selectionIdentiteIncr = null;
|
||||
|
||||
if (!collaborateursIdentite) {
|
||||
try {
|
||||
const reponse = await fetch('collaborateurs_identite.php');
|
||||
const data = await reponse.json();
|
||||
if (!reponse.ok) throw new Error(data.error || ('HTTP ' + reponse.status));
|
||||
collaborateursIdentite = data.collaborateurs || [];
|
||||
} catch (erreur) {
|
||||
overlayCollabPhotos.textContent = 'Impossible de charger les identités : ' + erreur.message;
|
||||
return;
|
||||
}
|
||||
}
|
||||
remplirLignePhotos(overlayCollabPhotos, collaborateursIdentite, selectionnerCollaborateurIdentite);
|
||||
}
|
||||
|
||||
function selectionnerCollaborateurIdentite(collab, bouton) {
|
||||
selectionIdentiteIncr = collab.AGN_Incr;
|
||||
marquerPhotoSelectionnee(overlayCollabPhotos, collab.AGN_Incr);
|
||||
overlayCollabTitre.textContent = 'Identité de ' + (collab.AGN_Prenom || '') +
|
||||
(collab.AGN_Fonction ? ' — ' + collab.AGN_Fonction : '');
|
||||
texteIdentite.value = collab.AGN_Identite || EBAUCHES_IDENTITE[collab.AGN_Prenom] || '';
|
||||
messageIdentite.textContent = '';
|
||||
messageIdentite.className = '';
|
||||
mettreAJourCompteurIdentite();
|
||||
corpsIdentite.hidden = false;
|
||||
texteIdentite.focus();
|
||||
}
|
||||
|
||||
btnAbandonnerIdentite.addEventListener('click', fermerOverlay);
|
||||
|
||||
btnValiderIdentite.addEventListener('click', async function () {
|
||||
if (!selectionIdentiteIncr) return;
|
||||
if (mettreAJourCompteurIdentite()) return; // garde-fou : ne jamais envoyer un texte au-delà de ce que la colonne MEDIUMTEXT peut stocker
|
||||
const bouton = this;
|
||||
bouton.disabled = true;
|
||||
messageIdentite.textContent = '';
|
||||
messageIdentite.className = '';
|
||||
try {
|
||||
const reponse = await fetch('collaborateurs_identite.php', {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ AGN_Incr: selectionIdentiteIncr, AGN_Identite: texteIdentite.value })
|
||||
});
|
||||
const data = await reponse.json();
|
||||
bouton.disabled = false;
|
||||
if (!reponse.ok) {
|
||||
messageIdentite.textContent = data.error || 'Erreur';
|
||||
messageIdentite.className = 'ko';
|
||||
return;
|
||||
}
|
||||
// Mémorise la valeur enregistrée localement, pour que la prochaine
|
||||
// sélection de ce même collaborateur affiche le texte à jour sans
|
||||
// recharger tout le tableau.
|
||||
const entree = collaborateursIdentite.find(function (c) { return c.AGN_Incr === selectionIdentiteIncr; });
|
||||
if (entree) entree.AGN_Identite = texteIdentite.value;
|
||||
messageIdentite.textContent = 'Enregistré.';
|
||||
messageIdentite.className = 'ok';
|
||||
setTimeout(fermerOverlay, 900);
|
||||
} catch (erreur) {
|
||||
bouton.disabled = false;
|
||||
messageIdentite.textContent = 'Erreur réseau';
|
||||
messageIdentite.className = 'ko';
|
||||
}
|
||||
});
|
||||
|
||||
btnConstruireIdentite.addEventListener('click', function () {
|
||||
if (modeOverlayActif === 'identite') {
|
||||
fermerOverlay();
|
||||
} else {
|
||||
ouvrirConstruireIdentite();
|
||||
}
|
||||
});
|
||||
|
||||
// -------- "Tester l'identité" --------
|
||||
// Dialogue en direct avec le collaborateur choisi, via l'API Anthropic
|
||||
// (collaborateur_dialogue.php, voir ce fichier) — l'identité elle-même
|
||||
// (AGN_Identite) n'est JAMAIS renvoyée au front-end, seule la voix du
|
||||
// collaborateur en sort, dans ses réponses.
|
||||
function ajouterBulleChat(role, texte, couleur) {
|
||||
const bulle = document.createElement('div');
|
||||
bulle.className = 'bulle-chat ' + role; // 'moi' | 'collab' | 'systeme'
|
||||
if (role === 'collab') bulle.style.background = couleur || '#ccc';
|
||||
bulle.textContent = texte;
|
||||
chatMessages.appendChild(bulle);
|
||||
chatMessages.scrollTop = chatMessages.scrollHeight;
|
||||
return bulle;
|
||||
}
|
||||
|
||||
function ouvrirTesterIdentite() {
|
||||
modeOverlayActif = 'test';
|
||||
overlayCollab.hidden = false;
|
||||
corpsIdentite.hidden = true;
|
||||
corpsTest.hidden = true; // tant qu'aucune photo n'est choisie
|
||||
overlayCollabTitre.textContent = "Tester l'identité — cliquer sur une photo";
|
||||
remplirLignePhotos(overlayCollabPhotos, agentsEcranAssistants, selectionnerCollaborateurTest);
|
||||
btnTesterIdentite.classList.add('actif');
|
||||
btnTesterIdentite.textContent = 'Fermer';
|
||||
btnConstruireIdentite.classList.remove('actif');
|
||||
btnConstruireIdentite.textContent = 'Construire une identité';
|
||||
collaborateurTest = null;
|
||||
}
|
||||
|
||||
function selectionnerCollaborateurTest(collab, bouton) {
|
||||
collaborateurTest = collab;
|
||||
historiqueChatTest = [];
|
||||
marquerPhotoSelectionnee(overlayCollabPhotos, collab.AGN_Incr);
|
||||
overlayCollabTitre.textContent = 'Discussion avec ' + (collab.AGN_Prenom || '') +
|
||||
(collab.AGN_Fonction ? ' — ' + collab.AGN_Fonction : '');
|
||||
chatMessages.innerHTML = '';
|
||||
ajouterBulleChat('systeme', 'Session de mise au point — ' + (collab.AGN_Prenom || 'ce collaborateur') + ' ne voit jamais ce message.');
|
||||
corpsTest.hidden = false;
|
||||
chatInput.value = '';
|
||||
chatInput.focus();
|
||||
}
|
||||
|
||||
async function envoyerMessageTest() {
|
||||
const texte = chatInput.value.trim();
|
||||
if (!texte || !collaborateurTest) return;
|
||||
chatInput.value = '';
|
||||
chatInput.disabled = true;
|
||||
btnChatEnvoyer.disabled = true;
|
||||
ajouterBulleChat('moi', texte);
|
||||
historiqueChatTest.push({ role: 'user', content: texte });
|
||||
const bulleAttente = ajouterBulleChat('systeme', (collaborateurTest.AGN_Prenom || 'Le collaborateur') + ' réfléchit…');
|
||||
try {
|
||||
const reponse = await fetch('collaborateur_dialogue.php', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ AGN_Incr: collaborateurTest.AGN_Incr, messages: historiqueChatTest })
|
||||
});
|
||||
const data = await reponse.json();
|
||||
bulleAttente.remove();
|
||||
if (!reponse.ok) {
|
||||
ajouterBulleChat('systeme', data.error || 'Erreur');
|
||||
historiqueChatTest.pop(); // pas de réponse obtenue : on ne le garde pas dans l'historique réexpédié au prochain essai
|
||||
return;
|
||||
}
|
||||
historiqueChatTest.push({ role: 'assistant', content: data.reponse });
|
||||
ajouterBulleChat('collab', data.reponse, collaborateurTest.AGN_Couleur);
|
||||
} catch (erreur) {
|
||||
bulleAttente.remove();
|
||||
ajouterBulleChat('systeme', 'Erreur réseau');
|
||||
historiqueChatTest.pop();
|
||||
} finally {
|
||||
chatInput.disabled = false;
|
||||
btnChatEnvoyer.disabled = false;
|
||||
chatInput.focus();
|
||||
}
|
||||
}
|
||||
|
||||
btnChatEnvoyer.addEventListener('click', envoyerMessageTest);
|
||||
chatInput.addEventListener('keydown', function (e) {
|
||||
if (e.key === 'Enter' && !e.shiftKey) {
|
||||
e.preventDefault();
|
||||
envoyerMessageTest();
|
||||
}
|
||||
});
|
||||
|
||||
btnFermerTestIdentite.addEventListener('click', fermerOverlay);
|
||||
|
||||
btnTesterIdentite.addEventListener('click', function () {
|
||||
if (modeOverlayActif === 'test') {
|
||||
fermerOverlay();
|
||||
} else {
|
||||
ouvrirTesterIdentite();
|
||||
}
|
||||
});
|
||||
|
||||
// Lien visible "→ Liste des tâches" retiré le 2 septembre 2026 à la
|
||||
// demande de Jean-Marc. Échap avait été conservé ce soir-là comme
|
||||
// échappatoire silencieuse vers liste_todolist.html, pour ne pas rester
|
||||
|
||||
Reference in New Issue
Block a user