mirror of
https://github.com/JMP-MS/ASPIC.git
synced 2026-10-27 16:07:10 +00:00
673 lines
34 KiB
HTML
673 lines
34 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="fr">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>ASPIC — Écran de fond (essai)</title>
|
||
<!-- Icône d'onglet (3 septembre 2026, voir journal) : logo Mévélia Solutions,
|
||
fourni par Jean-Marc, reprise à l'identique sur tous les écrans. -->
|
||
<link rel="icon" href="mevelia-icone.png">
|
||
<style>
|
||
/* ecran_fond.html — 2 septembre 2026
|
||
Premier essai de l'écran de fond "bureau à l'ancienne" (voir journal de
|
||
développement, section 28, et réflexions stratégiques, section 11).
|
||
Volontairement minimal pour ce test : affichage plein écran de l'image
|
||
de référence, sans aucun objet cliquable/fonctionnel — juste de quoi
|
||
voir le rendu réel après identification, avant d'aller plus loin.
|
||
Image mise à jour le 2 septembre 2026 (soir) — nouvelle version sans
|
||
tableau en liège, recadrée par Jean-Marc lui-même après un premier essai
|
||
où le "cover" rognait le bas de l'image sur son écran (proportions
|
||
d'écran différentes du 16:9 de l'image). Passage de background-size
|
||
"cover" à "contain" à cette occasion : l'image entière reste désormais
|
||
toujours visible, quelle que soit la proportion de l'écran, quitte à
|
||
laisser une bande de la couleur du mur de part et d'autre plutôt que de
|
||
rogner — plus robuste qu'un recadrage qui ne marche que pour une
|
||
proportion d'écran précise.
|
||
Image recadrée une 2ᵉ fois (toujours le 2 septembre 2026) : Jean-Marc a
|
||
utilisé une lecture de fenêtre temporaire (affichée le temps du recadrage
|
||
puis retirée) pour recadrer directement à ses proportions d'écran
|
||
réelles — les bandes "contain" restent le filet de sécurité pour tout
|
||
autre écran, mais ne devraient plus apparaître chez lui. Nouvelle image
|
||
1024×457 : toutes les coordonnées ci-dessous ont été remesurées sur
|
||
cette version (celles mesurées sur la précédente, 1280×650, ne
|
||
s'appliquent plus). */
|
||
@font-face{
|
||
font-family:"Metro DF";
|
||
src:url('MetroDF.woff2') format('woff2'),url('MetroDF.ttf') format('truetype');
|
||
font-weight:normal;font-style:normal;font-display:swap;
|
||
}
|
||
*{box-sizing:border-box;}
|
||
html,body{
|
||
margin:0;padding:0;border:0;width:100%;height:100%;overflow:hidden;
|
||
font-family:"Segoe UI","Arial",sans-serif;
|
||
}
|
||
body{
|
||
/* Couleur échantillonnée sur le mur de la photo, pour que les bandes
|
||
laissées par "contain" (si la proportion de l'écran diffère de celle
|
||
de l'image) se fondent avec la scène plutôt que de créer un bord net. */
|
||
background:#ad580d url('ecran_fond.jpg') center center / contain no-repeat;
|
||
position:relative;
|
||
}
|
||
/* Étiquette "Contacts" sur la tranche du 1er classeur (2 septembre 2026,
|
||
essai). Positionnée en JS plutôt qu'en CSS pur : comme l'image est en
|
||
"contain", sa taille et sa position réelles à l'écran changent avec la
|
||
proportion de la fenêtre — même calcul que "background-size:contain"
|
||
refait à la main pour caler ce texte sur l'étiquette blanche, quelle
|
||
que soit la fenêtre. Coordonnées mesurées sur ecran_fond.jpg (image de
|
||
référence 1280×650) : rectangle d'étiquette à peu près x:[54,91],
|
||
y:[376,481] sur cette image. rotate(-90deg) : lecture bas → haut,
|
||
comme demandé (on tourne la tête pour lire), plutôt que haut → bas. */
|
||
#etiquette-contacts{
|
||
position:fixed;
|
||
transform:translate(-50%,-50%) rotate(-90deg);
|
||
transform-origin:center;
|
||
white-space:nowrap;
|
||
color:#2a2a2a;
|
||
font-family:"Segoe UI","Arial",sans-serif;
|
||
font-weight:600;
|
||
letter-spacing:0.03em;
|
||
pointer-events:none;
|
||
user-select:none;
|
||
}
|
||
/* Zone cliquable calée sur la tranche visible (face plate, sans le
|
||
dessus incliné) du 1er classeur — même principe de positionnement en
|
||
JS que l'étiquette ci-dessus. Pas d'habillage visuel par défaut (le
|
||
classeur photographié suffit), juste un léger éclaircissement au survol
|
||
pour signaler que c'est cliquable, en plus du curseur et de l'infobulle. */
|
||
#classeur-contacts{
|
||
position:fixed;
|
||
display:block;
|
||
box-shadow:inset 0 0 0 0 rgba(255,255,255,0);
|
||
transition:box-shadow .12s ease;
|
||
}
|
||
#classeur-contacts:hover{
|
||
box-shadow:inset 0 0 0 2px rgba(255,255,255,.7);
|
||
}
|
||
/* Chevalet supprimé le 3 septembre 2026 (voir journal) : Jean-Marc l'a
|
||
rangé — "libère de la place sur le bureau" — dans la nouvelle photo qui
|
||
ne le montre plus. Le prénom de l'utilisateur reste affiché ailleurs,
|
||
dans le groupe société/prénom/marque repris de liste_contacts.html
|
||
(voir #statut-version-bloc plus bas), désormais fixé en bas à droite
|
||
de l'écran plutôt que calé sur un objet du bureau. */
|
||
/* Micro cliquable (2 septembre 2026, essai — retouché le même jour après
|
||
retour de Jean-Marc). Deux éléments distincts, superposés mais avec des
|
||
rôles différents :
|
||
- #micro-desk : la zone cliquable/survolable RÉELLE, calée sur tout le
|
||
silhouette du micro (pied, tige, corps, tête) — pas seulement la
|
||
petite tête. Corrige le souci remonté : l'infobulle ne se déclenchait
|
||
qu'en survolant précisément la petite grille du haut, zone trop
|
||
étroite pour être fiable à la souris. Invisible (pas de fond), juste
|
||
le curseur + l'infobulle (title) + le clic.
|
||
- #micro-icone : uniquement l'icône elle-même, petite, calée sur le
|
||
rond noir de la tête (voir positionner()). pointer-events:none pour
|
||
laisser passer le clic/survol vers #micro-desk en dessous. Le léger
|
||
liseré au survol (auparavant sur le bouton) est maintenant affiché
|
||
ici via le sélecteur de fratrie ~ quand #micro-desk est survolé, pour
|
||
rester visuellement au niveau de la tête plutôt qu'engober tout le
|
||
micro d'un cadre. Icônes redessinées en SVG à partir de
|
||
mic_repos.png / mic_ecoute.png (déjà utilisées pour le bouton micro
|
||
du bandeau, voir micro.js) — recolorées, sans le disque rouge
|
||
d'origine, pour se fondre dans la grille plutôt que ressortir comme
|
||
un autocollant. Fermé = glyphe micro en gris clair ; ouvert = barres
|
||
façon vu-mètre en --rouge-micro, animées (voir @keyframes) — "les
|
||
barres qui bougent" demandé par Jean-Marc. Toggle en pur JS pour ce
|
||
test : ne déclenche pas encore la reconnaissance vocale de micro.js. */
|
||
#micro-desk{
|
||
position:fixed;
|
||
border:none;background:transparent;padding:0;margin:0;
|
||
cursor:pointer;
|
||
}
|
||
#micro-icone{
|
||
position:fixed;
|
||
display:flex;align-items:center;justify-content:center;
|
||
border-radius:50%;
|
||
pointer-events:none;
|
||
box-shadow:0 0 0 0 rgba(255,255,255,0);
|
||
transition:box-shadow .12s ease;
|
||
}
|
||
#micro-desk:hover ~ #micro-icone{
|
||
box-shadow:0 0 0 3px rgba(255,255,255,.3);
|
||
}
|
||
#micro-icone svg{
|
||
width:70%;height:70%;display:block;
|
||
}
|
||
#micro-icone svg[hidden]{display:none;}
|
||
#glyphe-repos{
|
||
fill:#d9d9d3;
|
||
opacity:.92;
|
||
}
|
||
#glyphe-ecoute{
|
||
fill:#e14a5c; /* proche de --rouge-micro (#D9364B) de liste_contacts.html */
|
||
}
|
||
#glyphe-ecoute .barre{
|
||
transform-box:fill-box;
|
||
transform-origin:50% 100%;
|
||
animation:pulse-barre 1s ease-in-out infinite;
|
||
}
|
||
#glyphe-ecoute .barre:nth-child(1){ animation-delay:-.6s; }
|
||
#glyphe-ecoute .barre:nth-child(2){ animation-delay:-.3s; }
|
||
#glyphe-ecoute .barre:nth-child(3){ animation-delay:0s; }
|
||
#glyphe-ecoute .barre:nth-child(4){ animation-delay:-.45s; }
|
||
@keyframes pulse-barre{
|
||
0%,100%{ transform:scaleY(.55); }
|
||
50%{ transform:scaleY(1); }
|
||
}
|
||
/* Numéro de version : voir #statut-version-bloc plus haut — depuis le
|
||
3 septembre 2026, fixé en bas à droite de l'écran (même place et même
|
||
habillage que dans liste_contacts.html) plutôt que calé sur le bureau. */
|
||
/* Pastille "nombre de contacts" sur le classeur Contacts (2 septembre
|
||
2026, essai). Idée de Jean-Marc : au fur et à mesure que chaque classeur
|
||
sera branché à sa table, afficher une petite pastille collée dessous le
|
||
trou cerclé de métal, avec un chiffre — pour donner une indication
|
||
immédiate du contenu sans avoir à ouvrir le classeur. Couleur de fond
|
||
mesurée sur ecran_fond.jpg (teinte réelle du classeur "Contacts",
|
||
rgb≈3,79,99, plus sombre en photo qu'à l'écran) puis assombrie d'un
|
||
facteur ~0.55 pour que le chiffre blanc ressorte sans jurer avec le
|
||
classeur — pas une couleur arbitraire. Liseré blanc autour de la
|
||
pastille (2 fichiers/gris clair, cohérent avec le liseré déjà utilisé
|
||
sur les étiquettes de tous les classeurs sur la photo) + légère ombre
|
||
portée pour la détacher visuellement, plutôt qu'un anneau de couleur :
|
||
Jean-Marc n'avait pas de préférence tranchée sur la couleur de l'anneau,
|
||
le blanc a été choisi pour rester cohérent avec l'habillage existant
|
||
(à changer facilement si Jean-Marc préfère une autre couleur après test
|
||
réel). pointer-events:none : la pastille est purement informative, le
|
||
clic doit continuer à passer à travers vers #classeur-contacts en
|
||
dessous (même logique que #micro-icone au-dessus de #micro-desk). Le
|
||
pattern (variables IMG, positionnement dans positionner(), chargement
|
||
via un endpoint réel) est pensé pour être répliqué sur les prochains
|
||
classeurs au fur et à mesure de leur branchement — voir commentaire de
|
||
PASTILLE_CONTACTS_* plus bas dans le script. Retouchée le même jour
|
||
après premier test réel de Jean-Marc ("trop présente, liseré trop
|
||
pétant") : diamètre réduit (22px de référence → 17px) et liseré blanc
|
||
assoupli (opacité .85→.4, épaisseur 2px→1.5px) pour rester une
|
||
indication discrète plutôt qu'un élément qui attire l'œil en premier. */
|
||
#pastille-contacts{
|
||
position:fixed;
|
||
border-radius:50%;
|
||
background:#022b36;
|
||
box-shadow:0 0 0 1.5px rgba(255,255,255,.4), 0 1px 2px rgba(0,0,0,.45);
|
||
display:flex;align-items:center;justify-content:center;
|
||
pointer-events:none;
|
||
user-select:none;
|
||
}
|
||
#pastille-contacts span{
|
||
color:#fff;
|
||
font-family:"Segoe UI","Arial",sans-serif;
|
||
font-weight:700;
|
||
line-height:1;
|
||
}
|
||
/* Bloc société + prénom + marque (3 septembre 2026, voir journal). Posé
|
||
d'abord calé sur le bureau comme le reste de cet écran, puis Jean-Marc a
|
||
précisé vouloir "les infos en bas à droite à la même place que dans la
|
||
liste des contacts" — repris ici en position FIXE par rapport à l'écran
|
||
(et non plus en fraction de l'image), avec exactement les mêmes id, la
|
||
même mise en forme et les mêmes tailles qu'en bas de liste_contacts.html
|
||
(pixels fixes, pas de recalcul dans positionner()) : société, point
|
||
séparateur, prénom, point séparateur, puis #marque-aspic (icône Mévélia
|
||
+ logo ASPIC miniature + version). */
|
||
#statut-version-bloc{
|
||
position:fixed;
|
||
right:16px;
|
||
bottom:10px;
|
||
display:flex;align-items:center;gap:8px;
|
||
}
|
||
.point-separateur{
|
||
font-size:14px;line-height:1;color:#2a2a2a;
|
||
}
|
||
#societe-utilisateur, #prenom-utilisateur{
|
||
font-size:11px;font-weight:700;color:#2a2a2a;
|
||
font-family:"Segoe UI","Arial",sans-serif;
|
||
white-space:nowrap;
|
||
}
|
||
#marque-aspic{
|
||
display:flex;align-items:center;gap:4px;
|
||
}
|
||
#icone-mevelia{
|
||
width:16px;height:16px;
|
||
border-radius:50%;
|
||
display:block;
|
||
}
|
||
#logo-aspic-mini{
|
||
display:flex;align-items:center;
|
||
background:#1B1B1B;
|
||
border-radius:4px;
|
||
padding:2px 6px;
|
||
font-family:"Metro DF","Segoe UI Black","Segoe UI Semibold","Segoe UI",Arial,sans-serif;
|
||
font-weight:900;font-size:9px;letter-spacing:1px;
|
||
}
|
||
#logo-aspic-mini .A{color:#FF5A1F;}
|
||
#logo-aspic-mini .rest{color:#FFFFFF;}
|
||
#statut-version{
|
||
color:#4a4a4a;
|
||
font-family:"Segoe UI","Arial",sans-serif;
|
||
font-weight:700;
|
||
font-size:11px;
|
||
}
|
||
/* Post-its de la ToDoList personnelle (2 septembre 2026, premier
|
||
affichage réel — voir journal section 28). Posés dans la zone décrite
|
||
par ZONE_POSTITS_* plus bas dans le script : une bande occupant 30% de
|
||
la largeur de l'écran depuis le bord droit, jusqu'à la ligne du bureau
|
||
(chevalet supprimé le 3 septembre 2026, voir journal — la zone descend
|
||
désormais jusqu'à la nouvelle limite mesurée sur la photo, avec la même
|
||
marge de sécurité qu'avant) — décision de Jean-Marc ("le vilain petit canard sera l'humain", donc
|
||
plus de place que chaque assistant, dont la ToDoList n'est pas encore
|
||
gérée — voir Réflexions stratégiques section 11 et le fil du 2-3
|
||
septembre). Grille à 4 colonnes × 5 lignes = 20 cases, volontairement
|
||
alignée sur CAPACITE_TABLEAU_PAR_UTILISATEUR (todolist.php) pour cette
|
||
première version — les deux devront évoluer ensemble si la capacité
|
||
change. Chaque post-it reçoit une légère rotation/décalage à
|
||
l'intérieur de sa propre case (jamais à cheval sur la case voisine,
|
||
donc jamais de chevauchement ce soir — Jean-Marc a autorisé le
|
||
chevauchement partiel mais a refusé l'empilement en tas ; rester dans
|
||
sa case est la façon la plus simple de garantir qu'on ne recrée jamais
|
||
ce tas). Décalage/rotation calculés de façon déterministe à partir de
|
||
TDL_Incr (voir hashDeterministe() dans le script) : la disposition ne
|
||
"saute" pas à chaque rechargement de l'écran.
|
||
Couleur jaune classique pour l'utilisateur (--postit-jaune) — les
|
||
couleurs par assistant restent à faire le jour où leur ToDoList sera
|
||
gérée (AGN_Couleur, voir Réflexions stratégiques section 11).
|
||
Punaise ronde en haut (photo confirmée comme du vrai liège par
|
||
Jean-Marc le 2 septembre) plutôt qu'un simple post-it posé — cohérent
|
||
avec la matière réelle du fond d'écran. Pastille d'urgence toujours
|
||
dans le coin haut-droit, jamais recouverte par du jitter (voir calcul
|
||
de bornes dans positionnerPostits()) : c'est le repère visuel ET la
|
||
garantie d'une zone de clic non ambiguë même si deux post-its finissent
|
||
visuellement proches. Pas de texte sur le post-it (choix explicite de
|
||
Jean-Marc) : le titre de la tâche n'apparaît qu'au survol (title natif),
|
||
le clic ne fait rien pour l'instant (voir journal — navigation vers
|
||
liste_todolist.html volontairement pas encore branchée). */
|
||
.postit{
|
||
position:fixed;
|
||
box-shadow:2px 4px 8px rgba(0,0,0,.35);
|
||
background:
|
||
linear-gradient(135deg, transparent 50%, rgba(0,0,0,.12) 50%, rgba(0,0,0,.12) 62%, transparent 62%) bottom right / 22% 22% no-repeat,
|
||
var(--couleur-postit, #fbe14a);
|
||
cursor:default;
|
||
}
|
||
.postit::before{
|
||
content:'';
|
||
position:absolute;
|
||
top:-9px; left:50%;
|
||
transform:translateX(-50%);
|
||
width:13%; height:13%;
|
||
min-width:8px; min-height:8px;
|
||
border-radius:50%;
|
||
background:radial-gradient(circle at 35% 30%, #fff8, transparent 40%), #8a3b12;
|
||
box-shadow:0 2px 3px rgba(0,0,0,.5);
|
||
}
|
||
.postit .pastille-urgence-postit{
|
||
position:absolute;
|
||
top:-15%; right:-15%;
|
||
width:32%; height:32%;
|
||
border-radius:50%;
|
||
box-shadow:0 0 0 2px rgba(255,255,255,.9), 0 1px 2px rgba(0,0,0,.4);
|
||
}
|
||
.postit .pastille-urgence-postit.rouge{ background:#c0392b; }
|
||
.postit .pastille-urgence-postit.orange{ background:#ff5a1f; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<a id="classeur-contacts" href="liste_contacts.html" title="Ouvrir le classeur des contacts"></a>
|
||
<div id="etiquette-contacts">Contacts</div>
|
||
<button id="micro-desk" type="button"></button>
|
||
<div id="micro-icone">
|
||
<svg id="glyphe-repos" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||
<path d="M12 14c1.66 0 3-1.34 3-3V5c0-1.66-1.34-3-3-3S9 3.34 9 5v6c0 1.66 1.34 3 3 3z"/>
|
||
<path d="M17 11c0 2.76-2.24 5-5 5s-5-2.24-5-5H5c0 3.53 2.61 6.43 6 6.92V21h2v-3.08c3.39-.49 6-3.39 6-6.92h-2z"/>
|
||
</svg>
|
||
<svg id="glyphe-ecoute" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" hidden>
|
||
<rect class="barre" x="2" y="14" width="3.5" height="7" rx="1"/>
|
||
<rect class="barre" x="8" y="9" width="3.5" height="12" rx="1"/>
|
||
<rect class="barre" x="14" y="4" width="3.5" height="17" rx="1"/>
|
||
<rect class="barre" x="20" y="11" width="3.5" height="10" rx="1"/>
|
||
</svg>
|
||
</div>
|
||
<div id="pastille-contacts"><span id="pastille-contacts-nombre">…</span></div>
|
||
<div id="statut-version-bloc">
|
||
<span id="societe-utilisateur" hidden></span>
|
||
<span class="point-separateur" id="point-societe" hidden aria-hidden="true">•</span>
|
||
<span id="prenom-utilisateur"></span>
|
||
<span class="point-separateur" aria-hidden="true">•</span>
|
||
<div id="marque-aspic" title="ASPIC
|
||
Créé par Mevelia Solutions">
|
||
<img id="icone-mevelia" src="mevelia-icone.png" alt="Mévélia Solutions">
|
||
<span id="logo-aspic-mini"><span class="A">A</span><span class="rest">SPIC</span></span>
|
||
<span id="statut-version"></span>
|
||
</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
|
||
// le bureau de 13px en bas de la photo et ajouté 13px de liège en haut,
|
||
// chevalet retiré). Toutes les coordonnées ci-dessous sont exprimées en
|
||
// pixels de CETTE image de référence, puis converties en fraction pour
|
||
// rester calées quelle que soit la taille réelle d'affichage (voir calcul
|
||
// dans positionner()). Constantes remesurées directement sur la nouvelle
|
||
// photo (comparaison pixel à pixel avec l'ancienne, tranche du classeur et
|
||
// silhouette du micro) : décalage vertical uniforme de +13 confirmé pour
|
||
// tout ce qui était calé sur le bureau ou le mur, aucun décalage
|
||
// horizontal — cohérent avec la description de Jean-Marc.
|
||
const IMG_W = 1024, IMG_H = 457;
|
||
// Centre de l'étiquette du 1er classeur (57.5, 282 -> 57.5, 295 : +13).
|
||
const ETIQ_CENTRE_X_FRAC = 57.5 / IMG_W;
|
||
const ETIQ_CENTRE_Y_FRAC = 295 / IMG_H;
|
||
const ETIQ_POLICE_FRAC = 19 / IMG_W;
|
||
// Rectangle de la tranche visible du 1er classeur (x inchangé, y:[224,397]
|
||
// -> y:[237,410]).
|
||
const CLASSEUR_X1 = 37, CLASSEUR_X2 = 78;
|
||
const CLASSEUR_Y1 = 237, CLASSEUR_Y2 = 410;
|
||
// Chevalet supprimé le 3 septembre 2026 (voir journal) : plus d'objet à
|
||
// cet endroit sur la nouvelle photo, Jean-Marc l'a rangé.
|
||
// Zone cliquable/survolable du micro : silhouette complète, x inchangé,
|
||
// y:[236,406] -> y:[249,419].
|
||
const MICRO_ZONE_X1 = 465, MICRO_ZONE_X2 = 543;
|
||
const MICRO_ZONE_Y1 = 249, MICRO_ZONE_Y2 = 419;
|
||
// Centre du rond noir en haut du micro (grille), pour l'icône seule :
|
||
// (512, 255) -> (512, 268).
|
||
const ICONE_CENTRE_X = 512, ICONE_CENTRE_Y = 268;
|
||
const ICONE_TAILLE_FRAC = 22 / IMG_W;
|
||
// Pastille "nombre de contacts", sous le trou cerclé de métal du classeur
|
||
// Contacts : centre (58, 381) -> (58, 394).
|
||
const PASTILLE_CONTACTS_CENTRE_X = 58, PASTILLE_CONTACTS_CENTRE_Y = 394;
|
||
const PASTILLE_CONTACTS_DIAM_FRAC = 17 / IMG_W;
|
||
|
||
// Zone des post-its personnels (2 septembre 2026, premier affichage réel
|
||
// — voir commentaire CSS .postit ci-dessus pour le détail des décisions).
|
||
// 30% de la largeur de l'écran depuis le bord droit (1024 × 0.30 ≈ 307).
|
||
// Bas de la zone (Y2) recalculé le 3 septembre 2026 : n'était plus borné
|
||
// par le chevalet (supprimé) mais par la nouvelle ligne de bureau
|
||
// (CLASSEUR_Y2 = 410), avec la même marge de sécurité qu'avant (~15px) —
|
||
// 352 devient 395, quelques pixels de plus qu'un simple +13 puisque
|
||
// l'espace se libère réellement, comme relevé par Jean-Marc. Grille
|
||
// 4 colonnes × 5 lignes = 20 cases, volontairement alignée sur
|
||
// CAPACITE_TABLEAU_PAR_UTILISATEUR (todolist.php) — à faire évoluer
|
||
// ensemble le jour où cette capacité changera.
|
||
const ZONE_POSTITS_X1 = 717, ZONE_POSTITS_X2 = 1024;
|
||
const ZONE_POSTITS_Y1 = 6, ZONE_POSTITS_Y2 = 395;
|
||
const GRILLE_POSTITS_COLONNES = 4, GRILLE_POSTITS_LIGNES = 5;
|
||
// Taille d'un post-it : ~60% de la plus petite dimension de case, pour
|
||
// laisser de la marge au jitter sans jamais déborder sur la case voisine
|
||
// (voir positionnerPostits()).
|
||
const POSTIT_RATIO_CASE = 0.6;
|
||
|
||
/**
|
||
* Petit générateur pseudo-aléatoire déterministe (0..1), à partir d'un
|
||
* entier (TDL_Incr) — pour que le jitter (rotation/décalage) d'un post-it
|
||
* donné reste toujours le même d'un rechargement à l'autre de l'écran,
|
||
* plutôt que de "sauter" à chaque fois (mulberry32, implémentation
|
||
* minimale, largement suffisante ici — pas besoin de qualité crypto).
|
||
*/
|
||
function pseudoAleatoire(graine) {
|
||
let t = (graine + 0x6D2B79F5) | 0;
|
||
t = Math.imul(t ^ (t >>> 15), t | 1);
|
||
t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
|
||
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
|
||
}
|
||
|
||
function positionner() {
|
||
const vw = window.innerWidth, vh = window.innerHeight;
|
||
const ratioImg = IMG_W / IMG_H;
|
||
const ratioVp = vw / vh;
|
||
let dispW, dispH, offX, offY;
|
||
if (ratioVp > ratioImg) {
|
||
dispH = vh; dispW = dispH * ratioImg;
|
||
offX = (vw - dispW) / 2; offY = 0;
|
||
} else {
|
||
dispW = vw; dispH = dispW / ratioImg;
|
||
offX = 0; offY = (vh - dispH) / 2;
|
||
}
|
||
const etiquette = document.getElementById('etiquette-contacts');
|
||
etiquette.style.left = (offX + ETIQ_CENTRE_X_FRAC * dispW) + 'px';
|
||
etiquette.style.top = (offY + ETIQ_CENTRE_Y_FRAC * dispH) + 'px';
|
||
etiquette.style.fontSize = (ETIQ_POLICE_FRAC * dispW) + 'px';
|
||
|
||
const classeur = document.getElementById('classeur-contacts');
|
||
classeur.style.left = (offX + (CLASSEUR_X1 / IMG_W) * dispW) + 'px';
|
||
classeur.style.top = (offY + (CLASSEUR_Y1 / IMG_H) * dispH) + 'px';
|
||
classeur.style.width = (((CLASSEUR_X2 - CLASSEUR_X1) / IMG_W) * dispW) + 'px';
|
||
classeur.style.height = (((CLASSEUR_Y2 - CLASSEUR_Y1) / IMG_H) * dispH) + 'px';
|
||
|
||
const microDesk = document.getElementById('micro-desk');
|
||
microDesk.style.left = (offX + (MICRO_ZONE_X1 / IMG_W) * dispW) + 'px';
|
||
microDesk.style.top = (offY + (MICRO_ZONE_Y1 / IMG_H) * dispH) + 'px';
|
||
microDesk.style.width = (((MICRO_ZONE_X2 - MICRO_ZONE_X1) / IMG_W) * dispW) + 'px';
|
||
microDesk.style.height = (((MICRO_ZONE_Y2 - MICRO_ZONE_Y1) / IMG_H) * dispH) + 'px';
|
||
|
||
const microIcone = document.getElementById('micro-icone');
|
||
const iconeTaille = ICONE_TAILLE_FRAC * dispW;
|
||
microIcone.style.left = (offX + (ICONE_CENTRE_X / IMG_W) * dispW - iconeTaille / 2) + 'px';
|
||
microIcone.style.top = (offY + (ICONE_CENTRE_Y / IMG_H) * dispH - iconeTaille / 2) + 'px';
|
||
microIcone.style.width = iconeTaille + 'px';
|
||
microIcone.style.height = iconeTaille + 'px';
|
||
|
||
// #statut-version-bloc (société/prénom/marque) ne fait plus partie de ce
|
||
// calcul depuis le 3 septembre 2026 : position fixe en bas à droite de
|
||
// l'écran, en pixels fixes CSS — voir le commentaire au-dessus de sa
|
||
// règle #statut-version-bloc et le journal.
|
||
|
||
const pastilleContacts = document.getElementById('pastille-contacts');
|
||
const pastilleDiam = PASTILLE_CONTACTS_DIAM_FRAC * dispW;
|
||
pastilleContacts.style.left = (offX + (PASTILLE_CONTACTS_CENTRE_X / IMG_W) * dispW - pastilleDiam / 2) + 'px';
|
||
pastilleContacts.style.top = (offY + (PASTILLE_CONTACTS_CENTRE_Y / IMG_H) * dispH - pastilleDiam / 2) + 'px';
|
||
pastilleContacts.style.width = pastilleDiam + 'px';
|
||
pastilleContacts.style.height = pastilleDiam + 'px';
|
||
// Taille de police proportionnelle au diamètre, réduite si le nombre a
|
||
// 3 chiffres ou plus pour continuer à tenir dans le rond sans déborder.
|
||
const nbChiffres = document.getElementById('pastille-contacts-nombre').textContent.length;
|
||
const facteurPolice = nbChiffres >= 4 ? 0.34 : (nbChiffres === 3 ? 0.42 : 0.52);
|
||
document.getElementById('pastille-contacts-nombre').style.fontSize = (pastilleDiam * facteurPolice) + 'px';
|
||
|
||
// Post-its personnels : chaque .postit porte sa case (data-colonne/
|
||
// data-ligne, attribuées au chargement — voir creerPostits()) et une
|
||
// graine (data-graine = TDL_Incr) pour un jitter stable d'un
|
||
// rechargement à l'autre. Le jitter reste borné à l'intérieur de la
|
||
// case (marge × 0.8, jamais 1) pour ne jamais déborder sur la case
|
||
// voisine — c'est ce qui garantit l'absence d'empilement.
|
||
const zoneW = ((ZONE_POSTITS_X2 - ZONE_POSTITS_X1) / IMG_W) * dispW;
|
||
const zoneH = ((ZONE_POSTITS_Y2 - ZONE_POSTITS_Y1) / IMG_H) * dispH;
|
||
const caseW = zoneW / GRILLE_POSTITS_COLONNES;
|
||
const caseH = zoneH / GRILLE_POSTITS_LIGNES;
|
||
const postitTaille = Math.min(caseW, caseH) * POSTIT_RATIO_CASE;
|
||
const zoneLeft = offX + (ZONE_POSTITS_X1 / IMG_W) * dispW;
|
||
const zoneTop = offY + (ZONE_POSTITS_Y1 / IMG_H) * dispH;
|
||
const margeX = (caseW - postitTaille) / 2 * 0.8;
|
||
const margeY = (caseH - postitTaille) / 2 * 0.8;
|
||
document.querySelectorAll('.postit').forEach(function (postit) {
|
||
const col = Number(postit.dataset.colonne);
|
||
const ligne = Number(postit.dataset.ligne);
|
||
const graine = Number(postit.dataset.graine);
|
||
const caseLeft = zoneLeft + col * caseW;
|
||
const caseTop = zoneTop + ligne * caseH;
|
||
const decalageX = (pseudoAleatoire(graine) * 2 - 1) * margeX;
|
||
const decalageY = (pseudoAleatoire(graine + 1) * 2 - 1) * margeY;
|
||
const rotation = (pseudoAleatoire(graine + 2) * 2 - 1) * 7; // ±7°
|
||
postit.style.width = postitTaille + 'px';
|
||
postit.style.height = postitTaille + 'px';
|
||
postit.style.left = (caseLeft + (caseW - postitTaille) / 2 + decalageX) + 'px';
|
||
postit.style.top = (caseTop + (caseH - postitTaille) / 2 + decalageY) + 'px';
|
||
postit.style.transform = 'rotate(' + rotation.toFixed(1) + 'deg)';
|
||
});
|
||
}
|
||
window.addEventListener('resize', positionner);
|
||
positionner();
|
||
|
||
// Bascule ouvert/fermé du micro du bureau — pur affichage pour ce test
|
||
// (icône + infobulle), ne démarre pas encore la reconnaissance vocale
|
||
// réelle de micro.js. Fermé par défaut à l'arrivée sur l'écran.
|
||
let microOuvert = false;
|
||
function appliquerEtatMicroDesk() {
|
||
const bouton = document.getElementById('micro-desk');
|
||
const glyphRepos = document.getElementById('glyphe-repos');
|
||
const glyphEcoute = document.getElementById('glyphe-ecoute');
|
||
// setAttribute/removeAttribute plutôt que la propriété .hidden : sur un
|
||
// <svg> inline, la réflexion de .hidden s'est révélée peu fiable au test
|
||
// (attribut inchangé malgré l'affectation) — passage par l'attribut DOM
|
||
// directement pour un résultat garanti.
|
||
if (microOuvert) {
|
||
glyphRepos.setAttribute('hidden', '');
|
||
glyphEcoute.removeAttribute('hidden');
|
||
bouton.title = 'Le micro est ouvert, cliquez pour le fermer';
|
||
} else {
|
||
glyphRepos.removeAttribute('hidden');
|
||
glyphEcoute.setAttribute('hidden', '');
|
||
bouton.title = 'Le micro est fermé, cliquez pour l\'ouvrir';
|
||
}
|
||
}
|
||
document.getElementById('micro-desk').addEventListener('click', () => {
|
||
microOuvert = !microOuvert;
|
||
appliquerEtatMicroDesk();
|
||
});
|
||
appliquerEtatMicroDesk();
|
||
|
||
// Société et prénom de l'utilisateur connecté (3 septembre 2026, voir
|
||
// journal) : preferences.php, même endpoint et même logique que
|
||
// chargerPreferences() dans liste_contacts.html — repris ici à l'identique
|
||
// pour le groupe #statut-version-bloc (voir plus haut, id communs avec
|
||
// liste_contacts.html). Le chevalet qui affichait seul le prénom
|
||
// (USR_Prenom, ajouté le 2 septembre 2026 dans ASP_Users) a été retiré du
|
||
// bureau le 3 septembre — cette fonction ne porte donc plus que ce groupe.
|
||
// Repli silencieux si la session n'est pas active.
|
||
async function chargerIdentiteUtilisateur() {
|
||
const elSociete = document.getElementById('societe-utilisateur');
|
||
const elPointSociete = document.getElementById('point-societe');
|
||
const elPrenom = document.getElementById('prenom-utilisateur');
|
||
try {
|
||
const reponsePrefs = await fetch('preferences.php');
|
||
if (!reponsePrefs.ok) throw new Error('HTTP ' + reponsePrefs.status);
|
||
const prefs = await reponsePrefs.json();
|
||
if (prefs.USR_Societe) {
|
||
elSociete.textContent = prefs.USR_Societe;
|
||
elSociete.hidden = false;
|
||
elPointSociete.hidden = false;
|
||
} else {
|
||
elSociete.hidden = true;
|
||
elPointSociete.hidden = true;
|
||
}
|
||
if (prefs.USR_Prenom) elPrenom.textContent = prefs.USR_Prenom;
|
||
} catch (erreur) {
|
||
console.error('Erreur de chargement de preferences.php (identité) :', erreur);
|
||
}
|
||
}
|
||
chargerIdentiteUtilisateur();
|
||
|
||
// Numéro de version : même endpoint que #statut-version dans
|
||
// liste_contacts.html (chargerVersion(), voir journal). Repli silencieux
|
||
// si non joignable (fichier hors serveur, ou version.php absent de ce
|
||
// dossier) : l'écran reste utilisable sans.
|
||
async function chargerVersionBureau() {
|
||
try {
|
||
const reponse = await fetch('version.php');
|
||
if (!reponse.ok) throw new Error('HTTP ' + reponse.status);
|
||
const data = await reponse.json();
|
||
if (data.version) {
|
||
document.getElementById('statut-version').textContent = data.version;
|
||
// Infobulle du groupe icône Mévélia + logo ASPIC + version (3
|
||
// septembre 2026, voir journal), identique à liste_contacts.html.
|
||
const elMarque = document.getElementById('marque-aspic');
|
||
if (elMarque) elMarque.title = 'ASPIC ' + data.version + '\nCréé par Mevelia Solutions';
|
||
}
|
||
} catch (erreur) {
|
||
console.error('Erreur de chargement de version.php (bureau) :', erreur);
|
||
}
|
||
}
|
||
chargerVersionBureau();
|
||
|
||
// Pastille "nombre de contacts" du classeur Contacts : total réel de la
|
||
// table (contacts.php?compte=1, voir journal section 28 et commentaire
|
||
// ajouté dans contacts.php), jamais un chiffre figé. Repli visuel "—" en
|
||
// cas d'échec (même logique que chargerIdentiteUtilisateur), pour ne jamais
|
||
// laisser une pastille vide sans explication. Le pattern (constantes
|
||
// PASTILLE_*, positionnement dans positionner(), chargement ici) est
|
||
// pensé pour être dupliqué sur les prochains classeurs à mesure qu'ils
|
||
// seront branchés à leur table.
|
||
async function chargerCompteContacts() {
|
||
const elNombre = document.getElementById('pastille-contacts-nombre');
|
||
try {
|
||
const reponse = await fetch('contacts.php?compte=1');
|
||
if (!reponse.ok) throw new Error('HTTP ' + reponse.status);
|
||
const data = await reponse.json();
|
||
elNombre.textContent = (typeof data.total === 'number') ? String(data.total) : '—';
|
||
} catch (erreur) {
|
||
console.error('Erreur de chargement de contacts.php?compte=1 (pastille classeur) :', erreur);
|
||
elNombre.textContent = '—';
|
||
}
|
||
positionner(); // recalcule la taille de police selon le nombre réel de chiffres
|
||
}
|
||
chargerCompteContacts();
|
||
|
||
// Post-its personnels (2 septembre 2026, premier affichage réel — voir
|
||
// commentaire CSS .postit et constantes ZONE_POSTITS_*/GRILLE_POSTITS_*
|
||
// plus haut). Source : todolist.php (déjà utilisé par liste_todolist.html,
|
||
// voir journal section 26), filtré ici côté client sur l'utilisateur
|
||
// connecté (TDL_ToDoUser) et sur les tâches non réalisées (TDL_RealDate
|
||
// vide) — l'endpoint reste volontairement non filtré par utilisateur
|
||
// (voir son commentaire d'en-tête), donc le tri "c'est MES post-its" se
|
||
// fait ici. L'API renvoie déjà les tâches triées par urgence (rouge avant
|
||
// orange avant vert, voir calculerGroupe()) : cet ordre est conservé pour
|
||
// remplir la grille case par case, donc les plus urgentes arrivent
|
||
// toujours en premier (en haut à gauche de la zone). Une tâche déjà
|
||
// réalisée ne crée jamais de post-it : elle libère sa place sur le
|
||
// tableau dès qu'elle est marquée faite (voir aussi CAPACITE_TABLEAU_
|
||
// PAR_UTILISATEUR dans todolist.php — même logique des deux côtés).
|
||
// Coupé à la capacité de la grille par sécurité (ne devrait normalement
|
||
// jamais arriver, la capacité étant déjà appliquée à la création côté
|
||
// todolist.php).
|
||
async function chargerMesPostits() {
|
||
try {
|
||
const [reponsePrefs, reponseTaches] = await Promise.all([
|
||
fetch('preferences.php'),
|
||
fetch('todolist.php'),
|
||
]);
|
||
if (!reponsePrefs.ok) throw new Error('HTTP ' + reponsePrefs.status + ' (preferences.php)');
|
||
if (!reponseTaches.ok) throw new Error('HTTP ' + reponseTaches.status + ' (todolist.php)');
|
||
const prefs = await reponsePrefs.json();
|
||
const dataTaches = await reponseTaches.json();
|
||
const capacite = GRILLE_POSTITS_COLONNES * GRILLE_POSTITS_LIGNES;
|
||
const mesTaches = (dataTaches.taches || [])
|
||
.filter(t => t.TDL_ToDoUser === prefs.USR_Code && !t.TDL_RealDate)
|
||
.slice(0, capacite);
|
||
|
||
mesTaches.forEach((tache, index) => {
|
||
const postit = document.createElement('div');
|
||
postit.className = 'postit';
|
||
postit.style.setProperty('--couleur-postit', '#fbe14a'); // jaune classique, toujours pour l'humain
|
||
postit.title = tache.TDL_ToDoTxt || '';
|
||
postit.dataset.colonne = index % GRILLE_POSTITS_COLONNES;
|
||
postit.dataset.ligne = Math.floor(index / GRILLE_POSTITS_COLONNES);
|
||
postit.dataset.graine = tache.TDL_Incr;
|
||
|
||
const pastille = document.createElement('div');
|
||
// TDL_Groupe 0 = rouge, 1 = orange (le vert n'atteint jamais cet
|
||
// écran, voir filtre ci-dessus sur TDL_RealDate).
|
||
pastille.className = 'pastille-urgence-postit ' + (tache.TDL_Groupe === 0 ? 'rouge' : 'orange');
|
||
postit.appendChild(pastille);
|
||
|
||
document.body.appendChild(postit);
|
||
});
|
||
|
||
positionner(); // place les post-its qui viennent d'être créés
|
||
} catch (erreur) {
|
||
console.error('Erreur de chargement des post-its personnels (todolist.php/preferences.php) :', erreur);
|
||
}
|
||
}
|
||
chargerMesPostits();
|
||
|
||
// 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
|
||
// bloqué sur cet écran tant qu'il ne portait aucun objet fonctionnel.
|
||
// Retiré le 3 septembre 2026 (bug signalé par Jean-Marc) : cet écran est
|
||
// depuis la veille le véritable écran d'accueil post-identification (voir
|
||
// journal, section 28) — liste_contacts.html y ramène désormais par Échap
|
||
// (voir journal du 3 septembre), donc un Échap qui, depuis l'écran
|
||
// d'accueil lui-même, repartait ailleurs créait une boucle incohérente.
|
||
// Aucune redirection ne remplace celle-ci : Échap ne fait plus rien ici,
|
||
// faute d'un "retour" qui ait un sens depuis l'écran d'accueil. À revoir
|
||
// le jour où la ToDoList aura son propre objet cliquable sur le bureau.
|
||
</script>
|
||
|
||
</body>
|
||
</html>
|