Paillasse/design/resources/views/bibliotheque.blade.php

122 lines
21 KiB
PHP
Raw Permalink Normal View History

@extends('layouts.vitrine')
@section('titre', 'Bibliothèque')
@section('contenu')
<section class="border-b border-[#e3e5e8] bg-white">
<div class="mx-auto max-w-7xl px-5 pb-12 pt-12 lg:px-10">
<p class="inline-block border border-[#dadce0] rounded-lg bg-[#eef0fd] px-3 py-1 text-sm font-bold">Bibliothèque du prof</p>
<div class="min-w-0 flex-1"><p data-public="P" class="mb-2 inline-flex items-center gap-2 rounded-full border border-[#b8c1cf] bg-white px-3 py-1 text-sm font-bold text-[#1d3fa8]">Pour les profs</p>
<h1 class="mt-6 max-w-5xl text-6xl font-extrabold leading-[1.05] tracking-tight sm:text-7xl">Tout le référentiel, fiche par fiche.</h1></div>
<p class="mt-6 max-w-3xl text-xl font-medium leading-relaxed">Du SNT au master, en passant par l’école d’ingénieurs. Chaque fiche regroupe cours, quiz, TD, labs et examens. Un même chapitre sert plusieurs formations : OSPF compte pour la NSI, le BTS SIO, le BTS CIEL, le BUT R&amp;T et les titres pro.</p>
<dl id="stats" class="mt-10 grid max-w-4xl grid-cols-2 border border-[#e3e5e8] rounded-2xl sm:grid-cols-5"></dl>
<div class="mt-6 max-w-4xl border border-[#e3e5e8] rounded-2xl bg-[#e6f4ea] p-4 text-sm font-bold"><p>Construit à partir des programmes officiels. Vérifié le 29 septembre 2026 :</p><ul class="mt-2 flex flex-wrap gap-x-5 gap-y-1 underline underline-offset-4"><li><a href="https://www.legifrance.gouv.fr/jorf/id/JORFTEXT000049926463" target="_blank" rel="noopener">BTS SIO · arrêté du 8 juillet 2024 (épreuves E5, E6, E7)</a></li><li><a href="https://www.francecompetences.fr/recherche/rncp/37391/" target="_blank" rel="noopener">BTS CIEL · RNCP37391, arrêté du 25 janvier 2023</a></li><li><a href="https://www.onisep.fr/ressources/univers-formation/formations/post-bac/but-reseaux-et-telecommunications-parcours-cybersecurite" target="_blank" rel="noopener">BUT R&amp;T · 5 parcours (Onisep)</a></li></ul></div>
</div>
</section>
<div class="sticky top-0 z-20 border-b border-[#e3e5e8] bg-white">
<div class="mx-auto flex max-w-7xl flex-wrap items-center gap-3 px-5 py-3 lg:px-10">
<div class="flex border border-[#e3e5e8] rounded-2xl font-extrabold" role="tablist">
<button type="button" data-vue="themes" aria-selected="true" class="px-4 py-2 aria-selected:bg-black aria-selected:text-white">Par thème</button>
<button type="button" data-vue="formations" aria-selected="false" class="border-l border-[#e3e5e8] px-4 py-2 aria-selected:bg-black aria-selected:text-white">Par formation</button>
<button type="button" data-vue="examens" aria-selected="false" class="border-l border-[#e3e5e8] px-4 py-2 aria-selected:bg-black aria-selected:text-white">Examens</button>
</div>
<input id="b-recherche" class="min-w-[200px] flex-1 border border-[#e3e5e8] rounded-2xl px-3 py-2 font-semibold focus:bg-[#f8f9fa] focus:outline-none" placeholder="Rechercher : OSPF, chmod, RSA…" aria-label="Rechercher">
<select id="b-theme" class="border border-[#e3e5e8] rounded-2xl px-3 py-2 font-bold" aria-label="Thème"><option value="">Tous les thèmes</option></select>
<label class="flex items-center gap-2 font-bold"><input id="b-vm" type="checkbox" class="h-5 w-5 accent-black"> Masquer hors VM</label>
<p class="ml-auto border border-[#e3e5e8] rounded-2xl bg-[#e6f4ea] px-3 py-2 font-extrabold">Ma séquence : <span id="b-seq">0</span></p>
</div>
<div class="mx-auto flex max-w-7xl flex-wrap gap-2 px-5 pb-3 lg:px-10" role="group" aria-label="Activités">
<button type="button" data-tag="C" aria-pressed="false" class="border border-[#dadce0] rounded-lg bg-white px-2 py-0.5 font-mono text-xs font-semibold aria-pressed:outline aria-pressed:outline-4 aria-pressed:outline-offset-1 aria-pressed:outline-black">C · Cours</button>
<button type="button" data-tag="Q" aria-pressed="false" class="border border-[#dadce0] rounded-lg bg-[#fef7e0] px-2 py-0.5 font-mono text-xs font-semibold aria-pressed:outline aria-pressed:outline-4 aria-pressed:outline-offset-1 aria-pressed:outline-black">Q · Quiz</button>
<button type="button" data-tag="TD" aria-pressed="false" class="border border-[#dadce0] rounded-lg bg-[#eef0fd] px-2 py-0.5 font-mono text-xs font-semibold aria-pressed:outline aria-pressed:outline-4 aria-pressed:outline-offset-1 aria-pressed:outline-black">TD · TD</button>
<button type="button" data-tag="LAB" aria-pressed="false" class="border border-[#dadce0] rounded-lg bg-[#e6f4ea] px-2 py-0.5 font-mono text-xs font-semibold aria-pressed:outline aria-pressed:outline-4 aria-pressed:outline-offset-1 aria-pressed:outline-black">LAB · TP sur VM</button>
<button type="button" data-tag="LAB+" aria-pressed="false" class="border border-[#dadce0] rounded-lg bg-[#e6f4ea] px-2 py-0.5 font-mono text-xs font-semibold aria-pressed:outline aria-pressed:outline-4 aria-pressed:outline-offset-1 aria-pressed:outline-black">LAB+ · Lab multi-VM</button>
<button type="button" data-tag="IDE" aria-pressed="false" class="border border-[#dadce0] rounded-lg bg-[#13233A] text-white px-2 py-0.5 font-mono text-xs font-semibold aria-pressed:outline aria-pressed:outline-4 aria-pressed:outline-offset-1 aria-pressed:outline-black">IDE · IDE</button>
<button type="button" data-tag="GEN" aria-pressed="false" class="border border-[#dadce0] rounded-lg bg-[#fce8e6] px-2 py-0.5 font-mono text-xs font-semibold aria-pressed:outline aria-pressed:outline-4 aria-pressed:outline-offset-1 aria-pressed:outline-black">GEN · Générateur</button>
<button type="button" data-tag="EXAM" aria-pressed="false" class="border border-[#dadce0] rounded-lg bg-[#ec3013] text-white px-2 py-0.5 font-mono text-xs font-semibold aria-pressed:outline aria-pressed:outline-4 aria-pressed:outline-offset-1 aria-pressed:outline-black">EXAM · Examen</button>
<button type="button" data-tag="PROJ" aria-pressed="false" class="border border-[#dadce0] rounded-lg bg-white px-2 py-0.5 font-mono text-xs font-semibold aria-pressed:outline aria-pressed:outline-4 aria-pressed:outline-offset-1 aria-pressed:outline-black">PROJ · Projet</button>
<button type="button" data-tag="CTF" aria-pressed="false" class="border border-[#dadce0] rounded-lg bg-[#0b57d0] text-white px-2 py-0.5 font-mono text-xs font-semibold aria-pressed:outline aria-pressed:outline-4 aria-pressed:outline-offset-1 aria-pressed:outline-black">CTF · CTF</button>
</div>
</div>
<div id="b-contenu" class="mx-auto max-w-7xl px-5 py-12 lg:px-10" data-url-fiche="{{ route('cours.fiche') }}" data-url-cours="{{ route('cours.show') }}" data-url-ide="{{ route('ide') }}" data-url-lab="{{ route('labs.essai') }}" data-url-gen="{{ route('generateur') }}" data-url-exam="{{ route('examen') }}" data-url-banque="{{ route('banque') }}"></div>
<script src="{{ asset('js/referentiel.js') }}"></script>
<script src="{{ asset('js/contenus-1.js') }}"></script>
<script src="{{ asset('js/contenus-2.js') }}"></script>
<script src="{{ asset('js/contenus-3.js') }}"></script>
<script src="{{ asset('js/contenus-exemples.js') }}"></script>
<script src="{{ asset('js/contenus-reseau-1.js') }}"></script>
<script src="{{ asset('js/contenus-reseau-2.js') }}"></script>
<script src="{{ asset('js/contenus-devops-1.js') }}"></script>
<script src="{{ asset('js/contenus-devops-2.js') }}"></script>
<script src="{{ asset('js/contenus-infra-3.js') }}"></script>
<script src="{{ asset('js/contenus-data-4.js') }}"></script>
<script src="{{ asset('js/contenus-sys-5.js') }}"></script>
<script src="{{ asset('js/contenus-lot-6.js') }}"></script>
<script src="{{ asset('js/contenus-lot-7.js') }}"></script>
<script src="{{ asset('js/contenus-lot-8.js') }}"></script>
<script src="{{ asset('js/contenus-lot-9.js') }}"></script>
<script src="{{ asset('js/contenus-lot-10.js') }}"></script>
<script src="{{ asset('js/contenus-lot-11.js') }}"></script>
<script src="{{ asset('js/contenus-lot-12.js') }}"></script>
<script src="{{ asset('js/contenus-alias.js') }}"></script>
<script>
(function () {
var R = window.REFERENTIEL, box = document.getElementById('b-contenu'), vue = 'themes', seq = 0, actifs = [];
var BG = {"C":"bg-white","Q":"bg-[#fef7e0]","TD":"bg-[#eef0fd]","LAB":"bg-[#e6f4ea]","LAB+":"bg-[#e6f4ea]","IDE":"bg-[#13233A] text-white","GEN":"bg-[#fce8e6]","EXAM":"bg-[#ec3013] text-white","PROJ":"bg-white","CTF":"bg-[#0b57d0] text-white"}; BG.HV = 'bg-white border-dashed';
var U = box.dataset;
var KW = { ipv6: /ipv6/i, projet: /projet|sa[ée]|scrum|agile|itil|pilpro|erp|management|gestion|lean|comptabilit|cejm|épreuve|examen|grand oral|sre|observabilit/i, maths: /math|signal|transmission|modulation|suite|matrice|probab|fourier|complexe|ensemble|trigonom|intégral|différenti|numérisation|décibel/i, fichiers: /fichier|nfs|ftp|ntp|temps|glpi|inventaire|bonding|agrégation|tssr|ais|maintenir|infrastructure/i, archi: /architecture|processeur|mémoire|matériel|composant|machine|système d’exploitation|os\b/i, iot: /iot|node-red|modbus|coap|chaîne d’information|chaîne d’énergie|objet|capteur|embarqué|électronique|microcontr|arduino|esp32|mqtt|domotique/i, voip: /voip|téléphon|sip|voix|toip|visioconf/i, pandas: /pandas|donnée|data|statistique|tableau|csv|visualis/i, ml: /éthique|biais|ai act|kafka|spark|flux temps réel|distribué|apprentissage|machine learning|\bia\b|intelligence artificielle|modèle|knn|voisins|prédict/i, neurones: /neurone|deep|ocr|vocal|image|vision|langage/i, k8s: /kubernetes|orchestr|cloud|cluster/i, supervision: /supervision|monitoring|zabbix|prometheus|snmp|journalisation|logs centralis/i, annuaire: /ldap|active directory|annuaire|gpo|samba|identit|authentification|kerberos/i, git: /\bgit\b|version|collabor/i, cicd: /ci\/cd|pipeline|intégration continue|déploiement/i, docker: /docker|conteneur|virtualisation|devops|devcloud|cloud/i, compose: /compose/i, ansible: /ansible|iac|automatis|ci\/cd|pipeline/i, binaire: /binaire|hexa|représentation|entiers|codage/i, terminal: /terminal|linux|shell|commande|debian|système|fichiers|cp, mv|cat, less|nano|vim|processus|systemd|journalctl|apt|paquets|tar|archiv|compression|cron|planification|performance|noyau|démarrage|grub|sauvegarde|rsync|lvm/i, droits: /droits|permission|utilisateur|groupe|chmod|compte/i, tubes: /bash|script|grep|journaux|log|automatis|expressions régulières|regex/i, osi: /osi|tcp\/ip|couche|encapsulation|réseau|protocole|internet/i, sql: /sql|données|base|requête|mcd|merise|relationnel|group by|agrégats|ddl|dml|create|insert|vues|procédures|triggers|transactions|acid|index|explain|normalisation|réplication/i, python: /python|programm|algorithm|développ|code|fonction|listes|tuples|dictionnaires|chaînes|récursiv|tests|pytest|tris|dichotom|complexité|piles|graphes|pandas|json|csv|types construits/i, web: /web|photographie|pixel|rvb|exif|gps|nmea|cartographie|histoire|anglais|html|css|http|site|cms|apache|php|javascript|nginx|reverse proxy|proxy|api rest|laravel|mvc|oauth|authentification|framework/i, crypto: /crypto|sécurité|cyber|chiffr|mot de passe|ssh|tls|certificat|rgpd|gpg|vpn|wireguard|ipsec|fail2ban|force brute|nmap|scan|vulnérab|cve|ids|suricata|anssi|menaces|hameçonnage|rançongiciel|iso 27001|ebios|pssi|nis2|ctf|dmz|hygiène/i, diagnostic: /diagnostic|dépannage|incident|ping|supervision|maintenance|arp|tcp et udp|ss\b|tcpdump|pcap|trames|analyse de trames/i, ipv4: /ipv4|adressage|masque|cidr|sous-réseau|vlsm|plan d/i, vlan: /vlan|802\.1q|commutation|switch|ethernet|trunk/i, dhcp: /dhcp|bail|adresse automatique/i, dns: /dns|domaine|nom de|messagerie|postfix|dkim|dmarc/i, nat: /nat|pat|passerelle|pare-feu|nftables|firewall|dmz/i, routage: /routage|routeur|route|ospf|rip|interconnex|bgp|mpls|vrrp|keepalived/i };
var PRO = (window.CONTENUS_PRO || []).filter(function (c) { return c.intro; });
var statut = function (t) { if (PRO.some(function (c) { return c.match.test(t); })) return ['Complet', 'bg-[#e6f4ea]']; var p = [t].concat(t.split(/\s*[:,;·]\s*|\s+et\s+/)); if (PRO.some(function (c) { return p.some(function (x) { return x.length > 1 && (c.match.test(x) || (KW[c.id] && KW[c.id].test(x))); }); })) return ['Lié', 'bg-[#fef7e0]']; return ['Bientôt', 'bg-white']; };
var badgeS = function (t) { var s = statut(t); return '<span class="border border-[#dadce0] rounded-lg ' + s[1] + ' px-1.5 text-[11px] font-extrabold">' + s[0] + '</span>'; };
var esc = function (s) { return String(s).replace(/&/g, '&amp;').replace(/</g, '&lt;'); };
var badges = function (tags) { return tags.split(' ').map(function (t) { return '<span class="border border-[#dadce0] rounded-lg ' + (BG[t] || 'bg-white') + ' px-1.5 font-mono text-[11px] font-semibold">' + (t === 'HV' ? 'hors VM' : t) + '</span>'; }).join(''); };
var lienT = function (f) { var t = f.tags.split(' '); if (t.indexOf('IDE') > -1 || t.indexOf('GEN') > -1 || t.indexOf('LAB') > -1 || t.indexOf('EXAM') > -1) return lien(f.tags); return U.urlFiche + '?chapitre=' + encodeURIComponent(f.t); };
var lien = function (tags) { var t = tags.split(' '); return t.indexOf('IDE') > -1 ? U.urlIde : t.indexOf('GEN') > -1 ? U.urlGen : (t.indexOf('LAB') > -1 || t.indexOf('LAB+') > -1) ? U.urlLab : t.indexOf('EXAM') > -1 ? U.urlExam : U.urlCours; };
var tous = []; R.themes.forEach(function (th) { th.items.forEach(function (it, i) { tous.push({ code: th.code + '-' + (i + 1), theme: th.code, t: it[0], tags: it[1] }); }); });
var nb = function (tag) { return tous.filter(function (f) { return f.tags.split(' ').indexOf(tag) > -1; }).length; };
document.getElementById('stats').innerHTML = [['Fiches', tous.length, 'bg-white'], ['Quiz', nb('Q'), 'bg-[#fef7e0]'], ['Labs', nb('LAB') + nb('LAB+'), 'bg-[#e6f4ea]'], ['Formations', R.formations.length, 'bg-[#eef0fd]'], ['Examens', R.examens.length + nb('EXAM'), 'bg-[#fce8e6]']].map(function (s, i) { return '<div class="' + (i ? 'border-l-4 ' : '') + 'border-black ' + s[2] + ' p-4"><dt class="font-mono text-sm font-semibold">' + s[0] + '</dt><dd class="text-4xl font-extrabold">' + s[1] + '</dd></div>'; }).join('');
var sel = document.getElementById('b-theme'); R.themes.forEach(function (th) { var o = document.createElement('option'); o.value = th.code; o.textContent = th.code + ' · ' + th.nom; sel.appendChild(o); });
function garde(t, tags) {
var q = document.getElementById('b-recherche').value.toLowerCase(), tg = tags.split(' ');
if (q && t.toLowerCase().indexOf(q) === -1) return false;
if (document.getElementById('b-vm').checked && tg.indexOf('HV') > -1) return false;
return actifs.every(function (a) { return tg.indexOf(a) > -1; });
}
function carte(code, t, tags, sous) {
return '<li class="flex flex-col gap-3 border border-[#e3e5e8] rounded-2xl bg-white p-4"><div class="flex items-start justify-between gap-2"><span class="border border-[#dadce0] rounded-lg bg-black px-1.5 font-mono text-xs font-semibold text-white">' + code + '</span>' + (sous ? '<span class="text-right text-xs font-bold">' + sous + '</span>' : '') + '</div><p class="flex-1 font-extrabold leading-snug">' + esc(t) + '</p><div class="flex flex-wrap gap-1">' + badgeS(t) + badges(tags) + '</div><div class="flex flex-wrap gap-2 border-t border-[#e3e5e8] pt-3"><a href="' + lien(tags) + '" class="border border-[#dadce0] rounded-lg bg-[#fef7e0] px-2 py-0.5 text-sm font-extrabold hover:bg-[#0b57d0] hover:text-white">Ouvrir</a><button type="button" data-ajout class="border border-[#dadce0] rounded-lg bg-white px-2 py-0.5 text-sm font-extrabold hover:bg-[#e6f4ea]">+ Séquence</button></div></li>';
}
function rendre() {
var h = '', th = sel.value;
if (vue === 'themes') {
R.themes.filter(function (x) { return !th || x.code === th; }).forEach(function (x) {
var its = x.items.map(function (it, i) { return [x.code + '-' + (i + 1), it[0], it[1]]; }).filter(function (it) { return garde(it[1], it[2]); });
if (!its.length) return;
h += '<section class="mb-14"><h2 class="flex flex-wrap items-baseline gap-3 text-3xl font-extrabold tracking-tight"><span class="border border-[#e3e5e8] rounded-2xl bg-[#eef0fd] px-2 font-mono text-2xl">' + x.code + '</span>' + esc(x.nom) + '<span class="font-mono text-base font-semibold">' + its.length + ' fiche(s)</span></h2><ul class="mt-6 grid gap-5 sm:grid-cols-2 lg:grid-cols-4">' + its.map(function (it) { return carte(it[0], it[1], it[2]); }).join('') + '</ul></section>';
});
} else if (vue === 'formations') {
var gr = {}; R.formations.forEach(function (f) { (gr[f.groupe] = gr[f.groupe] || []).push(f); });
Object.keys(gr).forEach(function (g) {
var bloc = gr[g].map(function (f) {
var ch = f.chapitres.filter(function (c) { return garde(c[0], c[1]); });
if (!ch.length) return '';
return '<details class="border border-[#e3e5e8] rounded-2xl bg-white" open><summary class="cursor-pointer border-b border-[#e3e5e8] bg-[#e6f4ea] px-5 py-3 text-xl font-extrabold">' + esc(f.nom) + ' <span class="font-mono text-sm font-semibold">· ' + ch.length + ' chapitre(s)</span></summary><ul class="divide-y divide-[#e3e5e8]">' + ch.map(function (c, i) { return '<li class="flex flex-wrap items-center gap-3 px-5 py-3"><span class="font-mono text-sm font-semibold">' + (i + 1) + '</span><a href="' + (/IDE|GEN|LAB|EXAM/.test(c[1]) ? lien(c[1]) : U.urlFiche + '?chapitre=' + encodeURIComponent(c[0])) + '" class="min-w-0 flex-1 font-bold hover:underline">' + esc(c[0]) + '</a><span class="flex flex-wrap gap-1">' + badgeS(c[0]) + badges(c[1]) + '</span><button type="button" data-ajout class="border border-[#dadce0] rounded-lg px-2 text-xs font-extrabold hover:bg-[#e6f4ea]">+ Séquence</button></li>'; }).join('') + '</ul></details>';
}).join('');
if (bloc) h += '<section class="mb-12"><h2 class="text-3xl font-extrabold tracking-tight">' + esc(g) + '</h2><div class="mt-5 grid gap-6">' + bloc + '</div></section>';
});
} else {
var ex = R.examens.filter(function (e) { return garde(e[0] + ' ' + e[1], 'EXAM'); });
var ext = tous.filter(function (f) { return f.tags.split(' ').indexOf('EXAM') > -1 && garde(f.t, f.tags); });
h += '<section class="mb-12"><h2 class="text-3xl font-extrabold tracking-tight">Épreuves et examens blancs</h2><ul class="mt-6 grid gap-5 sm:grid-cols-2 lg:grid-cols-3">' + ex.map(function (e) { return '<li class="border border-[#e3e5e8] rounded-2xl bg-white p-5"><p class="font-mono text-sm font-semibold">⏱ ' + e[2] + '</p><p class="mt-2 text-xl font-extrabold">' + esc(e[0]) + '</p><p class="mt-1 font-semibold">' + esc(e[1]) + '</p><div class="mt-4 flex gap-2"><a href="' + U.urlExam + '" class="border border-[#dadce0] rounded-lg bg-[#ec3013] px-2 py-0.5 text-sm font-extrabold text-white">Aperçu</a><a href="' + U.urlBanque + '" class="border border-[#dadce0] rounded-lg bg-white px-2 py-0.5 text-sm font-extrabold hover:bg-[#fef7e0]">Personnaliser</a></div></li>'; }).join('') + '</ul></section>';
h += '<section><h2 class="text-3xl font-extrabold tracking-tight">Fiches avec mode examen</h2><ul class="mt-6 grid gap-5 sm:grid-cols-2 lg:grid-cols-4">' + ext.map(function (f) { return carte(f.code, f.t, f.tags); }).join('') + '</ul></section>';
}
box.innerHTML = h || '<p class="border border-[#e3e5e8] rounded-2xl bg-white p-6 text-xl font-extrabold">Aucune fiche ne correspond à ces filtres.</p>';
}
box.addEventListener('click', function (e) { var b = e.target.closest('[data-ajout]'); if (!b || b.disabled) return; seq++; document.getElementById('b-seq').textContent = seq; b.textContent = 'Ajoutée ✓'; b.disabled = true; b.classList.add('bg-[#e6f4ea]'); });
document.querySelectorAll('[data-vue]').forEach(function (b) { b.addEventListener('click', function () { vue = b.dataset.vue; document.querySelectorAll('[data-vue]').forEach(function (x) { x.setAttribute('aria-selected', x === b); }); rendre(); }); });
document.querySelectorAll('[data-tag]').forEach(function (b) { b.addEventListener('click', function () { var on = b.getAttribute('aria-pressed') !== 'true'; b.setAttribute('aria-pressed', on); actifs = Array.prototype.filter.call(document.querySelectorAll('[data-tag]'), function (x) { return x.getAttribute('aria-pressed') === 'true'; }).map(function (x) { return x.dataset.tag; }); rendre(); }); });
['b-recherche', 'b-theme', 'b-vm'].forEach(function (id) { document.getElementById(id).addEventListener('input', rendre); document.getElementById(id).addEventListener('change', rendre); });
rendre();
})();
</script>
@endsection