Tableaux de bord et interfaces d’administration

Panneau de chaleur du calendrier d’exploitation de la bibliothèque

Réaliser un tableau de bord en carte thermique du calendrier dans un contexte d’exploitation de bibliothèque : il comprend un contenu réel, des contrôles utilisables, un retour d’état clair et une mise en page adaptée à plusieurs tailles d’écran.

Tableaux de bord et interfaces d’administrationPanneau de chaleur du calendrierHTMLCanvasJavaScript
Après activation de JavaScript, ce cas peut être modifié, exécuté et enregistré. Code source fixe ci-dessous :
<!doctype html>
<html lang="fr" dir="ltr">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Panneau de chaleur du calendrier d’exploitation de la bibliothèque</title>
  <style>
    :root { --ink:#ecf7f3; --paper:#071310; --accent:#e6a23c; --secondary:#a8a1d8; --line:color-mix(in srgb, var(--ink) 18%, transparent); --shadow:0 24px 60px color-mix(in srgb, var(--ink) 16%, transparent); color-scheme:light; }
    * { box-sizing:border-box; }
    html { min-width:320px; background:var(--paper); }
    body { min-height:100vh; margin:0; overflow-x:hidden; color:var(--ink); font-family:ui-monospace,"SFMono-Regular",Consolas,monospace; background:linear-gradient(color-mix(in srgb,var(--secondary) 5%,transparent) 1px,transparent 1px),linear-gradient(90deg,color-mix(in srgb,var(--secondary) 5%,transparent) 1px,transparent 1px),var(--paper);background-size:24px 24px; }
    button,input,select,textarea { color:inherit; font:inherit; }
    button,[role="button"],input,select,textarea { accent-color:var(--accent); }
    button { cursor:pointer; }
    button:focus-visible,input:focus-visible,select:focus-visible,textarea:focus-visible,[tabindex]:focus-visible { outline:3px solid var(--accent); outline-offset:3px; }
    [hidden] { display:none !important; }
    @media (max-width:700px) { .mast h1 { line-height:1.08 !important; letter-spacing:-.035em !important; overflow-wrap:anywhere; } }
    @media (prefers-reduced-motion:reduce) { *,*::before,*::after { scroll-behavior:auto !important; animation-duration:.01ms !important; animation-iteration-count:1 !important; transition-duration:.01ms !important; } }
    .page{width:min(1080px,calc(100% - 30px));margin:auto;padding:48px 0}.eyebrow{color:var(--secondary);font:700 12px/1.3 ui-monospace,monospace}.eyebrow::before{content:"> ";color:var(--accent)}.mast{display:grid;grid-template-columns:1fr auto;border:1px solid var(--secondary);padding:18px;gap:24px;align-items:center;margin-bottom:16px}.mast h1{font-family:ui-monospace,"SFMono-Regular",monospace;font-weight:700;font-size:clamp(34px,7vw,72px);letter-spacing:-.055em;margin:12px 0;line-height:1.05}.intro{margin:0;color:color-mix(in srgb,var(--ink) 72%,transparent);font:400 14px/1.7 ui-monospace,monospace}.scenario{padding:7px 10px;border:1px dashed var(--secondary);font:700 11px/1 ui-monospace,monospace}.demo{border-color:var(--secondary);border-radius:8px;box-shadow:0 0 50px color-mix(in srgb,var(--secondary) 15%,transparent);position:relative;min-height:390px;padding:26px;background:color-mix(in srgb,var(--paper) 94%,#000)}@media(max-width:700px){.page{width:calc(100% - 18px);padding:20px 0}.mast{grid-template-columns:1fr}.demo{padding:14px}}
    .panel{padding:16px;border:3px double var(--accent);background:var(--paper)}.stack{display:grid;gap:14px}.row{display:grid;grid-auto-flow:column;grid-auto-columns:max-content;gap:10px;overflow:auto}.action{padding:10px 15px;border:1px solid var(--accent);background:transparent;font-family:Didot,serif;letter-spacing:.06em}.action.primary,.action.is-active{color:var(--paper);background:var(--ink)}.metric{font-family:Didot,serif;font-size:clamp(36px,8vw,74px);letter-spacing:.04em}.status{min-height:24px;padding:9px;border-inline:1px solid var(--accent);text-align:center}.muted{line-height:1.8}.chip{padding:5px 14px;border-inline:1px solid var(--accent);letter-spacing:.1em}.meter{height:5px;background:var(--line)}.meter>i{display:block;height:100%;background:linear-gradient(90deg,var(--accent),var(--secondary))}
    .composition-split{display:grid;grid-template-columns:150px minmax(0,1fr);gap:18px;align-items:start}.split-note{position:sticky;top:20px;display:grid;gap:14px;padding:18px 12px;border-block:3px double var(--accent);font-size:13px;line-height:1.65}.split-note b{font:900 12px/1 ui-monospace,monospace;letter-spacing:.12em}.split-note span{writing-mode:vertical-rl;min-height:120px;color:color-mix(in srgb,var(--ink) 60%,transparent)}.composition-split .demo{min-width:0}@media(max-width:900px){.composition-split{grid-template-columns:1fr}.split-note{position:static;grid-template-columns:auto 1fr}.split-note span{writing-mode:horizontal-tb;min-height:0}}
    .calendar-heatmap-demo{display:grid;gap:12px;max-width:760px;margin:auto}.calendar-heatmap-demo header{display:flex;justify-content:space-between;gap:18px;align-items:end}.calendar-heatmap-demo h2{margin:.3em 0}.weekday-row,.calendar-heatmap{display:grid;grid-template-columns:repeat(7,1fr);gap:7px}.weekday-row{text-align:center;font-size:12px}.calendar-heatmap button{aspect-ratio:1;border:0;border-radius:8px;background:color-mix(in srgb,var(--accent) calc(var(--heat)*20%),var(--paper))}.calendar-heatmap [data-heat="0"]{--heat:0}.calendar-heatmap [data-heat="1"]{--heat:1}.calendar-heatmap [data-heat="2"]{--heat:2}.calendar-heatmap [data-heat="3"]{--heat:3}.calendar-heatmap [data-heat="4"]{--heat:4;color:var(--paper)}.calendar-heatmap button[aria-selected="true"]{outline:3px solid var(--ink);outline-offset:2px}.calendar-heatmap-demo footer{display:flex;gap:5px;justify-content:flex-end;align-items:center}.calendar-heatmap-demo footer i{width:16px;height:16px;border-radius:4px;background:var(--line)}.calendar-heatmap-demo footer i:nth-of-type(2){background:color-mix(in srgb,var(--accent) 25%,var(--paper))}.calendar-heatmap-demo footer i:nth-of-type(3){background:color-mix(in srgb,var(--accent) 50%,var(--paper))}.calendar-heatmap-demo footer i:nth-of-type(4){background:color-mix(in srgb,var(--accent) 75%,var(--paper))}.calendar-heatmap-demo footer i:nth-of-type(5){background:var(--accent)}.workflow-dispatch{display:grid;grid-template-columns:minmax(210px,.48fr) minmax(0,1.52fr);gap:18px}.dispatch-console{display:grid;align-content:start;gap:16px;padding:22px;border:1px solid var(--line);border-radius:18px;background:color-mix(in srgb,var(--secondary) 12%,var(--paper))}.dispatch-console h2{margin:.2em 0}.dispatch-console form,.dispatch-console label{display:grid;gap:7px}.dispatch-console input,.dispatch-console select{min-width:0;width:100%;padding:10px;border:1px solid var(--line);border-radius:8px;background:var(--paper)}.dispatch-console fieldset{display:flex;gap:12px;border:1px solid var(--line);border-radius:10px}.dispatch-console fieldset label{display:flex;align-items:center}.dispatch-workbench{--dispatch-load:.22;min-width:0;padding:14px;border-left:8px solid color-mix(in srgb,var(--accent) calc(var(--dispatch-load)*100%),var(--line));background:linear-gradient(90deg,color-mix(in srgb,var(--accent) 8%,transparent),transparent 28%)}.dispatch-workbench[data-dispatch-priority="urgent"]{box-shadow:inset 0 0 0 2px var(--accent)}@media(max-width:900px){.workflow-dispatch{grid-template-columns:1fr}.dispatch-console{order:2}}
  </style>
</head>
<body data-aesthetic="terminal" data-composition="split-note">
  <main class="page">
    <header class="mast"><div><span class="eyebrow">terminal / panneau thermique de calendrier / BEGINNER</span><h1>Panneau de chaleur du calendrier d’exploitation de la bibliothèque</h1><p class="intro">Réaliser un tableau de bord en carte thermique du calendrier dans un contexte d’exploitation de bibliothèque : il comprend un contenu réel, des contrôles utilisables, un retour d’état clair et une mise en page adaptée à plusieurs tailles d’écran.</p></div><span class="scenario">Fonctionnement de la bibliothèque</span></header>
    <div class="composition-split"><aside class="split-note"><b>CASE 02</b><span>Maîtriser la structure sémantique, la composition responsive, les interactions au clavier et la synchronisation des états du panneau de chaleur du calendrier.</span></aside><section class="demo" aria-label="Zone interactive du tableau de bord thermique des opérations de bibliothèque"><div class="workflow-dispatch"><aside class="dispatch-console"><span class="eyebrow">Exploitation de la bibliothèque DISPATCH</span><h2>Poste de pilotage des tâches de la collection</h2><form id="workflowDispatchForm"><label>Type de tâche<select id="workflowDispatchKind"><option>Réapprovisionnement des emprunts</option><option>Traitement des rendez-vous</option><option>Inventaire des collections</option></select></label><label>Nombre de tâches en attente<input id="workflowDispatchAmount" type="number" min="1" max="9999" value="36"><span>élément</span></label><fieldset><legend>Priorité de traitement</legend><label><input type="radio" name="workflowPriority" value="normal" checked>Standard</label><label><input type="radio" name="workflowPriority" value="urgent">Urgent</label></fieldset><button class="action primary" type="submit">Affecter des tâches</button></form><label>Tâche en cours<select id="workflowDispatchActive" aria-label="Basculer la tâche en cours"><option value="1">Réapprovisionnement des emprunts</option><option value="2">Traitement des rendez-vous</option></select></label><output id="workflowDispatchSummary">En attente de planification</output></aside><section id="workflowCore" class="workflow-core dispatch-workbench" data-dispatch-priority="normal"><!-- 11DZ_CORE_START:dashboards-admin-calendar-heatmap --><div class="calendar-heatmap-demo"><header><span class="eyebrow">CALENDAR HEATMAP</span><h2>Calendrier d’activité des opérations de la bibliothèque</h2><output id="heatmapSelection">Sélectionnez une date pour voir le niveau d’activité</output></header><div class="weekday-row"><span>Un</span><span>Deux</span><span>trois</span><span>quatre</span><span>Cinq</span><span>Six</span><span>Jour</span></div><div class="calendar-heatmap" role="grid" aria-label="7 activité mensuelle"><button type="button" role="gridcell" data-heat="0" aria-label="7 mois 1 jour, activité 0" data-core-bridge="dashboards-admin-calendar-heatmap">1</button><button type="button" role="gridcell" data-heat="2" aria-label="7 mois 2 jour, activité 2">2</button><button type="button" role="gridcell" data-heat="4" aria-label="7 mois 3 jour, niveau d’activité 4">3</button><button type="button" role="gridcell" data-heat="1" aria-label="7 mois 4 jour, activité 1">4</button><button type="button" role="gridcell" data-heat="3" aria-label="7 mois 5 jour, activité 3">5</button><button type="button" role="gridcell" data-heat="0" aria-label="7 mois 6 jour, activité 0">6</button><button type="button" role="gridcell" data-heat="2" aria-label="7 mois 7 jour, activité 2">7</button><button type="button" role="gridcell" data-heat="4" aria-label="7 mois 8 jour, activité 4">8</button><button type="button" role="gridcell" data-heat="1" aria-label="7 mois 9 jour, activité 1">9</button><button type="button" role="gridcell" data-heat="3" aria-label="7 mois 10 jour, activité 3">10</button><button type="button" role="gridcell" data-heat="0" aria-label="7 mois 11 jour, activité 0">11</button><button type="button" role="gridcell" data-heat="2" aria-label="7 mois 12 jour, activité 2">12</button><button type="button" role="gridcell" data-heat="4" aria-label="7 mois 13 jour, activité 4">13</button><button type="button" role="gridcell" data-heat="1" aria-label="7 mois 14 jour, activité 1">14</button><button type="button" role="gridcell" data-heat="3" aria-label="7 mois 15 jour, activité 3">15</button><button type="button" role="gridcell" data-heat="0" aria-label="7 mois 16 jour, activité 0">16</button><button type="button" role="gridcell" data-heat="2" aria-label="7 mois 17 jour, activité 2">17</button><button type="button" role="gridcell" data-heat="4" aria-label="7 mois 18 jour, activité 4">18</button><button type="button" role="gridcell" data-heat="1" aria-label="7 mois 19 jour, activité 1">19</button><button type="button" role="gridcell" data-heat="3" aria-label="7 mois 20 jour, activité 3">20</button><button type="button" role="gridcell" data-heat="0" aria-label="7 mois 21 jour, activité 0">21</button><button type="button" role="gridcell" data-heat="2" aria-label="7 mois 22 jour, activité 2">22</button><button type="button" role="gridcell" data-heat="4" aria-label="7 mois 23 jour, activité 4">23</button><button type="button" role="gridcell" data-heat="1" aria-label="7 mois 24 jour, activité 1">24</button><button type="button" role="gridcell" data-heat="3" aria-label="7 mois 25 jour, activité 3">25</button><button type="button" role="gridcell" data-heat="0" aria-label="7 mois 26 jour, activité 0">26</button><button type="button" role="gridcell" data-heat="2" aria-label="7 mois 27 jour, activité 2">27</button><button type="button" role="gridcell" data-heat="4" aria-label="7 mois 28 jour, activité 4">28</button><button type="button" role="gridcell" data-heat="1" aria-label="7 mois 29 jour, activité 1">29</button><button type="button" role="gridcell" data-heat="3" aria-label="7 mois 30 jour, activité 3">30</button><button type="button" role="gridcell" data-heat="0" aria-label="7 mois 31 jour, activité 0">31</button></div><footer><span>Peu</span><i data-heat="0"></i><i data-heat="1"></i><i data-heat="2"></i><i data-heat="3"></i><i data-heat="4"></i><span>plusieurs</span></footer></div><!-- 11DZ_CORE_END --></section></div><p id="status" class="status" role="status" aria-live="polite"></p></section></div>
  </main>
  <script>
    'use strict';
    const statusOutput = document.querySelector('#status');
    const announce = message => { statusOutput.textContent = message; };
    /* 11DZ_CORE_SCRIPT_START:dashboards-admin-calendar-heatmap */
const heatCells=[...document.querySelectorAll('.calendar-heatmap button')];heatCells.forEach((cell,index)=>cell.addEventListener('click',()=>{heatCells.forEach(item=>item.removeAttribute('aria-selected'));cell.setAttribute('aria-selected','true');document.querySelector('#heatmapSelection').textContent='7 mois '+(index+1)+' Jour · activité '+cell.dataset.heat;announce(document.querySelector('#heatmapSelection').textContent);}));
/* 11DZ_CORE_SCRIPT_END */
const workflowBridgeSpec={"name":"dashboards-admin-calendar-heatmap","selector":"[data-core-bridge=\"dashboards-admin-calendar-heatmap\"]","mode":"click","key":"Enter"};const workflowBridgeCore=document.querySelector('#workflowCore');const workflowBridgeControl=workflowBridgeCore.querySelector(workflowBridgeSpec.selector);if(!workflowBridgeControl)throw new Error('Missing core bridge: '+workflowBridgeSpec.name);const workflowBridgeControlledId=workflowBridgeControl.getAttribute('aria-controls');const workflowBridgeControlled=workflowBridgeControlledId?document.getElementById(workflowBridgeControlledId):null;workflowBridgeCore.dataset.workflowBridge=workflowBridgeSpec.name;workflowBridgeCore.dataset.workflowVersion='0';function workflowBridgeSerialize(){const visualNode=workflowBridgeCore.firstElementChild||workflowBridgeCore;const visual=getComputedStyle(visualNode);const outputs=[...workflowBridgeCore.querySelectorAll('output,[role="status"],[aria-live],meter,progress')].map(node=>node.textContent.trim()+'|'+node.getAttribute('aria-valuenow')+'|'+node.getAttribute('value'));const canvas=[...workflowBridgeCore.querySelectorAll('canvas')].map(node=>{try{return node.toDataURL().slice(-160);}catch{return 'canvas-unavailable';}});return JSON.stringify({html:workflowBridgeCore.innerHTML,status:statusOutput.textContent,outputs,canvas,visual:[visual.display,visual.visibility,visual.opacity,visual.transform,visual.filter,visual.backgroundColor,visual.width,visual.height],scroll:workflowBridgeSpec.mode==='scroll'?workflowBridgeControl.scrollTop:null});}let workflowBridgeLastEffect=workflowBridgeSerialize();function workflowBridgeSummary(){const label=(workflowBridgeControl.getAttribute('aria-label')||workflowBridgeControl.textContent||workflowBridgeControl.value||workflowBridgeSpec.name).trim().replace(/\s+/g,' ').slice(0,72);return label+' · Version de base '+workflowBridgeCore.dataset.workflowVersion;}function workflowBridgeCommit(){const effect=workflowBridgeSerialize();if(effect===workflowBridgeLastEffect)return {changed:false,summary:workflowBridgeSummary()};workflowBridgeLastEffect=effect;workflowBridgeCore.dataset.workflowVersion=String(Number(workflowBridgeCore.dataset.workflowVersion||0)+1);return {changed:true,summary:workflowBridgeSummary()};}function workflowBridgeScheduleCommit(){queueMicrotask(workflowBridgeCommit);setTimeout(workflowBridgeCommit,0);}workflowBridgeControl.addEventListener("click",workflowBridgeScheduleCommit);const workflowBridgeStateTargets=new Set(workflowBridgeCore.querySelectorAll('dialog'));if(workflowBridgeControlled&&workflowBridgeCore.contains(workflowBridgeControlled))workflowBridgeStateTargets.add(workflowBridgeControlled);workflowBridgeStateTargets.forEach(target=>['close','cancel','toggle'].forEach(eventName=>target.addEventListener(eventName,workflowBridgeScheduleCommit)));function workflowBridgeActivate(){workflowBridgeControl.focus();workflowBridgeControl.click();const result=workflowBridgeCommit();setTimeout(workflowBridgeCommit,0);return result;}function workflowBridgeReset(){workflowBridgeCore.dataset.workflowVersion='0';workflowBridgeLastEffect=workflowBridgeSerialize();}
const workflowDispatchCore=document.querySelector('#workflowCore');const workflowDispatchForm=document.querySelector('#workflowDispatchForm');const workflowDispatchSelect=document.querySelector('#workflowDispatchActive');const workflowDispatchJobs=new Map([["1",{"label":"Réapprovisionnement des emprunts","amount":12,"priority":"normal","touches":0}],["2",{"label":"Traitement des rendez-vous","amount":21,"priority":"urgent","touches":0}]]);let workflowDispatchSerial=workflowDispatchJobs.size;function workflowDispatchDrive(job,activate=false){workflowDispatchCore.dataset.dispatchPriority=job.priority;workflowDispatchCore.style.setProperty('--dispatch-load',Math.min(.95,.16+job.amount/120));const bridge=activate?workflowBridgeActivate():{changed:false,summary:workflowBridgeSummary()};document.querySelector('#workflowDispatchSummary').textContent=job.label+' · '+job.amount+' élément · '+bridge.summary;announce('Tâche actuelle de la collection :'+job.label);}function workflowDispatchRender(activeId,activate=false){workflowDispatchSelect.replaceChildren(...[...workflowDispatchJobs].map(([id,job])=>{const option=document.createElement('option');option.value=id;option.textContent=job.label+(job.priority==='urgent'?' · Urgent':'');return option;}));workflowDispatchSelect.value=activeId;workflowDispatchDrive(workflowDispatchJobs.get(activeId),activate);}workflowDispatchForm.addEventListener('submit',event=>{event.preventDefault();const data=new FormData(workflowDispatchForm);const id=String(++workflowDispatchSerial);workflowDispatchJobs.set(id,{label:document.querySelector('#workflowDispatchKind').value,amount:Math.max(1,Number(document.querySelector('#workflowDispatchAmount').value)||1),priority:data.get('workflowPriority'),touches:0});workflowDispatchRender(id,true);});workflowDispatchSelect.addEventListener('change',()=>workflowDispatchDrive(workflowDispatchJobs.get(workflowDispatchSelect.value),true));workflowDispatchCore.addEventListener('focusin',()=>{const job=workflowDispatchJobs.get(workflowDispatchSelect.value);if(!job)return;job.touches+=1;document.querySelector('#workflowDispatchSummary').textContent=job.label+' · '+job.amount+' élément · '+workflowBridgeSummary();});workflowDispatchRender('1',false);
workflowBridgeReset();
  </script>
</body>
</html>
Commentaires sur la pageVous avez découvert un problème ou avez une suggestion d’amélioration ?
Les fonctions de retour nécessitent l’utilisation de la version en ligne

Veuillez activer JavaScript dans la version en ligne avant de soumettre ; les fonctionnalités locales de l’outil ou du cas ne sont pas affectées.

Aller vers la version en ligne pour envoyer des commentaires