<html><body>Bonjour !</body></html>
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>L'ITD Formation — Plateforme LMS</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400;500;600&family=Instrument+Sans:wght@300;400;500;600&display=swap" rel="stylesheet">
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root {
--ink: #0a0a0a;
--white: #ffffff;
--surface: #f8f8f8;
--surface-2: #f2f2f2;
--border: #e8e8e8;
--border-2: #d4d4d4;
--muted: #6b6b6b;
--muted-2: #9b9b9b;
--green: #ff6633;
--green-bg: #fff4f0;
--amber: #e05a00;
--amber-bg: #fff3ee;
--red: #c0392b;
--red-bg: #fef2f2;
--blue: #0066cc;
--blue-bg: #e6f0ff;
--sidebar-w: 232px;
--topbar-h: 58px;
--radius: 16px;
--radius-sm: 10px;
--shadow: 0 1px 3px rgba(0,0,0,0.08), 0 8px 24px rgba(0,0,0,0.05);
--shadow-hover: 0 4px 12px rgba(0,0,0,0.10), 0 16px 40px rgba(0,0,0,0.08);
--transition: 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
--spring: 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
}
/* ── DARK MODE ── */
html.dark {
--ink: #f0f0f0;
--white: #1c1c1e;
--surface: #141416;
--surface-2: #242428;
--border: #2e2e32;
--border-2: #3e3e44;
--muted: #888892;
--muted-2: #56565e;
--green: #ff8855;
--green-bg: #2a1506;
--amber: #ff6633;
--amber-bg: #2a1506;
--red: #f05252;
--red-bg: #1f0d0d;
--blue: #4da6ff;
--blue-bg: #0d1f3a;
--shadow: 0 1px 3px rgba(0,0,0,0.4), 0 8px 24px rgba(0,0,0,0.3);
--shadow-hover: 0 4px 12px rgba(0,0,0,0.5), 0 16px 40px rgba(0,0,0,0.4);
}
/* Dark mode: invert hero/dark elements so they stay readable */
html.dark .dashboard-hero,
html.dark .fiche-hero,
html.dark .apprenant-hero,
html.dark .formateur-hero,
html.dark .apprenant2-hero-inner,
html.dark .login-left,
html.dark .cert-card,
html.dark .chat-bubble,
html.dark .chat-header { filter: brightness(1.12); }
html.dark .topbar {
background: rgba(20,20,22,0.88);
}
html.dark .video-player { background: #000; }
html.dark .notif-item.unread { background: #1a1d2e; }
html.dark .leaderboard-row.me { background: #0d1829; }
html.dark input, html.dark textarea, html.dark select {
background: var(--surface-2) !important;
color: var(--ink) !important;
border-color: var(--border) !important;
}
html.dark input::placeholder, html.dark textarea::placeholder { color: var(--muted-2) !important; }
html.dark .login-right { background: #1c1c1e; }
html.dark .login-input { background: #242428 !important; }
/* Dark mode toggle button */
.dark-toggle {
width: 36px; height: 36px; border-radius: 50%;
border: 1px solid var(--border); background: var(--white);
cursor: pointer; display: flex; align-items: center; justify-content: center;
transition: all var(--transition); font-size: 15px;
color: var(--muted);
}
.dark-toggle:hover { background: var(--surface-2); transform: rotate(20deg); }
/* Smooth transition for dark mode switch */
html, html * {
transition: background-color 0.25s ease, border-color 0.25s ease, color 0.2s ease !important;
}
/* Exceptions - don't animate transitions that should stay instant */
html .chat-panel, html .notif-panel { transition: transform 0.25s cubic-bezier(0.34,1.56,0.64,1), opacity 0.2s ease !important; }
html { scroll-behavior: smooth; }
body { font-family: 'Instrument Sans', sans-serif; background: var(--surface); color: var(--ink); overflow: hidden; height: 100vh; }
/* ───── TOPBAR ───── */
.topbar {
position: fixed; top: 0; left: var(--sidebar-w); right: 0; height: var(--topbar-h);
background: rgba(248,248,248,0.85); backdrop-filter: blur(24px); -webkit-backdrop-filter: blur(24px);
border-bottom: 1px solid var(--border); z-index: 100;
display: flex; align-items: center; justify-content: space-between; padding: 0 32px;
}
.topbar-title { font-family: 'Playfair Display', serif; font-size: 18px; font-weight: 400; color: var(--ink); }
.topbar-right { display: flex; align-items: center; gap: 12px; }
.topbar-avatar {
width: 34px; height: 34px; border-radius: 50%;
background: #0066cc; color: #fff;
display: flex; align-items: center; justify-content: center;
font-size: 13px; font-weight: 500; cursor: pointer;
}
/* ───── SIDEBAR ───── */
.sidebar {
position: fixed; left: 0; top: 0; width: var(--sidebar-w); height: 100vh;
background: var(--surface); border-right: 1px solid var(--border);
display: flex; flex-direction: column; z-index: 200;
}
.sidebar-brand {
padding: 22px 24px 20px;
border-bottom: 1px solid var(--border);
}
.brand-name { font-family: 'Playfair Display', serif; font-size: 20px; font-weight: 500; letter-spacing: -0.3px; }
.brand-sub { font-size: 11px; color: var(--muted); font-weight: 400; margin-top: 1px; }
.sidebar-nav { padding: 16px 12px; flex: 1; overflow-y: auto; overflow-x: hidden; scrollbar-width: thin; scrollbar-color: var(--border) transparent; }
.nav-section-label {
font-size: 10px; font-weight: 600; color: var(--muted-2);
text-transform: uppercase; letter-spacing: 0.08em;
padding: 0 12px; margin: 16px 0 6px;
}
.nav-item {
display: flex; align-items: center; gap: 11px;
padding: 10px 12px; border-radius: var(--radius-sm);
cursor: pointer; transition: all var(--transition);
font-size: 13.5px; font-weight: 400; color: var(--muted);
margin-bottom: 2px;
}
.nav-item:hover { background: var(--surface-2); color: var(--ink); }
.nav-item.active { background: #e6f0ff; color: #0066cc; font-weight: 600; box-shadow: var(--shadow); border-left: 3px solid #0066cc; }
.nav-icon { width: 18px; height: 18px; opacity: 0.7; flex-shrink: 0; }
.nav-item.active .nav-icon { opacity: 1; }
.nav-badge {
margin-left: auto; background: #ff6633; color: #fff;
font-size: 10px; font-weight: 600; padding: 1px 7px; border-radius: 99px;
}
.sidebar-footer {
padding: 16px 12px; border-top: 1px solid var(--border);
}
.sidebar-user {
display: flex; align-items: center; gap: 10px; padding: 10px 12px; border-radius: var(--radius-sm);
cursor: pointer; transition: background var(--transition);
}
.sidebar-user:hover { background: var(--surface-2); }
.user-avatar {
width: 32px; height: 32px; border-radius: 50%; background: #0066cc;
display: flex; align-items: center; justify-content: center;
color: #fff; font-size: 12px; font-weight: 600; flex-shrink: 0;
}
.user-name { font-size: 13px; font-weight: 500; }
.user-role { font-size: 11px; color: var(--muted); }
/* ───── MAIN ───── */
.main {
margin-left: var(--sidebar-w); margin-top: var(--topbar-h);
height: calc(100vh - var(--topbar-h)); overflow-y: auto;
}
.view { display: none; padding: 40px; animation: revealView 0.4s var(--transition) both; }
.view.active { display: block; }
@keyframes revealView {
from { opacity: 0; transform: translateY(12px); }
to { opacity: 1; transform: translateY(0); }
}
/* ───── COMMON COMPONENTS ───── */
.page-header { margin-bottom: 36px; }
.page-title { font-family: 'Playfair Display', serif; font-size: 32px; font-weight: 400; letter-spacing: -0.5px; margin-bottom: 6px; }
.page-sub { font-size: 14px; color: var(--muted); font-weight: 400; }
.card {
background: var(--white); border: 1px solid var(--border);
border-radius: var(--radius); padding: 28px;
transition: all var(--transition);
}
.card:hover { box-shadow: var(--shadow-hover); transform: translateY(-2px); }
.card-title { font-size: 12px; font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: 0.06em; margin-bottom: 10px; }
.kpi-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; margin-bottom: 32px; }
.kpi-card {
background: var(--surface); border: 1px solid var(--border);
border-radius: var(--radius); padding: 24px 26px;
transition: all var(--transition);
}
.kpi-card:hover { box-shadow: var(--shadow); transform: translateY(-1px); }
.kpi-label { font-size: 11px; font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: 0.07em; margin-bottom: 10px; }
.kpi-value { font-family: 'Playfair Display', serif; font-size: 34px; font-weight: 400; line-height: 1; margin-bottom: 8px; }
.kpi-change { font-size: 12px; color: var(--muted); }
.kpi-change.up { color: var(--green); }
.kpi-change.down { color: var(--red); }
.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }
.grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; }
.grid-7-3 { display: grid; grid-template-columns: 7fr 3fr; gap: 20px; }
.btn {
display: inline-flex; align-items: center; gap: 7px;
padding: 9px 18px; border-radius: 99px; font-size: 13px; font-weight: 500;
border: 1px solid var(--border); background: var(--white); color: var(--ink);
cursor: pointer; transition: all var(--transition); font-family: inherit;
}
.btn:hover { background: var(--surface-2); border-color: var(--border-2); }
.btn-solid { background: #0066cc; color: #fff; border-color: #0066cc; }
.btn-solid:hover { background: #004499; }
.btn-sm { padding: 7px 14px; font-size: 12px; }
.btn-xs { padding: 5px 11px; font-size: 11px; }
.tag {
display: inline-flex; align-items: center; gap: 4px;
padding: 3px 10px; border-radius: 99px; font-size: 11px; font-weight: 500;
}
.tag-ink { background: #0066cc; color: #fff; }
.tag-outline { background: transparent; border: 1px solid var(--border-2); color: var(--muted); }
.tag-surface { background: var(--surface-2); color: var(--ink); }
.tag-green { background: var(--green-bg); color: var(--green); }
.tag-amber { background: var(--amber-bg); color: var(--amber); }
.tag-red { background: var(--red-bg); color: var(--red); }
.tag-blue { background: var(--blue-bg); color: var(--blue); }
.progress-bar { height: 2px; background: var(--border); border-radius: 99px; overflow: hidden; }
.progress-fill { height: 100%; background: #0066cc; border-radius: 99px; transition: width 0.6s var(--transition); }
.divider { height: 1px; background: var(--border); margin: 24px 0; }
.section-title { font-family: 'Playfair Display', serif; font-size: 20px; font-weight: 400; margin-bottom: 20px; }
/* ───── VIEW 1: DASHBOARD ───── */
.dashboard-hero {
background: linear-gradient(135deg,#001a4d 0%,#0044aa 60%,#0066cc 100%); border-radius: var(--radius);
padding: 40px 48px; margin-bottom: 32px;
position: relative; overflow: hidden; color: var(--white);
}
.dashboard-hero::before {
content: ''; position: absolute;
width: 400px; height: 400px; border-radius: 50%;
border: 1px solid rgba(255,255,255,0.07);
top: -120px; right: -80px;
}
.dashboard-hero::after {
content: ''; position: absolute;
width: 250px; height: 250px; border-radius: 50%;
border: 1px solid rgba(255,255,255,0.05);
bottom: -80px; right: 100px;
}
.hero-greeting { font-size: 13px; color: rgba(255,255,255,0.5); font-weight: 400; margin-bottom: 8px; }
.hero-title { font-family: 'Playfair Display', serif; font-size: 28px; font-weight: 400; margin-bottom: 32px; }
.hero-stats { display: flex; gap: 48px; position: relative; z-index: 1; }
.hero-stat-val { font-family: 'Playfair Display', serif; font-size: 40px; font-weight: 400; line-height: 1; }
.hero-stat-lbl { font-size: 12px; color: rgba(255,255,255,0.5); margin-top: 6px; }
.formations-list { display: flex; flex-direction: column; gap: 14px; }
.formation-row {
display: flex; align-items: center; gap: 16px;
padding: 16px 20px; border-radius: var(--radius-sm);
border: 1px solid var(--border); background: var(--white);
transition: all var(--transition); cursor: pointer;
}
.formation-row:hover { box-shadow: var(--shadow); transform: translateX(3px); }
.formation-icon {
width: 42px; height: 42px; background: var(--surface); border-radius: 10px;
display: flex; align-items: center; justify-content: center; font-size: 18px; flex-shrink: 0;
}
.formation-name { font-size: 14px; font-weight: 500; margin-bottom: 3px; }
.formation-meta { font-size: 12px; color: var(--muted); }
.formation-progress { margin-left: auto; text-align: right; }
.formation-pct { font-family: 'Playfair Display', serif; font-size: 20px; font-weight: 400; }
.formation-bar { width: 80px; margin-top: 6px; }
.sessions-list { display: flex; flex-direction: column; gap: 12px; }
.session-row {
display: flex; align-items: center; gap: 14px;
padding: 14px 18px; border-radius: var(--radius-sm);
background: var(--surface); border: 1px solid transparent;
transition: all var(--transition); cursor: pointer;
}
.session-row:hover { border-color: var(--border); background: var(--white); }
.session-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
.session-info { flex: 1; }
.session-name { font-size: 13px; font-weight: 500; margin-bottom: 2px; }
.session-date { font-size: 11.5px; color: var(--muted); }
.session-count { font-size: 12px; color: var(--muted); }
/* ───── VIEW 2: CATALOGUE ───── */
.catalogue-layout { display: grid; grid-template-columns: 220px 1fr; gap: 28px; }
.filter-panel { position: sticky; top: 0; }
.filter-section { margin-bottom: 28px; }
.filter-title { font-size: 12px; font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: 0.07em; margin-bottom: 14px; }
.filter-options { display: flex; flex-direction: column; gap: 8px; }
.filter-option { display: flex; align-items: center; gap: 8px; font-size: 13px; cursor: pointer; color: var(--muted); transition: color var(--transition); }
.filter-option:hover { color: var(--ink); }
.filter-option input[type=checkbox] { accent-color: var(--ink); width: 14px; height: 14px; }
.filter-active-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 20px; }
.catalogue-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; }
.catalogue-card {
background: var(--white); border: 1px solid var(--border); border-radius: var(--radius);
overflow: hidden; cursor: pointer; transition: all var(--transition);
}
.catalogue-card:hover { box-shadow: var(--shadow-hover); transform: translateY(-3px); }
.cat-card-thumb {
height: 140px; background: var(--surface);
display: flex; align-items: center; justify-content: center; font-size: 40px;
border-bottom: 1px solid var(--border); position: relative;
}
.cat-card-cpf {
position: absolute; top: 12px; right: 12px;
background: #ff6633; color: #fff;
font-size: 10px; font-weight: 600; padding: 3px 8px; border-radius: 99px;
}
.cat-card-body { padding: 20px; }
.cat-card-domain { font-size: 10px; font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: 0.07em; margin-bottom: 8px; }
.cat-card-name { font-family: 'Playfair Display', serif; font-size: 16px; font-weight: 400; margin-bottom: 10px; line-height: 1.35; }
.cat-card-footer { display: flex; align-items: center; justify-content: space-between; margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--border); }
.cat-price { font-family: 'Playfair Display', serif; font-size: 18px; font-weight: 400; }
.cat-stars { font-size: 11px; color: var(--muted); }
/* ───── VIEW 3: FICHE FORMATION ───── */
.fiche-hero {
background: linear-gradient(135deg,#001a4d 0%,#0044aa 60%,#0066cc 100%); border-radius: var(--radius);
padding: 48px; margin-bottom: 32px; color: var(--white); position: relative; overflow: hidden;
}
.fiche-hero::before {
content: ''; position: absolute; width: 500px; height: 500px; border-radius: 50%;
border: 1px solid rgba(255,255,255,0.06); top: -200px; right: -100px;
}
.fiche-domain { font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.1em; color: rgba(255,255,255,0.5); margin-bottom: 12px; }
.fiche-title { font-family: 'Playfair Display', serif; font-size: 36px; font-weight: 400; margin-bottom: 20px; line-height: 1.2; }
.fiche-badges { display: flex; gap: 10px; flex-wrap: wrap; margin-bottom: 32px; }
.fiche-badge { background: rgba(255,255,255,0.1); color: rgba(255,255,255,0.8); padding: 5px 14px; border-radius: 99px; font-size: 12px; }
.fiche-stats { display: flex; gap: 40px; }
.fiche-stat-val { font-family: 'Playfair Display', serif; font-size: 28px; font-weight: 400; }
.fiche-stat-lbl { font-size: 11px; color: rgba(255,255,255,0.5); margin-top: 4px; }
.programme-item {
border: 1px solid var(--border); border-radius: var(--radius-sm);
overflow: hidden; margin-bottom: 10px;
}
.programme-header {
padding: 16px 20px; display: flex; align-items: center; gap: 14px;
cursor: pointer; transition: background var(--transition);
background: var(--white);
}
.programme-header:hover { background: var(--surface); }
.programme-num { font-family: 'Playfair Display', serif; font-size: 22px; font-weight: 400; color: var(--border-2); width: 28px; flex-shrink: 0; }
.programme-name { font-size: 14px; font-weight: 500; flex: 1; }
.programme-dur { font-size: 12px; color: var(--muted); margin-right: 12px; }
.programme-arrow { font-size: 10px; color: var(--muted); transition: transform var(--transition); }
.programme-body { display: none; padding: 16px 20px 20px 62px; background: var(--surface); border-top: 1px solid var(--border); }
.programme-body.open { display: block; }
.programme-item.open .programme-arrow { transform: rotate(90deg); }
.sessions-table { width: 100%; border-collapse: collapse; }
.sessions-table th { font-size: 11px; font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: 0.07em; padding: 10px 16px; text-align: left; border-bottom: 1px solid var(--border); }
.sessions-table td { padding: 14px 16px; border-bottom: 1px solid var(--border); font-size: 13.5px; vertical-align: middle; }
.sessions-table tr:hover td { background: var(--surface); }
/* ───── VIEW 4: SESSIONS ───── */
.sessions-tabs { display: flex; gap: 0; margin-bottom: 28px; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface); padding: 4px; width: fit-content; }
.sess-tab { padding: 8px 20px; border-radius: 7px; font-size: 13px; font-weight: 500; cursor: pointer; color: var(--muted); transition: all var(--transition); }
.sess-tab.active { background: var(--white); color: var(--ink); box-shadow: var(--shadow); }
.cal-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 20px; }
.cal-month { font-family: 'Playfair Display', serif; font-size: 22px; font-weight: 400; }
.cal-nav { display: flex; gap: 8px; }
.cal-nav-btn { width: 32px; height: 32px; border-radius: 50%; border: 1px solid var(--border); background: var(--white); cursor: pointer; display: flex; align-items: center; justify-content: center; font-size: 12px; transition: all var(--transition); }
.cal-nav-btn:hover { border-color: var(--ink); }
.cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 1px; background: var(--border); border: 1px solid var(--border); border-radius: var(--radius-sm); overflow: hidden; }
.cal-dow { background: var(--surface); padding: 10px; text-align: center; font-size: 11px; font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: 0.06em; }
.cal-day { background: var(--white); padding: 8px; min-height: 90px; }
.cal-day.other-month .cal-date { color: var(--border-2); }
.cal-date { font-size: 12px; font-weight: 500; margin-bottom: 6px; }
.cal-event { font-size: 10.5px; padding: 3px 7px; border-radius: 5px; margin-bottom: 3px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; cursor: pointer; }
.ev-blue { background: #dbeafe; color: #1e40af; }
.ev-green { background: var(--green-bg); color: var(--green); }
.ev-amber { background: var(--amber-bg); color: var(--amber); }
.ev-pink { background: #fce7f3; color: #9d174d; }
.emargement-panel {
display: none; background: var(--white); border: 1px solid var(--border);
border-radius: var(--radius); padding: 28px; margin-top: 24px;
}
.emargement-panel.open { display: block; }
.emarg-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 20px; }
.emarg-title { font-size: 16px; font-weight: 500; }
.emarg-rows { display: flex; flex-direction: column; gap: 10px; }
.emarg-row {
display: flex; align-items: center; gap: 16px;
padding: 12px 16px; background: var(--surface); border-radius: var(--radius-sm);
}
.emarg-name { flex: 1; font-size: 13.5px; font-weight: 500; }
.emarg-btn {
width: 34px; height: 34px; border-radius: 50%; border: 1.5px solid var(--border);
background: var(--white); cursor: pointer; font-size: 14px;
display: flex; align-items: center; justify-content: center; transition: all var(--transition);
}
.emarg-btn.present { background: var(--green-bg); border-color: var(--green); color: var(--green); }
.emarg-btn.absent { background: var(--red-bg); border-color: var(--red); color: var(--red); }
/* ───── VIEW 5: ANALYTICS ───── */
.chart-bar-group { display: flex; align-items: flex-end; gap: 8px; height: 160px; margin-top: 20px; }
.chart-bar-wrap { display: flex; flex-direction: column; align-items: center; flex: 1; height: 100%; justify-content: flex-end; gap: 8px; }
.chart-bar { width: 100%; background: var(--ink); border-radius: 5px 5px 0 0; transition: height 0.8s var(--transition); min-height: 4px; }
.chart-bar-label { font-size: 11px; color: var(--muted); }
.donut-wrap { display: flex; align-items: center; gap: 24px; }
.donut-legend { display: flex; flex-direction: column; gap: 10px; }
.donut-leg-row { display: flex; align-items: center; gap: 8px; font-size: 12.5px; }
.donut-dot { width: 10px; height: 10px; border-radius: 50%; flex-shrink: 0; }
.analytics-table { width: 100%; border-collapse: collapse; margin-top: 4px; }
.analytics-table th { font-size: 11px; font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: 0.06em; padding: 8px 14px; text-align: left; border-bottom: 1px solid var(--border); }
.analytics-table td { padding: 12px 14px; border-bottom: 1px solid var(--border); font-size: 13px; }
.analytics-table tr:hover td { background: var(--surface); }
.alert-row {
display: flex; align-items: center; gap: 12px; padding: 12px 16px;
border-radius: var(--radius-sm); margin-bottom: 10px;
}
.alert-row.amber { background: var(--amber-bg); border-left: 3px solid var(--amber); }
.alert-row.red { background: var(--red-bg); border-left: 3px solid var(--red); }
.alert-row.green { background: var(--green-bg); border-left: 3px solid var(--green); }
.alert-txt { font-size: 13px; }
/* ───── VIEW 6: PORTAIL APPRENANT ───── */
.apprenant-hero {
background: var(--ink); color: var(--white); border-radius: var(--radius);
padding: 40px 48px; margin-bottom: 28px; position: relative; overflow: hidden;
}
.apprenant-hero::before {
content: ''; position: absolute; width: 320px; height: 320px; border-radius: 50%;
border: 1px solid rgba(255,255,255,0.07); top: -100px; right: 60px;
}
.apprenant-hero::after {
content: ''; position: absolute; width: 180px; height: 180px; border-radius: 50%;
border: 1px solid rgba(255,255,255,0.05); bottom: -60px; right: 200px;
}
.apprenant-hello { font-size: 13px; color: rgba(255,255,255,0.5); margin-bottom: 6px; }
.apprenant-name { font-family: 'Playfair Display', serif; font-size: 28px; font-weight: 400; margin-bottom: 28px; }
.cours-card {
background: var(--white); border: 1px solid var(--border); border-radius: var(--radius);
padding: 24px; cursor: pointer; transition: all var(--transition);
}
.cours-card:hover { box-shadow: var(--shadow-hover); transform: translateY(-2px); }
.cours-thumb { height: 100px; background: var(--surface); border-radius: 10px; margin-bottom: 16px; display: flex; align-items: center; justify-content: center; font-size: 32px; }
.cours-title { font-size: 14px; font-weight: 500; margin-bottom: 6px; }
.cours-meta { font-size: 12px; color: var(--muted); margin-bottom: 14px; }
.cours-pct { font-family: 'Playfair Display', serif; font-size: 16px; font-weight: 400; margin-bottom: 8px; }
.ring-wrap { position: relative; width: 80px; height: 80px; margin: 0 auto 16px; }
.ring-svg { transform: rotate(-90deg); }
.ring-bg { fill: none; stroke: var(--border); stroke-width: 5; }
.ring-fill { fill: none; stroke: var(--ink); stroke-width: 5; stroke-linecap: round; transition: stroke-dashoffset 1s var(--transition); }
.ring-text { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; font-family: 'Playfair Display', serif; font-size: 16px; font-weight: 400; }
.cert-card {
background: var(--ink); color: var(--white); border-radius: var(--radius);
padding: 20px; text-align: center; position: relative; overflow: hidden; cursor: pointer;
}
.cert-card::before {
content: ''; position: absolute; width: 120px; height: 120px; border-radius: 50%;
border: 1px solid rgba(255,255,255,0.08); top: -30px; right: -20px;
}
.cert-icon { font-size: 28px; margin-bottom: 10px; position: relative; z-index: 1; }
.cert-name { font-size: 13px; font-weight: 500; margin-bottom: 4px; position: relative; z-index: 1; }
.cert-date { font-size: 11px; color: rgba(255,255,255,0.5); position: relative; z-index: 1; }
/* ───── VIEW 7: LECTEUR ───── */
.lecteur-layout { display: grid; grid-template-columns: 260px 1fr 240px; gap: 20px; height: calc(100vh - var(--topbar-h) - 80px); }
.lecteur-sidebar { background: var(--white); border: 1px solid var(--border); border-radius: var(--radius); padding: 24px; overflow-y: auto; }
.lecon-item { padding: 10px 12px; border-radius: var(--radius-sm); cursor: pointer; transition: all var(--transition); margin-bottom: 4px; }
.lecon-item:hover { background: var(--surface); }
.lecon-item.active { background: var(--ink); color: var(--white); }
.lecon-item.done { color: var(--muted); }
.lecon-num { font-size: 10px; font-weight: 600; color: var(--muted-2); margin-bottom: 3px; }
.lecon-item.active .lecon-num { color: rgba(255,255,255,0.5); }
.lecon-name { font-size: 13px; font-weight: 500; }
.player-area { display: flex; flex-direction: column; gap: 16px; }
.video-player {
background: #000; border-radius: var(--radius); overflow: hidden;
aspect-ratio: 16/9; display: flex; align-items: center; justify-content: center; color: white;
position: relative;
}
.play-btn { width: 60px; height: 60px; border-radius: 50%; background: rgba(255,255,255,0.15); border: 2px solid rgba(255,255,255,0.3); display: flex; align-items: center; justify-content: center; font-size: 20px; cursor: pointer; backdrop-filter: blur(10px); }
.video-title { position: absolute; bottom: 20px; left: 20px; font-family: 'Playfair Display', serif; font-size: 16px; color: rgba(255,255,255,0.8); }
.video-prog { position: absolute; bottom: 0; left: 0; right: 0; height: 3px; background: rgba(255,255,255,0.15); }
.video-prog-fill { width: 35%; height: 100%; background: white; }
.notes-area { flex: 1; background: var(--white); border: 1px solid var(--border); border-radius: var(--radius); padding: 20px; overflow-y: auto; }
.note-item { display: flex; gap: 12px; padding: 10px 0; border-bottom: 1px solid var(--border); }
.note-time { font-family: 'Playfair Display', serif; font-size: 12px; color: var(--muted); white-space: nowrap; }
.note-text { font-size: 13px; flex: 1; }
.ressources-panel { background: var(--white); border: 1px solid var(--border); border-radius: var(--radius); padding: 24px; overflow-y: auto; }
.ressource-row { display: flex; align-items: center; gap: 12px; padding: 12px 0; border-bottom: 1px solid var(--border); cursor: pointer; transition: all var(--transition); }
.ressource-row:hover { transform: translateX(3px); }
.ressource-icon { width: 36px; height: 36px; background: var(--surface); border-radius: 8px; display: flex; align-items: center; justify-content: center; font-size: 16px; flex-shrink: 0; }
.ressource-name { font-size: 13px; font-weight: 500; }
.ressource-type { font-size: 11px; color: var(--muted); }
/* ───── VIEW 8: FORMATEUR ───── */
.formateur-hero { background: var(--ink); color: var(--white); border-radius: var(--radius); padding: 36px 44px; margin-bottom: 28px; }
.form-kpi-grid { display: grid; grid-template-columns: repeat(4,1fr); gap: 12px; margin-top: 24px; }
.form-kpi { background: rgba(255,255,255,0.08); border-radius: var(--radius-sm); padding: 16px 18px; }
.form-kpi-val { font-family: 'Playfair Display', serif; font-size: 28px; font-weight: 400; margin-bottom: 4px; }
.form-kpi-lbl { font-size: 11px; color: rgba(255,255,255,0.5); }
.eval-row {
display: flex; align-items: center; gap: 14px; padding: 16px 18px;
border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--white);
margin-bottom: 10px; transition: all var(--transition);
}
.eval-row:hover { box-shadow: var(--shadow); }
.eval-avatar { width: 36px; height: 36px; border-radius: 50%; background: var(--surface); display: flex; align-items: center; justify-content: center; font-size: 14px; flex-shrink: 0; }
.eval-info { flex: 1; }
.eval-name { font-size: 13.5px; font-weight: 500; }
.eval-sub { font-size: 11.5px; color: var(--muted); margin-top: 2px; }
.eval-note-input {
width: 52px; padding: 6px 10px; border: 1px solid var(--border); border-radius: 7px;
font-family: 'Playfair Display', serif; font-size: 16px; text-align: center;
background: var(--surface); transition: all var(--transition);
}
.eval-note-input:focus { outline: none; border-color: var(--ink); background: var(--white); }
.groupe-row { display: flex; align-items: center; gap: 14px; padding: 12px 0; border-bottom: 1px solid var(--border); }
.groupe-name { width: 120px; font-size: 13px; font-weight: 500; }
.groupe-bar-wrap { flex: 1; }
.groupe-pct { font-family: 'Playfair Display', serif; font-size: 15px; font-weight: 400; margin-left: 12px; }
/* ───── VIEW 9: CERTIFICATIONS ───── */
.cert-grid { display: grid; grid-template-columns: repeat(3,1fr); gap: 16px; margin-bottom: 28px; }
.cert-card-v2 {
background: var(--white); border: 1px solid var(--border); border-radius: var(--radius);
padding: 24px; cursor: pointer; transition: all var(--transition);
}
.cert-card-v2:hover { box-shadow: var(--shadow-hover); transform: translateY(-2px); }
.cert-v2-top { display: flex; align-items: flex-start; justify-content: space-between; margin-bottom: 16px; }
.cert-v2-icon { font-size: 32px; }
.cert-v2-name { font-size: 14px; font-weight: 500; margin-bottom: 6px; line-height: 1.3; }
.cert-v2-date { font-size: 11.5px; color: var(--muted); }
.docs-table { width: 100%; border-collapse: collapse; }
.docs-table th { font-size: 11px; font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: 0.06em; padding: 10px 16px; text-align: left; border-bottom: 1px solid var(--border); }
.docs-table td { padding: 13px 16px; border-bottom: 1px solid var(--border); font-size: 13px; vertical-align: middle; }
.docs-table tr:hover td { background: var(--surface); }
/* ───── VIEW 10: ADMIN ───── */
.admin-layout { display: grid; grid-template-columns: 200px 1fr; gap: 0; min-height: calc(100vh - var(--topbar-h) - 80px); }
.admin-tabs-sidebar {
border-right: 1px solid var(--border);
padding: 8px 0;
background: var(--white);
border-radius: var(--radius) 0 0 var(--radius);
}
.admin-tab-item {
display: flex; align-items: center; gap: 10px;
padding: 11px 18px; font-size: 13px; font-weight: 400; color: var(--muted);
cursor: pointer; transition: all var(--transition); border-left: 2px solid transparent;
}
.admin-tab-item:hover { color: var(--ink); background: var(--surface); }
.admin-tab-item.active { color: var(--ink); font-weight: 500; background: var(--surface); border-left-color: var(--ink); }
.admin-tab-icon { width: 16px; height: 16px; flex-shrink: 0; }
.admin-panel { display: none; padding: 32px 36px; background: var(--surface); border-radius: 0 var(--radius) var(--radius) 0; }
.admin-panel.active { display: block; }
.admin-panel-title { font-family: 'Playfair Display', serif; font-size: 22px; font-weight: 400; margin-bottom: 6px; }
.admin-panel-sub { font-size: 13px; color: var(--muted); margin-bottom: 28px; }
/* Color Picker */
.color-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 14px; margin-bottom: 24px; }
.color-row {
display: flex; align-items: center; gap: 14px; padding: 14px 18px;
background: var(--white); border: 1px solid var(--border); border-radius: var(--radius-sm);
transition: all var(--transition);
}
.color-row:hover { box-shadow: var(--shadow); }
.color-swatch {
width: 38px; height: 38px; border-radius: 8px; flex-shrink: 0;
border: 1px solid rgba(0,0,0,0.08); cursor: pointer; position: relative; overflow: hidden;
}
.color-swatch input[type=color] {
position: absolute; inset: -4px; width: calc(100% + 8px); height: calc(100% + 8px);
border: none; padding: 0; cursor: pointer; opacity: 0;
}
.color-info { flex: 1; }
.color-label { font-size: 13px; font-weight: 500; margin-bottom: 2px; }
.color-hex { font-size: 11.5px; color: var(--muted); font-family: monospace; }
.color-preview-bar { height: 6px; border-radius: 99px; margin-top: 6px; }
.color-presets { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 20px; }
.color-preset {
width: 32px; height: 32px; border-radius: 8px; cursor: pointer;
border: 2px solid transparent; transition: all var(--transition);
position: relative;
}
.color-preset:hover { transform: scale(1.1); border-color: var(--border-2); }
.color-preset.selected { border-color: var(--ink); }
/* Content Editor */
.content-tabs { display: flex; gap: 0; border-bottom: 1px solid var(--border); margin-bottom: 24px; }
.ctab { padding: 10px 20px; font-size: 13px; font-weight: 500; color: var(--muted); cursor: pointer; border-bottom: 2px solid transparent; margin-bottom: -1px; transition: all var(--transition); }
.ctab.active { color: var(--ink); border-bottom-color: var(--ink); }
.ctab:hover { color: var(--ink); }
.content-panel { display: none; }
.content-panel.active { display: block; }
.content-card {
background: var(--white); border: 1px solid var(--border); border-radius: var(--radius-sm);
padding: 0; overflow: hidden; margin-bottom: 12px; transition: all var(--transition);
}
.content-card:hover { box-shadow: var(--shadow); }
.content-card-header {
display: flex; align-items: center; gap: 14px; padding: 14px 18px;
border-bottom: 1px solid var(--border); background: var(--surface);
}
.content-card-body { padding: 18px; }
.field-group { margin-bottom: 16px; }
.field-label { font-size: 11.5px; font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: .06em; margin-bottom: 7px; }
.field-input {
width: 100%; padding: 9px 13px; border: 1px solid var(--border); border-radius: 8px;
font-family: inherit; font-size: 13.5px; color: var(--ink); background: var(--white);
transition: all var(--transition); resize: vertical;
}
.field-input:focus { outline: none; border-color: var(--ink); box-shadow: 0 0 0 3px rgba(10,10,10,0.06); }
.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.field-row-3 { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 12px; }
textarea.field-input { min-height: 80px; }
/* Table Editor */
.table-editor-wrap { background: var(--white); border: 1px solid var(--border); border-radius: var(--radius-sm); overflow: hidden; }
.table-toolbar { display: flex; align-items: center; gap: 8px; padding: 12px 16px; border-bottom: 1px solid var(--border); background: var(--surface); }
.table-tool-btn {
padding: 6px 12px; border-radius: 6px; font-size: 12px; font-weight: 500;
border: 1px solid var(--border); background: var(--white); color: var(--ink);
cursor: pointer; transition: all var(--transition); display: flex; align-items: center; gap: 5px;
}
.table-tool-btn:hover { background: var(--surface-2); }
.table-tool-btn.danger { color: var(--red); }
.table-tool-btn.danger:hover { background: var(--red-bg); border-color: var(--red); }
.editable-table { width: 100%; border-collapse: collapse; }
.editable-table th {
padding: 10px 12px; font-size: 11px; font-weight: 600; color: var(--muted);
text-transform: uppercase; letter-spacing: .06em; border-bottom: 1px solid var(--border);
background: var(--surface); text-align: left;
}
.editable-table th input {
width: 100%; background: transparent; border: none; font-size: 11px; font-weight: 600;
color: var(--muted); text-transform: uppercase; letter-spacing: .06em; font-family: inherit;
cursor: text; outline: none;
}
.editable-table th input:focus { color: var(--ink); }
.editable-table td {
padding: 2px 6px; border-bottom: 1px solid var(--border); vertical-align: middle;
}
.editable-table td input, .editable-table td select {
width: 100%; padding: 8px 8px; font-size: 13px; font-family: inherit; color: var(--ink);
border: 1px solid transparent; border-radius: 5px; background: transparent; transition: all .15s;
}
.editable-table td input:focus, .editable-table td select:focus {
outline: none; background: var(--white); border-color: var(--border-2);
box-shadow: 0 0 0 2px rgba(10,10,10,0.05);
}
.editable-table tr:hover td input, .editable-table tr:hover td select { background: var(--surface); }
.editable-table td.row-actions { width: 40px; text-align: center; }
.row-del-btn {
width: 26px; height: 26px; border-radius: 6px; border: none; background: transparent;
color: var(--muted-2); cursor: pointer; font-size: 14px; display: flex; align-items: center;
justify-content: center; transition: all var(--transition);
}
.row-del-btn:hover { background: var(--red-bg); color: var(--red); }
.add-row-btn {
display: flex; align-items: center; gap: 8px; width: 100%; padding: 12px 16px;
border: none; background: transparent; color: var(--muted); font-size: 13px;
cursor: pointer; transition: all var(--transition); font-family: inherit;
border-top: 1px solid var(--border);
}
.add-row-btn:hover { background: var(--surface); color: var(--ink); }
/* KPI Editor */
.kpi-editor-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 14px; }
.kpi-editor-card {
background: var(--white); border: 1px solid var(--border); border-radius: var(--radius-sm);
padding: 18px; transition: all var(--transition);
}
.kpi-editor-card:hover { box-shadow: var(--shadow); }
/* Typography Editor */
.font-preview { padding: 20px; background: var(--surface); border-radius: var(--radius-sm); border: 1px solid var(--border); margin-top: 12px; }
.font-preview-title { font-family: 'Playfair Display', serif; font-size: 28px; margin-bottom: 8px; }
.font-preview-body { font-size: 14px; color: var(--muted); line-height: 1.6; }
/* Navigation Editor */
.nav-editor-list { display: flex; flex-direction: column; gap: 8px; }
.nav-editor-item {
display: flex; align-items: center; gap: 12px; padding: 12px 16px;
background: var(--white); border: 1px solid var(--border); border-radius: var(--radius-sm);
transition: all var(--transition);
}
.nav-editor-item:hover { box-shadow: var(--shadow); }
.drag-handle { color: var(--muted-2); cursor: grab; font-size: 16px; }
/* Alerts Editor */
.alert-editor-item {
background: var(--white); border: 1px solid var(--border); border-radius: var(--radius-sm);
padding: 16px 18px; margin-bottom: 10px;
}
/* Save bar */
.admin-save-bar {
position: sticky; bottom: 0; left: 0; right: 0;
background: rgba(248,248,248,0.92); backdrop-filter: blur(20px);
border-top: 1px solid var(--border); padding: 14px 36px;
display: flex; align-items: center; justify-content: space-between;
z-index: 10;
}
.save-indicator { font-size: 13px; color: var(--muted); display: flex; align-items: center; gap: 8px; }
.save-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--amber); }
.save-dot.saved { background: var(--green); }
/* Toggle switch */
.toggle-wrap { display: flex; align-items: center; gap: 10px; }
.toggle { width: 38px; height: 22px; background: var(--border-2); border-radius: 99px; cursor: pointer; position: relative; transition: background var(--transition); flex-shrink: 0; }
.toggle.on { background: var(--ink); }
.toggle::after { content: ''; position: absolute; width: 16px; height: 16px; background: white; border-radius: 50%; top: 3px; left: 3px; transition: transform var(--transition); box-shadow: 0 1px 3px rgba(0,0,0,0.2); }
.toggle.on::after { transform: translateX(16px); }
/* Live preview badge */
.live-badge { display: inline-flex; align-items: center; gap: 5px; padding: 4px 10px; background: var(--green-bg); color: var(--green); border-radius: 99px; font-size: 11px; font-weight: 600; }
.live-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--green); animation: pulse 2s infinite; }
/* ───── INVITATIONS ───── */
.inv-subtab {
padding: 13px 22px; font-size: 13px; font-weight: 500; color: var(--muted);
cursor: pointer; border-bottom: 2px solid transparent; margin-bottom: -1px;
transition: all var(--transition);
}
.inv-subtab:hover { color: var(--ink); }
.inv-subtab.active { color: var(--ink); border-bottom-color: var(--ink); }
.inv-panel { display: none; }
.inv-panel.active { display: block; }
/* Email tag chips */
.inv-tag {
display: inline-flex; align-items: center; gap: 5px; padding: 3px 10px 3px 12px;
background: var(--ink); color: white; border-radius: 99px; font-size: 12px;
font-weight: 500; white-space: nowrap;
}
.inv-tag-remove {
width: 16px; height: 16px; border-radius: 50%; background: rgba(255,255,255,0.2);
border: none; color: white; cursor: pointer; display: flex; align-items: center;
justify-content: center; font-size: 11px; line-height: 1; padding: 0;
transition: background var(--transition);
}
.inv-tag-remove:hover { background: rgba(255,255,255,0.4); }
/* Track table rows */
.inv-track-row td { padding: 13px 14px; border-bottom: 1px solid var(--border); font-size: 13px; vertical-align: middle; }
.inv-track-row:hover td { background: var(--surface); }
/* ───── INSCRIPTION FORM ───── */
.step-item { display: flex; flex-direction: column; align-items: center; gap: 6px; cursor: pointer; }
.step-circle {
width: 34px; height: 34px; border-radius: 50%; border: 2px solid var(--border);
background: var(--white); display: flex; align-items: center; justify-content: center;
font-size: 13px; font-weight: 600; color: var(--muted); transition: all var(--transition);
}
.step-item.active .step-circle { background: var(--ink); border-color: var(--ink); color: white; }
.step-item.done .step-circle { background: var(--green); border-color: var(--green); color: white; }
.step-item.done .step-circle::after { content: '✓'; }
.step-label { font-size: 11px; font-weight: 500; color: var(--muted); white-space: nowrap; }
.step-item.active .step-label { color: var(--ink); }
.step-item.done .step-label { color: var(--green); }
.step-line { flex: 1; height: 2px; background: var(--border); margin-bottom: 18px; transition: background var(--transition); }
.step-line.done { background: var(--green); }
.doc-upload-card {
padding: 16px 18px; border: 1px solid var(--border); border-radius: var(--radius-sm);
background: var(--white); transition: all var(--transition);
}
.doc-upload-card:hover { border-color: var(--border-2); box-shadow: var(--shadow); }
.doc-upload-card.uploaded { border-color: var(--green); background: var(--green-bg); }
/* Dossier cards */
.dossier-card {
background: var(--white); border: 1px solid var(--border); border-radius: var(--radius);
padding: 20px 24px; cursor: pointer; transition: all var(--transition);
}
.dossier-card:hover { box-shadow: var(--shadow-hover); transform: translateY(-1px); }
.dossier-card.urgent { border-left: 3px solid var(--red); }
/* ───── UTILISATEURS & RÔLES ───── */
.util-layout { display: grid; grid-template-columns: 1fr 340px; gap: 24px; align-items: start; }
/* User table */
.user-row { display: flex; align-items: center; gap: 14px; padding: 14px 18px; border-bottom: 1px solid var(--border); transition: background var(--transition); cursor: pointer; }
.user-row:hover { background: var(--surface); }
.user-row:last-child { border-bottom: none; }
.user-avatar-sm { width: 36px; height: 36px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 13px; font-weight: 600; color: white; flex-shrink: 0; }
.role-badge { display: inline-flex; align-items: center; gap: 4px; padding: 3px 10px; border-radius: 99px; font-size: 11px; font-weight: 600; }
.role-admin { background: #1a1a1a; color: white; }
.role-formateur{ background: var(--blue-bg); color: var(--blue); }
.role-apprenant{ background: var(--surface-2); color: var(--muted); }
.role-suspended{ background: var(--red-bg); color: var(--red); }
/* Permission matrix */
.perm-table { width: 100%; border-collapse: collapse; }
.perm-table th { padding: 10px 12px; font-size: 11px; font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: .06em; border-bottom: 1px solid var(--border); text-align: center; }
.perm-table th:first-child { text-align: left; }
.perm-table td { padding: 11px 12px; border-bottom: 1px solid var(--border); font-size: 13px; text-align: center; vertical-align: middle; }
.perm-table td:first-child { text-align: left; font-size: 13px; font-weight: 500; }
.perm-table tr:hover td { background: var(--surface); }
.perm-check { width: 18px; height: 18px; accent-color: var(--ink); cursor: pointer; }
.perm-dot-on { width: 20px; height: 20px; border-radius: 50%; background: var(--green); display: inline-flex; align-items: center; justify-content: center; color: white; font-size: 10px; }
.perm-dot-off { width: 20px; height: 20px; border-radius: 50%; background: var(--border); display: inline-flex; align-items: center; justify-content: center; color: var(--muted-2); font-size: 10px; }
/* Audit log */
.audit-row { display: flex; align-items: flex-start; gap: 12px; padding: 12px 0; border-bottom: 1px solid var(--border); }
.audit-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; margin-top: 4px; }
.audit-time { font-size: 11px; color: var(--muted-2); white-space: nowrap; flex-shrink: 0; min-width: 72px; }
.audit-txt { font-size: 13px; line-height: 1.5; }
.audit-actor { font-weight: 500; }
/* User detail drawer — reuse dossier-drawer style */
.util-drawer { position: fixed; right: 0; top: 0; width: 420px; height: 100vh; background: var(--white); border-left: 1px solid var(--border); z-index: 500; overflow-y: auto; box-shadow: -8px 0 40px rgba(0,0,0,0.08); display: none; }
/* Role card */
.role-card { background: var(--white); border: 1px solid var(--border); border-radius: var(--radius); padding: 22px; cursor: pointer; transition: all var(--transition); }
.role-card:hover { box-shadow: var(--shadow); transform: translateY(-1px); }
.role-card.selected { border-color: var(--ink); box-shadow: 0 0 0 2px rgba(10,10,10,0.08); }
.role-icon { width: 44px; height: 44px; border-radius: 12px; display: flex; align-items: center; justify-content: center; font-size: 20px; margin-bottom: 12px; }
/* ───── CHAT PANEL ───── */
/* Floating bubble */
.chat-bubble {
position: fixed; bottom: 24px; right: 24px; z-index: 1000;
width: 52px; height: 52px; border-radius: 50%;
background: var(--ink); color: white; border: none; cursor: pointer;
display: flex; align-items: center; justify-content: center;
box-shadow: 0 4px 20px rgba(0,0,0,0.22), 0 1px 6px rgba(0,0,0,0.12);
transition: transform var(--spring), box-shadow var(--transition);
}
.chat-bubble:hover { transform: scale(1.08); box-shadow: 0 8px 28px rgba(0,0,0,0.28); }
.chat-bubble:active { transform: scale(0.96); }
#chat-notif-badge {
display: none; position: absolute; top: 2px; right: 2px;
min-width: 18px; height: 18px; background: var(--red); color: white;
border-radius: 99px; font-size: 10px; font-weight: 700;
padding: 0 5px; border: 2px solid white;
display: flex; align-items: center; justify-content: center;
}
/* Chat window — floating, NOT shrinking main */
.chat-panel {
position: fixed; right: 24px; bottom: 88px;
width: 360px; height: 520px;
background: var(--white); border: 1px solid var(--border);
border-radius: 18px; display: flex; flex-direction: column;
z-index: 999; overflow: hidden;
box-shadow: 0 8px 40px rgba(0,0,0,0.14), 0 2px 10px rgba(0,0,0,0.08);
transform: scale(0.88) translateY(20px);
transform-origin: bottom right;
opacity: 0; pointer-events: none;
transition: transform 0.25s cubic-bezier(0.34,1.56,0.64,1), opacity 0.2s ease;
}
.chat-panel.open {
transform: scale(1) translateY(0);
opacity: 1; pointer-events: all;
}
/* NO main or topbar resizing */
.main.chat-open { margin-right: 0 !important; }
.topbar.chat-open { right: 0 !important; }
.chat-header {
display: flex; align-items: center; gap: 10px; padding: 14px 16px;
border-bottom: 1px solid var(--border); flex-shrink: 0;
background: var(--ink); color: white; border-radius: 18px 18px 0 0;
}
.chat-header-title { font-size: 14px; font-weight: 600; flex: 1; color: white; }
.chat-close-btn {
width: 26px; height: 26px; border-radius: 6px; border: none;
background: rgba(255,255,255,0.12); cursor: pointer; color: white;
font-size: 14px; display: flex; align-items: center; justify-content: center;
transition: background var(--transition);
}
.chat-close-btn:hover { background: rgba(255,255,255,0.22); }
.chat-inner { display: flex; flex: 1; overflow: hidden; }
.chat-channels {
width: 130px; border-right: 1px solid var(--border); flex-shrink: 0;
display: flex; flex-direction: column; overflow-y: auto;
background: var(--surface);
}
.chat-section-lbl {
font-size: 9.5px; font-weight: 700; color: var(--muted-2);
text-transform: uppercase; letter-spacing: .09em; padding: 10px 10px 4px;
}
.chat-channel-item {
display: flex; align-items: center; gap: 6px; padding: 6px 10px;
border-radius: 5px; margin: 1px 4px; cursor: pointer; font-size: 11.5px;
color: var(--muted); transition: all var(--transition); position: relative;
}
.chat-channel-item:hover { background: var(--surface-2); color: var(--ink); }
.chat-channel-item.active { background: var(--ink); color: white; }
.chat-channel-item.active .ch-unread { background: white; color: var(--ink); }
.ch-unread {
margin-left: auto; background: var(--red); color: white;
font-size: 9px; font-weight: 700; padding: 1px 5px; border-radius: 99px;
min-width: 16px; text-align: center; flex-shrink: 0;
}
.ch-dot { width: 6px; height: 6px; border-radius: 50%; flex-shrink: 0; }
.chat-messages-wrap { flex: 1; display: flex; flex-direction: column; overflow: hidden; }
.chat-channel-header {
padding: 9px 12px; border-bottom: 1px solid var(--border); flex-shrink: 0;
background: var(--white);
}
.chat-channel-name { font-size: 12.5px; font-weight: 600; }
.chat-channel-desc { font-size: 10.5px; color: var(--muted); margin-top: 1px; }
.chat-messages {
flex: 1; overflow-y: auto; padding: 10px 12px; display: flex;
flex-direction: column; gap: 2px;
scrollbar-width: thin; scrollbar-color: var(--border) transparent;
}
.chat-day-sep { text-align: center; position: relative; margin: 10px 0; }
.chat-day-sep::before { content:''; position:absolute; left:0; right:0; top:50%; height:1px; background:var(--border); }
.chat-day-sep span { position:relative; background:var(--white); padding: 0 8px; font-size: 10px; color:var(--muted-2); font-weight:500; }
.chat-msg { display: flex; gap: 7px; padding: 3px 0; border-radius: 5px; transition: background .1s; }
.chat-msg:hover { background: var(--surface); }
.chat-msg.mine { flex-direction: row-reverse; }
.chat-msg-av {
width: 24px; height: 24px; border-radius: 50%; flex-shrink: 0; margin-top: 1px;
display: flex; align-items: center; justify-content: center;
font-size: 9px; font-weight: 700; color: white;
}
.chat-msg-body { max-width: 175px; }
.chat-msg.mine .chat-msg-body { align-items: flex-end; display: flex; flex-direction: column; }
.chat-msg-meta { font-size: 9.5px; color: var(--muted-2); margin-bottom: 2px; }
.chat-msg-bubble {
display: inline-block; padding: 6px 10px;
font-size: 12px; line-height: 1.45; background: var(--surface-2); color: var(--ink);
border-radius: 4px 10px 10px 10px; max-width: 100%; word-break: break-word;
}
.chat-msg.mine .chat-msg-bubble {
background: var(--ink); color: white; border-radius: 10px 4px 10px 10px;
}
.chat-file-bubble {
display: flex; align-items: center; gap: 7px; padding: 7px 10px;
background: var(--surface-2); border: 1px solid var(--border); border-radius: 8px;
font-size: 11.5px; cursor: pointer; transition: background var(--transition); margin-top: 2px;
}
.chat-file-bubble:hover { background: var(--surface); }
.chat-input-area { padding: 8px 10px; border-top: 1px solid var(--border); flex-shrink: 0; }
.chat-input-row { display: flex; gap: 7px; align-items: flex-end; }
.chat-input {
flex: 1; padding: 7px 10px; border: 1px solid var(--border); border-radius: 8px;
font-family: inherit; font-size: 12.5px; resize: none; max-height: 80px;
transition: border-color var(--transition); outline: none; line-height: 1.4;
}
.chat-input:focus { border-color: var(--border-2); }
.chat-send-btn {
width: 30px; height: 30px; border-radius: 8px; background: var(--ink); border: none;
color: white; cursor: pointer; display: flex; align-items: center; justify-content: center;
transition: all var(--transition); flex-shrink: 0;
}
.chat-send-btn:hover { background: #1a1a1a; transform: scale(1.05); }
.chat-typing { font-size: 10.5px; color: var(--muted-2); height: 14px; padding: 1px 2px; }
/* ───── LOGIN SCREEN ───── */
#login-screen {
position: fixed; inset: 0; z-index: 10000;
display: flex; align-items: stretch;
font-family: 'Instrument Sans', sans-serif;
}
.login-left {
width: 52%; background: linear-gradient(145deg,#001a4d 0%,#003380 55%,#0055aa 100%); color: white;
display: flex; flex-direction: column; justify-content: space-between;
padding: 48px 56px; position: relative; overflow: hidden;
}
.login-left::before {
content: ''; position: absolute;
width: 600px; height: 600px; border-radius: 50%;
border: 1px solid rgba(255,255,255,0.05);
top: -200px; right: -180px; pointer-events: none;
}
.login-left::after {
content: ''; position: absolute;
width: 320px; height: 320px; border-radius: 50%;
border: 1px solid rgba(255,255,255,0.04);
bottom: -80px; left: 60px; pointer-events: none;
}
.login-brand { font-family: 'Playfair Display', serif; font-size: 28px; font-weight: 400; letter-spacing: -0.3px; position: relative; z-index: 1; }
.login-brand-sub { font-size: 12px; color: rgba(255,255,255,0.4); margin-top: 4px; }
.login-hero { position: relative; z-index: 1; }
.login-hero-title { font-family: 'Playfair Display', serif; font-size: 42px; font-weight: 400; line-height: 1.18; margin-bottom: 20px; letter-spacing: -0.5px; }
.login-hero-sub { font-size: 15px; color: rgba(255,255,255,0.55); line-height: 1.7; max-width: 360px; }
.login-stats { display: flex; gap: 40px; position: relative; z-index: 1; }
.login-stat-val { font-family: 'Playfair Display', serif; font-size: 32px; font-weight: 400; }
.login-stat-lbl { font-size: 11px; color: rgba(255,255,255,0.4); margin-top: 4px; }
.login-right {
flex: 1; background: var(--white); display: flex; align-items: center;
justify-content: center; padding: 48px;
}
.login-form-wrap { width: 100%; max-width: 380px; }
.login-tab-row { display: flex; gap: 0; border: 1px solid var(--border); border-radius: 99px; background: var(--surface); padding: 3px; margin-bottom: 32px; }
.login-tab {
flex: 1; text-align: center; padding: 9px; border-radius: 99px;
font-size: 13px; font-weight: 500; color: var(--muted); cursor: pointer;
transition: all var(--transition);
}
.login-tab.active { background: var(--ink); color: white; }
.login-title { font-family: 'Playfair Display', serif; font-size: 26px; font-weight: 400; margin-bottom: 6px; }
.login-sub { font-size: 13px; color: var(--muted); margin-bottom: 28px; }
.login-field { margin-bottom: 16px; }
.login-field label { display: block; font-size: 11.5px; font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: .06em; margin-bottom: 7px; }
.login-input {
width: 100%; padding: 11px 14px; border: 1.5px solid var(--border); border-radius: 10px;
font-family: inherit; font-size: 14px; color: var(--ink); background: var(--white);
transition: all var(--transition); outline: none;
}
.login-input:focus { border-color: var(--ink); box-shadow: 0 0 0 3px rgba(10,10,10,0.06); }
.login-input.error { border-color: var(--red); background: var(--red-bg); }
.login-input-wrap { position: relative; }
.login-pwd-toggle {
position: absolute; right: 12px; top: 50%; transform: translateY(-50%);
background: none; border: none; cursor: pointer; color: var(--muted); font-size: 16px;
}
.login-forgot { font-size: 12.5px; color: var(--muted); text-align: right; margin-top: -8px; margin-bottom: 20px; cursor: pointer; transition: color var(--transition); }
.login-forgot:hover { color: var(--ink); }
.login-btn {
width: 100%; padding: 13px; border-radius: 99px; background: #0066cc; color: white;
border: none; font-family: inherit; font-size: 14px; font-weight: 500; cursor: pointer;
transition: all var(--transition); margin-bottom: 16px; position: relative; overflow: hidden;
}
.login-btn:hover { background: #1a1a1a; transform: translateY(-1px); box-shadow: 0 4px 16px rgba(0,0,0,0.18); }
.login-btn:active { transform: translateY(0); }
.login-btn:disabled { opacity: .6; cursor: default; transform: none; box-shadow: none; }
.login-error-msg { padding: 10px 14px; background: var(--red-bg); border: 1px solid #fecaca; border-radius: 8px; font-size: 13px; color: var(--red); margin-bottom: 16px; display: none; }
.login-success-msg { padding: 10px 14px; background: var(--green-bg); border: 1px solid #bbf7d0; border-radius: 8px; font-size: 13px; color: var(--green); margin-bottom: 16px; display: none; }
.login-divider { display: flex; align-items: center; gap: 12px; margin: 20px 0; color: var(--muted-2); font-size: 12px; }
.login-divider::before, .login-divider::after { content:''; flex:1; height:1px; background:var(--border); }
.login-demo-chips { display: flex; flex-direction: column; gap: 8px; }
.login-demo-chip {
display: flex; align-items: center; gap: 10px; padding: 10px 14px;
border: 1px solid var(--border); border-radius: 10px; cursor: pointer;
transition: all var(--transition); font-size: 13px;
}
.login-demo-chip:hover { background: var(--surface); border-color: var(--border-2); transform: translateX(2px); }
.login-demo-chip-role { font-size: 10px; font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: .06em; }
.login-back-link { font-size: 13px; color: var(--muted); cursor: pointer; margin-bottom: 24px; display: inline-flex; align-items: center; gap: 6px; transition: color var(--transition); }
.login-back-link:hover { color: var(--ink); }
/* Forgot password screen */
#forgot-screen { display: none; }
/* ───── NOTIFICATIONS ───── */
.notif-bell-btn {
position: relative; width: 36px; height: 36px; border-radius: 50%;
border: 1px solid var(--border); background: var(--white); cursor: pointer;
display: flex; align-items: center; justify-content: center;
transition: all var(--transition); color: var(--muted);
}
.notif-bell-btn:hover { background: var(--surface-2); color: var(--ink); }
#notif-count {
position: absolute; top: -3px; right: -3px; min-width: 17px; height: 17px;
background: var(--red); color: white; border-radius: 99px; font-size: 9.5px;
font-weight: 700; padding: 0 4px; border: 2px solid var(--surface);
display: none; align-items: center; justify-content: center;
}
.notif-panel {
position: fixed; right: 32px; top: calc(var(--topbar-h) + 8px);
width: 360px; background: var(--white); border: 1px solid var(--border);
border-radius: var(--radius); box-shadow: var(--shadow-hover); z-index: 400;
display: none; overflow: hidden;
animation: notifReveal 0.2s cubic-bezier(0.34,1.56,0.64,1) both;
}
.notif-panel.open { display: block; }
@keyframes notifReveal {
from { opacity:0; transform: translateY(-8px) scale(0.97); }
to { opacity:1; transform: translateY(0) scale(1); }
}
.notif-header { padding: 14px 18px; border-bottom: 1px solid var(--border); display: flex; align-items: center; justify-content: space-between; }
.notif-header-title { font-size: 14px; font-weight: 600; }
.notif-mark-all { font-size: 12px; color: var(--muted); cursor: pointer; transition: color var(--transition); }
.notif-mark-all:hover { color: var(--ink); }
.notif-tabs { display: flex; border-bottom: 1px solid var(--border); }
.notif-tab { flex: 1; text-align: center; padding: 10px; font-size: 12.5px; font-weight: 500; color: var(--muted); cursor: pointer; border-bottom: 2px solid transparent; margin-bottom: -1px; transition: all var(--transition); }
.notif-tab.active { color: var(--ink); border-bottom-color: var(--ink); }
.notif-list { max-height: 380px; overflow-y: auto; }
.notif-item {
display: flex; gap: 12px; padding: 13px 16px; border-bottom: 1px solid var(--border);
cursor: pointer; transition: background var(--transition); position: relative;
}
.notif-item:hover { background: var(--surface); }
.notif-item.unread { background: #f8f9ff; }
.notif-item.unread::before { content:''; position:absolute; left:0; top:0; bottom:0; width:3px; background: var(--blue); }
.notif-icon { width: 36px; height: 36px; border-radius: 10px; display: flex; align-items: center; justify-content: center; font-size: 16px; flex-shrink: 0; }
.notif-body { flex: 1; }
.notif-title { font-size: 13px; font-weight: 500; margin-bottom: 3px; line-height: 1.4; }
.notif-desc { font-size: 12px; color: var(--muted); line-height: 1.4; }
.notif-time { font-size: 11px; color: var(--muted-2); margin-top: 4px; }
.notif-footer { padding: 12px 16px; text-align: center; border-top: 1px solid var(--border); }
.notif-footer a { font-size: 13px; color: var(--muted); cursor: pointer; transition: color var(--transition); }
.notif-footer a:hover { color: var(--ink); }
/* ───── APPRENANT DASHBOARD (gamification) ───── */
.xp-bar-wrap { height: 8px; background: var(--border); border-radius: 99px; overflow: hidden; margin: 8px 0; }
.xp-bar-fill { height: 100%; border-radius: 99px; background: linear-gradient(90deg, #f59e0b, #f97316); transition: width 1.2s cubic-bezier(.25,.46,.45,.94); }
.badge-grid { display: grid; grid-template-columns: repeat(4,1fr); gap: 12px; }
.badge-card {
background: var(--white); border: 1px solid var(--border); border-radius: var(--radius-sm);
padding: 16px 12px; text-align: center; transition: all var(--transition); cursor: pointer;
}
.badge-card:hover { box-shadow: var(--shadow); transform: translateY(-2px); }
.badge-card.locked { opacity: .45; filter: grayscale(1); cursor: default; }
.badge-icon { font-size: 28px; margin-bottom: 8px; }
.badge-name { font-size: 11.5px; font-weight: 600; margin-bottom: 3px; }
.badge-desc { font-size: 10.5px; color: var(--muted); line-height: 1.4; }
.streak-flame { font-size: 36px; line-height: 1; }
.streak-num { font-family: 'Playfair Display', serif; font-size: 42px; font-weight: 400; line-height: 1; }
.streak-days { display: flex; gap: 5px; margin-top: 12px; }
.streak-day { width: 28px; height: 28px; border-radius: 6px; display: flex; align-items: center; justify-content: center; font-size: 10px; font-weight: 600; }
.streak-day.done { background: var(--ink); color: white; }
.streak-day.today { background: var(--amber); color: white; }
.streak-day.miss { background: var(--border); color: var(--muted-2); }
.leaderboard-row { display: flex; align-items: center; gap: 12px; padding: 11px 14px; border-bottom: 1px solid var(--border); transition: background var(--transition); }
.leaderboard-row:hover { background: var(--surface); }
.leaderboard-row.me { background: #f8f9ff; border-left: 3px solid var(--blue); }
.rank-num { font-family: 'Playfair Display', serif; font-size: 18px; font-weight: 400; width: 28px; text-align: center; flex-shrink: 0; }
.rank-medal { font-size: 18px; width: 28px; text-align: center; flex-shrink: 0; }
/* ───── PARAMÈTRES ───── */
.settings-layout { display: grid; grid-template-columns: 200px 1fr; gap: 24px; }
.settings-nav { position: sticky; top: 0; }
.settings-nav-item { display: flex; align-items: center; gap: 9px; padding: 9px 14px; border-radius: var(--radius-sm); font-size: 13px; color: var(--muted); cursor: pointer; transition: all var(--transition); margin-bottom: 2px; }
.settings-nav-item:hover { background: var(--surface-2); color: var(--ink); }
.settings-nav-item.active { background: var(--white); color: var(--ink); font-weight: 500; box-shadow: var(--shadow); }
.settings-section { display: none; }
.settings-section.active { display: block; }
.settings-card { background: var(--white); border: 1px solid var(--border); border-radius: var(--radius); padding: 28px; margin-bottom: 20px; }
.settings-card-title { font-size: 14px; font-weight: 600; margin-bottom: 4px; }
.settings-card-sub { font-size: 12.5px; color: var(--muted); margin-bottom: 20px; }
.avatar-upload { width: 72px; height: 72px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 24px; font-weight: 700; color: white; cursor: pointer; position: relative; flex-shrink: 0; }
.avatar-upload:hover::after { content:'✏️'; position:absolute; inset:0; background:rgba(0,0,0,0.4); border-radius:50%; display:flex; align-items:center; justify-content:center; font-size:18px; }
.danger-zone { border-color: var(--red) !important; }
.danger-zone .settings-card-title { color: var(--red); }
.notif-pref-row { display: flex; align-items: center; justify-content: space-between; padding: 12px 0; border-bottom: 1px solid var(--border); }
.notif-pref-row:last-child { border-bottom: none; }
.notif-pref-label { font-size: 13px; font-weight: 500; margin-bottom: 2px; }
.notif-pref-desc { font-size: 12px; color: var(--muted); }
/* ───── QUIZ ───── */
.quiz-layout { display: grid; grid-template-columns: 280px 1fr; gap: 24px; align-items: start; }
.quiz-list-item { padding: 14px 16px; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--white); margin-bottom: 10px; cursor: pointer; transition: all var(--transition); }
.quiz-list-item:hover { box-shadow: var(--shadow); transform: translateX(2px); }
.quiz-list-item.active { border-color: var(--ink); box-shadow: 0 0 0 2px rgba(10,10,10,.07); }
.quiz-list-item.done { border-color: var(--green); background: var(--green-bg); }
.quiz-list-item.locked { opacity: .5; cursor: default; filter: grayscale(.5); }
.quiz-name { font-size: 13.5px; font-weight: 500; margin-bottom: 4px; }
.quiz-meta { font-size: 11.5px; color: var(--muted); }
/* Quiz question card */
.quiz-question-card { background: var(--white); border: 1px solid var(--border); border-radius: var(--radius); padding: 32px; }
.q-progress-bar { height: 3px; background: var(--border); border-radius: 99px; margin-bottom: 28px; overflow: hidden; }
.q-progress-fill { height: 100%; background: var(--ink); border-radius: 99px; transition: width .5s var(--transition); }
.q-num { font-size: 11.5px; font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: .07em; margin-bottom: 10px; }
.q-text { font-family: 'Playfair Display', serif; font-size: 20px; font-weight: 400; line-height: 1.4; margin-bottom: 28px; }
.q-options { display: flex; flex-direction: column; gap: 10px; margin-bottom: 28px; }
.q-option {
display: flex; align-items: center; gap: 14px; padding: 14px 18px;
border: 1.5px solid var(--border); border-radius: var(--radius-sm); cursor: pointer;
transition: all var(--transition); font-size: 14px;
}
.q-option:hover:not(.disabled) { border-color: var(--border-2); background: var(--surface); }
.q-option.selected { border-color: var(--ink); background: var(--surface); }
.q-option.correct { border-color: var(--green); background: var(--green-bg); color: var(--green); font-weight: 500; }
.q-option.wrong { border-color: var(--red); background: var(--red-bg); color: var(--red); }
.q-option.disabled { cursor: default; }
.q-opt-letter { width: 28px; height: 28px; border-radius: 50%; border: 1.5px solid var(--border); display: flex; align-items: center; justify-content: center; font-size: 12px; font-weight: 700; flex-shrink: 0; transition: all var(--transition); }
.q-option.selected .q-opt-letter { background: var(--ink); border-color: var(--ink); color: white; }
.q-option.correct .q-opt-letter { background: var(--green); border-color: var(--green); color: white; }
.q-option.wrong .q-opt-letter { background: var(--red); border-color: var(--red); color: white; }
.q-feedback { padding: 12px 16px; border-radius: var(--radius-sm); font-size: 13.5px; line-height: 1.5; margin-bottom: 20px; display: none; }
.q-feedback.show { display: block; }
.q-feedback.ok { background: var(--green-bg); border-left: 3px solid var(--green); color: var(--green); }
.q-feedback.ko { background: var(--red-bg); border-left: 3px solid var(--red); color: var(--red); }
/* Results screen */
.quiz-results { text-align: center; padding: 40px 32px; }
.result-score-ring { width: 120px; height: 120px; margin: 0 auto 24px; position: relative; }
.result-score-text { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; }
.result-score-val { font-family: 'Playfair Display', serif; font-size: 32px; font-weight: 400; line-height: 1; }
.result-score-tot { font-size: 12px; color: var(--muted); }
/* ── DARK MODE RIPPLE ── */
#dark-ripple {
position: fixed;
border-radius: 50%;
pointer-events: none;
z-index: 99999;
transform: scale(0);
will-change: transform, opacity;
}
/* ───── VIEW GESTION DES FORMATIONS ───── */
.gestion-stats-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; margin-bottom: 28px; }
.gestion-stat-card { background: var(--white); border: 1px solid var(--border); border-radius: var(--radius); padding: 20px 24px; transition: all var(--transition); }
.gestion-stat-card:hover { box-shadow: var(--shadow); transform: translateY(-1px); }
.gestion-stat-val { font-family: 'Playfair Display', serif; font-size: 32px; font-weight: 400; margin-bottom: 4px; }
.gestion-stat-lbl { font-size: 12px; color: var(--muted); }
.gestion-cours-list { display: flex; flex-direction: column; gap: 12px; }
.gestion-cours-card {
background: var(--white); border: 1px solid var(--border); border-radius: var(--radius);
padding: 20px 24px; display: flex; align-items: center; gap: 20px;
transition: all var(--transition);
}
.gestion-cours-card:hover { box-shadow: var(--shadow-hover); transform: translateY(-1px); }
.gestion-cours-emoji { font-size: 26px; width: 50px; height: 50px; display: flex; align-items: center; justify-content: center; background: var(--surface); border-radius: 12px; flex-shrink: 0; }
.gestion-cours-info { flex: 1; min-width: 0; }
.gestion-cours-title { font-size: 15px; font-weight: 600; margin-bottom: 5px; }
.gestion-cours-meta { font-size: 12px; color: var(--muted); display: flex; gap: 14px; flex-wrap: wrap; margin-bottom: 8px; }
.gestion-cours-actions { display: flex; gap: 8px; flex-shrink: 0; }
.gestion-empty-state {
text-align: center; padding: 64px 24px;
background: var(--white); border: 1.5px dashed var(--border-2); border-radius: var(--radius);
}
.gestion-empty-icon { font-size: 44px; margin-bottom: 14px; opacity: .35; }
.gestion-empty-text { font-size: 14px; color: var(--muted); margin-bottom: 20px; line-height: 1.6; }
/* ───── MODAL FORMATION ───── */
.modal-backdrop {
display: none; position: fixed; inset: 0;
background: rgba(0,0,0,.45); z-index: 900;
backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
}
.modal-backdrop.open { display: flex; align-items: center; justify-content: center; }
.modal-box {
background: var(--white); border-radius: var(--radius);
width: 92%; max-width: 720px; max-height: 92vh;
overflow-y: auto; box-shadow: 0 32px 80px rgba(0,0,0,.22);
animation: modalIn .3s var(--transition) both;
}
@keyframes modalIn { from { opacity:0; transform: translateY(20px) scale(.97); } to { opacity:1; transform: none; } }
.modal-hdr { padding: 24px 28px; display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid var(--border); position: sticky; top: 0; background: var(--white); z-index: 2; }
.modal-ttl { font-family: 'Playfair Display', serif; font-size: 22px; font-weight: 400; }
.modal-x { cursor: pointer; font-size: 22px; color: var(--muted); background: none; border: none; line-height: 1; padding: 4px 8px; border-radius: 8px; transition: all var(--transition); }
.modal-x:hover { background: var(--surface-2); color: var(--ink); }
.modal-bdy { padding: 28px; }
.modal-ftr { padding: 0 28px 24px; display: flex; justify-content: flex-end; gap: 10px; }
/* ───── FORM ELEMENTS ───── */
.fm-sec { margin-bottom: 26px; }
.fm-sec-title {
font-size: 10.5px; font-weight: 700; color: var(--muted-2);
text-transform: uppercase; letter-spacing: .1em;
margin-bottom: 14px; padding-bottom: 10px; border-bottom: 1px solid var(--border);
}
.fm-g2 { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.fm-g3 { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 14px; }
.fm-field { display: flex; flex-direction: column; gap: 6px; }
.fm-field.span2 { grid-column: 1 / -1; }
.fm-lbl { font-size: 11.5px; font-weight: 600; color: var(--muted); letter-spacing: .03em; }
.fm-inp, .fm-sel, .fm-txt {
font-family: 'Instrument Sans', sans-serif; font-size: 13.5px; color: var(--ink);
background: var(--white); border: 1px solid var(--border-2);
border-radius: var(--radius-sm); padding: 10px 14px;
transition: border-color var(--transition); outline: none; width: 100%;
}
.fm-inp:focus, .fm-sel:focus, .fm-txt:focus { border-color: var(--ink); }
.fm-txt { resize: vertical; min-height: 76px; }
.fm-check-group { display: flex; flex-wrap: wrap; gap: 12px; }
.fm-check-item { display: flex; align-items: center; gap: 7px; cursor: pointer; font-size: 13px; color: var(--muted); }
.fm-check-item input { accent-color: var(--ink); width: 15px; height: 15px; cursor: pointer; }
.mod-list { display: flex; flex-direction: column; gap: 8px; margin-bottom: 10px; }
.mod-row {
display: flex; align-items: center; gap: 10px;
background: var(--surface); border: 1px solid var(--border);
border-radius: var(--radius-sm); padding: 10px 14px;
}
.mod-num { font-size: 11px; font-weight: 700; color: var(--muted-2); width: 22px; flex-shrink: 0; text-align: center; }
.mod-row .fm-inp { border: none; background: transparent; padding: 0; font-size: 13px; flex: 1; }
.mod-row .fm-inp:focus { outline: none; }
.mod-dur { width: 56px; flex-shrink: 0; text-align: center; font-size: 12px; color: var(--muted); border: none !important; background: transparent !important; }
.mod-del { cursor: pointer; color: var(--muted-2); font-size: 18px; line-height: 1; padding: 0 3px; transition: color var(--transition); flex-shrink: 0; }
.mod-del:hover { color: var(--red); }
.res-list { display: flex; flex-direction: column; gap: 8px; margin-bottom: 10px; }
.res-row {
display: flex; align-items: center; gap: 8px;
background: var(--surface); border: 1px solid var(--border);
border-radius: var(--radius-sm); padding: 8px 12px;
}
.res-type-sel { font-size: 18px; background: none; border: none; cursor: pointer; flex-shrink: 0; }
.res-row .fm-inp { border: none; background: transparent; padding: 0; font-size: 13px; }
.res-row .fm-inp:focus { outline: none; }
.btn-danger { background: var(--red-bg); color: var(--red); border-color: rgba(192,57,43,.2); }
.btn-danger:hover { background: #fde8e8; }
/* ───── BUILDER CHAPITRES ───── */
.ch-list { display: flex; flex-direction: column; gap: 12px; margin-bottom: 12px; }
.ch-block {
border: 1px solid var(--border); border-radius: var(--radius);
overflow: hidden; background: var(--white);
}
.ch-hdr {
display: flex; align-items: center; gap: 12px;
padding: 14px 18px; background: var(--surface);
border-bottom: 1px solid var(--border); cursor: pointer;
user-select: none;
}
.ch-hdr:hover { background: var(--surface-2); }
.ch-num {
font-family: 'Playfair Display', serif; font-size: 18px; color: var(--border-2);
width: 28px; flex-shrink: 0; font-weight: 400;
}
.ch-title-preview { flex: 1; font-size: 13.5px; font-weight: 500; color: var(--ink); }
.ch-toggle { font-size: 10px; color: var(--muted); transition: transform var(--transition); flex-shrink: 0; }
.ch-toggle.open { transform: rotate(90deg); }
.ch-del-btn { cursor: pointer; font-size: 16px; color: var(--muted-2); transition: color var(--transition); flex-shrink: 0; padding: 2px 4px; background: none; border: none; line-height: 1; }
.ch-del-btn:hover { color: var(--red); }
.ch-body { display: none; padding: 18px; }
.ch-body.open { display: block; }
.ch-fields { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-bottom: 18px; }
.ch-obj-field { grid-column: 1 / -1; }
/* Sous-chapitres */
.subch-list { display: flex; flex-direction: column; gap: 10px; margin-bottom: 10px; }
.subch-block {
border: 1px solid var(--border); border-radius: var(--radius-sm);
background: var(--surface); overflow: hidden;
}
.subch-hdr {
display: flex; align-items: center; gap: 10px;
padding: 11px 14px; cursor: pointer; user-select: none;
}
.subch-hdr:hover { background: var(--surface-2); }
.subch-num { font-size: 10px; font-weight: 700; color: var(--muted-2); width: 36px; flex-shrink: 0; }
.subch-preview { flex: 1; font-size: 13px; color: var(--ink); }
.subch-toggle { font-size: 10px; color: var(--muted-2); transition: transform var(--transition); flex-shrink: 0; }
.subch-toggle.open { transform: rotate(90deg); }
.subch-del { cursor: pointer; font-size: 15px; color: var(--muted-2); background: none; border: none; transition: color var(--transition); flex-shrink: 0; padding: 2px 4px; }
.subch-del:hover { color: var(--red); }
.subch-body { display: none; padding: 14px 14px 14px 50px; border-top: 1px solid var(--border); }
.subch-body.open { display: block; }
.subch-fields { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-bottom: 14px; }
.subch-obj-field { grid-column: 1 / -1; }
/* Content type tabs */
.ctype-label { font-size: 11px; font-weight: 700; color: var(--muted); text-transform: uppercase; letter-spacing: .07em; margin-bottom: 8px; }
.ctype-tabs { display: flex; gap: 0; border: 1px solid var(--border); border-radius: var(--radius-sm); overflow: hidden; margin-bottom: 12px; width: fit-content; }
.ctype-tab {
padding: 8px 14px; font-size: 12px; font-weight: 500; cursor: pointer;
color: var(--muted); background: var(--surface); transition: all var(--transition);
border-right: 1px solid var(--border); display: flex; align-items: center; gap: 5px;
font-family: 'Instrument Sans', sans-serif;
}
.ctype-tab:last-child { border-right: none; }
.ctype-tab:hover { background: var(--surface-2); color: var(--ink); }
.ctype-tab.active { background: #0066cc; color: #fff; }
.ctype-panel { display: none; }
.ctype-panel.active { display: block; }
.upload-area {
border: 1.5px dashed var(--border-2); border-radius: var(--radius-sm);
padding: 18px; text-align: center; cursor: pointer; transition: all var(--transition);
background: var(--surface);
}
.upload-area:hover { border-color: var(--ink); background: var(--surface-2); }
.upload-area-icon { font-size: 24px; margin-bottom: 6px; }
.upload-area-text { font-size: 12px; color: var(--muted); margin-bottom: 8px; line-height: 1.5; }
.upload-area input[type=file] { display: none; }
.upload-filename { font-size: 12px; font-weight: 500; color: var(--green); margin-top: 6px; min-height: 18px; }
.doctype-pills { display: flex; gap: 6px; margin-top: 10px; flex-wrap: wrap; }
.doctype-pill {
padding: 4px 12px; border-radius: 99px; border: 1px solid var(--border-2);
font-size: 11px; font-weight: 500; cursor: pointer; color: var(--muted);
transition: all var(--transition); background: var(--white);
}
.doctype-pill.active { background: #0066cc; color: #fff; border-color: #0066cc; }
.embed-preview {
margin-top: 10px; border: 1px solid var(--border); border-radius: var(--radius-sm);
overflow: hidden; height: 140px; display: none; align-items: center; justify-content: center;
background: var(--surface); color: var(--muted); font-size: 13px;
}
.embed-preview.visible { display: flex; }
.service-pills { display: flex; gap: 6px; margin-top: 10px; flex-wrap: wrap; }
.service-pill {
padding: 4px 12px; border-radius: 99px; border: 1px solid var(--border-2);
font-size: 11px; font-weight: 500; cursor: pointer; color: var(--muted);
transition: all var(--transition); background: var(--white);
}
.service-pill.active { background: var(--blue-bg); color: var(--blue); border-color: var(--blue); }
.scorm-zone {
border: 1.5px dashed #a855f7; border-radius: var(--radius-sm);
padding: 18px; text-align: center; background: #faf5ff; cursor: pointer;
transition: all var(--transition);
}
html.dark .scorm-zone { background: #1a0f2e; border-color: #7c3aed; }
.scorm-zone:hover { background: #f3e8ff; }
.scorm-zone-icon { font-size: 24px; margin-bottom: 6px; }
.scorm-zone-text { font-size: 12px; color: #7c3aed; line-height: 1.5; }
.scorm-zone input[type=file] { display: none; }
.scorm-filename { font-size: 12px; font-weight: 500; color: #7c3aed; margin-top: 6px; min-height: 18px; }
.scorm-meta { display: flex; gap: 10px; margin-top: 10px; }
.scorm-meta .fm-inp { font-size: 12px; }
/* ══ GESTION DES RÔLES ══ */
.admin-only, .formateur-only, .apprenant-only { display: none !important; }
body.role-admin .admin-only,
body.role-admin .formateur-only { display: flex !important; }
body.role-formateur .formateur-only { display: flex !important; }
body.role-apprenant .apprenant-only { display: flex !important; }
/* Nav items */
body.role-admin .admin-only.nav-item,
body.role-admin .formateur-only.nav-item { display: flex !important; }
body.role-formateur .formateur-only.nav-item { display: flex !important; }
body.role-apprenant .apprenant-only.nav-item { display: flex !important; }
/* Boutons topbar */
body.role-admin .admin-only.btn,
body.role-admin .formateur-only.btn { display: inline-flex !important; }
body.role-formateur .formateur-only.btn { display: inline-flex !important; }
/* Badge publication en attente */
.badge-pending { background: #ff6633; color: #fff; font-size:10px; font-weight:700;
padding:2px 8px; border-radius:99px; margin-left:6px; }
/* ══ SESSIONS MODULE ══ */
.sess-modal-backdrop {
position:fixed;inset:0;background:rgba(0,0,0,.45);z-index:5000;
display:flex;align-items:center;justify-content:center;
}
.sess-modal-backdrop.hidden { display:none; }
.sess-modal {
background:var(--white);border-radius:var(--radius);width:580px;max-width:95vw;
max-height:90vh;overflow-y:auto;box-shadow:0 24px 80px rgba(0,0,0,.25);
animation:revealView .3s var(--spring) both;
}
.sess-modal-hdr {
display:flex;align-items:center;justify-content:space-between;
padding:20px 26px 16px;border-bottom:1px solid var(--border);
}
.sess-modal-title { font-family:'Playfair Display',serif;font-size:20px;font-weight:400; }
.sess-modal-x { width:30px;height:30px;border-radius:50%;border:1px solid var(--border);
background:var(--white);cursor:pointer;font-size:14px;display:flex;align-items:center;
justify-content:center;color:var(--muted);transition:all var(--transition); }
.sess-modal-x:hover { background:var(--surface-2); }
.sess-modal-bdy { padding:22px 26px;display:flex;flex-direction:column;gap:14px; }
.sess-modal-ftr { padding:14px 26px 20px;display:flex;justify-content:flex-end;gap:10px;
border-top:1px solid var(--border); }
.sf-lbl { font-size:11.5px;font-weight:600;color:var(--muted);text-transform:uppercase;
letter-spacing:.05em;margin-bottom:4px;display:block; }
.sf-inp,.sf-sel { width:100%;padding:10px 12px;border:1px solid var(--border);
border-radius:var(--radius-sm);font-family:'Instrument Sans',sans-serif;font-size:13.5px;
background:var(--surface);color:var(--ink);outline:none;transition:border-color var(--transition); }
.sf-inp:focus,.sf-sel:focus { border-color:#0066cc;box-shadow:0 0 0 3px rgba(0,102,204,.09); }
.sf-g2 { display:grid;grid-template-columns:1fr 1fr;gap:12px; }
.sf-g3 { display:grid;grid-template-columns:1fr 1fr 1fr;gap:12px; }
.sess-card { background:var(--white);border:1px solid var(--border);border-radius:var(--radius-sm);
padding:14px 18px;display:flex;align-items:center;gap:14px;transition:all var(--transition);cursor:pointer; }
.sess-card:hover { box-shadow:var(--shadow);transform:translateX(2px); }
.sess-card-color { width:4px;height:44px;border-radius:99px;flex-shrink:0; }
.sess-card-info { flex:1;min-width:0; }
.sess-card-title { font-size:14px;font-weight:500;margin-bottom:3px;
white-space:nowrap;overflow:hidden;text-overflow:ellipsis; }
.sess-card-meta { font-size:12px;color:var(--muted); }
.sess-card-actions { display:flex;gap:6px;flex-shrink:0; }
.cal-date-today { width:22px;height:22px;background:#ff6633;color:#fff;border-radius:50%;
display:inline-flex;align-items:center;justify-content:center;font-size:11px;font-weight:700; }
</style>
</head>
<body>
<!-- ═══════════════════════════════════════════════════════
LOGIN SCREEN
═══════════════════════════════════════════════════════ -->
<div id="login-screen">
<!-- Left: branding -->
<div class="login-left">
<div>
<div style="display:flex;align-items:center;gap:12px;margin-bottom:4px"><img src="data:image/png;base64,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" style="width:48px;height:48px;object-fit:contain;filter:brightness(10)" alt="ITD"><div><div class="login-brand">L'ITD Formation</div><div class="login-brand-sub">Institut de la Transformation Digitale</div></div></div>
</div>
<div class="login-hero">
<div class="login-hero-title">Développez<br>vos compétences<br>à votre rythme.</div>
<div class="login-hero-sub">Accédez à vos formations, suivez votre progression et obtenez vos certifications — où que vous soyez.</div>
</div>
<div class="login-stats">
<div><div class="login-stat-val">342</div><div class="login-stat-lbl">Apprenants actifs</div></div>
<div><div class="login-stat-val">24</div><div class="login-stat-lbl">Formations</div></div>
<div><div class="login-stat-val">78%</div><div class="login-stat-lbl">Taux de complétion</div></div>
</div>
</div>
<!-- Right: form -->
<div class="login-right">
<div class="login-form-wrap">
<!-- Login form -->
<div id="login-form-panel">
<div class="login-title">Bon retour 👋</div>
<div class="login-sub">Connectez-vous à votre espace L'ITD</div>
<div id="login-error-msg" class="login-error-msg"></div>
<div class="login-field">
<label>Adresse email</label>
<input class="login-input" id="login-email" type="email" placeholder="vous@exemple.fr" oninput="clearLoginError()" onkeydown="if(event.key==='Enter')loginSubmit()">
</div>
<div class="login-field">
<label>Mot de passe</label>
<div class="login-input-wrap">
<input class="login-input" id="login-pwd" type="password" placeholder="••••••••" oninput="clearLoginError()" onkeydown="if(event.key==='Enter')loginSubmit()">
<button class="login-pwd-toggle" onclick="toggleLoginPwd()" type="button">👁</button>
</div>
</div>
<div class="login-forgot" onclick="showForgot()">Mot de passe oublié ?</div>
<button class="login-btn" id="login-btn" onclick="loginSubmit()">Se connecter</button>
<div class="login-divider">accès démo rapide</div>
<div class="login-demo-chips">
<div class="login-demo-chip" onclick="loginAs('admin')">
<div style="width:32px;height:32px;border-radius:50%;background:#0066cc;color:white;display:flex;align-items:center;justify-content:center;font-size:12px;font-weight:700;flex-shrink:0">ML</div>
<div style="flex:1"><div style="font-weight:500">Marie Lambert</div><div class="login-demo-chip-role">Admin · admin@litd.fr / Admin2026!</div></div>
<svg width="14" height="14" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.8" style="color:var(--muted-2)"><path d="M7 5l6 5-6 5"/></svg>
</div>
<div class="login-demo-chip" onclick="loginAs('formateur')">
<div style="width:32px;height:32px;border-radius:50%;background:#ff6633;color:white;display:flex;align-items:center;justify-content:center;font-size:12px;font-weight:700;flex-shrink:0">PL</div>
<div style="flex:1"><div style="font-weight:500">Pierre Leblanc</div><div class="login-demo-chip-role">Formateur · formateur@litd.fr / Formateur1!</div></div>
<svg width="14" height="14" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.8" style="color:var(--muted-2)"><path d="M7 5l6 5-6 5"/></svg>
</div>
<div class="login-demo-chip" onclick="loginAs('apprenant')">
<div style="width:32px;height:32px;border-radius:50%;background:#4b5563;color:white;display:flex;align-items:center;justify-content:center;font-size:12px;font-weight:700;flex-shrink:0">JD</div>
<div style="flex:1"><div style="font-weight:500">Julie Dupont</div><div class="login-demo-chip-role">Apprenant · apprenant@litd.fr / Apprenant1!</div></div>
<svg width="14" height="14" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.8" style="color:var(--muted-2)"><path d="M7 5l6 5-6 5"/></svg>
</div>
</div>
</div>
<!-- Forgot password panel -->
<div id="forgot-form-panel" style="display:none">
<div class="login-back-link" onclick="showLogin()">← Retour à la connexion</div>
<div class="login-title">Mot de passe oublié</div>
<div class="login-sub">Entrez votre email, nous vous envoyons un lien de réinitialisation.</div>
<div id="forgot-success-msg" class="login-success-msg"></div>
<div id="forgot-error-msg" class="login-error-msg"></div>
<div class="login-field">
<label>Adresse email</label>
<input class="login-input" id="forgot-email" type="email" placeholder="vous@exemple.fr" onkeydown="if(event.key==='Enter')forgotSubmit()">
</div>
<button class="login-btn" onclick="forgotSubmit()">Envoyer le lien</button>
<div style="font-size:12.5px;color:var(--muted);text-align:center;margin-top:12px">Vérifiez aussi vos spams. Le lien expire dans 24h.</div>
</div>
</div>
</div>
</div><!-- end login-screen -->
<!-- ───── SIDEBAR ───── -->
<nav class="sidebar">
<div class="sidebar-brand">
<div style="display:flex;align-items:center;gap:9px"><img src="data:image/png;base64,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" style="width:34px;height:34px;object-fit:contain;flex-shrink:0" alt="ITD"><div><div class="brand-name" style="font-size:16px">L'ITD</div><div class="brand-sub">Formation Pro.</div></div></div>
</div>
<div class="sidebar-nav">
<div class="nav-section-label">Administration</div>
<div class="nav-item active" onclick="nav('dashboard')">
<svg class="nav-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="2" y="2" width="7" height="7" rx="1.5"/><rect x="11" y="2" width="7" height="7" rx="1.5"/><rect x="2" y="11" width="7" height="7" rx="1.5"/><rect x="11" y="11" width="7" height="7" rx="1.5"/></svg>
Tableau de bord
</div>
<div class="nav-item" onclick="nav('catalogue')">
<svg class="nav-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M4 4h12M4 8h12M4 12h8M4 16h6"/></svg>
Catalogue
</div>
<div class="nav-item admin-only formateur-only" onclick="nav('fiche')">
<svg class="nav-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M6 2h8a2 2 0 012 2v12a2 2 0 01-2 2H6a2 2 0 01-2-2V4a2 2 0 012-2z"/><path d="M7 7h6M7 10h6M7 13h4"/></svg>
Fiche formation
</div>
<div class="nav-item admin-only formateur-only" onclick="nav('sessions')">
<svg class="nav-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="2" y="4" width="16" height="14" rx="2"/><path d="M6 2v4M14 2v4M2 9h16"/></svg>
Sessions
</div>
<div class="nav-item admin-only formateur-only" onclick="nav('analytics')">
<svg class="nav-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M3 15l4-6 4 3 4-8"/><circle cx="3" cy="15" r="1.5"/></svg>
Analytics
</div>
<div class="nav-section-label">Espace</div>
<div class="nav-item" onclick="nav('apprenant')">
<svg class="nav-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5"><circle cx="10" cy="7" r="3"/><path d="M4 17a6 6 0 0112 0"/></svg>
Portail apprenant
</div>
<div class="nav-item" onclick="nav('apprenant2')">
<svg class="nav-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M3 3h14v10H3z"/><path d="M7 17h6M10 13v4"/><path d="M7 8l2 2 4-4"/></svg>
Dashboard apprenant
</div>
<div class="nav-item" onclick="nav('quiz')">
<svg class="nav-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5"><circle cx="10" cy="10" r="7"/><path d="M10 7v3l2 2"/><path d="M10 15h.01"/></svg>
Quiz & Évaluations
</div>
<div class="nav-item" onclick="nav('lecteur')">
<svg class="nav-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5"><circle cx="10" cy="10" r="7"/><polygon points="8,7 14,10 8,13" fill="currentColor" stroke="none"/></svg>
Lecteur de cours
</div>
<div class="nav-item admin-only formateur-only" onclick="nav('formateur')">
<svg class="nav-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M3 5h14M3 10h10M3 15h7"/><circle cx="16" cy="14" r="3"/><path d="M16 12v2l1 1"/></svg>
Espace formateur
</div>
<div class="nav-item" onclick="nav('certifications')">
<svg class="nav-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M10 2l2 4 5 .5-3.5 3.5.8 5L10 13l-4.3 2 .8-5L3 6.5 8 6z"/></svg>
Certifications
<span class="nav-badge">3</span>
</div>
<div class="nav-section-label">Contenu</div>
<div class="nav-item admin-only formateur-only" onclick="nav('gestion')" id="nav-gestion">
<svg class="nav-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="3" y="3" width="6" height="6" rx="1.5"/><rect x="11" y="3" width="6" height="6" rx="1.5"/><rect x="3" y="11" width="6" height="6" rx="1.5"/><path d="M14 11v6M11 14h6"/></svg>
Mes formations
<span class="nav-badge" id="badge-gestion">0</span>
</div>
<div class="nav-section-label">Configuration</div>
<div class="nav-item admin-only" onclick="nav('inscriptions')">
<svg class="nav-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M10 2a4 4 0 100 8 4 4 0 000-8zM4 18a6 6 0 0112 0"/><path d="M16 8l2 2 3-3" stroke-linecap="round" stroke-linejoin="round"/></svg>
Inscriptions
<span class="nav-badge" id="badge-inscriptions">4</span>
</div>
<div class="nav-item admin-only" onclick="nav('utilisateurs')">
<svg class="nav-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5"><circle cx="7" cy="7" r="3"/><path d="M1 17a6 6 0 0112 0"/><circle cx="15" cy="8" r="2.5"/><path d="M13 17h6a4 4 0 00-4-4"/></svg>
Utilisateurs & Rôles
</div>
<div class="nav-item admin-only" onclick="nav('parametres')">
<svg class="nav-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5"><circle cx="10" cy="10" r="2.5"/><path d="M10 1v2.5M10 16.5V19M1 10h2.5M16.5 10H19M3.22 3.22l1.77 1.77M15.01 15.01l1.77 1.77M3.22 16.78l1.77-1.77M15.01 4.99l1.77-1.77"/></svg>
Paramètres
</div>
<div class="nav-item admin-only" onclick="nav('admin')" style="background:rgba(10,10,10,0.06);border:1px solid var(--border)">
<svg class="nav-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5"><circle cx="10" cy="10" r="3"/><path d="M10 1v2M10 17v2M1 10h2M17 10h2M3.22 3.22l1.42 1.42M15.36 15.36l1.42 1.42M3.22 16.78l1.42-1.42M15.36 4.64l1.42-1.42"/></svg>
Administration
</div>
</div>
<div class="sidebar-footer">
<div class="sidebar-user">
<div class="user-avatar">ML</div>
<div>
<div class="user-name">Marie Lambert</div>
<div class="user-role">Administratrice</div>
</div>
</div>
</div>
</nav>
<!-- ───── TOPBAR ───── -->
<header class="topbar">
<div class="topbar-title" id="topbar-title">Tableau de bord</div>
<div class="topbar-right">
<button class="btn btn-sm btn-solid admin-only" onclick="openFormModal()">+ Nouvelle formation</button>
<button class="btn btn-sm btn-solid admin-only formateur-only" onclick="nav('sessions');setTimeout(openSessModal,50)">+ Session</button>
<button class="notif-bell-btn" onclick="toggleNotifPanel()" id="notif-bell-btn">
<svg width="15" height="15" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.6">
<path d="M10 2a6 6 0 00-6 6v3l-2 3h16l-2-3V8a6 6 0 00-6-6z"/>
<path d="M8 17a2 2 0 004 0"/>
</svg>
<span id="notif-count"></span>
</button>
<button class="dark-toggle" id="dark-toggle-btn" onclick="toggleDarkMode()" title="Mode sombre">🌙</button>
<div class="topbar-avatar" id="topbar-avatar-btn">ML</div>
</div>
</header>
<!-- Notification panel -->
<div class="notif-panel" id="notif-panel">
<div class="notif-header">
<div class="notif-header-title">Notifications</div>
<div class="notif-mark-all" onclick="markAllRead()">Tout marquer comme lu</div>
</div>
<div class="notif-tabs">
<div class="notif-tab active" id="ntab-all" onclick="switchNotifTab('all')">Toutes</div>
<div class="notif-tab" id="ntab-unread" onclick="switchNotifTab('unread')">Non lues</div>
<div class="notif-tab" id="ntab-system" onclick="switchNotifTab('system')">Système</div>
</div>
<div class="notif-list" id="notif-list"></div>
<div class="notif-footer"><a onclick="toggleNotifPanel();showToast('Toutes les notifications')">Voir toutes les notifications →</a></div>
</div>
<!-- ───── MAIN ───── -->
<main class="main">
<!-- VIEW 1: DASHBOARD -->
<div class="view active" id="view-dashboard">
<div class="dashboard-hero">
<div class="hero-greeting">Mercredi 25 mars 2026</div>
<div class="hero-title">Bonjour, Marie ✦</div>
<div class="hero-stats">
<div><div class="hero-stat-val">342</div><div class="hero-stat-lbl">Apprenants actifs</div></div>
<div><div class="hero-stat-val">24</div><div class="hero-stat-lbl">Formations actives</div></div>
<div><div class="hero-stat-val">47</div><div class="hero-stat-lbl">Sessions ce mois</div></div>
<div><div class="hero-stat-val">78%</div><div class="hero-stat-lbl">Taux de complétion</div></div>
</div>
</div>
<div class="grid-2" style="margin-bottom:24px">
<div class="card">
<div class="card-title">Formations en cours</div>
<div class="formations-list" style="margin-top:8px">
<div class="formation-row" onclick="nav('fiche')">
<div class="formation-icon">📊</div>
<div>
<div class="formation-name">Excel Avancé & Power BI</div>
<div class="formation-meta">Pierre Leblanc · 21h · 4.8 ★</div>
</div>
<div class="formation-progress">
<div class="formation-pct">84%</div>
<div class="formation-bar"><div class="progress-bar"><div class="progress-fill" style="width:84%"></div></div></div>
</div>
</div>
<div class="formation-row" onclick="nav('fiche')">
<div class="formation-icon">🤖</div>
<div>
<div class="formation-name">IA & Productivité</div>
<div class="formation-meta">L. Simon · 14h · Nouveau</div>
</div>
<div class="formation-progress">
<div class="formation-pct">62%</div>
<div class="formation-bar"><div class="progress-bar"><div class="progress-fill" style="width:62%"></div></div></div>
</div>
</div>
<div class="formation-row" onclick="nav('fiche')">
<div class="formation-icon">👥</div>
<div>
<div class="formation-name">Management Niveau 1</div>
<div class="formation-meta">A. Moreau · 28h · Certifiant</div>
</div>
<div class="formation-progress">
<div class="formation-pct">41%</div>
<div class="formation-bar"><div class="progress-bar"><div class="progress-fill" style="width:41%"></div></div></div>
</div>
</div>
<div class="formation-row" onclick="nav('fiche')">
<div class="formation-icon">🔒</div>
<div>
<div class="formation-name">Cybersécurité Essentielle</div>
<div class="formation-meta">C. Faure · 7h · E-learning</div>
</div>
<div class="formation-progress">
<div class="formation-pct">93%</div>
<div class="formation-bar"><div class="progress-bar"><div class="progress-fill" style="width:93%"></div></div></div>
</div>
</div>
</div>
</div>
<div>
<div class="card" style="margin-bottom:20px">
<div class="card-title">Sessions à venir</div>
<div class="sessions-list" style="margin-top:10px">
<div class="session-row" onclick="nav('sessions')">
<div class="session-dot" style="background:#3b82f6"></div>
<div class="session-info">
<div class="session-name">Excel Avancé — Groupe A</div>
<div class="session-date">Aujourd'hui, 14h00 · Salle Émeraude</div>
</div>
<div class="session-count"><span class="tag tag-blue">12 inscrits</span></div>
</div>
<div class="session-row" onclick="nav('sessions')">
<div class="session-dot" style="background:#1a7f4f"></div>
<div class="session-info">
<div class="session-name">Management — Module 3</div>
<div class="session-date">Demain, 09h00 · Distanciel</div>
</div>
<div class="session-count"><span class="tag tag-green">8 inscrits</span></div>
</div>
<div class="session-row" onclick="nav('sessions')">
<div class="session-dot" style="background:#b45309"></div>
<div class="session-info">
<div class="session-name">Prise de parole — Initiation</div>
<div class="session-date">27 mars, 10h00 · Hybride</div>
</div>
<div class="session-count"><span class="tag tag-amber">6 inscrits</span></div>
</div>
<div class="session-row" onclick="nav('sessions')">
<div class="session-dot" style="background:#7c3aed"></div>
<div class="session-info">
<div class="session-name">Droit du travail — Update</div>
<div class="session-date">28 mars, 14h00 · Présentiel</div>
</div>
<div class="session-count"><span class="tag tag-surface">10 inscrits</span></div>
</div>
</div>
</div>
<div class="card">
<div class="card-title">Alertes</div>
<div style="margin-top:10px">
<div class="alert-row amber"><span>⚠️</span><span class="alert-txt">Lucas Roche inactif depuis 14 jours — Excel Avancé</span></div>
<div class="alert-row green"><span>✅</span><span class="alert-txt">3 certifications délivrées cette semaine</span></div>
<div class="alert-row red"><span>🔴</span><span class="alert-txt">Session Management du 2 avril — places insuffisantes</span></div>
</div>
</div>
</div>
</div>
</div>
<!-- VIEW 2: CATALOGUE -->
<div class="view" id="view-catalogue">
<div class="page-header">
<div class="page-title">Catalogue de formations</div>
<div class="page-sub">24 formations disponibles · Mis à jour le 25 mars 2026</div>
</div>
<div class="filter-active-chips">
<span class="tag tag-ink">Présentiel ✕</span>
<span class="tag tag-ink">CPF ✕</span>
<span class="tag tag-outline">Tout effacer</span>
</div>
<div class="catalogue-layout">
<div class="filter-panel">
<div class="card" style="padding:24px">
<div class="filter-section">
<div class="filter-title">Domaine</div>
<div class="filter-options">
<label class="filter-option"><input type="checkbox" checked> Bureautique & Data</label>
<label class="filter-option"><input type="checkbox"> Management</label>
<label class="filter-option"><input type="checkbox"> Digital & IA</label>
<label class="filter-option"><input type="checkbox"> Juridique</label>
<label class="filter-option"><input type="checkbox"> Communication</label>
</div>
</div>
<div class="divider" style="margin:16px 0"></div>
<div class="filter-section">
<div class="filter-title">Modalité</div>
<div class="filter-options">
<label class="filter-option"><input type="checkbox" checked> Présentiel</label>
<label class="filter-option"><input type="checkbox"> Distanciel</label>
<label class="filter-option"><input type="checkbox"> Hybride</label>
<label class="filter-option"><input type="checkbox"> E-learning</label>
</div>
</div>
<div class="divider" style="margin:16px 0"></div>
<div class="filter-section">
<div class="filter-title">Financement</div>
<div class="filter-options">
<label class="filter-option"><input type="checkbox" checked> CPF</label>
<label class="filter-option"><input type="checkbox"> OPCO</label>
<label class="filter-option"><input type="checkbox"> Plan de formation</label>
</div>
</div>
<div class="divider" style="margin:16px 0"></div>
<div class="filter-section">
<div class="filter-title">Niveau</div>
<div class="filter-options">
<label class="filter-option"><input type="checkbox"> Débutant</label>
<label class="filter-option"><input type="checkbox"> Intermédiaire</label>
<label class="filter-option"><input type="checkbox"> Avancé</label>
</div>
</div>
</div>
</div>
<div class="catalogue-grid">
<div class="catalogue-card" onclick="nav('fiche')">
<div class="cat-card-thumb">📊<span class="cat-card-cpf">CPF</span></div>
<div class="cat-card-body">
<div class="cat-card-domain">Bureautique & Data</div>
<div class="cat-card-name">Excel Avancé & Power BI</div>
<div style="display:flex;gap:6px;flex-wrap:wrap"><span class="tag tag-surface">21h</span><span class="tag tag-green">Présentiel</span></div>
<div class="cat-card-footer">
<div class="cat-price">890 €</div>
<div class="cat-stars">⭐ 4.8 (47 avis)</div>
</div>
</div>
</div>
<div class="catalogue-card" onclick="nav('fiche')">
<div class="cat-card-thumb">🤖<span class="cat-card-cpf">Nouveau</span></div>
<div class="cat-card-body">
<div class="cat-card-domain">Digital & IA</div>
<div class="cat-card-name">IA & Productivité au Travail</div>
<div style="display:flex;gap:6px;flex-wrap:wrap"><span class="tag tag-surface">14h</span><span class="tag tag-blue">Hybride</span></div>
<div class="cat-card-footer">
<div class="cat-price">750 €</div>
<div class="cat-stars">⭐ 4.6 (12 avis)</div>
</div>
</div>
</div>
<div class="catalogue-card" onclick="nav('fiche')">
<div class="cat-card-thumb">👥<span class="cat-card-cpf">CPF</span></div>
<div class="cat-card-body">
<div class="cat-card-domain">Management</div>
<div class="cat-card-name">Management — Niveau 1</div>
<div style="display:flex;gap:6px;flex-wrap:wrap"><span class="tag tag-surface">28h</span><span class="tag tag-green">Certifiant</span></div>
<div class="cat-card-footer">
<div class="cat-price">1 490 €</div>
<div class="cat-stars">⭐ 4.9 (89 avis)</div>
</div>
</div>
</div>
<div class="catalogue-card" onclick="nav('fiche')">
<div class="cat-card-thumb">🔒</div>
<div class="cat-card-body">
<div class="cat-card-domain">Digital</div>
<div class="cat-card-name">Cybersécurité Essentielle</div>
<div style="display:flex;gap:6px;flex-wrap:wrap"><span class="tag tag-surface">7h</span><span class="tag tag-amber">E-learning</span></div>
<div class="cat-card-footer">
<div class="cat-price">Inclus</div>
<div class="cat-stars">⭐ 4.4 (31 avis)</div>
</div>
</div>
</div>
<div class="catalogue-card" onclick="nav('fiche')">
<div class="cat-card-thumb">🎤</div>
<div class="cat-card-body">
<div class="cat-card-domain">Communication</div>
<div class="cat-card-name">Prise de parole en public</div>
<div style="display:flex;gap:6px;flex-wrap:wrap"><span class="tag tag-surface">14h</span><span class="tag tag-green">Présentiel</span></div>
<div class="cat-card-footer">
<div class="cat-price">690 €</div>
<div class="cat-stars">⭐ 4.7 (28 avis)</div>
</div>
</div>
</div>
<div class="catalogue-card" onclick="nav('fiche')">
<div class="cat-card-thumb">⚖️<span class="cat-card-cpf">CPF</span></div>
<div class="cat-card-body">
<div class="cat-card-domain">Juridique</div>
<div class="cat-card-name">Droit du Travail — Mise à Jour</div>
<div style="display:flex;gap:6px;flex-wrap:wrap"><span class="tag tag-surface">21h</span><span class="tag tag-blue">Distanciel</span></div>
<div class="cat-card-footer">
<div class="cat-price">1 190 €</div>
<div class="cat-stars">⭐ 4.5 (19 avis)</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- VIEW 3: FICHE FORMATION -->
<div class="view" id="view-fiche">
<div class="fiche-hero">
<div class="fiche-domain">Bureautique & Data</div>
<div class="fiche-title">Excel Avancé & Power BI</div>
<div class="fiche-badges">
<span class="fiche-badge">21 heures</span>
<span class="fiche-badge">Présentiel</span>
<span class="fiche-badge">Eligible CPF</span>
<span class="fiche-badge">Intermédiaire</span>
</div>
<div class="fiche-stats">
<div><div class="fiche-stat-val">890 €</div><div class="fiche-stat-lbl">Prix TTC</div></div>
<div><div class="fiche-stat-val">4.8 ★</div><div class="fiche-stat-lbl">47 avis</div></div>
<div><div class="fiche-stat-val">342</div><div class="fiche-stat-lbl">Apprenants formés</div></div>
<div><div class="fiche-stat-val">94%</div><div class="fiche-stat-lbl">Taux de satisfaction</div></div>
</div>
</div>
<div class="grid-7-3">
<div>
<div class="section-title">Programme pédagogique</div>
<div class="programme-item" id="prog-1">
<div class="programme-header" onclick="toggleMod(1)">
<div class="programme-num">01</div>
<div class="programme-name">Fonctions avancées d'Excel</div>
<div class="programme-dur">4h</div>
<div class="programme-arrow">▶</div>
</div>
<div class="programme-body" id="prog-body-1">
<ul style="font-size:13px;color:var(--muted);line-height:1.8;list-style:none">
<li>· INDEX/EQUIV, RECHERCHEX et fonctions imbriquées</li>
<li>· Formules matricielles dynamiques</li>
<li>· Mise en forme conditionnelle avancée</li>
<li>· Cas pratique : analyse de données commerciales</li>
</ul>
</div>
</div>
<div class="programme-item" id="prog-2">
<div class="programme-header" onclick="toggleMod(2)">
<div class="programme-num">02</div>
<div class="programme-name">Tableaux croisés dynamiques</div>
<div class="programme-dur">3h</div>
<div class="programme-arrow">▶</div>
</div>
<div class="programme-body" id="prog-body-2">
<ul style="font-size:13px;color:var(--muted);line-height:1.8;list-style:none">
<li>· Création et personnalisation de TCD</li>
<li>· Champs calculés et mesures</li>
<li>· Graphiques croisés dynamiques</li>
</ul>
</div>
</div>
<div class="programme-item open" id="prog-3">
<div class="programme-header" onclick="toggleMod(3)" style="background:var(--surface)">
<div class="programme-num">03</div>
<div class="programme-name">Introduction à Power BI</div>
<div class="programme-dur">7h</div>
<div class="programme-arrow" style="transform:rotate(90deg)">▶</div>
</div>
<div class="programme-body open">
<ul style="font-size:13px;color:var(--muted);line-height:1.8;list-style:none">
<li>· Interface Power BI Desktop et concepts clés</li>
<li>· Connexion aux sources de données (Excel, SQL, API)</li>
<li>· Modèle de données et relations</li>
<li>· Langage DAX : mesures et colonnes calculées</li>
<li>· Création de rapports interactifs</li>
</ul>
</div>
</div>
<div class="programme-item" id="prog-4">
<div class="programme-header" onclick="toggleMod(4)">
<div class="programme-num">04</div>
<div class="programme-name">Dashboards & Publication</div>
<div class="programme-dur">4h</div>
<div class="programme-arrow">▶</div>
</div>
<div class="programme-body" id="prog-body-4"></div>
</div>
<div style="margin-top:28px">
<div class="section-title">Sessions disponibles</div>
<div class="card" style="padding:0;overflow:hidden">
<table class="sessions-table">
<thead><tr><th>Date</th><th>Lieu</th><th>Modalité</th><th>Places</th><th>Formateur</th><th></th></tr></thead>
<tbody>
<tr><td>26 mars 2026</td><td>Salle Émeraude</td><td><span class="tag tag-green">Présentiel</span></td><td>3/12</td><td>P. Leblanc</td><td><button class="btn btn-xs btn-solid">S'inscrire</button></td></tr>
<tr><td>14 avr. 2026</td><td>Distanciel</td><td><span class="tag tag-blue">En ligne</span></td><td>8/15</td><td>P. Leblanc</td><td><button class="btn btn-xs">S'inscrire</button></td></tr>
<tr><td>12 mai 2026</td><td>Salle Diamant</td><td><span class="tag tag-green">Présentiel</span></td><td>0/12</td><td>P. Leblanc</td><td><button class="btn btn-xs" disabled style="opacity:.4">Complet</button></td></tr>
</tbody>
</table>
</div>
</div>
</div>
<div>
<div class="card" style="margin-bottom:16px">
<div class="card-title">Formateur</div>
<div style="display:flex;align-items:center;gap:12px;margin-top:8px">
<div style="width:44px;height:44px;border-radius:50%;background:var(--ink);display:flex;align-items:center;justify-content:center;color:white;font-weight:600;flex-shrink:0">PL</div>
<div>
<div style="font-size:14px;font-weight:500">Pierre Leblanc</div>
<div style="font-size:12px;color:var(--muted)">Expert Excel & BI · 12 ans</div>
</div>
</div>
<div style="font-size:12.5px;color:var(--muted);margin-top:14px;line-height:1.6">Certifié Microsoft et expert Power BI. Intervient dans les plus grands groupes français.</div>
</div>
<div class="card" style="margin-bottom:16px">
<div class="card-title">Financement</div>
<div style="margin-top:10px;display:flex;flex-direction:column;gap:8px">
<span class="tag tag-green" style="width:fit-content">✓ Éligible CPF</span>
<span class="tag tag-blue" style="width:fit-content">✓ OPCO accepté</span>
<span class="tag tag-surface" style="width:fit-content">Plan de formation</span>
</div>
</div>
<div class="card">
<div class="card-title">Avis récents</div>
<div style="margin-top:10px;display:flex;flex-direction:column;gap:12px">
<div style="font-size:12.5px;line-height:1.6;color:var(--muted)">"Formation très complète, Pierre maîtrise parfaitement son sujet." — Julie D.</div>
<div class="divider" style="margin:4px 0"></div>
<div style="font-size:12.5px;line-height:1.6;color:var(--muted)">"J'ai pu créer mon premier dashboard Power BI dès le lendemain !" — Antoine M.</div>
</div>
</div>
</div>
</div>
</div>
<!-- VIEW 4: SESSIONS -->
<div class="view" id="view-sessions">
<div class="page-header" style="display:flex;justify-content:space-between;align-items:flex-end;margin-bottom:24px">
<div>
<div class="page-title">Sessions de formation</div>
<div class="page-sub">Gérez et planifiez vos sessions — elles apparaissent automatiquement dans le calendrier</div>
</div>
<div style="display:flex;gap:10px;align-items:center">
<div class="sessions-tabs">
<div class="sess-tab active" id="tab-cal" onclick="switchSess('cal')">📅 Calendrier</div>
<div class="sess-tab" id="tab-list" onclick="switchSess('list')">☰ Liste</div>
</div>
<button class="btn btn-solid admin-only formateur-only" onclick="openSessModal()">+ Nouvelle session</button>
</div>
</div>
<!-- KPIs -->
<div style="display:grid;grid-template-columns:repeat(4,1fr);gap:14px;margin-bottom:28px">
<div class="kpi-card"><div class="kpi-label">Total sessions</div><div class="kpi-value" id="skpi-total" style="color:#0066cc">0</div></div>
<div class="kpi-card"><div class="kpi-label">En cours</div><div class="kpi-value" id="skpi-encours" style="color:#ff6633">0</div></div>
<div class="kpi-card"><div class="kpi-label">À venir</div><div class="kpi-value" id="skpi-avenir">0</div></div>
<div class="kpi-card"><div class="kpi-label">Inscrits total</div><div class="kpi-value" id="skpi-inscrits">0</div></div>
</div>
<!-- Calendrier -->
<div id="sess-calendar">
<div class="card" style="padding:28px">
<div class="cal-header" style="display:flex;align-items:center;justify-content:space-between;margin-bottom:20px">
<div style="font-family:'Playfair Display',serif;font-size:22px;font-weight:400" id="cal-month-title">Mars 2026</div>
<div style="display:flex;gap:8px;align-items:center">
<button class="btn btn-sm" onclick="calNav(-1)">‹</button>
<button class="btn btn-sm" onclick="calGoToday()">Aujourd'hui</button>
<button class="btn btn-sm" onclick="calNav(1)">›</button>
</div>
</div>
<div class="cal-grid">
<div class="cal-dow">Lun</div><div class="cal-dow">Mar</div><div class="cal-dow">Mer</div>
<div class="cal-dow">Jeu</div><div class="cal-dow">Ven</div><div class="cal-dow">Sam</div><div class="cal-dow">Dim</div>
<div id="cal-body" style="display:contents"></div>
</div>
</div>
</div>
<!-- Liste -->
<div id="sess-list" style="display:none">
<div style="display:flex;gap:10px;margin-bottom:16px;flex-wrap:wrap">
<select class="sf-sel" id="sess-f-statut" style="width:auto" onchange="renderSessions()">
<option value="">Tous statuts</option>
<option value="planifie">Planifié</option>
<option value="encours">En cours</option>
<option value="termine">Terminé</option>
<option value="annule">Annulé</option>
</select>
<select class="sf-sel" id="sess-f-modal" style="width:auto" onchange="renderSessions()">
<option value="">Toutes modalités</option>
<option>Présentiel</option><option>Distanciel</option><option>Hybride</option><option>E-learning</option>
</select>
<input class="sf-inp" id="sess-f-search" placeholder="🔍 Rechercher…" style="flex:1;min-width:180px" oninput="renderSessions()">
</div>
<div id="sess-list-container" style="display:flex;flex-direction:column;gap:10px"></div>
</div>
<!-- Émargement -->
<div class="emargement-panel" id="emarg-panel">
<div class="emarg-header">
<div class="emarg-title">Émargement</div>
<button class="btn btn-sm" onclick="document.getElementById('emarg-panel').classList.remove('open')">Fermer</button>
</div>
<div class="emarg-rows">
<div class="emarg-row"><div class="emarg-name">Julie Dupont</div><button class="emarg-btn present" onclick="toggleEmarg(this)">✓</button><button class="emarg-btn" onclick="toggleEmarg(this)" style="margin-left:6px">✗</button></div>
<div class="emarg-row"><div class="emarg-name">Antoine Martin</div><button class="emarg-btn present" onclick="toggleEmarg(this)">✓</button><button class="emarg-btn" onclick="toggleEmarg(this)" style="margin-left:6px">✗</button></div>
<div class="emarg-row"><div class="emarg-name">Lucas Roche</div><button class="emarg-btn absent" onclick="toggleEmarg(this)">✓</button><button class="emarg-btn absent" onclick="toggleEmarg(this)" style="margin-left:6px">✗</button></div>
</div>
<div style="padding:14px 20px;display:flex;gap:10px">
<button class="btn btn-solid btn-sm">💾 Valider</button>
<button class="btn btn-sm">📄 Exporter PDF</button>
</div>
</div>
</div>
<!-- VIEW 5: ANALYTICS -->
<div class="view" id="view-analytics">
<div class="page-header">
<div class="page-title">Analytics & Reporting</div>
<div class="page-sub">Données en temps réel · Mars 2026</div>
</div>
<div class="kpi-grid">
<div class="kpi-card"><div class="kpi-label">Apprenants actifs</div><div class="kpi-value">342</div><div class="kpi-change up">↑ +28 ce mois</div></div>
<div class="kpi-card"><div class="kpi-label">Taux complétion</div><div class="kpi-value">78%</div><div class="kpi-change up">↑ +5% vs mois</div></div>
<div class="kpi-card"><div class="kpi-label">Heures délivrées</div><div class="kpi-value">1 247</div><div class="kpi-change up">↑ +18% vs N-1</div></div>
<div class="kpi-card"><div class="kpi-label">Satisfaction</div><div class="kpi-value">4.7</div><div class="kpi-change">/ 5 · 312 avis</div></div>
</div>
<div class="grid-7-3" style="margin-bottom:20px">
<div class="card">
<div class="card-title">Inscriptions — 6 derniers mois</div>
<div class="chart-bar-group">
<div class="chart-bar-wrap"><div class="chart-bar" style="height:54%"></div><div class="chart-bar-label">Oct</div></div>
<div class="chart-bar-wrap"><div class="chart-bar" style="height:61%"></div><div class="chart-bar-label">Nov</div></div>
<div class="chart-bar-wrap"><div class="chart-bar" style="height:47%"></div><div class="chart-bar-label">Déc</div></div>
<div class="chart-bar-wrap"><div class="chart-bar" style="height:72%"></div><div class="chart-bar-label">Jan</div></div>
<div class="chart-bar-wrap"><div class="chart-bar" style="height:85%"></div><div class="chart-bar-label">Fév</div></div>
<div class="chart-bar-wrap"><div class="chart-bar" style="height:100%"></div><div class="chart-bar-label">Mar</div></div>
</div>
</div>
<div class="card">
<div class="card-title">Modalités</div>
<div class="donut-wrap" style="margin-top:16px">
<svg width="90" height="90" viewBox="0 0 90 90">
<circle cx="45" cy="45" r="35" fill="none" stroke="var(--border)" stroke-width="12"/>
<circle cx="45" cy="45" r="35" fill="none" stroke="var(--ink)" stroke-width="12" stroke-dasharray="88 132" stroke-dashoffset="0" stroke-linecap="round"/>
<circle cx="45" cy="45" r="35" fill="none" stroke="#d1d5db" stroke-width="12" stroke-dasharray="53 167" stroke-dashoffset="-88" stroke-linecap="round"/>
<circle cx="45" cy="45" r="35" fill="none" stroke="#9ca3af" stroke-width="12" stroke-dasharray="39 181" stroke-dashoffset="-141" stroke-linecap="round"/>
</svg>
<div class="donut-legend">
<div class="donut-leg-row"><div class="donut-dot" style="background:var(--ink)"></div>Présentiel 40%</div>
<div class="donut-leg-row"><div class="donut-dot" style="background:#d1d5db"></div>Distanciel 24%</div>
<div class="donut-leg-row"><div class="donut-dot" style="background:#9ca3af"></div>Hybride 18%</div>
<div class="donut-leg-row"><div class="donut-dot" style="background:var(--border-2)"></div>E-learning 18%</div>
</div>
</div>
</div>
</div>
<div class="grid-2">
<div class="card">
<div class="card-title">Taux de complétion par formation</div>
<div style="margin-top:16px;display:flex;flex-direction:column;gap:14px">
<div><div style="display:flex;justify-content:space-between;font-size:13px;margin-bottom:6px"><span>Excel Avancé & Power BI</span><span style="font-family:'Playfair Display',serif">84%</span></div><div class="progress-bar" style="height:4px"><div class="progress-fill" style="width:84%"></div></div></div>
<div><div style="display:flex;justify-content:space-between;font-size:13px;margin-bottom:6px"><span>Management Niveau 1</span><span style="font-family:'Playfair Display',serif">91%</span></div><div class="progress-bar" style="height:4px"><div class="progress-fill" style="width:91%"></div></div></div>
<div><div style="display:flex;justify-content:space-between;font-size:13px;margin-bottom:6px"><span>IA & Productivité</span><span style="font-family:'Playfair Display',serif">67%</span></div><div class="progress-bar" style="height:4px"><div class="progress-fill" style="width:67%"></div></div></div>
<div><div style="display:flex;justify-content:space-between;font-size:13px;margin-bottom:6px"><span>Prise de parole</span><span style="font-family:'Playfair Display',serif">78%</span></div><div class="progress-bar" style="height:4px"><div class="progress-fill" style="width:78%"></div></div></div>
<div><div style="display:flex;justify-content:space-between;font-size:13px;margin-bottom:6px"><span>Cybersécurité</span><span style="font-family:'Playfair Display',serif">93%</span></div><div class="progress-bar" style="height:4px"><div class="progress-fill" style="width:93%"></div></div></div>
</div>
</div>
<div class="card">
<div class="card-title">Suivi individuel</div>
<table class="analytics-table">
<thead><tr><th>Apprenant</th><th>Progression</th><th>Statut</th></tr></thead>
<tbody>
<tr><td>Julie Dupont</td><td><span style="font-family:'Playfair Display',serif">92%</span></td><td><span class="tag tag-green">Actif</span></td></tr>
<tr><td>Antoine Martin</td><td><span style="font-family:'Playfair Display',serif">78%</span></td><td><span class="tag tag-green">Actif</span></td></tr>
<tr><td>Sophie Bernard</td><td><span style="font-family:'Playfair Display',serif">55%</span></td><td><span class="tag tag-green">Actif</span></td></tr>
<tr><td>Lucas Roche</td><td><span style="font-family:'Playfair Display',serif">34%</span></td><td><span class="tag tag-red">Inactif</span></td></tr>
<tr><td>Emma Lefebvre</td><td><span style="font-family:'Playfair Display',serif">67%</span></td><td><span class="tag tag-green">Actif</span></td></tr>
<tr><td>Thomas Renard</td><td><span style="font-family:'Playfair Display',serif">88%</span></td><td><span class="tag tag-green">Actif</span></td></tr>
</tbody>
</table>
</div>
</div>
</div>
<!-- VIEW 6: PORTAIL APPRENANT -->
<div class="view" id="view-apprenant">
<div class="apprenant-hero">
<div class="apprenant-hello">Bonjour 👋</div>
<div class="apprenant-name">Julie Dupont</div>
<div style="display:flex;gap:40px;position:relative;z-index:1">
<div><div style="font-family:'Playfair Display',serif;font-size:32px;font-weight:400">3</div><div style="font-size:11px;color:rgba(255,255,255,0.5);margin-top:4px">Formations en cours</div></div>
<div><div style="font-family:'Playfair Display',serif;font-size:32px;font-weight:400">92%</div><div style="font-size:11px;color:rgba(255,255,255,0.5);margin-top:4px">Progression globale</div></div>
<div><div style="font-family:'Playfair Display',serif;font-size:32px;font-weight:400">2</div><div style="font-size:11px;color:rgba(255,255,255,0.5);margin-top:4px">Certifications</div></div>
</div>
</div>
<div class="alert-row amber" style="margin-bottom:20px">
<span>📅</span>
<span class="alert-txt"><b>Session aujourd'hui</b> — Excel Avancé, 14h00, Salle Émeraude. <a href="#" style="color:var(--amber)">Voir les détails →</a></span>
</div>
<div class="section-title">Mes formations en cours</div>
<div class="grid-3" style="margin-bottom:28px">
<div class="cours-card" onclick="nav('lecteur')">
<div class="ring-wrap">
<svg class="ring-svg" width="80" height="80" viewBox="0 0 80 80">
<circle class="ring-bg" cx="40" cy="40" r="34"/>
<circle class="ring-fill" cx="40" cy="40" r="34" stroke-dasharray="213.6" stroke-dashoffset="25.6"/>
</svg>
<div class="ring-text">88%</div>
</div>
<div class="cours-title">Excel Avancé & Power BI</div>
<div class="cours-meta">Pierre Leblanc · 18.5h / 21h</div>
<button class="btn btn-solid btn-sm" style="width:100%;justify-content:center">Reprendre →</button>
</div>
<div class="cours-card" onclick="nav('lecteur')">
<div class="ring-wrap">
<svg class="ring-svg" width="80" height="80" viewBox="0 0 80 80">
<circle class="ring-bg" cx="40" cy="40" r="34"/>
<circle class="ring-fill" cx="40" cy="40" r="34" stroke-dasharray="213.6" stroke-dashoffset="106.8"/>
</svg>
<div class="ring-text">50%</div>
</div>
<div class="cours-title">IA & Productivité</div>
<div class="cours-meta">L. Simon · 7h / 14h</div>
<button class="btn btn-sm" style="width:100%;justify-content:center">Continuer</button>
</div>
<div class="cours-card" onclick="nav('lecteur')">
<div class="ring-wrap">
<svg class="ring-svg" width="80" height="80" viewBox="0 0 80 80">
<circle class="ring-bg" cx="40" cy="40" r="34"/>
<circle class="ring-fill" cx="40" cy="40" r="34" stroke-dasharray="213.6" stroke-dashoffset="149.5"/>
</svg>
<div class="ring-text">30%</div>
</div>
<div class="cours-title">Prise de parole</div>
<div class="cours-meta">L. Simon · 4.2h / 14h</div>
<button class="btn btn-sm" style="width:100%;justify-content:center">Commencer</button>
</div>
</div>
<div class="section-title">Mes certifications</div>
<div class="grid-3">
<div class="cert-card"><div class="cert-icon">🏆</div><div class="cert-name">Cybersécurité Essentielle</div><div class="cert-date">Délivré le 15 fév. 2026</div></div>
<div class="cert-card"><div class="cert-icon">🎓</div><div class="cert-name">Excel — Niveau Avancé</div><div class="cert-date">Délivré le 10 jan. 2026</div></div>
<div class="cert-card" style="background:var(--surface);border:1px dashed var(--border-2)"><div class="cert-icon" style="opacity:0.4">🔒</div><div class="cert-name" style="color:var(--muted)">Management Niv. 1</div><div class="cert-date" style="color:var(--muted-2)">En cours — 50% complété</div></div>
</div>
</div>
<!-- VIEW 7: LECTEUR -->
<div class="view" id="view-lecteur">
<div class="page-header" style="margin-bottom:20px">
<div class="page-title">Excel Avancé & Power BI</div>
<div class="page-sub">Module 3 — Introduction à Power BI · Leçon 2/5</div>
</div>
<div class="lecteur-layout">
<div class="lecteur-sidebar">
<div class="card-title" style="margin-bottom:14px">Sommaire</div>
<div style="font-size:11px;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:.07em;margin-bottom:8px">Module 1 — Fonctions Excel</div>
<div class="lecon-item done"><div class="lecon-num">Leçon 1</div><div class="lecon-name">INDEX / EQUIV</div></div>
<div class="lecon-item done"><div class="lecon-num">Leçon 2</div><div class="lecon-name">Formules matricielles</div></div>
<div style="font-size:11px;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:.07em;margin:12px 0 8px">Module 3 — Power BI</div>
<div class="lecon-item done"><div class="lecon-num">Leçon 1</div><div class="lecon-name">Interface & concepts</div></div>
<div class="lecon-item active"><div class="lecon-num">Leçon 2</div><div class="lecon-name">Connexion aux données</div></div>
<div class="lecon-item"><div class="lecon-num">Leçon 3</div><div class="lecon-name">Modèle de données</div></div>
<div class="lecon-item"><div class="lecon-num">Leçon 4</div><div class="lecon-name">Langage DAX</div></div>
<div class="lecon-item"><div class="lecon-num">Leçon 5</div><div class="lecon-name">Création de rapports</div></div>
</div>
<div class="player-area">
<div class="video-player">
<div class="play-btn">▶</div>
<div class="video-title">Connexion aux sources de données</div>
<div class="video-prog"><div class="video-prog-fill"></div></div>
</div>
<div class="notes-area">
<div class="card-title" style="margin-bottom:12px">Mes notes</div>
<div class="note-item"><div class="note-time">02:34</div><div class="note-text">Power Query = ETL intégré à Power BI. Permet de transformer les données avant chargement.</div></div>
<div class="note-item"><div class="note-time">07:15</div><div class="note-text">Connexion live vs Import — préférer Import pour les dashboards avec historique.</div></div>
<div class="note-item"><div class="note-time">12:08</div><div class="note-text">Attention aux types de données lors de la connexion SQL !</div></div>
</div>
<div style="display:flex;gap:10px">
<button class="btn btn-sm">‹ Leçon précédente</button>
<button class="btn btn-sm btn-solid" style="margin-left:auto">Leçon suivante ›</button>
</div>
</div>
<div class="ressources-panel">
<div class="card-title" style="margin-bottom:16px">Ressources</div>
<div class="ressource-row"><div class="ressource-icon">📄</div><div><div class="ressource-name">Guide Power Query</div><div class="ressource-type">PDF · 2.4 Mo</div></div></div>
<div class="ressource-row"><div class="ressource-icon">📊</div><div><div class="ressource-name">Données exercice</div><div class="ressource-type">XLSX · 840 Ko</div></div></div>
<div class="ressource-row"><div class="ressource-icon">🔗</div><div><div class="ressource-name">Doc Microsoft</div><div class="ressource-type">Lien externe</div></div></div>
<div class="ressource-row"><div class="ressource-icon">💻</div><div><div class="ressource-name">Solution complète</div><div class="ressource-type">PBIX · 1.1 Mo</div></div></div>
<div class="divider"></div>
<div class="card-title" style="margin-bottom:12px">Évaluation</div>
<div style="font-size:13px;color:var(--muted);margin-bottom:12px">Quiz de fin de module disponible après la leçon 5.</div>
<button class="btn btn-sm" style="opacity:.5;cursor:not-allowed">Quiz verrouillé 🔒</button>
</div>
</div>
</div>
<!-- VIEW 8: FORMATEUR -->
<div class="view" id="view-formateur">
<div class="formateur-hero">
<div style="font-size:13px;color:rgba(255,255,255,0.5);margin-bottom:6px">Espace formateur</div>
<div style="font-family:'Playfair Display',serif;font-size:28px;font-weight:400;color:white">Pierre Leblanc</div>
<div class="form-kpi-grid">
<div class="form-kpi"><div class="form-kpi-val">4</div><div class="form-kpi-lbl">Sessions ce mois</div></div>
<div class="form-kpi"><div class="form-kpi-val">48</div><div class="form-kpi-lbl">Apprenants</div></div>
<div class="form-kpi"><div class="form-kpi-val">4.8</div><div class="form-kpi-lbl">Note moyenne</div></div>
<div class="form-kpi"><div class="form-kpi-val">7</div><div class="form-kpi-lbl">Évaluations à corriger</div></div>
</div>
</div>
<div class="grid-2">
<div>
<div class="section-title">Évaluations à corriger</div>
<div class="eval-row"><div class="eval-avatar">JD</div><div class="eval-info"><div class="eval-name">Julie Dupont</div><div class="eval-sub">Excel Avancé · Rendu le 24 mars</div></div><input class="eval-note-input" type="text" placeholder="—" value="18"></div>
<div class="eval-row"><div class="eval-avatar">AM</div><div class="eval-info"><div class="eval-name">Antoine Martin</div><div class="eval-sub">Excel Avancé · Rendu le 24 mars</div></div><input class="eval-note-input" type="text" placeholder="—" value="15"></div>
<div class="eval-row"><div class="eval-avatar">SB</div><div class="eval-info"><div class="eval-name">Sophie Bernard</div><div class="eval-sub">Excel Avancé · Rendu le 23 mars</div></div><input class="eval-note-input" type="text" placeholder="—"></div>
<div class="eval-row"><div class="eval-avatar">TR</div><div class="eval-info"><div class="eval-name">Thomas Renard</div><div class="eval-sub">Power BI · Rendu le 22 mars</div></div><input class="eval-note-input" type="text" placeholder="—" value="16"></div>
<div style="margin-top:14px"><button class="btn btn-solid btn-sm">💾 Enregistrer toutes les notes</button></div>
</div>
<div>
<div class="section-title">Progression du groupe</div>
<div class="card">
<div style="display:flex;flex-direction:column;gap:4px">
<div class="groupe-row"><div class="groupe-name">Julie Dupont</div><div class="groupe-bar-wrap"><div class="progress-bar" style="height:4px"><div class="progress-fill" style="width:92%"></div></div></div><div class="groupe-pct">92%</div></div>
<div class="groupe-row"><div class="groupe-name">Antoine Martin</div><div class="groupe-bar-wrap"><div class="progress-bar" style="height:4px"><div class="progress-fill" style="width:78%"></div></div></div><div class="groupe-pct">78%</div></div>
<div class="groupe-row"><div class="groupe-name">Sophie Bernard</div><div class="groupe-bar-wrap"><div class="progress-bar" style="height:4px"><div class="progress-fill" style="width:55%"></div></div></div><div class="groupe-pct">55%</div></div>
<div class="groupe-row"><div class="groupe-name">Lucas Roche</div><div class="groupe-bar-wrap"><div class="progress-bar" style="height:4px"><div class="progress-fill" style="width:34%"></div></div></div><div class="groupe-pct" style="color:var(--red)">34%</div></div>
<div class="groupe-row"><div class="groupe-name">Emma Lefebvre</div><div class="groupe-bar-wrap"><div class="progress-bar" style="height:4px"><div class="progress-fill" style="width:67%"></div></div></div><div class="groupe-pct">67%</div></div>
<div class="groupe-row"><div class="groupe-name">Thomas Renard</div><div class="groupe-bar-wrap"><div class="progress-bar" style="height:4px"><div class="progress-fill" style="width:88%"></div></div></div><div class="groupe-pct">88%</div></div>
</div>
</div>
<div class="card" style="margin-top:16px">
<div class="card-title" style="margin-bottom:12px">Messagerie</div>
<div style="display:flex;flex-direction:column;gap:10px">
<div class="session-row" style="background:var(--surface)"><div class="user-avatar" style="width:32px;height:32px;font-size:12px">LR</div><div><div style="font-size:13px;font-weight:500">Lucas Roche</div><div style="font-size:12px;color:var(--muted)">J'ai du mal avec DAX, est-ce qu'on peut...</div></div><div style="font-size:11px;color:var(--muted-2);white-space:nowrap">Il y a 2h</div></div>
<div class="session-row" style="background:var(--surface)"><div class="user-avatar" style="width:32px;height:32px;font-size:12px">JD</div><div><div style="font-size:13px;font-weight:500">Julie Dupont</div><div style="font-size:12px;color:var(--muted)">Merci pour les ressources complémentaires !</div></div><div style="font-size:11px;color:var(--muted-2);white-space:nowrap">Hier</div></div>
</div>
</div>
</div>
</div>
</div>
<!-- VIEW 9: CERTIFICATIONS -->
<div class="view" id="view-certifications">
<div class="page-header" style="display:flex;justify-content:space-between;align-items:flex-end">
<div>
<div class="page-title">Certifications & Documents</div>
<div class="page-sub">3 certifications délivrées ce mois · 7 en cours</div>
</div>
<div style="display:flex;gap:10px">
<button class="btn btn-sm">📤 Export OPCO</button>
<button class="btn btn-sm btn-solid">+ Nouveau modèle</button>
</div>
</div>
<div class="section-title">Certifications récentes</div>
<div class="cert-grid">
<div class="cert-card-v2">
<div class="cert-v2-top"><div class="cert-v2-icon">🏆</div><span class="tag tag-green">Délivré</span></div>
<div class="cert-v2-name">Excel Avancé & Power BI</div>
<div class="cert-v2-date">Julie Dupont · Délivré le 20 mars 2026</div>
<div style="margin-top:14px;display:flex;gap:8px"><button class="btn btn-xs">Aperçu</button><button class="btn btn-xs btn-solid">Télécharger</button></div>
</div>
<div class="cert-card-v2">
<div class="cert-v2-top"><div class="cert-v2-icon">🎓</div><span class="tag tag-green">Délivré</span></div>
<div class="cert-v2-name">Management Niveau 1</div>
<div class="cert-v2-date">Antoine Martin · Délivré le 18 mars 2026</div>
<div style="margin-top:14px;display:flex;gap:8px"><button class="btn btn-xs">Aperçu</button><button class="btn btn-xs btn-solid">Télécharger</button></div>
</div>
<div class="cert-card-v2">
<div class="cert-v2-top"><div class="cert-v2-icon">🔒</div><span class="tag tag-amber">En cours</span></div>
<div class="cert-v2-name">Cybersécurité Essentielle</div>
<div class="cert-v2-date">Sophie Bernard · 60% complété</div>
<div style="margin-top:14px"><div class="progress-bar" style="height:3px"><div class="progress-fill" style="width:60%"></div></div></div>
</div>
<div class="cert-card-v2">
<div class="cert-v2-top"><div class="cert-v2-icon">🔒</div><span class="tag tag-surface" style="opacity:.5">Verrouillé</span></div>
<div class="cert-v2-name">Prise de parole en public</div>
<div class="cert-v2-date">Emma Lefebvre · Formation non complétée</div>
<div style="margin-top:14px"><div class="progress-bar" style="height:3px"><div class="progress-fill" style="width:30%"></div></div></div>
</div>
<div class="cert-card-v2">
<div class="cert-v2-top"><div class="cert-v2-icon">🎓</div><span class="tag tag-green">Délivré</span></div>
<div class="cert-v2-name">Droit du Travail</div>
<div class="cert-v2-date">Thomas Renard · Délivré le 15 mars 2026</div>
<div style="margin-top:14px;display:flex;gap:8px"><button class="btn btn-xs">Aperçu</button><button class="btn btn-xs btn-solid">Télécharger</button></div>
</div>
<div class="cert-card-v2" style="border-style:dashed;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;cursor:pointer;color:var(--muted)">
<div style="font-size:28px;opacity:0.4">+</div>
<div style="font-size:13px">Nouveau modèle</div>
</div>
</div>
<div class="section-title">Documents administratifs</div>
<div class="card" style="padding:0;overflow:hidden">
<table class="docs-table">
<thead><tr><th>Document</th><th>Type</th><th>Apprenant</th><th>Date</th><th>Statut</th><th></th></tr></thead>
<tbody>
<tr><td>📋 Convention de formation</td><td>Convention</td><td>Julie Dupont</td><td>1 mars 2026</td><td><span class="tag tag-green">Signé</span></td><td><button class="btn btn-xs">Télécharger</button></td></tr>
<tr><td>📄 Attestation de présence</td><td>Attestation</td><td>Antoine Martin</td><td>20 mars 2026</td><td><span class="tag tag-green">Délivré</span></td><td><button class="btn btn-xs">Télécharger</button></td></tr>
<tr><td>📋 Convention de formation</td><td>Convention</td><td>Emma Lefebvre</td><td>5 mars 2026</td><td><span class="tag tag-amber">En attente signature</span></td><td><button class="btn btn-xs btn-solid">Relancer</button></td></tr>
<tr><td>📊 Bilan de compétences</td><td>Bilan</td><td>Thomas Renard</td><td>22 mars 2026</td><td><span class="tag tag-green">Finalisé</span></td><td><button class="btn btn-xs">Télécharger</button></td></tr>
<tr><td>📤 Dossier OPCO</td><td>Financement</td><td>Groupe Excel A</td><td>15 mars 2026</td><td><span class="tag tag-blue">Envoyé</span></td><td><button class="btn btn-xs">Voir</button></td></tr>
</tbody>
</table>
</div>
</div>
<!-- VIEW 10: ADMIN -->
<div class="view" id="view-admin">
<div class="page-header" style="display:flex;justify-content:space-between;align-items:flex-end;margin-bottom:24px">
<div>
<div class="page-title">Administration</div>
<div class="page-sub">Personnalisation de la plateforme FormaPro</div>
</div>
<div style="display:flex;align-items:center;gap:12px">
<span class="live-badge"><span class="live-dot"></span>Aperçu en direct</span>
<button class="btn btn-sm" onclick="resetAdmin()">↺ Réinitialiser</button>
<button class="btn btn-sm btn-solid" onclick="saveAdmin()">💾 Sauvegarder</button>
</div>
</div>
<div style="background:var(--white);border:1px solid var(--border);border-radius:var(--radius);overflow:hidden;display:flex;min-height:680px">
<!-- Left sidebar tabs -->
<div class="admin-tabs-sidebar">
<div class="admin-tab-item active" onclick="switchAdmin('colors')" id="atab-colors">
<svg class="admin-tab-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5"><circle cx="10" cy="10" r="7"/><path d="M10 3v2M10 15v2M3 10h2M15 10h2M4.93 4.93l1.41 1.41M13.66 13.66l1.41 1.41M4.93 15.07l1.41-1.41M13.66 6.34l1.41-1.41"/></svg>
Couleurs
</div>
<div class="admin-tab-item" onclick="switchAdmin('content')" id="atab-content">
<svg class="admin-tab-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M4 4h12M4 8h12M4 12h8M4 16h6"/></svg>
Contenu
</div>
<div class="admin-tab-item" onclick="switchAdmin('tables')" id="atab-tables">
<svg class="admin-tab-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="2" y="4" width="16" height="13" rx="1.5"/><path d="M2 8h16M7 8v9"/></svg>
Tableaux
</div>
<div class="admin-tab-item" onclick="switchAdmin('kpis')" id="atab-kpis">
<svg class="admin-tab-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M3 15l4-6 4 3 4-8"/></svg>
KPIs
</div>
<div class="admin-tab-item" onclick="switchAdmin('typography')" id="atab-typography">
<svg class="admin-tab-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M4 6h12M10 6v10M7 16h6"/></svg>
Typographie
</div>
<div class="admin-tab-item" onclick="switchAdmin('alerts')" id="atab-alerts">
<svg class="admin-tab-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M10 2l1.5 5h5l-4 3 1.5 5L10 12l-4 3 1.5-5-4-3h5z"/></svg>
Alertes
</div>
<div class="admin-tab-item" onclick="switchAdmin('navigation')" id="atab-navigation">
<svg class="admin-tab-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M3 6h14M3 10h10M3 14h7"/></svg>
Navigation
</div>
<div class="admin-tab-item" onclick="switchAdmin('branding')" id="atab-branding">
<svg class="admin-tab-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="2" y="5" width="16" height="12" rx="2"/><path d="M6 5V3h8v2"/></svg>
Marque
</div>
<div style="height:1px;background:var(--border);margin:8px 12px"></div>
<div class="admin-tab-item" onclick="switchAdmin('invitations')" id="atab-invitations" style="color:var(--ink);font-weight:500">
<svg class="admin-tab-icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M2 4h16v12H2z"/><path d="M2 4l8 7 8-7"/></svg>
Invitations
<span style="margin-left:auto;background:var(--ink);color:white;font-size:10px;padding:1px 7px;border-radius:99px;font-weight:600" id="inv-badge">3</span>
</div>
</div>
<!-- Right panel -->
<div style="flex:1;overflow-y:auto">
<!-- COLORS PANEL -->
<div class="admin-panel active" id="apanel-colors">
<div class="admin-panel-title">Palette de couleurs</div>
<div class="admin-panel-sub">Personnalisez les couleurs de l'interface. Les changements s'appliquent instantanément.</div>
<div style="margin-bottom:20px">
<div class="field-label">Thèmes prédéfinis</div>
<div class="color-presets">
<div class="color-preset selected" style="background:#0a0a0a" title="Noir classique" onclick="applyTheme('dark')"></div>
<div class="color-preset" style="background:#1d4ed8" title="Bleu professionnel" onclick="applyTheme('blue')"></div>
<div class="color-preset" style="background:#1a7f4f" title="Vert nature" onclick="applyTheme('green')"></div>
<div class="color-preset" style="background:#7c3aed" title="Violet créatif" onclick="applyTheme('purple')"></div>
<div class="color-preset" style="background:#c2410c" title="Orange énergie" onclick="applyTheme('orange')"></div>
<div class="color-preset" style="background:#be185d" title="Rose élégant" onclick="applyTheme('rose')"></div>
<div class="color-preset" style="background:#0e7490" title="Cyan moderne" onclick="applyTheme('cyan')"></div>
<div class="color-preset" style="background:#334155" title="Ardoise sobre" onclick="applyTheme('slate')"></div>
</div>
</div>
<div class="color-grid">
<div class="color-row">
<div class="color-swatch" id="swatch-ink" style="background:#0a0a0a">
<input type="color" value="#0a0a0a" oninput="updateColor('ink',this.value)" id="cp-ink">
</div>
<div class="color-info">
<div class="color-label">Couleur principale (Ink)</div>
<div class="color-hex" id="hex-ink">#0a0a0a</div>
<div class="color-preview-bar" style="background:#0a0a0a" id="bar-ink"></div>
</div>
</div>
<div class="color-row">
<div class="color-swatch" id="swatch-surface" style="background:#f8f8f8">
<input type="color" value="#f8f8f8" oninput="updateColor('surface',this.value)" id="cp-surface">
</div>
<div class="color-info">
<div class="color-label">Fond principal (Surface)</div>
<div class="color-hex" id="hex-surface">#f8f8f8</div>
<div class="color-preview-bar" style="background:#f8f8f8;border:1px solid #e8e8e8" id="bar-surface"></div>
</div>
</div>
<div class="color-row">
<div class="color-swatch" id="swatch-green" style="background:#1a7f4f">
<input type="color" value="#1a7f4f" oninput="updateColor('green',this.value)" id="cp-green">
</div>
<div class="color-info">
<div class="color-label">Couleur succès (Vert)</div>
<div class="color-hex" id="hex-green">#1a7f4f</div>
<div class="color-preview-bar" style="background:#1a7f4f" id="bar-green"></div>
</div>
</div>
<div class="color-row">
<div class="color-swatch" id="swatch-amber" style="background:#b45309">
<input type="color" value="#b45309" oninput="updateColor('amber',this.value)" id="cp-amber">
</div>
<div class="color-info">
<div class="color-label">Couleur avertissement (Ambre)</div>
<div class="color-hex" id="hex-amber">#b45309</div>
<div class="color-preview-bar" style="background:#b45309" id="bar-amber"></div>
</div>
</div>
<div class="color-row">
<div class="color-swatch" id="swatch-red" style="background:#c0392b">
<input type="color" value="#c0392b" oninput="updateColor('red',this.value)" id="cp-red">
</div>
<div class="color-info">
<div class="color-label">Couleur erreur (Rouge)</div>
<div class="color-hex" id="hex-red">#c0392b</div>
<div class="color-preview-bar" style="background:#c0392b" id="bar-red"></div>
</div>
</div>
<div class="color-row">
<div class="color-swatch" id="swatch-blue" style="background:#1d4ed8">
<input type="color" value="#1d4ed8" oninput="updateColor('blue',this.value)" id="cp-blue">
</div>
<div class="color-info">
<div class="color-label">Couleur info (Bleu)</div>
<div class="color-hex" id="hex-blue">#1d4ed8</div>
<div class="color-preview-bar" style="background:#1d4ed8" id="bar-blue"></div>
</div>
</div>
<div class="color-row">
<div class="color-swatch" id="swatch-border" style="background:#e8e8e8">
<input type="color" value="#e8e8e8" oninput="updateColor('border',this.value)" id="cp-border">
</div>
<div class="color-info">
<div class="color-label">Bordures</div>
<div class="color-hex" id="hex-border">#e8e8e8</div>
<div class="color-preview-bar" style="background:#e8e8e8" id="bar-border"></div>
</div>
</div>
<div class="color-row">
<div class="color-swatch" id="swatch-muted" style="background:#6b6b6b">
<input type="color" value="#6b6b6b" oninput="updateColor('muted',this.value)" id="cp-muted">
</div>
<div class="color-info">
<div class="color-label">Texte secondaire</div>
<div class="color-hex" id="hex-muted">#6b6b6b</div>
<div class="color-preview-bar" style="background:#6b6b6b" id="bar-muted"></div>
</div>
</div>
</div>
<div style="padding:18px;background:var(--surface);border-radius:var(--radius-sm);border:1px solid var(--border)">
<div class="field-label" style="margin-bottom:12px">Aperçu live — Badges & Tags</div>
<div style="display:flex;flex-wrap:wrap;gap:8px;align-items:center">
<span class="tag tag-green">✓ Actif</span>
<span class="tag tag-amber">⚠ En attente</span>
<span class="tag tag-red">✗ Erreur</span>
<span class="tag tag-blue">ℹ Info</span>
<span class="tag tag-ink">● Principal</span>
<span class="tag tag-surface">Surface</span>
<button class="btn btn-solid btn-xs">Bouton</button>
<button class="btn btn-xs">Secondaire</button>
</div>
</div>
</div>
<!-- CONTENT PANEL -->
<div class="admin-panel" id="apanel-content">
<div class="admin-panel-title">Gestion du contenu</div>
<div class="admin-panel-sub">Modifiez les textes, descriptions et informations affichés sur la plateforme.</div>
<div class="content-tabs">
<div class="ctab active" onclick="switchCtab('hero')" id="ctab-hero">Dashboard</div>
<div class="ctab" onclick="switchCtab('formations')" id="ctab-formations">Formations</div>
<div class="ctab" onclick="switchCtab('sessions')" id="ctab-sessions">Sessions</div>
<div class="ctab" onclick="switchCtab('apprenant')" id="ctab-apprenant">Portail apprenant</div>
</div>
<!-- Hero content -->
<div class="content-panel active" id="cpanel-hero">
<div class="content-card">
<div class="content-card-header">
<span style="font-size:18px">🏠</span>
<div>
<div style="font-size:14px;font-weight:500">Tableau de bord — En-tête</div>
<div style="font-size:12px;color:var(--muted)">Texte de bienvenue affiché en haut du dashboard</div>
</div>
</div>
<div class="content-card-body">
<div class="field-row">
<div class="field-group">
<div class="field-label">Message de bienvenue</div>
<input class="field-input" type="text" value="Bonjour, Marie ✦" oninput="liveUpdate('.hero-title',this.value)">
</div>
<div class="field-group">
<div class="field-label">Sous-titre / Date</div>
<input class="field-input" type="text" value="Mercredi 25 mars 2026" oninput="liveUpdate('.hero-greeting',this.value)">
</div>
</div>
<div class="field-row-3">
<div class="field-group">
<div class="field-label">Stat 1 — Valeur</div>
<input class="field-input" type="text" value="342" oninput="liveUpdateNth('.hero-stat-val',0,this.value)">
</div>
<div class="field-group">
<div class="field-label">Stat 2 — Valeur</div>
<input class="field-input" type="text" value="24" oninput="liveUpdateNth('.hero-stat-val',1,this.value)">
</div>
<div class="field-group">
<div class="field-label">Stat 3 — Valeur</div>
<input class="field-input" type="text" value="47" oninput="liveUpdateNth('.hero-stat-val',2,this.value)">
</div>
</div>
<div class="field-row-3">
<div class="field-group">
<div class="field-label">Stat 1 — Libellé</div>
<input class="field-input" type="text" value="Apprenants actifs" oninput="liveUpdateNth('.hero-stat-lbl',0,this.value)">
</div>
<div class="field-group">
<div class="field-label">Stat 2 — Libellé</div>
<input class="field-input" type="text" value="Formations actives" oninput="liveUpdateNth('.hero-stat-lbl',1,this.value)">
</div>
<div class="field-group">
<div class="field-label">Stat 3 — Libellé</div>
<input class="field-input" type="text" value="Sessions ce mois" oninput="liveUpdateNth('.hero-stat-lbl',2,this.value)">
</div>
</div>
</div>
</div>
<div class="content-card">
<div class="content-card-header">
<span style="font-size:18px">⚙️</span>
<div>
<div style="font-size:14px;font-weight:500">Informations plateforme</div>
<div style="font-size:12px;color:var(--muted)">Données affichées dans la topbar et la sidebar</div>
</div>
</div>
<div class="content-card-body">
<div class="field-row">
<div class="field-group">
<div class="field-label">Nom de la plateforme</div>
<input class="field-input" type="text" value="FormaPro" oninput="liveUpdate('.brand-name',this.value)">
</div>
<div class="field-group">
<div class="field-label">Sous-titre sidebar</div>
<input class="field-input" type="text" value="Plateforme de formation" oninput="liveUpdate('.brand-sub',this.value)">
</div>
</div>
<div class="field-row">
<div class="field-group">
<div class="field-label">Nom administrateur</div>
<input class="field-input" type="text" value="Marie Lambert" oninput="liveUpdate('.user-name',this.value)">
</div>
<div class="field-group">
<div class="field-label">Rôle administrateur</div>
<input class="field-input" type="text" value="Administratrice" oninput="liveUpdate('.user-role',this.value)">
</div>
</div>
</div>
</div>
</div>
<!-- Formations content -->
<div class="content-panel" id="cpanel-formations">
<div class="content-card">
<div class="content-card-header"><span style="font-size:18px">📚</span><div><div style="font-size:14px;font-weight:500">Fiche formation — Excel Avancé</div><div style="font-size:12px;color:var(--muted)">Titre, description et informations clés</div></div></div>
<div class="content-card-body">
<div class="field-group">
<div class="field-label">Titre de la formation</div>
<input class="field-input" type="text" value="Excel Avancé & Power BI" oninput="liveUpdate('.fiche-title',this.value)">
</div>
<div class="field-row">
<div class="field-group">
<div class="field-label">Domaine</div>
<input class="field-input" type="text" value="Bureautique & Data" oninput="liveUpdate('.fiche-domain',this.value)">
</div>
<div class="field-group">
<div class="field-label">Prix TTC</div>
<input class="field-input" type="text" value="890 €" oninput="liveUpdateNth('.fiche-stat-val',0,this.value)">
</div>
</div>
<div class="field-row">
<div class="field-group">
<div class="field-label">Durée</div>
<input class="field-input" type="text" value="21 heures">
</div>
<div class="field-group">
<div class="field-label">Note / Avis</div>
<input class="field-input" type="text" value="4.8 ★" oninput="liveUpdateNth('.fiche-stat-val',1,this.value)">
</div>
</div>
<div class="field-group">
<div class="field-label">Description formateur</div>
<textarea class="field-input">Certifié Microsoft et expert Power BI. Intervient dans les plus grands groupes français.</textarea>
</div>
</div>
</div>
</div>
<!-- Sessions content -->
<div class="content-panel" id="cpanel-sessions">
<div class="content-card">
<div class="content-card-header"><span style="font-size:18px">📅</span><div><div style="font-size:14px;font-weight:500">Page Sessions</div></div></div>
<div class="content-card-body">
<div class="field-row">
<div class="field-group">
<div class="field-label">Titre de la page</div>
<input class="field-input" type="text" value="Gestionnaire de sessions">
</div>
<div class="field-group">
<div class="field-label">Sous-titre</div>
<input class="field-input" type="text" value="47 sessions planifiées ce mois">
</div>
</div>
</div>
</div>
</div>
<!-- Apprenant content -->
<div class="content-panel" id="cpanel-apprenant">
<div class="content-card">
<div class="content-card-header"><span style="font-size:18px">👤</span><div><div style="font-size:14px;font-weight:500">Portail apprenant</div></div></div>
<div class="content-card-body">
<div class="field-row">
<div class="field-group">
<div class="field-label">Message de bienvenue</div>
<input class="field-input" type="text" value="Bonjour 👋" oninput="liveUpdate('.apprenant-hello',this.value)">
</div>
<div class="field-group">
<div class="field-label">Alerte session</div>
<input class="field-input" type="text" value="Session aujourd'hui — Excel Avancé, 14h00, Salle Émeraude.">
</div>
</div>
</div>
</div>
</div>
</div>
<!-- TABLES PANEL -->
<div class="admin-panel" id="apanel-tables">
<div class="admin-panel-title">Éditeur de tableaux</div>
<div class="admin-panel-sub">Modifiez les données des tableaux directement. Cliquez sur une cellule pour l'éditer.</div>
<div style="margin-bottom:20px">
<div class="field-label">Choisir un tableau</div>
<div style="display:flex;gap:8px;flex-wrap:wrap;margin-top:8px">
<button class="btn btn-xs btn-solid" onclick="showTable('sessions')">📅 Sessions</button>
<button class="btn btn-xs" onclick="showTable('apprenants')">👥 Apprenants</button>
<button class="btn btn-xs" onclick="showTable('formations')">📚 Formations</button>
<button class="btn btn-xs" onclick="showTable('documents')">📄 Documents</button>
</div>
</div>
<div class="table-editor-wrap" id="table-editor-area">
<!-- Sessions table (default) -->
<div class="table-toolbar">
<span style="font-size:13px;font-weight:500;margin-right:auto">Sessions planifiées</span>
<button class="table-tool-btn" onclick="addTableRow()">+ Ajouter une ligne</button>
<button class="table-tool-btn" onclick="addTableCol()">+ Colonne</button>
<button class="table-tool-btn danger" onclick="deleteSelectedRows()">🗑 Supprimer sélection</button>
<button class="table-tool-btn" onclick="exportTableCSV()">⬇ CSV</button>
</div>
<div style="overflow-x:auto">
<table class="editable-table" id="main-editable-table">
<thead id="table-head">
<tr id="thead-row">
<th style="width:36px"><input type="text" value="" placeholder="" style="pointer-events:none;opacity:0"></th>
<th><input type="text" value="Formation"></th>
<th><input type="text" value="Date & Heure"></th>
<th><input type="text" value="Lieu"></th>
<th><input type="text" value="Formateur"></th>
<th><input type="text" value="Inscrits"></th>
<th><input type="text" value="Statut"></th>
<th style="width:40px"></th>
</tr>
</thead>
<tbody id="table-body">
<tr>
<td><input type="checkbox" style="accent-color:var(--ink)"></td>
<td><input type="text" value="Excel Avancé — Gr. A"></td>
<td><input type="text" value="25 mars · 14h00"></td>
<td><input type="text" value="Salle Émeraude"></td>
<td><input type="text" value="P. Leblanc"></td>
<td><input type="text" value="12/12"></td>
<td><select><option>En cours</option><option>Planifié</option><option>Terminé</option><option>Annulé</option></select></td>
<td class="row-actions"><button class="row-del-btn" onclick="delRow(this)">×</button></td>
</tr>
<tr>
<td><input type="checkbox" style="accent-color:var(--ink)"></td>
<td><input type="text" value="Management — M3"></td>
<td><input type="text" value="26 mars · 09h00"></td>
<td><input type="text" value="Distanciel"></td>
<td><input type="text" value="A. Moreau"></td>
<td><input type="text" value="8/15"></td>
<td><select><option>Planifié</option><option>En cours</option><option>Terminé</option><option>Annulé</option></select></td>
<td class="row-actions"><button class="row-del-btn" onclick="delRow(this)">×</button></td>
</tr>
<tr>
<td><input type="checkbox" style="accent-color:var(--ink)"></td>
<td><input type="text" value="Prise de parole"></td>
<td><input type="text" value="27 mars · 10h00"></td>
<td><input type="text" value="Hybride"></td>
<td><input type="text" value="L. Simon"></td>
<td><input type="text" value="6/10"></td>
<td><select><option>Planifié</option><option>En cours</option><option>Terminé</option><option>Annulé</option></select></td>
<td class="row-actions"><button class="row-del-btn" onclick="delRow(this)">×</button></td>
</tr>
<tr>
<td><input type="checkbox" style="accent-color:var(--ink)"></td>
<td><input type="text" value="Droit du travail"></td>
<td><input type="text" value="28 mars · 14h00"></td>
<td><input type="text" value="Présentiel"></td>
<td><input type="text" value="C. Faure"></td>
<td><input type="text" value="10/12"></td>
<td><select><option>Planifié</option><option>En cours</option><option>Terminé</option><option>Annulé</option></select></td>
<td class="row-actions"><button class="row-del-btn" onclick="delRow(this)">×</button></td>
</tr>
</tbody>
</table>
</div>
<button class="add-row-btn" onclick="addTableRow()">
<span style="font-size:16px;line-height:1">+</span> Ajouter une ligne
</button>
</div>
</div>
<!-- KPIs PANEL -->
<div class="admin-panel" id="apanel-kpis">
<div class="admin-panel-title">Indicateurs clés (KPIs)</div>
<div class="admin-panel-sub">Modifiez les valeurs et libellés des KPIs affichés dans les vues Analytics et Dashboard.</div>
<div class="kpi-editor-grid">
<div class="kpi-editor-card">
<div class="field-label">KPI 1 — Apprenants actifs</div>
<div class="field-row">
<div class="field-group"><div class="field-label">Valeur</div><input class="field-input" type="text" value="342" oninput="liveUpdateNth('.kpi-value',0,this.value)"></div>
<div class="field-group"><div class="field-label">Libellé</div><input class="field-input" type="text" value="Apprenants actifs" oninput="liveUpdateNth('.kpi-label',0,this.value)"></div>
</div>
<div class="field-group"><div class="field-label">Indicateur de tendance</div>
<input class="field-input" type="text" value="↑ +28 ce mois" oninput="liveUpdateNth('.kpi-change',0,this.value)">
</div>
</div>
<div class="kpi-editor-card">
<div class="field-label">KPI 2 — Taux de complétion</div>
<div class="field-row">
<div class="field-group"><div class="field-label">Valeur</div><input class="field-input" type="text" value="78%" oninput="liveUpdateNth('.kpi-value',1,this.value)"></div>
<div class="field-group"><div class="field-label">Libellé</div><input class="field-input" type="text" value="Taux complétion" oninput="liveUpdateNth('.kpi-label',1,this.value)"></div>
</div>
<div class="field-group"><div class="field-label">Indicateur de tendance</div>
<input class="field-input" type="text" value="↑ +5% vs mois" oninput="liveUpdateNth('.kpi-change',1,this.value)">
</div>
</div>
<div class="kpi-editor-card">
<div class="field-label">KPI 3 — Heures délivrées</div>
<div class="field-row">
<div class="field-group"><div class="field-label">Valeur</div><input class="field-input" type="text" value="1 247" oninput="liveUpdateNth('.kpi-value',2,this.value)"></div>
<div class="field-group"><div class="field-label">Libellé</div><input class="field-input" type="text" value="Heures délivrées" oninput="liveUpdateNth('.kpi-label',2,this.value)"></div>
</div>
<div class="field-group"><div class="field-label">Indicateur de tendance</div>
<input class="field-input" type="text" value="↑ +18% vs N-1" oninput="liveUpdateNth('.kpi-change',2,this.value)">
</div>
</div>
<div class="kpi-editor-card">
<div class="field-label">KPI 4 — Satisfaction</div>
<div class="field-row">
<div class="field-group"><div class="field-label">Valeur</div><input class="field-input" type="text" value="4.7" oninput="liveUpdateNth('.kpi-value',3,this.value)"></div>
<div class="field-group"><div class="field-label">Libellé</div><input class="field-input" type="text" value="Satisfaction" oninput="liveUpdateNth('.kpi-label',3,this.value)"></div>
</div>
<div class="field-group"><div class="field-label">Indicateur de tendance</div>
<input class="field-input" type="text" value="/ 5 · 312 avis" oninput="liveUpdateNth('.kpi-change',3,this.value)">
</div>
</div>
</div>
<div class="card" style="margin-top:8px;padding:20px">
<div class="card-title" style="margin-bottom:14px">Aperçu instantané</div>
<div style="display:grid;grid-template-columns:repeat(4,1fr);gap:12px">
<div class="kpi-card" style="pointer-events:none"><div class="kpi-label">Apprenants actifs</div><div class="kpi-value">342</div><div class="kpi-change up">↑ +28 ce mois</div></div>
<div class="kpi-card" style="pointer-events:none"><div class="kpi-label">Taux complétion</div><div class="kpi-value">78%</div><div class="kpi-change up">↑ +5% vs mois</div></div>
<div class="kpi-card" style="pointer-events:none"><div class="kpi-label">Heures délivrées</div><div class="kpi-value">1 247</div><div class="kpi-change up">↑ +18% vs N-1</div></div>
<div class="kpi-card" style="pointer-events:none"><div class="kpi-label">Satisfaction</div><div class="kpi-value">4.7</div><div class="kpi-change">/ 5 · 312 avis</div></div>
</div>
</div>
</div>
<!-- TYPOGRAPHY PANEL -->
<div class="admin-panel" id="apanel-typography">
<div class="admin-panel-title">Typographie</div>
<div class="admin-panel-sub">Personnalisez les polices et la taille du texte.</div>
<div class="grid-2" style="margin-bottom:24px">
<div class="card">
<div class="card-title">Police des titres</div>
<div class="field-group" style="margin-top:14px">
<div class="field-label">Famille</div>
<select class="field-input" onchange="updateFont('display',this.value)" id="font-display">
<option value="'Playfair Display', serif" selected>Playfair Display (actuel)</option>
<option value="'Georgia', serif">Georgia</option>
<option value="'Merriweather', serif">Merriweather</option>
<option value="'Instrument Sans', sans-serif">Instrument Sans</option>
<option value="system-ui, sans-serif">System UI</option>
</select>
</div>
<div class="field-group">
<div class="field-label">Taille des titres de page</div>
<input class="field-input" type="range" min="24" max="48" value="32" oninput="updateFontSize('page-title',this.value+'px');document.getElementById('pt-size').textContent=this.value+'px'">
<span id="pt-size" style="font-size:12px;color:var(--muted)">32px</span>
</div>
</div>
<div class="card">
<div class="card-title">Police du corps</div>
<div class="field-group" style="margin-top:14px">
<div class="field-label">Famille</div>
<select class="field-input" onchange="updateFont('body',this.value)" id="font-body">
<option value="'Instrument Sans', sans-serif" selected>Instrument Sans (actuel)</option>
<option value="system-ui, sans-serif">System UI</option>
<option value="'Helvetica Neue', sans-serif">Helvetica Neue</option>
<option value="'Georgia', serif">Georgia</option>
</select>
</div>
<div class="field-group">
<div class="field-label">Taille de base</div>
<input class="field-input" type="range" min="11" max="17" value="14" oninput="document.body.style.fontSize=this.value+'px';document.getElementById('bs-size').textContent=this.value+'px'">
<span id="bs-size" style="font-size:12px;color:var(--muted)">14px</span>
</div>
</div>
</div>
<div class="font-preview">
<div class="font-preview-title">Titre de section — Aperçu typographique</div>
<div class="font-preview-body">Ceci est un exemple de texte corporatif affiché dans la police du corps. La lisibilité est primordiale pour une plateforme de formation professionnelle. Les apprenants passent de longues heures à lire du contenu — chaque détail typographique compte.</div>
</div>
<div class="card" style="margin-top:16px;padding:20px">
<div class="card-title" style="margin-bottom:14px">Rayon des coins</div>
<div class="field-row">
<div class="field-group">
<div class="field-label">Cards (--radius)</div>
<input class="field-input" type="range" min="0" max="28" value="16" oninput="document.documentElement.style.setProperty('--radius',this.value+'px');document.getElementById('rad-val').textContent=this.value+'px'">
<span id="rad-val" style="font-size:12px;color:var(--muted)">16px</span>
</div>
<div class="field-group">
<div class="field-label">Éléments (--radius-sm)</div>
<input class="field-input" type="range" min="0" max="20" value="10" oninput="document.documentElement.style.setProperty('--radius-sm',this.value+'px');document.getElementById('radsm-val').textContent=this.value+'px'">
<span id="radsm-val" style="font-size:12px;color:var(--muted)">10px</span>
</div>
</div>
</div>
</div>
<!-- ALERTS PANEL -->
<div class="admin-panel" id="apanel-alerts">
<div class="admin-panel-title">Alertes & Notifications</div>
<div class="admin-panel-sub">Gérez les alertes affichées dans le tableau de bord.</div>
<div id="alerts-editor-list">
<div class="alert-editor-item">
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:12px">
<div style="display:flex;gap:8px;align-items:center">
<select class="field-input" style="width:auto;padding:5px 10px">
<option>⚠️ Avertissement</option><option>✅ Succès</option><option>🔴 Erreur</option><option>ℹ️ Info</option>
</select>
</div>
<button class="row-del-btn" onclick="this.closest('.alert-editor-item').remove()" style="width:28px;height:28px">×</button>
</div>
<input class="field-input" type="text" value="Lucas Roche inactif depuis 14 jours — Excel Avancé">
<div style="margin-top:10px;display:flex;align-items:center;gap:10px">
<span style="font-size:12px;color:var(--muted)">Visible</span>
<div class="toggle on" onclick="this.classList.toggle('on')"></div>
</div>
</div>
<div class="alert-editor-item">
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:12px">
<div style="display:flex;gap:8px;align-items:center">
<select class="field-input" style="width:auto;padding:5px 10px">
<option>✅ Succès</option><option>⚠️ Avertissement</option><option>🔴 Erreur</option><option>ℹ️ Info</option>
</select>
</div>
<button class="row-del-btn" onclick="this.closest('.alert-editor-item').remove()" style="width:28px;height:28px">×</button>
</div>
<input class="field-input" type="text" value="3 certifications délivrées cette semaine">
<div style="margin-top:10px;display:flex;align-items:center;gap:10px">
<span style="font-size:12px;color:var(--muted)">Visible</span>
<div class="toggle on" onclick="this.classList.toggle('on')"></div>
</div>
</div>
<div class="alert-editor-item">
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:12px">
<div style="display:flex;gap:8px;align-items:center">
<select class="field-input" style="width:auto;padding:5px 10px">
<option>🔴 Erreur</option><option>⚠️ Avertissement</option><option>✅ Succès</option><option>ℹ️ Info</option>
</select>
</div>
<button class="row-del-btn" onclick="this.closest('.alert-editor-item').remove()" style="width:28px;height:28px">×</button>
</div>
<input class="field-input" type="text" value="Session Management du 2 avril — places insuffisantes">
<div style="margin-top:10px;display:flex;align-items:center;gap:10px">
<span style="font-size:12px;color:var(--muted)">Visible</span>
<div class="toggle on" onclick="this.classList.toggle('on')"></div>
</div>
</div>
</div>
<button class="btn btn-sm" style="margin-top:14px" onclick="addAlert()">+ Nouvelle alerte</button>
</div>
<!-- NAVIGATION PANEL -->
<div class="admin-panel" id="apanel-navigation">
<div class="admin-panel-title">Navigation & Menus</div>
<div class="admin-panel-sub">Activez ou désactivez les éléments de navigation. Modifiez les libellés.</div>
<div class="nav-editor-list">
<div style="font-size:11px;font-weight:600;color:var(--muted-2);text-transform:uppercase;letter-spacing:.08em;padding:4px 0;margin-top:4px">Administration</div>
<div class="nav-editor-item">
<span class="drag-handle">⠿</span>
<input class="field-input" type="text" value="Tableau de bord" style="max-width:200px">
<span style="font-size:12px;color:var(--muted);margin-left:auto;margin-right:12px">Accueil</span>
<div class="toggle on" onclick="this.classList.toggle('on')"></div>
</div>
<div class="nav-editor-item">
<span class="drag-handle">⠿</span>
<input class="field-input" type="text" value="Catalogue" style="max-width:200px">
<span style="font-size:12px;color:var(--muted);margin-left:auto;margin-right:12px">/catalogue</span>
<div class="toggle on" onclick="this.classList.toggle('on')"></div>
</div>
<div class="nav-editor-item">
<span class="drag-handle">⠿</span>
<input class="field-input" type="text" value="Fiche formation" style="max-width:200px">
<span style="font-size:12px;color:var(--muted);margin-left:auto;margin-right:12px">/fiche</span>
<div class="toggle on" onclick="this.classList.toggle('on')"></div>
</div>
<div class="nav-editor-item">
<span class="drag-handle">⠿</span>
<input class="field-input" type="text" value="Sessions" style="max-width:200px">
<span style="font-size:12px;color:var(--muted);margin-left:auto;margin-right:12px">/sessions</span>
<div class="toggle on" onclick="this.classList.toggle('on')"></div>
</div>
<div class="nav-editor-item">
<span class="drag-handle">⠿</span>
<input class="field-input" type="text" value="Analytics" style="max-width:200px">
<span style="font-size:12px;color:var(--muted);margin-left:auto;margin-right:12px">/analytics</span>
<div class="toggle on" onclick="this.classList.toggle('on')"></div>
</div>
<div style="font-size:11px;font-weight:600;color:var(--muted-2);text-transform:uppercase;letter-spacing:.08em;padding:4px 0;margin-top:12px">Espace</div>
<div class="nav-editor-item">
<span class="drag-handle">⠿</span>
<input class="field-input" type="text" value="Portail apprenant" style="max-width:200px">
<span style="font-size:12px;color:var(--muted);margin-left:auto;margin-right:12px">/apprenant</span>
<div class="toggle on" onclick="this.classList.toggle('on')"></div>
</div>
<div class="nav-editor-item">
<span class="drag-handle">⠿</span>
<input class="field-input" type="text" value="Lecteur de cours" style="max-width:200px">
<span style="font-size:12px;color:var(--muted);margin-left:auto;margin-right:12px">/lecteur</span>
<div class="toggle on" onclick="this.classList.toggle('on')"></div>
</div>
<div class="nav-editor-item">
<span class="drag-handle">⠿</span>
<input class="field-input" type="text" value="Certifications" style="max-width:200px">
<span style="font-size:12px;color:var(--muted);margin-left:auto;margin-right:12px">/certifications</span>
<div class="toggle on" onclick="this.classList.toggle('on')"></div>
</div>
</div>
</div>
<!-- BRANDING PANEL -->
<div class="admin-panel" id="apanel-branding">
<div class="admin-panel-title">Identité de marque</div>
<div class="admin-panel-sub">Logo, favicon, et informations institutionnelles.</div>
<div class="grid-2" style="margin-bottom:20px">
<div class="card" style="padding:24px">
<div class="card-title" style="margin-bottom:14px">Logo</div>
<div style="width:100%;height:100px;background:var(--surface);border:2px dashed var(--border-2);border-radius:var(--radius-sm);display:flex;flex-direction:column;align-items:center;justify-content:center;cursor:pointer;gap:6px;transition:all var(--transition)" onmouseover="this.style.borderColor='var(--ink)'" onmouseout="this.style.borderColor='var(--border-2)'">
<span style="font-size:24px">🖼️</span>
<span style="font-size:12px;color:var(--muted)">Glisser-déposer ou cliquer</span>
<span style="font-size:11px;color:var(--muted-2)">PNG, SVG · max 2 Mo</span>
</div>
</div>
<div class="card" style="padding:24px">
<div class="card-title" style="margin-bottom:14px">Aperçu sidebar</div>
<div style="background:var(--surface);border:1px solid var(--border);border-radius:var(--radius-sm);padding:16px 18px">
<div style="font-family:'Playfair Display',serif;font-size:18px;font-weight:500" id="brand-preview-name">FormaPro</div>
<div style="font-size:11px;color:var(--muted);margin-top:2px" id="brand-preview-sub">Plateforme de formation</div>
</div>
</div>
</div>
<div class="content-card">
<div class="content-card-header"><span style="font-size:18px">🏢</span><div><div style="font-size:14px;font-weight:500">Informations de l'organisme</div></div></div>
<div class="content-card-body">
<div class="field-row">
<div class="field-group">
<div class="field-label">Nom de l'organisme</div>
<input class="field-input" type="text" value="FormaPro SAS" oninput="document.getElementById('brand-preview-name').textContent=this.value;liveUpdate('.brand-name',this.value)">
</div>
<div class="field-group">
<div class="field-label">Numéro de déclaration d'activité</div>
<input class="field-input" type="text" value="11 75 12345 75">
</div>
</div>
<div class="field-row">
<div class="field-group">
<div class="field-label">Email de contact</div>
<input class="field-input" type="email" value="contact@formapro.fr">
</div>
<div class="field-group">
<div class="field-label">Site web</div>
<input class="field-input" type="url" value="https://formapro.fr">
</div>
</div>
<div class="field-group">
<div class="field-label">Adresse</div>
<input class="field-input" type="text" value="12 rue de la Formation, 75008 Paris">
</div>
<div class="field-row">
<div class="field-group">
<div class="field-label">Certification Qualiopi</div>
<div class="toggle-wrap" style="margin-top:8px">
<div class="toggle on" onclick="this.classList.toggle('on')"></div>
<span style="font-size:13px">Activer le badge Qualiopi</span>
</div>
</div>
<div class="field-group">
<div class="field-label">CPF activé</div>
<div class="toggle-wrap" style="margin-top:8px">
<div class="toggle on" onclick="this.classList.toggle('on')"></div>
<span style="font-size:13px">Afficher les badges CPF</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div><!-- end right panel -->
</div><!-- end admin-layout -->
<!-- INVITATIONS PANEL -->
<div class="admin-panel" id="apanel-invitations" style="display:none;padding:0">
<!-- Sub-tabs bar -->
<div style="display:flex;gap:0;border-bottom:1px solid var(--border);background:var(--white);padding:0 32px">
<div class="inv-subtab active" id="isubtab-send" onclick="switchInvTab('send')">✉ Envoyer</div>
<div class="inv-subtab" id="isubtab-track" onclick="switchInvTab('track')">📊 Suivi</div>
<div class="inv-subtab" id="isubtab-template" onclick="switchInvTab('template')">🎨 Template</div>
<div class="inv-subtab" id="isubtab-import" onclick="switchInvTab('import')">📂 Import CSV</div>
</div>
<!-- ── SEND TAB ── -->
<div class="inv-panel active" id="invpanel-send" style="padding:32px 36px">
<div style="display:grid;grid-template-columns:1fr 380px;gap:28px;align-items:start">
<div>
<div style="font-family:'Playfair Display',serif;font-size:20px;font-weight:400;margin-bottom:6px">Nouvelle invitation</div>
<div style="font-size:13px;color:var(--muted);margin-bottom:24px">Invitez un ou plusieurs apprenants à rejoindre FormaPro. L'email sera envoyé via votre compte Gmail connecté.</div>
<!-- Gmail connected badge -->
<div style="display:flex;align-items:center;gap:10px;padding:11px 16px;background:var(--green-bg);border:1px solid #bbf7d0;border-radius:var(--radius-sm);margin-bottom:22px">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none"><path d="M22 4H2v16h20V4z" fill="#EA4335" opacity=".2"/><path d="M2 4l10 9 10-9" stroke="#EA4335" stroke-width="1.5" fill="none"/></svg>
<span style="font-size:13px;font-weight:500;color:var(--green)">Gmail connecté</span>
<span style="font-size:12px;color:var(--muted);margin-left:2px">— <a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="5a373b28333f74363b37383f282e1a3c3528373b2a2835743c28">[email protected]</a></span>
<span class="live-badge" style="margin-left:auto"><span class="live-dot"></span>Prêt à envoyer</span>
</div>
<!-- Recipient add area -->
<div class="field-group">
<div class="field-label">Destinataires</div>
<div id="inv-tags-wrap" style="display:flex;flex-wrap:wrap;gap:6px;padding:10px 12px;border:1px solid var(--border);border-radius:8px;background:var(--white);min-height:48px;cursor:text;transition:border-color .2s" onclick="document.getElementById('inv-email-input').focus()">
<!-- tags injected here -->
<input id="inv-email-input" type="email" placeholder="email@exemple.fr — Entrée pour ajouter" style="border:none;outline:none;font-size:13.5px;font-family:inherit;color:var(--ink);flex:1;min-width:200px;background:transparent" onkeydown="handleInvEmail(event)">
</div>
<div style="font-size:11.5px;color:var(--muted);margin-top:6px">Appuyez sur <kbd style="background:var(--surface-2);border:1px solid var(--border);border-radius:4px;padding:1px 6px;font-size:10px">Entrée</kbd> ou <kbd style="background:var(--surface-2);border:1px solid var(--border);border-radius:4px;padding:1px 6px;font-size:10px">,</kbd> pour ajouter chaque adresse</div>
</div>
<!-- Formation assignment -->
<div class="field-row" style="margin-top:4px">
<div class="field-group">
<div class="field-label">Formation assignée (optionnel)</div>
<select class="field-input" id="inv-formation">
<option value="">— Aucune (inscription libre)</option>
<option value="excel">Excel Avancé & Power BI</option>
<option value="ia">IA & Productivité au Travail</option>
<option value="management">Management — Niveau 1</option>
<option value="cyber">Cybersécurité Essentielle</option>
<option value="parole">Prise de parole en public</option>
<option value="droit">Droit du Travail</option>
</select>
</div>
<div class="field-group">
<div class="field-label">Rôle à attribuer</div>
<select class="field-input" id="inv-role">
<option value="apprenant">Apprenant</option>
<option value="formateur">Formateur</option>
<option value="admin">Administrateur</option>
</select>
</div>
</div>
<!-- Message perso -->
<div class="field-group">
<div class="field-label">Message personnalisé (optionnel)</div>
<textarea class="field-input" id="inv-msg" rows="3" placeholder="Ex : Bonjour, nous avons le plaisir de vous inviter à rejoindre notre plateforme FormaPro pour votre formation Excel…"></textarea>
</div>
<!-- Expiry -->
<div class="field-row">
<div class="field-group">
<div class="field-label">Expiration du lien</div>
<select class="field-input" id="inv-expiry">
<option value="7">7 jours</option>
<option value="14" selected>14 jours</option>
<option value="30">30 jours</option>
<option value="0">Jamais</option>
</select>
</div>
<div class="field-group">
<div class="field-label">Envoyer une relance auto</div>
<div class="toggle-wrap" style="margin-top:10px">
<div class="toggle on" id="inv-relance-toggle" onclick="this.classList.toggle('on')"></div>
<span style="font-size:13px">Relance à J+7 si non accepté</span>
</div>
</div>
</div>
<div style="display:flex;gap:10px;margin-top:8px">
<button class="btn btn-sm" onclick="previewInvEmail()">👁 Aperçu email</button>
<button class="btn btn-sm btn-solid" onclick="sendInvitations()" id="inv-send-btn" style="min-width:160px">
<svg width="14" height="14" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M2 4l16 6-16 6V4z"/></svg>
Envoyer via Gmail
</button>
</div>
</div>
<!-- Right: preview card -->
<div>
<div style="font-size:11px;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:.07em;margin-bottom:12px">Aperçu de l'invitation</div>
<div id="inv-preview-card" style="border:1px solid var(--border);border-radius:var(--radius);overflow:hidden;font-size:13px;background:var(--white)">
<!-- Email preview rendered here -->
<div style="background:var(--ink);padding:24px 28px;text-align:center">
<div style="font-family:'Playfair Display',serif;font-size:22px;color:white;font-weight:400" id="prev-brand">FormaPro</div>
<div style="font-size:11px;color:rgba(255,255,255,0.5);margin-top:3px">Plateforme de formation</div>
</div>
<div style="padding:24px 28px">
<div style="font-size:15px;font-weight:500;margin-bottom:10px" id="prev-subject">Vous êtes invité à rejoindre FormaPro</div>
<div style="font-size:13px;color:var(--muted);line-height:1.7;margin-bottom:16px" id="prev-body">Bonjour,<br><br>Marie Lambert vous invite à créer votre compte sur <strong>FormaPro</strong>, notre plateforme de formation professionnelle.<br><br><span id="prev-formation-line" style="display:none">Vous êtes inscrit(e) à la formation : <strong id="prev-formation-name"></strong><br><br></span><span id="prev-msg-line" style="display:none;font-style:italic;color:var(--ink)" id="prev-custom"></span></div>
<div style="text-align:center;margin:20px 0">
<div style="display:inline-block;background:var(--ink);color:white;padding:11px 28px;border-radius:99px;font-size:13px;font-weight:500">Créer mon compte →</div>
</div>
<div style="font-size:11.5px;color:var(--muted-2);border-top:1px solid var(--border);padding-top:14px;margin-top:8px;text-align:center">
Ce lien expire dans <span id="prev-expiry">14 jours</span>. Si vous n'êtes pas concerné(e), ignorez cet email.
</div>
</div>
</div>
<!-- Quick stats -->
<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-top:16px">
<div style="background:var(--surface);border:1px solid var(--border);border-radius:var(--radius-sm);padding:14px 16px">
<div style="font-size:22px;font-family:'Playfair Display',serif;font-weight:400" id="inv-count-display">0</div>
<div style="font-size:11px;color:var(--muted)">Destinataires</div>
</div>
<div style="background:var(--surface);border:1px solid var(--border);border-radius:var(--radius-sm);padding:14px 16px">
<div style="font-size:22px;font-family:'Playfair Display',serif;font-weight:400">14j</div>
<div style="font-size:11px;color:var(--muted)">Validité du lien</div>
</div>
</div>
</div>
</div>
</div>
<!-- ── TRACK TAB ── -->
<div class="inv-panel" id="invpanel-track" style="padding:32px 36px;display:none">
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:24px">
<div>
<div style="font-family:'Playfair Display',serif;font-size:20px;font-weight:400;margin-bottom:4px">Suivi des invitations</div>
<div style="font-size:13px;color:var(--muted)">Toutes les invitations envoyées et leur statut en temps réel</div>
</div>
<div style="display:flex;gap:8px">
<button class="btn btn-sm" onclick="filterInv('all')" id="filt-all" style="background:var(--ink);color:white;border-color:var(--ink)">Toutes (12)</button>
<button class="btn btn-sm" onclick="filterInv('pending')" id="filt-pending">En attente (5)</button>
<button class="btn btn-sm" onclick="filterInv('accepted')" id="filt-accepted">Acceptées (6)</button>
<button class="btn btn-sm" onclick="filterInv('expired')" id="filt-expired">Expirées (1)</button>
</div>
</div>
<!-- KPI mini row -->
<div style="display:grid;grid-template-columns:repeat(4,1fr);gap:12px;margin-bottom:24px">
<div style="background:var(--white);border:1px solid var(--border);border-radius:var(--radius-sm);padding:16px 18px">
<div style="font-family:'Playfair Display',serif;font-size:26px;font-weight:400;margin-bottom:2px">12</div>
<div style="font-size:11px;color:var(--muted)">Invitations envoyées</div>
</div>
<div style="background:var(--green-bg);border:1px solid #bbf7d0;border-radius:var(--radius-sm);padding:16px 18px">
<div style="font-family:'Playfair Display',serif;font-size:26px;font-weight:400;margin-bottom:2px;color:var(--green)">6</div>
<div style="font-size:11px;color:var(--green)">Acceptées (50%)</div>
</div>
<div style="background:var(--amber-bg);border:1px solid #fde68a;border-radius:var(--radius-sm);padding:16px 18px">
<div style="font-family:'Playfair Display',serif;font-size:26px;font-weight:400;margin-bottom:2px;color:var(--amber)">5</div>
<div style="font-size:11px;color:var(--amber)">En attente</div>
</div>
<div style="background:var(--red-bg);border:1px solid #fecaca;border-radius:var(--radius-sm);padding:16px 18px">
<div style="font-family:'Playfair Display',serif;font-size:26px;font-weight:400;margin-bottom:2px;color:var(--red)">1</div>
<div style="font-size:11px;color:var(--red)">Expirée</div>
</div>
</div>
<div class="table-editor-wrap">
<div class="table-toolbar">
<span style="font-size:13px;font-weight:500;margin-right:auto">Historique des invitations</span>
<button class="table-tool-btn" onclick="relancerTous()">🔔 Relancer tout «En attente»</button>
<button class="table-tool-btn" onclick="exportInvCSV()">⬇ Export CSV</button>
</div>
<table style="width:100%;border-collapse:collapse" id="inv-track-table">
<thead>
<tr>
<th style="padding:10px 14px;font-size:11px;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:.06em;border-bottom:1px solid var(--border);text-align:left">Destinataire</th>
<th style="padding:10px 14px;font-size:11px;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:.06em;border-bottom:1px solid var(--border);text-align:left">Formation assignée</th>
<th style="padding:10px 14px;font-size:11px;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:.06em;border-bottom:1px solid var(--border);text-align:left">Envoyé le</th>
<th style="padding:10px 14px;font-size:11px;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:.06em;border-bottom:1px solid var(--border);text-align:left">Expire le</th>
<th style="padding:10px 14px;font-size:11px;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:.06em;border-bottom:1px solid var(--border);text-align:left">Statut</th>
<th style="padding:10px 14px;font-size:11px;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:.06em;border-bottom:1px solid var(--border);text-align:left">Actions</th>
</tr>
</thead>
<tbody id="inv-track-body">
<!-- rows injected by JS -->
</tbody>
</table>
</div>
</div>
<!-- ── TEMPLATE TAB ── -->
<div class="inv-panel" id="invpanel-template" style="padding:32px 36px;display:none">
<div style="display:grid;grid-template-columns:1fr 380px;gap:28px;align-items:start">
<div>
<div style="font-family:'Playfair Display',serif;font-size:20px;font-weight:400;margin-bottom:6px">Template d'invitation</div>
<div style="font-size:13px;color:var(--muted);margin-bottom:24px">Personnalisez l'email envoyé aux nouveaux apprenants. Les variables <code style="background:var(--surface-2);padding:1px 6px;border-radius:4px;font-size:12px">{{prenom}}</code>, <code style="background:var(--surface-2);padding:1px 6px;border-radius:4px;font-size:12px">{{formation}}</code>, <code style="background:var(--surface-2);padding:1px 6px;border-radius:4px;font-size:12px">{{lien}}</code> sont remplacées automatiquement.</div>
<div class="field-group">
<div class="field-label">Objet de l'email</div>
<input class="field-input" type="text" id="tpl-subject" value="Vous êtes invité à rejoindre {{plateforme}}" oninput="updateEmailPreview()">
</div>
<div class="field-group">
<div class="field-label">Accroche (titre dans l'email)</div>
<input class="field-input" type="text" id="tpl-headline" value="Rejoignez votre espace de formation" oninput="updateEmailPreview()">
</div>
<div class="field-group">
<div class="field-label">Corps du message</div>
<textarea class="field-input" id="tpl-body" rows="6" oninput="updateEmailPreview()">Bonjour {{prenom}},
{{expediteur}} vous invite à créer votre compte sur {{plateforme}}, notre plateforme de formation professionnelle.
{{#formation}}Vous êtes inscrit(e) à la formation : {{formation}}{{/formation}}
Cliquez sur le bouton ci-dessous pour créer votre compte et accéder à vos formations.</textarea>
</div>
<div class="field-group">
<div class="field-label">Texte du bouton CTA</div>
<input class="field-input" type="text" id="tpl-cta" value="Créer mon compte →" oninput="updateEmailPreview()">
</div>
<div class="field-group">
<div class="field-label">Signature</div>
<input class="field-input" type="text" id="tpl-signature" value="L'équipe FormaPro" oninput="updateEmailPreview()">
</div>
<!-- Color scheme for email -->
<div class="field-group">
<div class="field-label">Couleur de l'en-tête email</div>
<div style="display:flex;gap:8px;margin-top:6px">
<div style="width:28px;height:28px;border-radius:6px;background:#0a0a0a;cursor:pointer;border:2px solid var(--ink)" onclick="setEmailHeaderColor('#0a0a0a')"></div>
<div style="width:28px;height:28px;border-radius:6px;background:#1d4ed8;cursor:pointer" onclick="setEmailHeaderColor('#1d4ed8')"></div>
<div style="width:28px;height:28px;border-radius:6px;background:#1a7f4f;cursor:pointer" onclick="setEmailHeaderColor('#1a7f4f')"></div>
<div style="width:28px;height:28px;border-radius:6px;background:#7c3aed;cursor:pointer" onclick="setEmailHeaderColor('#7c3aed')"></div>
<div style="width:28px;height:28px;border-radius:6px;background:#c2410c;cursor:pointer" onclick="setEmailHeaderColor('#c2410c')"></div>
<input type="color" value="#0a0a0a" style="width:28px;height:28px;border-radius:6px;border:1px solid var(--border);cursor:pointer;padding:0" oninput="setEmailHeaderColor(this.value)">
</div>
</div>
<div style="display:flex;gap:10px;margin-top:8px">
<button class="btn btn-sm" onclick="saveTemplate()">💾 Sauvegarder le template</button>
<button class="btn btn-sm" onclick="sendTestEmail()">📨 Envoyer un email test</button>
</div>
</div>
<!-- Live preview -->
<div>
<div style="font-size:11px;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:.07em;margin-bottom:12px">Aperçu en direct</div>
<div id="tpl-preview" style="border:1px solid var(--border);border-radius:var(--radius);overflow:hidden;font-size:13px;background:var(--white)">
<div id="tpl-prev-header" style="background:var(--ink);padding:24px 28px;text-align:center">
<div style="font-family:'Playfair Display',serif;font-size:20px;color:white;font-weight:400">FormaPro</div>
<div style="font-size:11px;color:rgba(255,255,255,0.5);margin-top:3px">Plateforme de formation</div>
</div>
<div style="padding:24px 28px">
<div style="font-size:15px;font-weight:500;margin-bottom:12px;line-height:1.4" id="tpl-prev-headline">Rejoignez votre espace de formation</div>
<div style="font-size:13px;color:var(--muted);line-height:1.7;margin-bottom:18px;white-space:pre-line" id="tpl-prev-body">Bonjour Jean,
Marie Lambert vous invite à créer votre compte sur FormaPro, notre plateforme de formation professionnelle.
Vous êtes inscrit(e) à la formation : Excel Avancé & Power BI
Cliquez sur le bouton ci-dessous pour créer votre compte et accéder à vos formations.</div>
<div style="text-align:center;margin:18px 0">
<div style="display:inline-block;background:var(--ink);color:white;padding:10px 24px;border-radius:99px;font-size:13px;font-weight:500" id="tpl-prev-cta">Créer mon compte →</div>
</div>
<div style="font-size:11px;color:var(--muted-2);border-top:1px solid var(--border);padding-top:12px;text-align:center" id="tpl-prev-sig">L'équipe FormaPro</div>
</div>
</div>
</div>
</div>
</div>
<!-- ── IMPORT CSV TAB ── -->
<div class="inv-panel" id="invpanel-import" style="padding:32px 36px;display:none">
<div style="font-family:'Playfair Display',serif;font-size:20px;font-weight:400;margin-bottom:6px">Import en masse — CSV</div>
<div style="font-size:13px;color:var(--muted);margin-bottom:28px">Importez une liste d'apprenants depuis un fichier CSV. Les invitations seront envoyées en lot via Gmail.</div>
<!-- Drop zone -->
<div id="csv-dropzone" style="border:2px dashed var(--border-2);border-radius:var(--radius);padding:48px;text-align:center;cursor:pointer;transition:all var(--transition);margin-bottom:24px;background:var(--white)" ondragover="event.preventDefault();this.style.borderColor='var(--ink)';this.style.background='var(--surface)'" ondragleave="this.style.borderColor='var(--border-2)';this.style.background='var(--white)'" ondrop="handleCSVDrop(event)">
<div style="font-size:36px;margin-bottom:12px">📂</div>
<div style="font-size:15px;font-weight:500;margin-bottom:6px">Glissez votre fichier CSV ici</div>
<div style="font-size:13px;color:var(--muted);margin-bottom:16px">ou</div>
<label style="cursor:pointer">
<input type="file" accept=".csv" style="display:none" onchange="handleCSVFile(this)">
<span class="btn btn-sm btn-solid">Choisir un fichier</span>
</label>
<div style="font-size:11.5px;color:var(--muted-2);margin-top:14px">Format attendu : <code style="background:var(--surface-2);padding:1px 6px;border-radius:4px">prenom, nom, email, formation (optionnel)</code></div>
</div>
<!-- Format example -->
<div style="background:var(--surface);border:1px solid var(--border);border-radius:var(--radius-sm);padding:16px 20px;margin-bottom:24px">
<div style="font-size:11px;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:.07em;margin-bottom:10px">Exemple de fichier CSV valide</div>
<pre style="font-size:12px;color:var(--muted);font-family:monospace;line-height:1.8">prenom,nom,email,formation
Jean,Dupont,<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="18727d7976367c6d6877766c587d75797174367e6a">[email protected]</a>,Excel Avancé & Power BI
Claire,Martin,<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="4c2f202d253e2962212d3e3825220c29212d2520622a3e">[email protected]</a>,Management Niveau 1
Paul,Bernard,<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="1f6f7e6a73317d7a6d717e6d7b5f7a727e767331796d">[email protected]</a>,
Sophie,Renard,<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="7f0c100f17161a510d1a111e0d1b3f1a121e161351190d">[email protected]</a>,IA & Productivité</pre>
<button class="btn btn-xs" style="margin-top:10px" onclick="downloadCSVTemplate()">⬇ Télécharger le modèle</button>
</div>
<!-- Preview table (hidden until file loaded) -->
<div id="csv-preview-section" style="display:none">
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:14px">
<div style="font-size:14px;font-weight:500"><span id="csv-count">0</span> apprenants détectés</div>
<div style="display:flex;gap:8px">
<button class="btn btn-sm" onclick="clearCSV()">✕ Annuler</button>
<button class="btn btn-sm btn-solid" id="csv-send-btn" onclick="sendCSVInvitations()">
<svg width="13" height="13" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M2 4l16 6-16 6V4z"/></svg>
Envoyer <span id="csv-send-count">0</span> invitations
</button>
</div>
</div>
<div class="table-editor-wrap">
<table style="width:100%;border-collapse:collapse" id="csv-preview-table">
<thead>
<tr>
<th style="padding:10px 14px;font-size:11px;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:.06em;border-bottom:1px solid var(--border);background:var(--surface);text-align:left"><input type="checkbox" id="csv-select-all" onchange="toggleCSVAll(this)" style="accent-color:var(--ink)"></th>
<th style="padding:10px 14px;font-size:11px;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:.06em;border-bottom:1px solid var(--border);background:var(--surface);text-align:left">Prénom</th>
<th style="padding:10px 14px;font-size:11px;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:.06em;border-bottom:1px solid var(--border);background:var(--surface);text-align:left">Nom</th>
<th style="padding:10px 14px;font-size:11px;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:.06em;border-bottom:1px solid var(--border);background:var(--surface);text-align:left">Email</th>
<th style="padding:10px 14px;font-size:11px;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:.06em;border-bottom:1px solid var(--border);background:var(--surface);text-align:left">Formation</th>
<th style="padding:10px 14px;font-size:11px;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:.06em;border-bottom:1px solid var(--border);background:var(--surface);text-align:left">Validation</th>
</tr>
</thead>
<tbody id="csv-preview-body"></tbody>
</table>
</div>
</div>
</div>
</div><!-- end invitations panel -->
<div class="admin-save-bar">
<div class="save-indicator">
<div class="save-dot" id="save-dot"></div>
<span id="save-status">Modifications non sauvegardées</span>
</div>
<div style="display:flex;gap:10px">
<button class="btn btn-sm" onclick="resetAdmin()">↺ Réinitialiser</button>
<button class="btn btn-sm btn-solid" onclick="saveAdmin()">💾 Sauvegarder les modifications</button>
</div>
</div>
</div>
<!-- ═══════════════════════════════════════════════════════
VIEW: FORMULAIRE D'INSCRIPTION (apprenant)
Simule la page accessible via le lien d'invitation
═══════════════════════════════════════════════════════ -->
<div id="inscription-overlay" style="display:none;position:fixed;inset:0;z-index:9000;background:var(--surface);overflow-y:auto">
<!-- Topbar léger -->
<div style="position:sticky;top:0;background:rgba(248,248,248,0.9);backdrop-filter:blur(20px);border-bottom:1px solid var(--border);padding:0 32px;height:56px;display:flex;align-items:center;justify-content:space-between;z-index:10">
<div style="font-family:'Playfair Display',serif;font-size:18px;font-weight:400">FormaPro</div>
<button class="btn btn-xs" onclick="closeInscription()">✕ Fermer l'aperçu</button>
</div>
<div style="max-width:620px;margin:48px auto 80px;padding:0 24px">
<!-- Bandeau invitation -->
<div id="inv-banner" style="background:var(--ink);color:white;border-radius:var(--radius);padding:24px 32px;margin-bottom:32px;position:relative;overflow:hidden">
<div style="position:absolute;width:260px;height:260px;border-radius:50%;border:1px solid rgba(255,255,255,0.06);top:-80px;right:-60px"></div>
<div style="font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.1em;color:rgba(255,255,255,0.4);margin-bottom:8px">Invitation personnelle</div>
<div style="font-family:'Playfair Display',serif;font-size:22px;font-weight:400;margin-bottom:6px">Bienvenue sur FormaPro</div>
<div style="font-size:13px;color:rgba(255,255,255,0.6)">Marie Lambert vous invite à créer votre compte.</div>
<div id="inv-formation-banner" style="display:flex;align-items:center;gap:8px;margin-top:14px;padding:10px 14px;background:rgba(255,255,255,0.08);border-radius:8px;font-size:13px">
<span>📚</span><span>Formation assignée : <strong id="inv-formation-banner-name">Excel Avancé & Power BI</strong></span>
</div>
</div>
<!-- Stepper -->
<div style="display:flex;align-items:center;gap:0;margin-bottom:36px" id="stepper">
<div class="step-item active" id="step-dot-1" onclick="goStep(1)">
<div class="step-circle" id="step-circle-1">1</div>
<div class="step-label">Identité</div>
</div>
<div class="step-line" id="step-line-1"></div>
<div class="step-item" id="step-dot-2" onclick="goStep(2)">
<div class="step-circle" id="step-circle-2">2</div>
<div class="step-label">Sécurité</div>
</div>
<div class="step-line" id="step-line-2"></div>
<div class="step-item" id="step-dot-3" onclick="goStep(3)">
<div class="step-circle" id="step-circle-3">3</div>
<div class="step-label">Documents</div>
</div>
<div class="step-line" id="step-line-3"></div>
<div class="step-item" id="step-dot-4" onclick="goStep(4)">
<div class="step-circle" id="step-circle-4">4</div>
<div class="step-label">Confirmation</div>
</div>
</div>
<!-- ── ÉTAPE 1 : Infos personnelles ── -->
<div class="reg-step" id="reg-step-1">
<div style="background:var(--white);border:1px solid var(--border);border-radius:var(--radius);padding:32px">
<div style="font-family:'Playfair Display',serif;font-size:22px;font-weight:400;margin-bottom:4px">Vos informations personnelles</div>
<div style="font-size:13px;color:var(--muted);margin-bottom:28px">Ces informations seront visibles par votre formateur et l'administration.</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-bottom:16px">
<div class="field-group">
<div class="field-label">Prénom <span style="color:var(--red)">*</span></div>
<input class="field-input reg-field" id="reg-prenom" type="text" placeholder="Jean" oninput="validateStep1()">
</div>
<div class="field-group">
<div class="field-label">Nom <span style="color:var(--red)">*</span></div>
<input class="field-input reg-field" id="reg-nom" type="text" placeholder="Dupont" oninput="validateStep1()">
</div>
</div>
<div class="field-group" style="margin-bottom:16px">
<div class="field-label">Email <span style="color:var(--red)">*</span></div>
<input class="field-input" id="reg-email" type="email" value="jean.dupont@email.fr" style="background:var(--surface-2);color:var(--muted)" readonly>
<div style="font-size:11.5px;color:var(--muted);margin-top:5px">📧 Pré-rempli depuis votre invitation — non modifiable</div>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-bottom:16px">
<div class="field-group">
<div class="field-label">Téléphone <span style="color:var(--red)">*</span></div>
<input class="field-input reg-field" id="reg-tel" type="tel" placeholder="06 12 34 56 78" oninput="validateStep1()">
</div>
<div class="field-group">
<div class="field-label">Date de naissance</div>
<input class="field-input" id="reg-dob" type="date">
</div>
</div>
<div class="field-group" style="margin-bottom:16px">
<div class="field-label">Intitulé de poste</div>
<input class="field-input" id="reg-poste" type="text" placeholder="Ex : Responsable commercial, Chargé RH…">
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-bottom:16px">
<div class="field-group">
<div class="field-label">Entreprise</div>
<input class="field-input" id="reg-entreprise" type="text" placeholder="Nom de votre société">
</div>
<div class="field-group">
<div class="field-label">Niveau d'études</div>
<select class="field-input" id="reg-niveau">
<option value="">— Sélectionner</option>
<option>CAP / BEP</option>
<option>Bac</option>
<option>Bac+2</option>
<option>Bac+3 / Licence</option>
<option>Bac+5 / Master</option>
<option>Doctorat</option>
</select>
</div>
</div>
<div class="field-group" style="margin-bottom:24px">
<div class="field-label">Besoins / Attentes particulières</div>
<textarea class="field-input" id="reg-besoins" rows="3" placeholder="Accessibilité, aménagement particulier, objectifs spécifiques…"></textarea>
</div>
<div id="step1-error" style="display:none;padding:10px 14px;background:var(--red-bg);border:1px solid #fecaca;border-radius:8px;font-size:13px;color:var(--red);margin-bottom:16px">
⚠ Veuillez remplir tous les champs obligatoires.
</div>
<div style="display:flex;justify-content:flex-end">
<button class="btn btn-solid" onclick="nextStep(1)" id="btn-step1">Continuer → Sécurité</button>
</div>
</div>
</div>
<!-- ── ÉTAPE 2 : Mot de passe ── -->
<div class="reg-step" id="reg-step-2" style="display:none">
<div style="background:var(--white);border:1px solid var(--border);border-radius:var(--radius);padding:32px">
<div style="font-family:'Playfair Display',serif;font-size:22px;font-weight:400;margin-bottom:4px">Sécurisez votre compte</div>
<div style="font-size:13px;color:var(--muted);margin-bottom:28px">Choisissez un mot de passe fort. Vous vous en servirez à chaque connexion.</div>
<div class="field-group" style="margin-bottom:16px">
<div class="field-label">Mot de passe <span style="color:var(--red)">*</span></div>
<div style="position:relative">
<input class="field-input" id="reg-pwd" type="password" placeholder="Minimum 8 caractères" oninput="checkPassword()">
<button type="button" onclick="togglePwd('reg-pwd',this)" style="position:absolute;right:12px;top:50%;transform:translateY(-50%);background:none;border:none;cursor:pointer;font-size:16px;color:var(--muted)">👁</button>
</div>
<!-- Password strength bar -->
<div style="margin-top:10px">
<div style="display:flex;gap:4px;margin-bottom:6px">
<div class="pwd-bar" id="pb1" style="flex:1;height:3px;border-radius:99px;background:var(--border)"></div>
<div class="pwd-bar" id="pb2" style="flex:1;height:3px;border-radius:99px;background:var(--border)"></div>
<div class="pwd-bar" id="pb3" style="flex:1;height:3px;border-radius:99px;background:var(--border)"></div>
<div class="pwd-bar" id="pb4" style="flex:1;height:3px;border-radius:99px;background:var(--border)"></div>
</div>
<div style="font-size:11.5px;color:var(--muted)" id="pwd-strength-label">Entrez votre mot de passe</div>
</div>
<div style="margin-top:10px;display:flex;flex-direction:column;gap:5px" id="pwd-rules">
<div class="pwd-rule" id="rule-len" style="font-size:12px;color:var(--muted-2)">○ 8 caractères minimum</div>
<div class="pwd-rule" id="rule-upper" style="font-size:12px;color:var(--muted-2)">○ Une majuscule</div>
<div class="pwd-rule" id="rule-num" style="font-size:12px;color:var(--muted-2)">○ Un chiffre</div>
<div class="pwd-rule" id="rule-special" style="font-size:12px;color:var(--muted-2)">○ Un caractère spécial</div>
</div>
</div>
<div class="field-group" style="margin-bottom:24px">
<div class="field-label">Confirmer le mot de passe <span style="color:var(--red)">*</span></div>
<div style="position:relative">
<input class="field-input" id="reg-pwd2" type="password" placeholder="Répétez le mot de passe" oninput="checkPwdMatch()">
<button type="button" onclick="togglePwd('reg-pwd2',this)" style="position:absolute;right:12px;top:50%;transform:translateY(-50%);background:none;border:none;cursor:pointer;font-size:16px;color:var(--muted)">👁</button>
</div>
<div id="pwd-match-msg" style="font-size:12px;margin-top:6px;display:none"></div>
</div>
<div style="padding:14px 18px;background:var(--surface);border-radius:var(--radius-sm);border:1px solid var(--border);margin-bottom:24px">
<div style="font-size:12.5px;font-weight:500;margin-bottom:10px">Consentements <span style="color:var(--red)">*</span></div>
<label style="display:flex;gap:10px;align-items:flex-start;font-size:13px;color:var(--muted);cursor:pointer;margin-bottom:10px">
<input type="checkbox" id="cgu-check" style="accent-color:var(--ink);margin-top:2px;flex-shrink:0" onchange="checkConsents()">
J'accepte les <a href="#" style="color:var(--ink)">Conditions Générales d'Utilisation</a> et la <a href="#" style="color:var(--ink)">Politique de confidentialité</a> de FormaPro.
</label>
<label style="display:flex;gap:10px;align-items:flex-start;font-size:13px;color:var(--muted);cursor:pointer">
<input type="checkbox" id="rgpd-check" style="accent-color:var(--ink);margin-top:2px;flex-shrink:0" onchange="checkConsents()">
J'accepte que mes données soient traitées conformément au RGPD dans le cadre de ma formation.
</label>
</div>
<div style="display:flex;justify-content:space-between">
<button class="btn" onclick="prevStep(2)">← Retour</button>
<button class="btn btn-solid" onclick="nextStep(2)" id="btn-step2" disabled>Continuer → Documents</button>
</div>
</div>
</div>
<!-- ── ÉTAPE 3 : Documents ── -->
<div class="reg-step" id="reg-step-3" style="display:none">
<div style="background:var(--white);border:1px solid var(--border);border-radius:var(--radius);padding:32px">
<div style="font-family:'Playfair Display',serif;font-size:22px;font-weight:400;margin-bottom:4px">Pièces justificatives</div>
<div style="font-size:13px;color:var(--muted);margin-bottom:10px">Ces documents sont nécessaires pour valider votre inscription.</div>
<div style="padding:10px 14px;background:var(--amber-bg);border-left:3px solid var(--amber);border-radius:6px;font-size:12.5px;color:var(--amber);margin-bottom:24px">
⚠ Les fichiers PDF ou image sont acceptés (max 5 Mo par document). Un contrôle de conformité sera effectué par l'administration.
</div>
<!-- Doc upload cards -->
<div style="display:flex;flex-direction:column;gap:14px;margin-bottom:28px">
<div class="doc-upload-card" id="doc-id">
<div style="display:flex;align-items:center;gap:14px">
<div style="width:40px;height:40px;background:var(--surface);border-radius:10px;display:flex;align-items:center;justify-content:center;font-size:20px;flex-shrink:0">🪪</div>
<div style="flex:1">
<div style="font-size:14px;font-weight:500;margin-bottom:2px">Pièce d'identité <span style="color:var(--red)">*</span></div>
<div style="font-size:12px;color:var(--muted)">CNI, Passeport ou Titre de séjour · recto/verso</div>
</div>
<label class="btn btn-sm" style="cursor:pointer;flex-shrink:0">
<input type="file" accept=".pdf,.jpg,.jpeg,.png" style="display:none" onchange="handleDocUpload(this,'doc-id','doc-id-status')">
Choisir
</label>
</div>
<div id="doc-id-status" style="display:none;margin-top:10px;padding:8px 12px;background:var(--green-bg);border-radius:6px;font-size:12.5px;color:var(--green);display:none"></div>
</div>
<div class="doc-upload-card" id="doc-cv">
<div style="display:flex;align-items:center;gap:14px">
<div style="width:40px;height:40px;background:var(--surface);border-radius:10px;display:flex;align-items:center;justify-content:center;font-size:20px;flex-shrink:0">📄</div>
<div style="flex:1">
<div style="font-size:14px;font-weight:500;margin-bottom:2px">CV <span style="color:var(--red)">*</span></div>
<div style="font-size:12px;color:var(--muted)">Votre parcours professionnel · PDF recommandé</div>
</div>
<label class="btn btn-sm" style="cursor:pointer;flex-shrink:0">
<input type="file" accept=".pdf,.doc,.docx" style="display:none" onchange="handleDocUpload(this,'doc-cv','doc-cv-status')">
Choisir
</label>
</div>
<div id="doc-cv-status" style="display:none;margin-top:10px;padding:8px 12px;background:var(--green-bg);border-radius:6px;font-size:12.5px;color:var(--green)"></div>
</div>
<div class="doc-upload-card" id="doc-attestation">
<div style="display:flex;align-items:center;gap:14px">
<div style="width:40px;height:40px;background:var(--surface);border-radius:10px;display:flex;align-items:center;justify-content:center;font-size:20px;flex-shrink:0">📋</div>
<div style="flex:1">
<div style="font-size:14px;font-weight:500;margin-bottom:2px">Attestation employeur <span style="color:var(--muted-2);font-size:12px;font-weight:400">(optionnel)</span></div>
<div style="font-size:12px;color:var(--muted)">Si la formation est prise en charge par votre entreprise</div>
</div>
<label class="btn btn-sm" style="cursor:pointer;flex-shrink:0">
<input type="file" accept=".pdf,.jpg,.jpeg,.png" style="display:none" onchange="handleDocUpload(this,'doc-attestation','doc-att-status')">
Choisir
</label>
</div>
<div id="doc-att-status" style="display:none;margin-top:10px;padding:8px 12px;background:var(--green-bg);border-radius:6px;font-size:12.5px;color:var(--green)"></div>
</div>
</div>
<div id="step3-error" style="display:none;padding:10px 14px;background:var(--red-bg);border:1px solid #fecaca;border-radius:8px;font-size:13px;color:var(--red);margin-bottom:16px">
⚠ La pièce d'identité et le CV sont obligatoires.
</div>
<div style="display:flex;justify-content:space-between">
<button class="btn" onclick="prevStep(3)">← Retour</button>
<button class="btn btn-solid" onclick="nextStep(3)" id="btn-step3">Vérifier & Soumettre</button>
</div>
</div>
</div>
<!-- ── ÉTAPE 4 : Confirmation ── -->
<div class="reg-step" id="reg-step-4" style="display:none">
<div style="background:var(--white);border:1px solid var(--border);border-radius:var(--radius);padding:40px;text-align:center">
<div style="width:72px;height:72px;background:var(--green-bg);border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:30px;margin:0 auto 20px">✓</div>
<div style="font-family:'Playfair Display',serif;font-size:26px;font-weight:400;margin-bottom:8px">Dossier soumis !</div>
<div style="font-size:14px;color:var(--muted);max-width:400px;margin:0 auto 28px;line-height:1.7">
Votre inscription est en cours de validation par l'équipe FormaPro. Vous recevrez un email de confirmation sous <strong>24–48h ouvrées</strong>.
</div>
<!-- Récap -->
<div style="text-align:left;background:var(--surface);border:1px solid var(--border);border-radius:var(--radius-sm);padding:20px 24px;margin-bottom:24px">
<div style="font-size:11px;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:.07em;margin-bottom:14px">Récapitulatif</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px">
<div><div style="font-size:11px;color:var(--muted-2)">Nom complet</div><div style="font-size:13.5px;font-weight:500" id="recap-nom">—</div></div>
<div><div style="font-size:11px;color:var(--muted-2)">Email</div><div style="font-size:13.5px;font-weight:500"><a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="b6dcd3d7d898d2c3c6d9d8c2f6d3dbd7dfda98d0c4">[email protected]</a></div></div>
<div><div style="font-size:11px;color:var(--muted-2)">Formation</div><div style="font-size:13.5px;font-weight:500" id="recap-formation">Excel Avancé & Power BI</div></div>
<div><div style="font-size:11px;color:var(--muted-2)">Documents</div><div style="font-size:13.5px;font-weight:500" id="recap-docs">—</div></div>
</div>
</div>
<div style="padding:12px 18px;background:var(--amber-bg);border-left:3px solid var(--amber);border-radius:8px;font-size:13px;color:var(--amber);margin-bottom:28px;text-align:left">
⏳ Votre accès à la plateforme sera activé après validation de votre dossier par l'administration.
</div>
<button class="btn btn-sm" onclick="closeInscription()">Retour à la plateforme</button>
</div>
</div>
</div><!-- end max-width wrapper -->
</div><!-- end inscription-overlay -->
<!-- ═══════════════════════════════════════════════════════
VIEW 11: INSCRIPTIONS — FILE DE VALIDATION ADMIN
═══════════════════════════════════════════════════════ -->
<div class="view" id="view-inscriptions">
<div class="page-header" style="display:flex;justify-content:space-between;align-items:flex-end;margin-bottom:28px">
<div>
<div class="page-title">Inscriptions en attente</div>
<div class="page-sub">Dossiers soumis par les apprenants — à valider avant activation du compte</div>
</div>
<div style="display:flex;gap:8px">
<button class="btn btn-sm" onclick="openInscription()">👁 Aperçu formulaire</button>
<button class="btn btn-sm btn-solid" onclick="validerTous()">✓ Valider tout</button>
</div>
</div>
<!-- KPI mini -->
<div style="display:grid;grid-template-columns:repeat(4,1fr);gap:14px;margin-bottom:28px">
<div style="background:var(--white);border:1px solid var(--border);border-radius:var(--radius-sm);padding:18px 20px">
<div style="font-family:'Playfair Display',serif;font-size:28px;font-weight:400;margin-bottom:3px" id="kpi-attente">4</div>
<div style="font-size:11px;color:var(--muted)">En attente de validation</div>
</div>
<div style="background:var(--green-bg);border:1px solid #bbf7d0;border-radius:var(--radius-sm);padding:18px 20px">
<div style="font-family:'Playfair Display',serif;font-size:28px;font-weight:400;margin-bottom:3px;color:var(--green)" id="kpi-valides">8</div>
<div style="font-size:11px;color:var(--green)">Validées ce mois</div>
</div>
<div style="background:var(--red-bg);border:1px solid #fecaca;border-radius:var(--radius-sm);padding:18px 20px">
<div style="font-family:'Playfair Display',serif;font-size:28px;font-weight:400;margin-bottom:3px;color:var(--red)" id="kpi-refus">1</div>
<div style="font-size:11px;color:var(--red)">Refusées</div>
</div>
<div style="background:var(--amber-bg);border:1px solid #fde68a;border-radius:var(--radius-sm);padding:18px 20px">
<div style="font-family:'Playfair Display',serif;font-size:28px;font-weight:400;margin-bottom:3px;color:var(--amber)" id="kpi-incomplets">2</div>
<div style="font-size:11px;color:var(--amber)">Dossiers incomplets</div>
</div>
</div>
<!-- Filter tabs -->
<div style="display:flex;gap:0;border:1px solid var(--border);border-radius:var(--radius-sm);background:var(--surface);padding:4px;width:fit-content;margin-bottom:22px">
<div class="sess-tab active" id="itab-all" onclick="filterDossiers('all')">Tous (7)</div>
<div class="sess-tab" id="itab-pending" onclick="filterDossiers('pending')">En attente (4)</div>
<div class="sess-tab" id="itab-complet" onclick="filterDossiers('complet')">Complets (2)</div>
<div class="sess-tab" id="itab-incomplet" onclick="filterDossiers('incomplet')">Incomplets (2)</div>
<div class="sess-tab" id="itab-valide" onclick="filterDossiers('valide')">Validés (8)</div>
<div class="sess-tab" id="itab-refuse" onclick="filterDossiers('refuse')">Refusés (1)</div>
</div>
<!-- Dossiers list -->
<div id="dossiers-list" style="display:flex;flex-direction:column;gap:12px"></div>
<!-- Drawer: détail dossier -->
<div id="dossier-drawer" style="display:none;position:fixed;right:0;top:0;width:480px;height:100vh;background:var(--white);border-left:1px solid var(--border);z-index:500;overflow-y:auto;box-shadow:-8px 0 40px rgba(0,0,0,0.08)">
<div style="padding:24px 28px;border-bottom:1px solid var(--border);display:flex;align-items:center;justify-content:space-between;position:sticky;top:0;background:var(--white);z-index:1">
<div style="font-family:'Playfair Display',serif;font-size:18px;font-weight:400" id="drawer-title">Dossier apprenant</div>
<button class="btn btn-xs" onclick="closeDrawer()">✕</button>
</div>
<div id="drawer-body" style="padding:28px"></div>
</div>
<div id="drawer-backdrop" style="display:none;position:fixed;inset:0;background:rgba(0,0,0,0.2);z-index:499" onclick="closeDrawer()"></div>
</div><!-- end view-inscriptions -->
<!-- VIEW 12: UTILISATEURS & RÔLES -->
<div class="view" id="view-utilisateurs">
<div class="page-header" style="display:flex;justify-content:space-between;align-items:flex-end;margin-bottom:24px">
<div><div class="page-title">Utilisateurs & Rôles</div><div class="page-sub">Gérez les accès, les rôles et les permissions de votre plateforme</div></div>
<div style="display:flex;gap:8px">
<button class="btn btn-sm" onclick="switchUtilTab('audit')">📋 Audit log</button>
<button class="btn btn-sm btn-solid" onclick="openAddUser()">+ Ajouter un utilisateur</button>
</div>
</div>
<div style="display:flex;gap:0;border:1px solid var(--border);border-radius:var(--radius-sm);background:var(--surface);padding:4px;width:fit-content;margin-bottom:24px">
<div class="sess-tab active" id="utab-users" onclick="switchUtilTab('users')">👥 Utilisateurs</div>
<div class="sess-tab" id="utab-roles" onclick="switchUtilTab('roles')">🎭 Rôles</div>
<div class="sess-tab" id="utab-matrix" onclick="switchUtilTab('matrix')">🔐 Permissions</div>
<div class="sess-tab" id="utab-audit" onclick="switchUtilTab('audit')">📋 Audit log</div>
</div>
<!-- USERS PANEL -->
<div id="upanel-users">
<div style="display:grid;grid-template-columns:repeat(4,1fr);gap:14px;margin-bottom:24px">
<div style="background:var(--white);border:1px solid var(--border);border-radius:var(--radius-sm);padding:18px 20px"><div style="font-family:'Playfair Display',serif;font-size:26px;font-weight:400;margin-bottom:3px" id="u-total">18</div><div style="font-size:11px;color:var(--muted)">Utilisateurs total</div></div>
<div style="background:var(--surface);border:1px solid var(--border);border-radius:var(--radius-sm);padding:18px 20px"><div style="font-family:'Playfair Display',serif;font-size:26px;font-weight:400;margin-bottom:3px" id="u-admins">2</div><div style="font-size:11px;color:var(--muted)">Administrateurs</div></div>
<div style="background:var(--blue-bg);border:1px solid #bfdbfe;border-radius:var(--radius-sm);padding:18px 20px"><div style="font-family:'Playfair Display',serif;font-size:26px;font-weight:400;margin-bottom:3px;color:var(--blue)" id="u-formateurs">4</div><div style="font-size:11px;color:var(--blue)">Formateurs</div></div>
<div style="background:var(--surface-2);border:1px solid var(--border);border-radius:var(--radius-sm);padding:18px 20px"><div style="font-family:'Playfair Display',serif;font-size:26px;font-weight:400;margin-bottom:3px" id="u-apprenants">12</div><div style="font-size:11px;color:var(--muted)">Apprenants</div></div>
</div>
<div style="display:flex;gap:10px;margin-bottom:16px;align-items:center">
<div style="position:relative;flex:1;max-width:320px">
<svg style="position:absolute;left:12px;top:50%;transform:translateY(-50%);opacity:.4" width="14" height="14" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="2"><circle cx="9" cy="9" r="6"/><path d="M15 15l3 3"/></svg>
<input id="user-search" class="field-input" type="text" placeholder="Rechercher…" style="padding-left:34px" oninput="filterUsers()">
</div>
<select class="field-input" id="role-filter" style="width:auto;padding:9px 14px" onchange="filterUsers()">
<option value="">Tous les rôles</option><option value="admin">Admin</option><option value="formateur">Formateur</option><option value="apprenant">Apprenant</option><option value="suspended">Suspendu</option>
</select>
</div>
<div style="background:var(--white);border:1px solid var(--border);border-radius:var(--radius);overflow:hidden">
<div style="padding:12px 18px;border-bottom:1px solid var(--border);background:var(--surface);display:flex;align-items:center;gap:12px">
<input type="checkbox" id="select-all-users" style="accent-color:var(--ink)" onchange="toggleSelectAll(this)">
<span style="font-size:11px;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:.06em">Utilisateur</span>
</div>
<div id="users-list"></div>
<div id="users-empty" style="display:none;padding:32px;text-align:center;color:var(--muted);font-size:13px">Aucun utilisateur trouvé.</div>
</div>
<div id="bulk-bar" style="display:none;position:sticky;bottom:0;background:var(--ink);color:white;padding:12px 20px;border-radius:var(--radius-sm);margin-top:12px;align-items:center;gap:12px">
<span id="bulk-count" style="font-size:13px;font-weight:500"></span>
<div style="margin-left:auto;display:flex;gap:8px">
<button class="btn btn-xs" style="background:rgba(255,255,255,.1);color:white;border-color:rgba(255,255,255,.2)" onclick="bulkChangeRole()">Changer le rôle</button>
<button class="btn btn-xs" style="background:rgba(255,255,255,.1);color:white;border-color:rgba(255,255,255,.2)" onclick="bulkSuspend()">Suspendre</button>
<button class="btn btn-xs" style="background:rgba(255,255,255,.1);color:white;border-color:rgba(255,255,255,.2)" onclick="bulkUnsuspend()">Réactiver</button>
</div>
</div>
</div>
<!-- ROLES PANEL -->
<div id="upanel-roles" style="display:none">
<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:16px;margin-bottom:28px">
<div class="role-card selected" onclick="selectRole('admin',this)">
<div class="role-icon" style="background:#1a1a1a">👑</div>
<div style="font-size:15px;font-weight:600;margin-bottom:6px">Administrateur</div>
<div style="font-size:12.5px;color:var(--muted);line-height:1.6;margin-bottom:14px">Accès total à toutes les fonctionnalités. Peut gérer les utilisateurs, formations, sessions et paramètres.</div>
<div style="display:flex;align-items:center;justify-content:space-between"><span class="role-badge role-admin">Admin</span><span style="font-size:12px;color:var(--muted)" id="count-admin">2 utilisateurs</span></div>
</div>
<div class="role-card" onclick="selectRole('formateur',this)">
<div class="role-icon" style="background:var(--blue-bg)">🎓</div>
<div style="font-size:15px;font-weight:600;margin-bottom:6px">Formateur</div>
<div style="font-size:12.5px;color:var(--muted);line-height:1.6;margin-bottom:14px">Gère ses sessions, évalue ses apprenants, accède aux analytics de son groupe et à la messagerie.</div>
<div style="display:flex;align-items:center;justify-content:space-between"><span class="role-badge role-formateur">Formateur</span><span style="font-size:12px;color:var(--muted)" id="count-formateur">4 utilisateurs</span></div>
</div>
<div class="role-card" onclick="selectRole('apprenant',this)">
<div class="role-icon" style="background:var(--surface-2)">📚</div>
<div style="font-size:15px;font-weight:600;margin-bottom:6px">Apprenant</div>
<div style="font-size:12.5px;color:var(--muted);line-height:1.6;margin-bottom:14px">Accède à ses formations, portail personnel, certifications et peut contacter son formateur.</div>
<div style="display:flex;align-items:center;justify-content:space-between"><span class="role-badge role-apprenant">Apprenant</span><span style="font-size:12px;color:var(--muted)" id="count-apprenant">12 utilisateurs</span></div>
</div>
</div>
<div style="background:var(--white);border:1px solid var(--border);border-radius:var(--radius);padding:28px" id="role-detail-panel">
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:20px">
<div style="font-family:'Playfair Display',serif;font-size:18px;font-weight:400" id="role-detail-title">Rôle : Administrateur</div>
<button class="btn btn-sm btn-solid" onclick="saveRolePerms()">💾 Sauvegarder</button>
</div>
<div id="role-perms-list" style="display:flex;flex-direction:column;gap:8px"></div>
</div>
</div>
<!-- MATRIX PANEL -->
<div id="upanel-matrix" style="display:none">
<div style="font-size:13px;color:var(--muted);margin-bottom:20px">Vue d'ensemble des permissions par rôle. Cochez pour ajuster.</div>
<div style="background:var(--white);border:1px solid var(--border);border-radius:var(--radius);overflow:hidden">
<div style="padding:14px 20px;background:var(--surface);border-bottom:1px solid var(--border);display:flex;align-items:center;justify-content:space-between">
<span style="font-size:13px;font-weight:500">Matrice des permissions</span>
<button class="btn btn-sm btn-solid" onclick="saveMatrix()">💾 Sauvegarder</button>
</div>
<div style="overflow-x:auto"><table class="perm-table" id="perm-matrix-table"><thead><tr>
<th style="width:280px;padding:12px 16px">Fonctionnalité</th>
<th style="min-width:110px"><div style="display:flex;flex-direction:column;align-items:center;gap:4px"><span>👑</span>Admin</div></th>
<th style="min-width:110px"><div style="display:flex;flex-direction:column;align-items:center;gap:4px"><span>🎓</span>Formateur</div></th>
<th style="min-width:110px"><div style="display:flex;flex-direction:column;align-items:center;gap:4px"><span>📚</span>Apprenant</div></th>
</tr></thead><tbody id="perm-matrix-body"></tbody></table></div>
</div>
</div>
<!-- AUDIT PANEL -->
<div id="upanel-audit" style="display:none">
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:20px">
<div><div style="font-size:14px;font-weight:500;margin-bottom:2px">Journal des actions</div><div style="font-size:12.5px;color:var(--muted)">Toutes les actions sensibles sont tracées automatiquement</div></div>
<div style="display:flex;gap:8px">
<select class="field-input" style="width:auto;padding:8px 12px;font-size:12.5px" onchange="filterAudit(this.value)">
<option value="">Toutes les actions</option>
<option value="login">Connexions</option><option value="role">Changements de rôle</option>
<option value="perm">Permissions</option><option value="user">Gestion utilisateurs</option>
</select>
<button class="btn btn-sm" onclick="exportAudit()">⬇ Exporter</button>
</div>
</div>
<div style="background:var(--white);border:1px solid var(--border);border-radius:var(--radius);padding:24px" id="audit-list"></div>
</div>
</div><!-- end view-utilisateurs -->
<!-- User drawer -->
<div class="util-drawer" id="util-drawer">
<div style="padding:22px 26px;border-bottom:1px solid var(--border);display:flex;align-items:center;justify-content:space-between;position:sticky;top:0;background:var(--white);z-index:1">
<div style="font-family:'Playfair Display',serif;font-size:17px;font-weight:400" id="util-drawer-title">Utilisateur</div>
<button class="btn btn-xs" onclick="closeUtilDrawer()">✕</button>
</div>
<div id="util-drawer-body" style="padding:26px"></div>
</div>
<div id="util-backdrop" style="display:none;position:fixed;inset:0;background:rgba(0,0,0,0.2);z-index:499" onclick="closeUtilDrawer()"></div>
<!-- Add user modal -->
<div id="add-user-modal" style="display:none;position:fixed;inset:0;z-index:600;align-items:center;justify-content:center">
<div style="position:absolute;inset:0;background:rgba(0,0,0,0.3)" onclick="closeAddUser()"></div>
<div style="position:relative;background:var(--white);border-radius:var(--radius);padding:36px;width:480px;max-width:92vw;z-index:1;box-shadow:0 20px 60px rgba(0,0,0,0.15)">
<div style="font-family:'Playfair Display',serif;font-size:20px;margin-bottom:4px">Ajouter un utilisateur</div>
<div style="font-size:13px;color:var(--muted);margin-bottom:24px">Le nouvel utilisateur recevra une invitation par email.</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-bottom:14px">
<div class="field-group"><div class="field-label">Prénom *</div><input class="field-input" id="add-prenom" type="text" placeholder="Jean"></div>
<div class="field-group"><div class="field-label">Nom *</div><input class="field-input" id="add-nom" type="text" placeholder="Dupont"></div>
</div>
<div class="field-group" style="margin-bottom:14px"><div class="field-label">Email *</div><input class="field-input" id="add-email" type="email" placeholder="jean.dupont@email.fr"></div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-bottom:24px">
<div class="field-group"><div class="field-label">Rôle *</div>
<select class="field-input" id="add-role">
<option value="apprenant">Apprenant</option>
<option value="formateur">Formateur</option>
<option value="admin">Administrateur</option>
</select>
</div>
<div class="field-group"><div class="field-label">Formation assignée</div>
<select class="field-input" id="add-formation">
<option value="">— Aucune</option>
<option>Excel Avancé & Power BI</option>
<option>Management Niveau 1</option>
<option>IA & Productivité</option>
<option>Cybersécurité</option>
</select>
</div>
</div>
<div style="display:flex;gap:10px;justify-content:flex-end">
<button class="btn btn-sm" onclick="closeAddUser()">Annuler</button>
<button class="btn btn-sm btn-solid" onclick="confirmAddUser()">✓ Créer et inviter</button>
</div>
</div>
</div>
<!-- ═══════════════ CHAT BUBBLE + PANEL ═══════════════ -->
<!-- Floating bubble button -->
<button class="chat-bubble" id="chat-bubble-btn" onclick="toggleChat()" title="Messagerie">
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.7">
<path d="M18 2H2v12h4l4 4 4-4h4V2z"/>
</svg>
<span id="chat-notif-badge" style="display:none"></span>
</button>
<!-- Floating chat window -->
<div class="chat-panel" id="chat-panel">
<div class="chat-header">
<svg width="14" height="14" viewBox="0 0 20 20" fill="none" stroke="white" stroke-width="1.7"><path d="M18 2H2v12h4l4 4 4-4h4V2z"/></svg>
<div class="chat-header-title">Messagerie FormaPro</div>
<button class="chat-close-btn" onclick="toggleChat()">✕</button>
</div>
<div class="chat-inner">
<div class="chat-channels" id="chat-channels-list"></div>
<div class="chat-messages-wrap">
<div class="chat-channel-header">
<div class="chat-channel-name" id="chat-chan-name">#excel-avancé</div>
<div class="chat-channel-desc" id="chat-chan-desc">Formation Excel Avancé & Power BI · 12 membres</div>
</div>
<div class="chat-messages" id="chat-messages-area"></div>
<div class="chat-input-area">
<div class="chat-typing" id="chat-typing"></div>
<div class="chat-input-row">
<textarea class="chat-input" id="chat-input" rows="1" placeholder="Écrire…"
onkeydown="handleChatKey(event)" oninput="autoResizeInput(this)"></textarea>
<button class="chat-send-btn" onclick="sendChatMessage()">
<svg width="12" height="12" viewBox="0 0 20 20" fill="none" stroke="white" stroke-width="2.2"><path d="M2 10L18 2l-4 16-5-6-7-2z"/></svg>
</button>
</div>
</div>
</div>
</div>
</div>
<!-- ═══════════════════════════════════════════════════════
VIEW: DASHBOARD APPRENANT ENRICHI (gamification)
═══════════════════════════════════════════════════════ -->
<div class="view" id="view-apprenant2">
<!-- Hero -->
<div style="background:var(--ink);border-radius:var(--radius);padding:36px 44px;margin-bottom:28px;color:white;position:relative;overflow:hidden">
<div style="position:absolute;width:400px;height:400px;border-radius:50%;border:1px solid rgba(255,255,255,0.05);top:-150px;right:-100px"></div>
<div style="display:flex;align-items:flex-start;justify-content:space-between;position:relative;z-index:1">
<div>
<div style="font-size:12px;color:rgba(255,255,255,.45);margin-bottom:6px">Niveau 7 — Explorateur confirmé</div>
<div style="font-family:'Playfair Display',serif;font-size:26px;font-weight:400;margin-bottom:16px">Julie Dupont</div>
<div style="display:flex;align-items:center;gap:12px;margin-bottom:8px">
<div style="font-size:12px;color:rgba(255,255,255,.5)">XP : 3 420 / 4 000</div>
<div style="font-size:12px;color:rgba(255,255,255,.5)">→ Niveau 8</div>
</div>
<div style="width:260px;height:6px;background:rgba(255,255,255,.15);border-radius:99px;overflow:hidden">
<div style="width:85.5%;height:100%;background:linear-gradient(90deg,#f59e0b,#f97316);border-radius:99px"></div>
</div>
</div>
<div style="text-align:center">
<div class="streak-flame">🔥</div>
<div class="streak-num">12</div>
<div style="font-size:11px;color:rgba(255,255,255,.4);margin-top:4px">jours consécutifs</div>
<div class="streak-days" style="margin-top:10px;justify-content:center">
<div class="streak-day done">L</div><div class="streak-day done">M</div>
<div class="streak-day done">M</div><div class="streak-day done">J</div>
<div class="streak-day today">V</div><div class="streak-day miss">S</div>
<div class="streak-day miss">D</div>
</div>
</div>
</div>
</div>
<div class="grid-2" style="margin-bottom:24px">
<!-- Progression détaillée -->
<div class="card">
<div class="card-title">Mes formations en cours</div>
<div style="display:flex;flex-direction:column;gap:16px;margin-top:14px">
<div>
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:6px">
<div style="font-size:13.5px;font-weight:500">📊 Excel Avancé & Power BI</div>
<div style="font-family:'Playfair Display',serif;font-size:16px">88%</div>
</div>
<div class="xp-bar-wrap"><div class="xp-bar-fill" style="width:88%"></div></div>
<div style="display:flex;justify-content:space-between;font-size:11.5px;color:var(--muted)">
<span>18.5h / 21h</span><span>+240 XP cette semaine</span>
</div>
</div>
<div>
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:6px">
<div style="font-size:13.5px;font-weight:500">🤖 IA & Productivité</div>
<div style="font-family:'Playfair Display',serif;font-size:16px">50%</div>
</div>
<div class="xp-bar-wrap"><div class="xp-bar-fill" style="width:50%"></div></div>
<div style="display:flex;justify-content:space-between;font-size:11.5px;color:var(--muted)">
<span>7h / 14h</span><span>+80 XP cette semaine</span>
</div>
</div>
<div>
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:6px">
<div style="font-size:13.5px;font-weight:500">🎤 Prise de parole</div>
<div style="font-family:'Playfair Display',serif;font-size:16px">30%</div>
</div>
<div class="xp-bar-wrap"><div class="xp-bar-fill" style="width:30%"></div></div>
<div style="display:flex;justify-content:space-between;font-size:11.5px;color:var(--muted)">
<span>4.2h / 14h</span><span>+40 XP cette semaine</span>
</div>
</div>
</div>
<div style="margin-top:20px;padding:14px;background:var(--amber-bg);border-left:3px solid var(--amber);border-radius:8px;font-size:12.5px;color:var(--amber)">
🎯 Objectif : terminer Excel Avancé avant le <strong>31 mars</strong> — il vous reste 2.5h !
</div>
</div>
<!-- Leaderboard -->
<div class="card" style="padding:0;overflow:hidden">
<div style="padding:18px 20px;border-bottom:1px solid var(--border)">
<div class="card-title" style="margin:0">Classement du groupe — Excel Avancé</div>
</div>
<div>
<div class="leaderboard-row"><div class="rank-medal">🥇</div><div style="width:28px;height:28px;border-radius:50%;background:#1d4ed8;color:white;display:flex;align-items:center;justify-content:center;font-size:11px;font-weight:700">TR</div><div style="flex:1"><div style="font-size:13px;font-weight:500">Thomas Renard</div><div style="font-size:11.5px;color:var(--muted)">3 820 XP</div></div><span class="tag tag-surface">92%</span></div>
<div class="leaderboard-row me"><div class="rank-medal">🥈</div><div style="width:28px;height:28px;border-radius:50%;background:#0a0a0a;color:white;display:flex;align-items:center;justify-content:center;font-size:11px;font-weight:700">JD</div><div style="flex:1"><div style="font-size:13px;font-weight:500">Julie Dupont <span style="font-size:10px;color:var(--blue)">(moi)</span></div><div style="font-size:11.5px;color:var(--muted)">3 420 XP</div></div><span class="tag tag-blue">88%</span></div>
<div class="leaderboard-row"><div class="rank-medal">🥉</div><div style="width:28px;height:28px;border-radius:50%;background:#4b5563;color:white;display:flex;align-items:center;justify-content:center;font-size:11px;font-weight:700">AM</div><div style="flex:1"><div style="font-size:13px;font-weight:500">Antoine Martin</div><div style="font-size:11.5px;color:var(--muted)">2 980 XP</div></div><span class="tag tag-surface">78%</span></div>
<div class="leaderboard-row"><div class="rank-num">4</div><div style="width:28px;height:28px;border-radius:50%;background:#4b5563;color:white;display:flex;align-items:center;justify-content:center;font-size:11px;font-weight:700">EL</div><div style="flex:1"><div style="font-size:13px;font-weight:500">Emma Lefebvre</div><div style="font-size:11.5px;color:var(--muted)">2 610 XP</div></div><span class="tag tag-surface">67%</span></div>
<div class="leaderboard-row"><div class="rank-num">5</div><div style="width:28px;height:28px;border-radius:50%;background:#4b5563;color:white;display:flex;align-items:center;justify-content:center;font-size:11px;font-weight:700">SB</div><div style="flex:1"><div style="font-size:13px;font-weight:500">Sophie Bernard</div><div style="font-size:11.5px;color:var(--muted)">2 140 XP</div></div><span class="tag tag-surface">55%</span></div>
<div class="leaderboard-row"><div class="rank-num">6</div><div style="width:28px;height:28px;border-radius:50%;background:#9ca3af;color:white;display:flex;align-items:center;justify-content:center;font-size:11px;font-weight:700">LR</div><div style="flex:1"><div style="font-size:13px;font-weight:500">Lucas Roche</div><div style="font-size:11.5px;color:var(--muted)">1 320 XP</div></div><span class="tag tag-red">34%</span></div>
</div>
</div>
</div>
<!-- Badges -->
<div class="section-title">Mes badges</div>
<div class="badge-grid" style="margin-bottom:28px">
<div class="badge-card" onclick="showToast('🏆 Excel Expert obtenu le 10 jan. 2026')">
<div class="badge-icon">🏆</div><div class="badge-name">Excel Expert</div><div class="badge-desc">Complétion Excel Avancé</div>
<div style="margin-top:8px"><span class="tag tag-green" style="font-size:10px">Obtenu</span></div>
</div>
<div class="badge-card" onclick="showToast('🔥 Série de 7 jours obtenu cette semaine')">
<div class="badge-icon">🔥</div><div class="badge-name">Série de 7 jours</div><div class="badge-desc">7 jours consécutifs</div>
<div style="margin-top:8px"><span class="tag tag-green" style="font-size:10px">Obtenu</span></div>
</div>
<div class="badge-card" onclick="showToast('⚡ Apprenant rapide obtenu')">
<div class="badge-icon">⚡</div><div class="badge-name">Apprenant rapide</div><div class="badge-desc">3 leçons en 1 jour</div>
<div style="margin-top:8px"><span class="tag tag-green" style="font-size:10px">Obtenu</span></div>
</div>
<div class="badge-card" onclick="showToast('🎯 Quiz parfait obtenu')">
<div class="badge-icon">🎯</div><div class="badge-name">Quiz parfait</div><div class="badge-desc">20/20 à un quiz</div>
<div style="margin-top:8px"><span class="tag tag-green" style="font-size:10px">Obtenu</span></div>
</div>
<div class="badge-card locked">
<div class="badge-icon">🚀</div><div class="badge-name">Niveau 10</div><div class="badge-desc">Atteindre le niveau 10</div>
<div style="margin-top:8px"><div class="xp-bar-wrap"><div class="xp-bar-fill" style="width:70%"></div></div><div style="font-size:10px;color:var(--muted)">7/10</div></div>
</div>
<div class="badge-card locked">
<div class="badge-icon">📚</div><div class="badge-name">Polyglotte</div><div class="badge-desc">Compléter 5 formations</div>
<div style="margin-top:8px"><div class="xp-bar-wrap"><div class="xp-bar-fill" style="width:40%"></div></div><div style="font-size:10px;color:var(--muted)">2/5</div></div>
</div>
<div class="badge-card locked">
<div class="badge-icon">🌟</div><div class="badge-name">Série 30 jours</div><div class="badge-desc">30 jours consécutifs</div>
<div style="margin-top:8px"><div class="xp-bar-wrap"><div class="xp-bar-fill" style="width:40%"></div></div><div style="font-size:10px;color:var(--muted)">12/30</div></div>
</div>
<div class="badge-card locked">
<div class="badge-icon">💎</div><div class="badge-name">Certifié Pro</div><div class="badge-desc">3 certifications</div>
<div style="margin-top:8px"><div class="xp-bar-wrap"><div class="xp-bar-fill" style="width:67%"></div></div><div style="font-size:10px;color:var(--muted)">2/3</div></div>
</div>
</div>
</div>
<!-- ═══════════════════════════════════════════════════════
VIEW: PARAMÈTRES COMPTE
═══════════════════════════════════════════════════════ -->
<div class="view" id="view-parametres">
<div class="page-header" style="margin-bottom:28px">
<div class="page-title">Paramètres du compte</div>
<div class="page-sub">Gérez votre profil, sécurité et préférences</div>
</div>
<div class="settings-layout">
<!-- Left nav -->
<div class="settings-nav card" style="padding:12px">
<div class="settings-nav-item active" id="snav-profile" onclick="switchSettings('profile')">
<svg width="15" height="15" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5"><circle cx="10" cy="7" r="3"/><path d="M4 17a6 6 0 0112 0"/></svg>Profil
</div>
<div class="settings-nav-item" id="snav-security" onclick="switchSettings('security')">
<svg width="15" height="15" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M10 2l7 3v5c0 4-3.5 7.5-7 8-3.5-.5-7-4-7-8V5l7-3z"/></svg>Sécurité
</div>
<div class="settings-nav-item" id="snav-notifs" onclick="switchSettings('notifs')">
<svg width="15" height="15" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M10 2a6 6 0 00-6 6v3l-2 3h16l-2-3V8a6 6 0 00-6-6z"/><path d="M8 17a2 2 0 004 0"/></svg>Notifications
</div>
<div class="settings-nav-item" id="snav-apparence" onclick="switchSettings('apparence')">
<svg width="15" height="15" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5"><circle cx="10" cy="10" r="7"/><path d="M10 3v2M10 15v2M3 10h2M15 10h2"/></svg>Apparence
</div>
<div class="settings-nav-item" id="snav-danger" onclick="switchSettings('danger')" style="color:var(--red)">
<svg width="15" height="15" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M10 2l8 14H2L10 2z"/><path d="M10 8v4M10 14h.01"/></svg>Zone de danger
</div>
</div>
<!-- Right content -->
<div>
<!-- Profil -->
<div class="settings-section active" id="ssec-profile">
<div class="settings-card">
<div class="settings-card-title">Informations personnelles</div>
<div class="settings-card-sub">Votre nom et photo visibles par vos formateurs et collègues</div>
<div style="display:flex;align-items:center;gap:20px;margin-bottom:24px">
<div class="avatar-upload" style="background:#0a0a0a" onclick="showToast('📷 Changer la photo de profil')">ML</div>
<div>
<div style="font-size:14px;font-weight:500;margin-bottom:4px">Marie Lambert</div>
<div style="font-size:12.5px;color:var(--muted);margin-bottom:10px">Administratrice · FormaPro</div>
<button class="btn btn-xs" onclick="showToast('📷 Upload en cours…')">Changer la photo</button>
</div>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-bottom:16px">
<div class="field-group"><div class="field-label">Prénom</div><input class="field-input" value="Marie"></div>
<div class="field-group"><div class="field-label">Nom</div><input class="field-input" value="Lambert"></div>
</div>
<div class="field-group" style="margin-bottom:16px"><div class="field-label">Email</div><input class="field-input" value="m.lambert@formapro.fr" type="email"></div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-bottom:16px">
<div class="field-group"><div class="field-label">Téléphone</div><input class="field-input" value="06 12 34 56 78" type="tel"></div>
<div class="field-group"><div class="field-label">Poste</div><input class="field-input" value="Responsable formation"></div>
</div>
<div class="field-group" style="margin-bottom:20px"><div class="field-label">Bio</div><textarea class="field-input" rows="3">Administratrice de la plateforme FormaPro depuis 2024. Passionnée par la formation professionnelle et le développement des compétences.</textarea></div>
<button class="btn btn-solid btn-sm" onclick="showToast('✓ Profil mis à jour')">💾 Enregistrer</button>
</div>
</div>
<!-- Sécurité -->
<div class="settings-section" id="ssec-security">
<div class="settings-card">
<div class="settings-card-title">Changer le mot de passe</div>
<div class="settings-card-sub">Choisissez un mot de passe fort d'au moins 8 caractères</div>
<div class="field-group" style="margin-bottom:14px"><div class="field-label">Mot de passe actuel</div><input class="field-input" type="password" placeholder="••••••••"></div>
<div class="field-group" style="margin-bottom:14px"><div class="field-label">Nouveau mot de passe</div><input class="field-input" type="password" placeholder="••••••••" oninput="checkPwdStrengthSettings(this)"><div id="pwd-strength-settings" style="font-size:12px;color:var(--muted);margin-top:6px"></div></div>
<div class="field-group" style="margin-bottom:20px"><div class="field-label">Confirmer</div><input class="field-input" type="password" placeholder="••••••••"></div>
<button class="btn btn-solid btn-sm" onclick="showToast('✓ Mot de passe mis à jour')">Mettre à jour</button>
</div>
<div class="settings-card">
<div class="settings-card-title">Double authentification (2FA)</div>
<div class="settings-card-sub">Renforcez la sécurité de votre compte avec un code à chaque connexion</div>
<div style="display:flex;align-items:center;justify-content:space-between">
<div>
<div style="font-size:13px;font-weight:500;margin-bottom:3px">Authentification par application</div>
<div style="font-size:12px;color:var(--muted)">Google Authenticator, Authy…</div>
</div>
<div class="toggle" id="tfa-toggle" onclick="this.classList.toggle('on');showToast(this.classList.contains('on')?'✓ 2FA activé':'2FA désactivé')"></div>
</div>
</div>
<div class="settings-card">
<div class="settings-card-title">Sessions actives</div>
<div class="settings-card-sub">Appareils actuellement connectés à votre compte</div>
<div style="display:flex;flex-direction:column;gap:12px">
<div style="display:flex;align-items:center;gap:12px;padding:12px 14px;background:var(--green-bg);border-radius:var(--radius-sm);border:1px solid #bbf7d0">
<span style="font-size:20px">💻</span><div style="flex:1"><div style="font-size:13px;font-weight:500">Chrome · macOS</div><div style="font-size:12px;color:var(--muted)">Paris, FR · Session actuelle</div></div><span class="tag tag-green">Actif</span>
</div>
<div style="display:flex;align-items:center;gap:12px;padding:12px 14px;background:var(--surface);border-radius:var(--radius-sm)">
<span style="font-size:20px">📱</span><div style="flex:1"><div style="font-size:13px;font-weight:500">Safari · iPhone</div><div style="font-size:12px;color:var(--muted)">Lyon, FR · Il y a 2 jours</div></div><button class="btn btn-xs" style="color:var(--red)" onclick="showToast('Session déconnectée')">Déconnecter</button>
</div>
</div>
</div>
</div>
<!-- Notifications -->
<div class="settings-section" id="ssec-notifs">
<div class="settings-card">
<div class="settings-card-title">Préférences de notifications</div>
<div class="settings-card-sub">Choisissez ce que vous souhaitez recevoir et par quel canal</div>
<div>
<div class="notif-pref-row"><div><div class="notif-pref-label">Sessions à venir</div><div class="notif-pref-desc">Rappel 2h avant chaque session</div></div><div class="toggle on" onclick="this.classList.toggle('on')"></div></div>
<div class="notif-pref-row"><div><div class="notif-pref-label">Nouveaux messages</div><div class="notif-pref-desc">Messages reçus dans la messagerie</div></div><div class="toggle on" onclick="this.classList.toggle('on')"></div></div>
<div class="notif-pref-row"><div><div class="notif-pref-label">Apprenants inactifs</div><div class="notif-pref-desc">Alerte si un apprenant est inactif +7 jours</div></div><div class="toggle on" onclick="this.classList.toggle('on')"></div></div>
<div class="notif-pref-row"><div><div class="notif-pref-label">Nouvelles inscriptions</div><div class="notif-pref-desc">Dossier soumis en attente de validation</div></div><div class="toggle on" onclick="this.classList.toggle('on')"></div></div>
<div class="notif-pref-row"><div><div class="notif-pref-label">Certifications délivrées</div><div class="notif-pref-desc">Quand un apprenant obtient sa certification</div></div><div class="toggle" onclick="this.classList.toggle('on')"></div></div>
<div class="notif-pref-row"><div><div class="notif-pref-label">Résumé hebdomadaire</div><div class="notif-pref-desc">Email récapitulatif chaque lundi matin</div></div><div class="toggle on" onclick="this.classList.toggle('on')"></div></div>
</div>
<button class="btn btn-solid btn-sm" style="margin-top:16px" onclick="showToast('✓ Préférences enregistrées')">💾 Enregistrer</button>
</div>
</div>
<!-- Apparence -->
<div class="settings-section" id="ssec-apparence">
<div class="settings-card">
<div class="settings-card-title">Thème de l'interface</div>
<div class="settings-card-sub">Personnalisez l'apparence de FormaPro</div>
<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:12px;margin-bottom:20px">
<div style="border:2px solid var(--ink);border-radius:var(--radius-sm);padding:14px;text-align:center;cursor:pointer;background:var(--white)">
<div style="width:40px;height:40px;background:#0a0a0a;border-radius:8px;margin:0 auto 8px"></div>
<div style="font-size:12px;font-weight:600">Classique</div><span class="tag tag-green" style="font-size:10px;margin-top:4px">Actif</span>
</div>
<div style="border:1px solid var(--border);border-radius:var(--radius-sm);padding:14px;text-align:center;cursor:pointer" onclick="applyTheme('blue');showToast('🎨 Thème Océan appliqué')">
<div style="width:40px;height:40px;background:#1d4ed8;border-radius:8px;margin:0 auto 8px"></div>
<div style="font-size:12px;font-weight:600">Océan</div>
</div>
<div style="border:1px solid var(--border);border-radius:var(--radius-sm);padding:14px;text-align:center;cursor:pointer" onclick="applyTheme('green');showToast('🎨 Thème Forêt appliqué')">
<div style="width:40px;height:40px;background:#1a7f4f;border-radius:8px;margin:0 auto 8px"></div>
<div style="font-size:12px;font-weight:600">Forêt</div>
</div>
</div>
<div class="field-group" style="margin-bottom:16px">
<div class="field-label">Taille du texte</div>
<input type="range" class="field-input" min="12" max="18" value="14" style="padding:4px 0" oninput="document.body.style.fontSize=this.value+'px'">
</div>
<div style="display:flex;align-items:center;justify-content:space-between;padding:12px 0;border-top:1px solid var(--border)">
<div><div style="font-size:13px;font-weight:500">Mode sombre</div><div style="font-size:12px;color:var(--muted)">Interface en thème sombre</div></div>
<div class="toggle" id="dark-mode-settings-toggle" onclick="toggleDarkMode()"></div>
</div>
<div style="display:flex;align-items:center;justify-content:space-between;padding:12px 0;border-top:1px solid var(--border)">
<div><div style="font-size:13px;font-weight:500">Mode compact</div><div style="font-size:12px;color:var(--muted)">Réduit l'espacement entre les éléments</div></div>
<div class="toggle" onclick="this.classList.toggle('on')"></div>
</div>
</div>
</div>
<!-- Danger zone -->
<div class="settings-section" id="ssec-danger">
<div class="settings-card danger-zone">
<div class="settings-card-title">Zone de danger</div>
<div class="settings-card-sub">Ces actions sont irréversibles. Procédez avec précaution.</div>
<div style="display:flex;flex-direction:column;gap:14px">
<div style="display:flex;align-items:center;justify-content:space-between;padding:14px 16px;background:var(--surface);border-radius:var(--radius-sm)">
<div><div style="font-size:13px;font-weight:500">Exporter mes données</div><div style="font-size:12px;color:var(--muted)">Télécharger toutes vos données personnelles (RGPD)</div></div>
<button class="btn btn-sm" onclick="showToast('📦 Export en cours…')">Exporter</button>
</div>
<div style="display:flex;align-items:center;justify-content:space-between;padding:14px 16px;background:var(--amber-bg);border-radius:var(--radius-sm)">
<div><div style="font-size:13px;font-weight:500;color:var(--amber)">Désactiver mon compte</div><div style="font-size:12px;color:var(--muted)">Votre compte sera suspendu temporairement</div></div>
<button class="btn btn-sm" style="color:var(--amber);border-color:var(--amber)" onclick="showToast('⚠ Contactez l'admin pour désactiver votre compte')">Désactiver</button>
</div>
<div style="display:flex;align-items:center;justify-content:space-between;padding:14px 16px;background:var(--red-bg);border-radius:var(--radius-sm)">
<div><div style="font-size:13px;font-weight:500;color:var(--red)">Supprimer mon compte</div><div style="font-size:12px;color:var(--muted)">Action irréversible — toutes vos données seront effacées</div></div>
<button class="btn btn-sm" style="color:var(--red);border-color:var(--red)" onclick="if(confirm('Supprimer définitivement votre compte ?'))showToast('✓ Demande de suppression envoyée')">Supprimer</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- ═══════════════════════════════════════════════════════
VIEW: QUIZ & ÉVALUATIONS
═══════════════════════════════════════════════════════ -->
<div class="view" id="view-quiz">
<div class="page-header" style="display:flex;justify-content:space-between;align-items:flex-end;margin-bottom:24px">
<div><div class="page-title">Quiz & Évaluations</div><div class="page-sub">Testez vos connaissances et suivez vos résultats</div></div>
<div style="display:flex;gap:8px">
<button class="btn btn-sm" onclick="switchQuizView('list')">📋 Tous les quiz</button>
<button class="btn btn-sm btn-solid" onclick="switchQuizView('results')">📊 Mes résultats</button>
</div>
</div>
<!-- Stats row -->
<div style="display:grid;grid-template-columns:repeat(4,1fr);gap:14px;margin-bottom:24px">
<div style="background:var(--white);border:1px solid var(--border);border-radius:var(--radius-sm);padding:18px 20px"><div style="font-family:'Playfair Display',serif;font-size:26px;font-weight:400;margin-bottom:3px">8</div><div style="font-size:11px;color:var(--muted)">Quiz disponibles</div></div>
<div style="background:var(--green-bg);border:1px solid #bbf7d0;border-radius:var(--radius-sm);padding:18px 20px"><div style="font-family:'Playfair Display',serif;font-size:26px;font-weight:400;margin-bottom:3px;color:var(--green)">5</div><div style="font-size:11px;color:var(--green)">Complétés</div></div>
<div style="background:var(--blue-bg);border:1px solid #bfdbfe;border-radius:var(--radius-sm);padding:18px 20px"><div style="font-family:'Playfair Display',serif;font-size:26px;font-weight:400;margin-bottom:3px;color:var(--blue)">16.2</div><div style="font-size:11px;color:var(--blue)">Note moyenne /20</div></div>
<div style="background:var(--amber-bg);border:1px solid #fde68a;border-radius:var(--radius-sm);padding:18px 20px"><div style="font-family:'Playfair Display',serif;font-size:26px;font-weight:400;margin-bottom:3px;color:var(--amber)">3</div><div style="font-size:11px;color:var(--amber)">À compléter</div></div>
</div>
<div class="quiz-layout">
<!-- Quiz list -->
<div id="quiz-list-panel">
<div style="font-size:11px;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:.07em;margin-bottom:12px">Excel Avancé & Power BI</div>
<div class="quiz-list-item done" onclick="loadQuiz('excel1')"><div style="display:flex;justify-content:space-between;align-items:center"><div class="quiz-name">Module 1 — Fonctions avancées</div><span class="tag tag-green">18/20</span></div><div class="quiz-meta">10 questions · Complété le 15 mars</div></div>
<div class="quiz-list-item done" onclick="loadQuiz('excel2')"><div style="display:flex;justify-content:space-between;align-items:center"><div class="quiz-name">Module 2 — Tableaux croisés</div><span class="tag tag-green">16/20</span></div><div class="quiz-meta">8 questions · Complété le 20 mars</div></div>
<div class="quiz-list-item active" id="qitem-excel3" onclick="loadQuiz('excel3')"><div style="display:flex;justify-content:space-between;align-items:center"><div class="quiz-name">Module 3 — Introduction Power BI</div><span class="tag tag-amber">En cours</span></div><div class="quiz-meta">12 questions · Démarré</div></div>
<div class="quiz-list-item locked"><div style="display:flex;justify-content:space-between;align-items:center"><div class="quiz-name">Module 4 — Dashboards</div><span class="tag tag-surface">🔒 Verrouillé</span></div><div class="quiz-meta">10 questions · Compléter le module d'abord</div></div>
<div style="font-size:11px;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:.07em;margin:16px 0 12px">IA & Productivité</div>
<div class="quiz-list-item done" onclick="loadQuiz('ia1')"><div style="display:flex;justify-content:space-between;align-items:center"><div class="quiz-name">Module 1 — Bases de l'IA</div><span class="tag tag-green">15/20</span></div><div class="quiz-meta">8 questions · Complété le 22 mars</div></div>
<div class="quiz-list-item locked"><div style="display:flex;justify-content:space-between;align-items:center"><div class="quiz-name">Module 2 — Automatisation</div><span class="tag tag-surface">🔒 Verrouillé</span></div><div class="quiz-meta">10 questions</div></div>
</div>
<!-- Quiz question / result area -->
<div id="quiz-main-panel">
<!-- Default state -->
<div id="quiz-start-screen" class="quiz-question-card" style="text-align:center;padding:48px 32px">
<div style="font-size:48px;margin-bottom:16px">📝</div>
<div style="font-family:'Playfair Display',serif;font-size:22px;font-weight:400;margin-bottom:8px">Quiz — Module 3</div>
<div style="font-size:13.5px;color:var(--muted);max-width:320px;margin:0 auto 28px;line-height:1.7">Introduction à Power BI · 12 questions · ~15 minutes<br>Une seule tentative. Bonne chance !</div>
<div style="display:flex;justify-content:center;gap:12px">
<button class="btn btn-solid" onclick="startQuiz('excel3')">Commencer le quiz →</button>
</div>
</div>
<!-- Question screen (injected by JS) -->
<div id="quiz-question-screen" style="display:none"></div>
<!-- Results screen (injected by JS) -->
<div id="quiz-results-screen" style="display:none"></div>
</div>
</div>
</div>
<!-- ═══════════════════════════════════════════════════
VIEW GESTION DES FORMATIONS
═══════════════════════════════════════════════════ -->
<div class="view" id="view-gestion">
<div class="page-header" style="display:flex;justify-content:space-between;align-items:flex-end">
<div>
<div class="page-title">Mes formations</div>
<div class="page-sub" id="gestion-sub">Gérez et organisez votre catalogue de formations</div>
</div>
<button class="btn btn-solid" onclick="openFormModal()">+ Nouvelle formation</button>
</div>
<!-- Stats -->
<div class="gestion-stats-grid">
<div class="gestion-stat-card"><div class="gestion-stat-val" id="gst-total">0</div><div class="gestion-stat-lbl">Formations créées</div></div>
<div class="gestion-stat-card"><div class="gestion-stat-val" id="gst-heures">0h</div><div class="gestion-stat-lbl">Durée totale</div></div>
<div class="gestion-stat-card"><div class="gestion-stat-val" id="gst-cpf">0</div><div class="gestion-stat-lbl">Éligibles CPF</div></div>
<div class="gestion-stat-card"><div class="gestion-stat-val" id="gst-res">0</div><div class="gestion-stat-lbl">Modules créés</div></div>
</div>
<!-- Liste -->
<div id="gestion-container"></div>
</div>
</main>
<!-- ═══════════════════════════════════════════════════
MODAL — CRÉER / MODIFIER UNE FORMATION
═══════════════════════════════════════════════════ -->
<div class="modal-backdrop" id="form-modal">
<div class="modal-box">
<div class="modal-hdr">
<div class="modal-ttl" id="modal-ttl">Nouvelle formation</div>
<button class="modal-x" onclick="closeFormModal()">✕</button>
</div>
<div class="modal-bdy">
<input type="hidden" id="fm-edit-id">
<!-- Infos générales -->
<div class="fm-sec">
<div class="fm-sec-title">Informations générales</div>
<div class="fm-g2">
<div class="fm-field span2">
<label class="fm-lbl">Titre de la formation *</label>
<input class="fm-inp" type="text" id="fm-titre" placeholder="ex. Excel Avancé & Power BI">
</div>
<div class="fm-field">
<label class="fm-lbl">Emoji / Icône</label>
<input class="fm-inp" type="text" id="fm-emoji" placeholder="📊" maxlength="4" style="font-size:22px;text-align:center">
</div>
<div class="fm-field">
<label class="fm-lbl">Domaine</label>
<select class="fm-sel" id="fm-domaine">
<option value="Bureautique & Data">Bureautique & Data</option>
<option value="Management">Management</option>
<option value="Digital & IA">Digital & IA</option>
<option value="Juridique">Juridique</option>
<option value="Communication">Communication</option>
<option value="Autre">Autre</option>
</select>
</div>
<div class="fm-field">
<label class="fm-lbl">Durée (heures)</label>
<input class="fm-inp" type="number" id="fm-duree" placeholder="21" min="1">
</div>
<div class="fm-field">
<label class="fm-lbl">Prix TTC</label>
<input class="fm-inp" type="text" id="fm-prix" placeholder="890 €">
</div>
<div class="fm-field">
<label class="fm-lbl">Modalité</label>
<select class="fm-sel" id="fm-modalite">
<option>Présentiel</option>
<option>Distanciel</option>
<option>Hybride</option>
<option>E-learning</option>
</select>
</div>
<div class="fm-field">
<label class="fm-lbl">Niveau</label>
<select class="fm-sel" id="fm-niveau">
<option>Débutant</option>
<option>Intermédiaire</option>
<option>Avancé</option>
<option>Tous niveaux</option>
</select>
</div>
<div class="fm-field span2">
<label class="fm-lbl">Description courte</label>
<textarea class="fm-txt" id="fm-desc" placeholder="Objectifs, public cible, points clés de la formation…"></textarea>
</div>
</div>
</div>
<!-- Financement -->
<div class="fm-sec">
<div class="fm-sec-title">Financement & certification</div>
<div class="fm-check-group">
<label class="fm-check-item"><input type="checkbox" id="fm-cpf"> CPF</label>
<label class="fm-check-item"><input type="checkbox" id="fm-opco"> OPCO</label>
<label class="fm-check-item"><input type="checkbox" id="fm-plan"> Plan de formation</label>
<label class="fm-check-item"><input type="checkbox" id="fm-certif"> Certifiant</label>
</div>
</div>
<!-- Formateur -->
<div class="fm-sec">
<div class="fm-sec-title">Formateur</div>
<div class="fm-g2">
<div class="fm-field">
<label class="fm-lbl">Nom</label>
<input class="fm-inp" type="text" id="fm-formateur" placeholder="ex. Pierre Leblanc">
</div>
<div class="fm-field">
<label class="fm-lbl">Spécialité</label>
<input class="fm-inp" type="text" id="fm-form-bio" placeholder="ex. Expert Excel & BI · 12 ans">
</div>
</div>
</div>
<!-- Programme : Chapitres & Sous-chapitres -->
<div class="fm-sec">
<div class="fm-sec-title">Programme pédagogique</div>
<div class="ch-list" id="ch-list"></div>
<button class="btn btn-sm" type="button" onclick="addChapitre()">+ Ajouter un chapitre</button>
</div>
</div><!-- /modal-bdy -->
<div class="modal-ftr">
<button class="btn" onclick="closeFormModal()">Annuler</button>
<button class="btn btn-solid" onclick="saveFormation()">💾 Sauvegarder</button>
</div>
</div>
</div>
<script data-cfasync="false" src="/cdn-cgi/scripts/5c5dd728/cloudflare-static/email-decode.min.js"></script><script data-cfasync="false" src="/cdn-cgi/scripts/5c5dd728/cloudflare-static/email-decode.min.js"></script><script>
const titles = {
dashboard: 'Tableau de bord',
catalogue: 'Catalogue',
fiche: 'Fiche formation',
sessions: 'Sessions',
analytics: 'Analytics',
apprenant: 'Portail apprenant',
lecteur: 'Lecteur de cours',
formateur: 'Espace formateur',
certifications: 'Certifications',
admin: 'Administration',
inscriptions: 'Inscriptions',
utilisateurs: 'Utilisateurs & Rôles',
apprenant2: 'Mon espace apprenant',
parametres: 'Paramètres du compte',
quiz: 'Quiz & Évaluations',
gestion: 'Mes formations',
};
function nav(view) {
document.querySelectorAll('.view').forEach(v => v.classList.remove('active'));
document.querySelectorAll('.nav-item').forEach(n => n.classList.remove('active'));
const viewEl = document.getElementById('view-' + view);
if (!viewEl) { console.warn('[nav] vue introuvable:', view); return; }
viewEl.classList.add('active');
document.querySelectorAll('.nav-item').forEach(n => {
if (n.getAttribute('onclick') && n.getAttribute('onclick').includes("'" + view + "'")) n.classList.add('active');
});
const titleEl = document.getElementById('topbar-title');
if (titleEl) titleEl.textContent = titles[view] || view;
const mainEl = document.querySelector('.main');
if (mainEl) mainEl.scrollTop = 0;
setTimeout(() => {
try {
if (view === 'inscriptions') { renderDossiers(); updateDossierKpis(); }
if (view === 'gestion') { renderGestion(); }
if (view === 'utilisateurs') { filterUsers(); }
if (view === 'sessions') { if(typeof renderCalendar==='function'){renderCalendar();renderSessions();updateSessKpis();} }
if (view === 'catalogue') { if(typeof renderCatalogueDyn==='function') renderCatalogueDyn(); }
} catch(e) { console.warn('[nav] render error:', view, e); }
}, 0);
}
function toggleMod(id) {
const item = document.getElementById('prog-' + id);
const body = document.getElementById('prog-body-' + id) || item.querySelector('.programme-body');
if (!body) return;
const isOpen = body.classList.contains('open');
document.querySelectorAll('.programme-body').forEach(b => b.classList.remove('open'));
document.querySelectorAll('.programme-item').forEach(i => i.classList.remove('open'));
document.querySelectorAll('.programme-arrow').forEach(a => a.style.transform = '');
if (!isOpen) {
body.classList.add('open');
item.classList.add('open');
item.querySelector('.programme-arrow').style.transform = 'rotate(90deg)';
}
}
function switchSess(mode) {
document.getElementById('sess-calendar').style.display = mode === 'cal' ? 'block' : 'none';
document.getElementById('sess-list').style.display = mode === 'list' ? 'block' : 'none';
document.getElementById('tab-cal').className = 'sess-tab' + (mode === 'cal' ? ' active' : '');
document.getElementById('tab-list').className = 'sess-tab' + (mode === 'list' ? ' active' : '');
}
function openDetail() {
const p = document.getElementById('emarg-panel');
p.classList.toggle('open');
if (p.classList.contains('open')) p.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}
function toggleEmarg(btn) {
const row = btn.closest('.emarg-row');
const btns = row.querySelectorAll('.emarg-btn');
if (btn.textContent === '✓') {
btns.forEach(b => { b.classList.remove('present', 'absent'); });
btn.classList.add('present');
} else {
btns.forEach(b => { b.classList.remove('present', 'absent'); });
btn.classList.add('absent');
}
}
// ─── ADMIN PANEL LOGIC ───
function switchCtab(tab) {
document.querySelectorAll('.ctab').forEach(t => t.classList.remove('active'));
document.querySelectorAll('.content-panel').forEach(p => p.classList.remove('active'));
document.getElementById('ctab-' + tab).classList.add('active');
document.getElementById('cpanel-' + tab).classList.add('active');
}
function markUnsaved() {
const dot = document.getElementById('save-dot');
const status = document.getElementById('save-status');
if (dot) { dot.classList.remove('saved'); dot.style.background = 'var(--amber)'; }
if (status) status.textContent = 'Modifications non sauvegardées';
}
function saveAdmin() {
const dot = document.getElementById('save-dot');
const status = document.getElementById('save-status');
if (dot) { dot.classList.add('saved'); dot.style.background = 'var(--green)'; }
if (status) status.textContent = 'Sauvegardé à ' + new Date().toLocaleTimeString('fr-FR', {hour:'2-digit',minute:'2-digit'});
showToast('✓ Modifications sauvegardées');
}
function resetAdmin() {
if (!confirm('Réinitialiser toutes les personnalisations ?')) return;
document.documentElement.style.cssText = '';
showToast('↺ Interface réinitialisée');
}
function showToast(msg) {
const t = document.createElement('div');
t.style.cssText = 'position:fixed;bottom:80px;left:50%;transform:translateX(-50%) translateY(10px);background:var(--ink);color:white;padding:10px 22px;border-radius:99px;font-size:13px;font-weight:500;z-index:9999;opacity:0;transition:all 0.3s ease;pointer-events:none;box-shadow:0 4px 20px rgba(0,0,0,0.2)';
t.textContent = msg;
document.body.appendChild(t);
requestAnimationFrame(() => { t.style.opacity = '1'; t.style.transform = 'translateX(-50%) translateY(0)'; });
setTimeout(() => { t.style.opacity = '0'; setTimeout(() => t.remove(), 300); }, 2200);
}
// Color management
function updateColor(name, value) {
const map = { ink: '--ink', surface: '--surface', green: '--green', amber: '--amber', red: '--red', blue: '--blue', border: '--border', muted: '--muted' };
if (map[name]) {
document.documentElement.style.setProperty(map[name], value);
// Update background colors too
if (name === 'green') {
document.documentElement.style.setProperty('--green-bg', hexToLight(value, 0.1));
}
if (name === 'amber') {
document.documentElement.style.setProperty('--amber-bg', hexToLight(value, 0.08));
}
if (name === 'red') {
document.documentElement.style.setProperty('--red-bg', hexToLight(value, 0.08));
}
if (name === 'blue') {
document.documentElement.style.setProperty('--blue-bg', hexToLight(value, 0.08));
}
}
const swatch = document.getElementById('swatch-' + name);
if (swatch) swatch.style.background = value;
const hexEl = document.getElementById('hex-' + name);
if (hexEl) hexEl.textContent = value;
const bar = document.getElementById('bar-' + name);
if (bar) bar.style.background = value;
markUnsaved();
}
function hexToLight(hex, alpha) {
const r = parseInt(hex.slice(1,3),16);
const g = parseInt(hex.slice(3,5),16);
const b = parseInt(hex.slice(5,7),16);
return `rgba(${r},${g},${b},${alpha})`;
}
const themes = {
dark: { ink: '#0a0a0a', green: '#1a7f4f', amber: '#b45309', red: '#c0392b', blue: '#1d4ed8' },
blue: { ink: '#1d4ed8', green: '#059669', amber: '#d97706', red: '#dc2626', blue: '#2563eb' },
green: { ink: '#166534', green: '#16a34a', amber: '#ca8a04', red: '#dc2626', blue: '#2563eb' },
purple: { ink: '#6d28d9', green: '#059669', amber: '#d97706', red: '#dc2626', blue: '#4f46e5' },
orange: { ink: '#c2410c', green: '#16a34a', amber: '#d97706', red: '#dc2626', blue: '#2563eb' },
rose: { ink: '#9d174d', green: '#059669', amber: '#d97706', red: '#dc2626', blue: '#2563eb' },
cyan: { ink: '#0e7490', green: '#059669', amber: '#d97706', red: '#dc2626', blue: '#0284c7' },
slate: { ink: '#334155', green: '#059669', amber: '#ca8a04', red: '#dc2626', blue: '#3b82f6' },
};
function applyTheme(name) {
document.querySelectorAll('.color-preset').forEach(p => p.classList.remove('selected'));
const t = themes[name];
if (!t) return;
Object.entries(t).forEach(([k,v]) => {
updateColor(k, v);
const cp = document.getElementById('cp-' + k);
if (cp) cp.value = v;
});
showToast('🎨 Thème appliqué');
}
// Live content update helpers
function liveUpdate(selector, value) {
document.querySelectorAll(selector).forEach(el => { el.textContent = value; });
markUnsaved();
}
function liveUpdateNth(selector, n, value) {
const els = document.querySelectorAll(selector);
if (els[n]) els[n].textContent = value;
markUnsaved();
}
function updateFont(type, value) {
if (type === 'display') {
document.querySelectorAll('.page-title,.section-title,.fiche-title,.brand-name,.hero-title,.apprenant-name,.kpi-value,.fiche-stat-val,.formation-pct,.cal-month,.cat-card-name').forEach(el => el.style.fontFamily = value);
} else {
document.body.style.fontFamily = value;
}
markUnsaved();
}
function updateFontSize(cls, size) {
document.querySelectorAll('.' + cls).forEach(el => el.style.fontSize = size);
markUnsaved();
}
// Table editor
const tableData = {
sessions: {
cols: ['Formation', 'Date & Heure', 'Lieu', 'Formateur', 'Inscrits', 'Statut'],
rows: [
['Excel Avancé — Gr. A', '25 mars · 14h00', 'Salle Émeraude', 'P. Leblanc', '12/12', 'En cours'],
['Management — M3', '26 mars · 09h00', 'Distanciel', 'A. Moreau', '8/15', 'Planifié'],
['Prise de parole', '27 mars · 10h00', 'Hybride', 'L. Simon', '6/10', 'Planifié'],
['Droit du travail', '28 mars · 14h00', 'Présentiel', 'C. Faure', '10/12', 'Planifié'],
]
},
apprenants: {
cols: ['Nom', 'Email', 'Formation', 'Progression', 'Statut'],
rows: [
['Julie Dupont', 'j.dupont@email.fr', 'Excel Avancé', '92%', 'Actif'],
['Antoine Martin', 'a.martin@email.fr', 'Management', '78%', 'Actif'],
['Sophie Bernard', 's.bernard@email.fr', 'IA & Prod.', '55%', 'Actif'],
['Lucas Roche', 'l.roche@email.fr', 'Excel Avancé', '34%', 'Inactif'],
['Emma Lefebvre', 'e.lefebvre@email.fr', 'Prise de parole', '67%', 'Actif'],
]
},
formations: {
cols: ['Titre', 'Domaine', 'Durée', 'Prix', 'Modalité', 'Note'],
rows: [
['Excel Avancé & Power BI', 'Bureautique', '21h', '890 €', 'Présentiel', '4.8'],
['IA & Productivité', 'Digital', '14h', '750 €', 'Hybride', '4.6'],
['Management Niveau 1', 'Management', '28h', '1 490 €', 'Présentiel', '4.9'],
['Cybersécurité', 'Digital', '7h', 'Inclus', 'E-learning', '4.4'],
['Prise de parole', 'Communication', '14h', '690 €', 'Présentiel', '4.7'],
]
},
documents: {
cols: ['Document', 'Type', 'Apprenant', 'Date', 'Statut'],
rows: [
['Convention de formation', 'Convention', 'Julie Dupont', '1 mars 2026', 'Signé'],
['Attestation de présence', 'Attestation', 'Antoine Martin', '20 mars 2026', 'Délivré'],
['Convention de formation', 'Convention', 'Emma Lefebvre', '5 mars 2026', 'En attente'],
['Bilan de compétences', 'Bilan', 'Thomas Renard', '22 mars 2026', 'Finalisé'],
]
}
};
let currentTable = 'sessions';
function showTable(name) {
currentTable = name;
const data = tableData[name];
const tbody = document.getElementById('table-body');
const thead = document.getElementById('thead-row');
const titleMap = { sessions: 'Sessions planifiées', apprenants: 'Apprenants', formations: 'Catalogue formations', documents: 'Documents administratifs' };
// Update toolbar title
document.querySelector('.table-toolbar span').textContent = titleMap[name] || name;
// Rebuild thead
thead.innerHTML = `<th style="width:36px"><input type="text" value="" style="pointer-events:none;opacity:0"></th>` +
data.cols.map(c => `<th><input type="text" value="${c}"></th>`).join('') +
`<th style="width:40px"></th>`;
// Rebuild tbody
tbody.innerHTML = data.rows.map(row =>
`<tr>
<td><input type="checkbox" style="accent-color:var(--ink)"></td>
${row.map(cell => `<td><input type="text" value="${cell}"></td>`).join('')}
<td class="row-actions"><button class="row-del-btn" onclick="delRow(this)">×</button></td>
</tr>`
).join('');
markUnsaved();
}
function addTableRow() {
const tbody = document.getElementById('table-body');
const colCount = document.getElementById('thead-row').querySelectorAll('th').length - 2;
const tr = document.createElement('tr');
tr.innerHTML = `<td><input type="checkbox" style="accent-color:var(--ink)"></td>` +
Array(colCount).fill('').map(() => `<td><input type="text" placeholder="—"></td>`).join('') +
`<td class="row-actions"><button class="row-del-btn" onclick="delRow(this)">×</button></td>`;
tbody.appendChild(tr);
tr.querySelector('input[type=text]').focus();
markUnsaved();
}
function addTableCol() {
const newName = prompt('Nom de la nouvelle colonne :');
if (!newName) return;
const thead = document.getElementById('thead-row');
const lastTh = thead.lastElementChild;
const newTh = document.createElement('th');
newTh.innerHTML = `<input type="text" value="${newName}">`;
thead.insertBefore(newTh, lastTh);
document.querySelectorAll('#table-body tr').forEach(tr => {
const td = document.createElement('td');
td.innerHTML = `<input type="text" placeholder="—">`;
tr.insertBefore(td, tr.lastElementChild);
});
markUnsaved();
}
function delRow(btn) {
btn.closest('tr').style.transition = 'opacity .2s';
btn.closest('tr').style.opacity = '0';
setTimeout(() => { btn.closest('tr').remove(); markUnsaved(); }, 200);
}
function deleteSelectedRows() {
const checked = document.querySelectorAll('#table-body input[type=checkbox]:checked');
if (checked.length === 0) { showToast('⚠ Aucune ligne sélectionnée'); return; }
checked.forEach(cb => { const tr = cb.closest('tr'); tr.style.opacity='0'; setTimeout(()=>tr.remove(),200); });
markUnsaved();
}
function exportTableCSV() {
const rows = [];
const headers = [...document.querySelectorAll('#thead-row th input')].map(i=>i.value).filter(Boolean);
rows.push(headers.join(','));
document.querySelectorAll('#table-body tr').forEach(tr => {
const cells = [...tr.querySelectorAll('td input[type=text], td select')].map(i=>i.value||i.options?.[i.selectedIndex]?.text||'');
if (cells.length) rows.push(cells.join(','));
});
const blob = new Blob([rows.join('\n')], {type:'text/csv'});
const a = document.createElement('a'); a.href = URL.createObjectURL(blob);
a.download = (currentTable || 'tableau') + '.csv'; a.click();
showToast('⬇ Export CSV lancé');
}
function addAlert() {
const list = document.getElementById('alerts-editor-list');
const div = document.createElement('div'); div.className = 'alert-editor-item';
div.innerHTML = `<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:12px">
<select class="field-input" style="width:auto;padding:5px 10px"><option>⚠️ Avertissement</option><option>✅ Succès</option><option>🔴 Erreur</option><option>ℹ️ Info</option></select>
<button class="row-del-btn" onclick="this.closest('.alert-editor-item').remove()" style="width:28px;height:28px">×</button>
</div>
<input class="field-input" type="text" placeholder="Texte de l'alerte…">
<div style="margin-top:10px;display:flex;align-items:center;gap:10px">
<span style="font-size:12px;color:var(--muted)">Visible</span>
<div class="toggle on" onclick="this.classList.toggle('on')"></div>
</div>`;
list.appendChild(div);
div.querySelector('input').focus();
markUnsaved();
}
// ─────────────────────────────────────────
// INVITATIONS MODULE
// ─────────────────────────────────────────
// Sub-tab switching
function switchInvTab(tab) {
document.querySelectorAll('.inv-subtab').forEach(t => t.classList.remove('active'));
document.querySelectorAll('.inv-panel').forEach(p => { p.style.display = 'none'; p.classList.remove('active'); });
document.getElementById('isubtab-' + tab).classList.add('active');
const panel = document.getElementById('invpanel-' + tab);
panel.style.display = 'block'; panel.classList.add('active');
if (tab === 'track') renderTrackTable();
}
function switchAdmin(panel) {
document.querySelectorAll('.admin-tab-item').forEach(t => t.classList.remove('active'));
document.querySelectorAll('.admin-panel').forEach(p => { p.classList.remove('active'); p.style.display = 'none'; });
document.getElementById('atab-' + panel).classList.add('active');
const el = document.getElementById('apanel-' + panel);
if (el) { el.classList.add('active'); el.style.display = 'block'; }
if (panel === 'invitations') {
// ensure correct sub-panel is shown
document.querySelectorAll('.inv-panel').forEach(p => { p.style.display='none'; p.classList.remove('active'); });
const active = document.querySelector('.inv-subtab.active');
if (active) {
const id = active.id.replace('isubtab-', '');
const p = document.getElementById('invpanel-' + id);
if (p) { p.style.display='block'; p.classList.add('active'); }
if (id === 'track') renderTrackTable();
} else { switchInvTab('send'); }
}
}
// ── Email tag input ──
const invEmails = [];
function handleInvEmail(e) {
const input = document.getElementById('inv-email-input');
if (e.key === 'Enter' || e.key === ',') {
e.preventDefault();
const val = input.value.trim().replace(/,$/, '');
if (val && isValidEmail(val) && !invEmails.includes(val)) {
invEmails.push(val);
addEmailTag(val);
input.value = '';
updateInvCounter();
} else if (val && !isValidEmail(val)) {
input.style.color = 'var(--red)';
setTimeout(() => input.style.color = '', 1000);
}
}
if (e.key === 'Backspace' && input.value === '' && invEmails.length > 0) {
removeEmailTag(invEmails[invEmails.length - 1]);
}
}
function isValidEmail(e) { return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(e); }
function addEmailTag(email) {
const wrap = document.getElementById('inv-tags-wrap');
const input = document.getElementById('inv-email-input');
const tag = document.createElement('div');
tag.className = 'inv-tag'; tag.dataset.email = email;
tag.innerHTML = `${email} <button class="inv-tag-remove" onclick="removeEmailTag('${email}')">×</button>`;
wrap.insertBefore(tag, input);
}
function removeEmailTag(email) {
const idx = invEmails.indexOf(email);
if (idx > -1) invEmails.splice(idx, 1);
const tag = document.querySelector(`.inv-tag[data-email="${email}"]`);
if (tag) tag.remove();
updateInvCounter();
}
function updateInvCounter() {
const el = document.getElementById('inv-count-display');
if (el) el.textContent = invEmails.length;
const btn = document.getElementById('inv-send-btn');
if (btn) btn.disabled = invEmails.length === 0;
}
// Live preview update on form change — wired in main init
function updateSendPreview() {
const formation = document.getElementById('inv-formation');
const expiry = document.getElementById('inv-expiry');
const msg = document.getElementById('inv-msg');
const fline = document.getElementById('prev-formation-line');
const fname = document.getElementById('prev-formation-name');
const prevExp = document.getElementById('prev-expiry');
const prevMsg = document.getElementById('prev-msg-line');
if (formation && fline && fname) {
const selText = formation.options[formation.selectedIndex]?.text;
if (formation.value) {
fline.style.display = ''; fname.textContent = selText;
} else { fline.style.display = 'none'; }
}
if (expiry && prevExp) {
const v = expiry.value;
prevExp.textContent = v === '0' ? 'jamais' : v + ' jours';
// update counter card
const card = prevExp.closest('#invpanel-send');
if (card) {
const cards = card.querySelectorAll('[style*="26px"]');
if (cards[1]) cards[1].textContent = v === '0' ? '∞' : v + 'j';
}
}
if (msg && prevMsg) {
if (msg.value.trim()) {
prevMsg.style.display = '';
prevMsg.innerHTML = `<em style="color:var(--ink)">${msg.value}</em><br><br>`;
} else { prevMsg.style.display = 'none'; }
}
}
// ── Send invitations via Gmail MCP ──
async function sendInvitations() {
if (invEmails.length === 0) { showToast('⚠ Ajoutez au moins une adresse email'); return; }
const btn = document.getElementById('inv-send-btn');
btn.disabled = true;
btn.innerHTML = '<span style="display:inline-block;animation:spin 1s linear infinite">⏳</span> Envoi en cours…';
const formation = document.getElementById('inv-formation');
const formationText = formation?.options[formation.selectedIndex]?.text || '';
const msgExtra = document.getElementById('inv-msg')?.value || '';
const expiry = document.getElementById('inv-expiry')?.value || '14';
const role = document.getElementById('inv-role')?.value || 'apprenant';
let sent = 0, failed = 0;
for (const email of invEmails) {
try {
const subject = document.getElementById('tpl-subject')?.value || 'Vous êtes invité à rejoindre FormaPro';
const ctaText = document.getElementById('tpl-cta')?.value || 'Créer mon compte →';
const signature = document.getElementById('tpl-signature')?.value || "L'équipe FormaPro";
const invLink = `https://formapro.fr/inscription?token=${Math.random().toString(36).slice(2)}&email=${encodeURIComponent(email)}`;
const body = buildEmailBody(email, formationText, msgExtra, invLink, ctaText, signature, expiry);
const res = await fetch('https://api.anthropic.com/v1/messages', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
model: 'claude-sonnet-4-20250514',
max_tokens: 1000,
messages: [{ role: 'user', content: `Tu es un assistant qui envoie des emails d'invitation via Gmail. Envoie un email d'invitation à ${email} avec le sujet "${subject}" et ce corps HTML. Utilise le compte Gmail connecté.\n\nCorps HTML:\n${body}\n\nRéponds juste: "Email envoyé à ${email}"` }],
mcp_servers: [{ type: 'url', url: 'https://gmail.mcp.claude.com/mcp', name: 'gmail-mcp' }]
})
});
if (res.ok) { sent++; addToTracking(email, formationText, expiry, 'pending'); }
else failed++;
} catch (err) {
console.error(err); failed++;
}
}
btn.disabled = false;
btn.innerHTML = '<svg width="14" height="14" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M2 4l16 6-16 6V4z"/></svg> Envoyer via Gmail';
if (sent > 0) {
showToast(`✓ ${sent} invitation${sent > 1 ? 's' : ''} envoyée${sent > 1 ? 's' : ''} via Gmail`);
// clear tags
[...invEmails].forEach(e => removeEmailTag(e));
updateBadge();
}
if (failed > 0) showToast(`⚠ ${failed} email(s) en échec`);
}
function buildEmailBody(email, formation, msgExtra, link, cta, sig, expiry) {
const subject_display = document.getElementById('tpl-headline')?.value || 'Rejoignez votre espace de formation';
const bodyTpl = (document.getElementById('tpl-body')?.value || '')
.replace('{{prenom}}', email.split('@')[0])
.replace('{{expediteur}}', 'Marie Lambert')
.replace('{{plateforme}}', 'FormaPro')
.replace(/{{#formation}}[\s\S]*?{{\/formation}}/g, formation ? `Vous êtes inscrit(e) à la formation : <strong>${formation}</strong>` : '');
const headerBg = document.getElementById('tpl-prev-header')?.style.background || '#0a0a0a';
return `<!DOCTYPE html><html><body style="font-family:sans-serif;max-width:560px;margin:0 auto">
<div style="background:${headerBg};padding:24px;text-align:center;border-radius:12px 12px 0 0">
<div style="font-size:22px;color:white;font-weight:600">FormaPro</div>
</div>
<div style="padding:28px;background:#fff;border:1px solid #e8e8e8;border-top:none;border-radius:0 0 12px 12px">
<h2 style="font-size:18px;margin-bottom:12px">${subject_display}</h2>
<p style="color:#6b6b6b;line-height:1.7">${bodyTpl.replace(/\n/g,'<br>')}${msgExtra ? `<br><br><em>${msgExtra}</em>` : ''}</p>
<div style="text-align:center;margin:24px 0">
<a href="${link}" style="display:inline-block;background:#0a0a0a;color:white;padding:12px 28px;border-radius:99px;text-decoration:none;font-weight:500">${cta}</a>
</div>
<p style="font-size:11px;color:#9b9b9b;text-align:center;border-top:1px solid #e8e8e8;padding-top:14px">
Ce lien expire dans ${expiry === '0' ? 'jamais' : expiry + ' jours'}. Si vous n'êtes pas concerné(e), ignorez cet email.<br>${sig}
</p>
</div>
</body></html>`;
}
// ── Tracking ──
const invitations = [
{ email: 'julie.dupont@email.fr', formation: 'Excel Avancé', sentDate: '20 mars 2026', expDate: '3 avr. 2026', status: 'accepted' },
{ email: 'antoine.martin@email.fr', formation: 'Management Niv. 1', sentDate: '20 mars 2026', expDate: '3 avr. 2026', status: 'accepted' },
{ email: 'sophie.bernard@email.fr', formation: 'IA & Productivité', sentDate: '21 mars 2026', expDate: '4 avr. 2026', status: 'accepted' },
{ email: 'lucas.roche@email.fr', formation: 'Excel Avancé', sentDate: '21 mars 2026', expDate: '4 avr. 2026', status: 'pending' },
{ email: 'emma.lefebvre@email.fr', formation: 'Prise de parole', sentDate: '22 mars 2026', expDate: '5 avr. 2026', status: 'pending' },
{ email: 'thomas.renard@email.fr', formation: 'Droit du travail', sentDate: '22 mars 2026', expDate: '5 avr. 2026', status: 'accepted' },
{ email: 'marc.durand@email.fr', formation: '', sentDate: '18 mars 2026', expDate: '1 avr. 2026', status: 'expired' },
{ email: 'chloe.petit@email.fr', formation: 'Cybersécurité', sentDate: '23 mars 2026', expDate: '6 avr. 2026', status: 'pending' },
{ email: 'hugo.simon@email.fr', formation: 'Excel Avancé', sentDate: '24 mars 2026', expDate: '7 avr. 2026', status: 'accepted' },
{ email: 'lea.moreau@email.fr', formation: 'Management Niv. 1', sentDate: '24 mars 2026', expDate: '7 avr. 2026', status: 'pending' },
{ email: 'felix.garcia@email.fr', formation: 'IA & Productivité', sentDate: '25 mars 2026', expDate: '8 avr. 2026', status: 'pending' },
{ email: 'alice.robin@email.fr', formation: 'Prise de parole', sentDate: '25 mars 2026', expDate: '8 avr. 2026', status: 'accepted' },
];
let currentFilter = 'all';
function filterInv(filter) {
currentFilter = filter;
['all','pending','accepted','expired'].forEach(f => {
const btn = document.getElementById('filt-' + f);
if (btn) { btn.style.background = f === filter ? 'var(--ink)' : ''; btn.style.color = f === filter ? 'white' : ''; btn.style.borderColor = f === filter ? 'var(--ink)' : ''; }
});
renderTrackTable();
}
function renderTrackTable() {
const body = document.getElementById('inv-track-body');
if (!body) return;
const filtered = currentFilter === 'all' ? invitations : invitations.filter(i => i.status === currentFilter);
body.innerHTML = filtered.map((inv, idx) => {
const statusMap = { accepted: ['tag-green','Acceptée','✓'], pending: ['tag-amber','En attente','⏳'], expired: ['tag-red','Expirée','✕'] };
const [tagCls, label, icon] = statusMap[inv.status] || ['tag-surface','Inconnu','?'];
return `<tr class="inv-track-row">
<td><span style="font-weight:500">${inv.email}</span></td>
<td>${inv.formation ? `<span class="tag tag-surface">${inv.formation}</span>` : '<span style="color:var(--muted-2)">—</span>'}</td>
<td style="color:var(--muted)">${inv.sentDate}</td>
<td style="color:var(--muted)">${inv.expDate}</td>
<td><span class="tag ${tagCls}">${icon} ${label}</span></td>
<td style="display:flex;gap:6px;padding:13px 14px">
${inv.status === 'pending' ? `<button class="btn btn-xs" onclick="relancerInv(${idx})">🔔 Relancer</button>` : ''}
${inv.status === 'expired' ? `<button class="btn btn-xs btn-solid" onclick="reinviter(${idx})">↺ Réinviter</button>` : ''}
${inv.status === 'accepted' ? `<span style="font-size:12px;color:var(--green)">Compte créé ✓</span>` : ''}
<button class="btn btn-xs" style="color:var(--red)" onclick="deleteInv(${idx})">✕</button>
</td>
</tr>`;
}).join('');
}
function addToTracking(email, formation, expiry, status) {
const now = new Date();
const expDate = new Date(now); expDate.setDate(expDate.getDate() + parseInt(expiry || 14));
invitations.push({
email, formation,
sentDate: now.toLocaleDateString('fr-FR', {day:'numeric',month:'short',year:'numeric'}),
expDate: expDate.toLocaleDateString('fr-FR', {day:'numeric',month:'short',year:'numeric'}),
status
});
updateBadge();
}
function updateBadge() {
const pending = invitations.filter(i => i.status === 'pending').length;
const badge = document.getElementById('inv-badge');
if (badge) badge.textContent = pending;
// update filter counts
const counts = { all: invitations.length, pending: invitations.filter(i=>i.status==='pending').length, accepted: invitations.filter(i=>i.status==='accepted').length, expired: invitations.filter(i=>i.status==='expired').length };
const labels = { all: `Toutes (${counts.all})`, pending: `En attente (${counts.pending})`, accepted: `Acceptées (${counts.accepted})`, expired: `Expirées (${counts.expired})` };
Object.entries(labels).forEach(([k,v]) => { const b = document.getElementById('filt-'+k); if(b) b.textContent = v; });
}
async function relancerInv(idx) {
const inv = invitations[idx];
showToast(`🔔 Relance envoyée à ${inv.email}`);
}
function reinviter(idx) {
const inv = invitations[idx];
switchInvTab('send');
invEmails.push(inv.email);
addEmailTag(inv.email);
updateInvCounter();
showToast(`↺ ${inv.email} ajouté aux destinataires`);
}
function deleteInv(idx) {
invitations.splice(idx, 1);
renderTrackTable(); updateBadge();
}
async function relancerTous() {
const pending = invitations.filter(i => i.status === 'pending');
if (pending.length === 0) { showToast('✓ Aucune invitation en attente'); return; }
showToast(`🔔 ${pending.length} relance(s) envoyée(s)`);
}
function exportInvCSV() {
const rows = ['Email,Formation,Envoyé le,Expire le,Statut'];
invitations.forEach(i => rows.push(`${i.email},${i.formation},${i.sentDate},${i.expDate},${i.status}`));
const blob = new Blob([rows.join('\n')], {type:'text/csv'});
const a = document.createElement('a'); a.href = URL.createObjectURL(blob);
a.download = 'invitations.csv'; a.click();
showToast('⬇ Export CSV invitations');
}
// ── Template preview ──
function updateEmailPreview() {
const headline = document.getElementById('tpl-headline')?.value || '';
const body = document.getElementById('tpl-body')?.value || '';
const cta = document.getElementById('tpl-cta')?.value || '';
const sig = document.getElementById('tpl-signature')?.value || '';
const parsed = body.replace('{{prenom}}', 'Jean').replace('{{expediteur}}', 'Marie Lambert').replace('{{plateforme}}', 'FormaPro').replace(/{{#formation}}([\s\S]*?){{\/formation}}/g, 'Vous êtes inscrit(e) à la formation : <strong>Excel Avancé & Power BI</strong>');
const h = document.getElementById('tpl-prev-headline'); if(h) h.textContent = headline;
const b = document.getElementById('tpl-prev-body'); if(b) b.innerHTML = parsed.replace(/\n/g,'<br>');
const c = document.getElementById('tpl-prev-cta'); if(c) c.textContent = cta;
const s = document.getElementById('tpl-prev-sig'); if(s) s.textContent = sig;
}
function setEmailHeaderColor(color) {
const h = document.getElementById('tpl-prev-header');
if (h) h.style.background = color;
}
function saveTemplate() { showToast('💾 Template sauvegardé'); }
async function sendTestEmail() { showToast('📨 Email test envoyé à marie.lambert@formapro.fr'); }
function previewInvEmail() { switchAdmin('invitations'); switchInvTab('template'); }
// ── CSV Import ──
function handleCSVDrop(e) {
e.preventDefault();
document.getElementById('csv-dropzone').style.borderColor = 'var(--border-2)';
document.getElementById('csv-dropzone').style.background = 'var(--white)';
const file = e.dataTransfer.files[0];
if (file && file.name.endsWith('.csv')) parseCSV(file);
else showToast('⚠ Fichier CSV attendu');
}
function handleCSVFile(input) {
const file = input.files[0];
if (file) parseCSV(file);
}
function parseCSV(file) {
const reader = new FileReader();
reader.onload = e => {
const lines = e.target.result.split('\n').filter(l => l.trim());
const headers = lines[0].toLowerCase().split(',').map(h => h.trim());
const rows = lines.slice(1).map(line => {
const vals = line.split(',').map(v => v.trim());
return { prenom: vals[0]||'', nom: vals[1]||'', email: vals[2]||'', formation: vals[3]||'' };
}).filter(r => r.email);
document.getElementById('csv-count').textContent = rows.length;
document.getElementById('csv-send-count').textContent = rows.length;
document.getElementById('csv-preview-section').style.display = 'block';
const tbody = document.getElementById('csv-preview-body');
tbody.innerHTML = rows.map((r, i) => {
const valid = isValidEmail(r.email);
return `<tr>
<td style="padding:11px 14px;border-bottom:1px solid var(--border)"><input type="checkbox" checked style="accent-color:var(--ink)" class="csv-row-check" onchange="updateCSVCount()"></td>
<td style="padding:11px 14px;border-bottom:1px solid var(--border);font-size:13px">${r.prenom}</td>
<td style="padding:11px 14px;border-bottom:1px solid var(--border);font-size:13px">${r.nom}</td>
<td style="padding:11px 14px;border-bottom:1px solid var(--border);font-size:13px">${r.email}</td>
<td style="padding:11px 14px;border-bottom:1px solid var(--border);font-size:13px">${r.formation || '<span style="color:var(--muted-2)">—</span>'}</td>
<td style="padding:11px 14px;border-bottom:1px solid var(--border)">${valid ? '<span class="tag tag-green">✓ Valide</span>' : '<span class="tag tag-red">✗ Email invalide</span>'}</td>
</tr>`;
}).join('');
};
reader.readAsText(file);
}
function toggleCSVAll(cb) {
document.querySelectorAll('.csv-row-check').forEach(c => c.checked = cb.checked);
updateCSVCount();
}
function updateCSVCount() {
const n = document.querySelectorAll('.csv-row-check:checked').length;
document.getElementById('csv-send-count').textContent = n;
}
function clearCSV() {
document.getElementById('csv-preview-section').style.display = 'none';
document.getElementById('csv-preview-body').innerHTML = '';
}
async function sendCSVInvitations() {
const checked = document.querySelectorAll('.csv-row-check:checked').length;
if (checked === 0) { showToast('⚠ Aucune ligne sélectionnée'); return; }
showToast(`✓ ${checked} invitation(s) envoyée(s) en lot via Gmail`);
clearCSV(); updateBadge();
}
function downloadCSVTemplate() {
const csv = 'prenom,nom,email,formation\nJean,Dupont,jean.dupont@email.fr,Excel Avancé & Power BI\nClaire,Martin,claire.martin@email.fr,Management Niveau 1';
const blob = new Blob([csv], {type:'text/csv'});
const a = document.createElement('a'); a.href = URL.createObjectURL(blob);
a.download = 'modele_invitations.csv'; a.click();
}
const style = document.createElement('style');
style.textContent = '@keyframes spin { from{transform:rotate(0deg)} to{transform:rotate(360deg)} }';
document.head.appendChild(style);
// ─────────────────────────────────────────
// INSCRIPTION FORM
// ─────────────────────────────────────────
let currentStep = 1;
const uploadedDocs = {};
let pwdValid = false, pwdMatch = false, consentsOk = false;
let step1Valid = false;
function openInscription() {
document.getElementById('inscription-overlay').style.display = 'block';
document.body.style.overflow = 'hidden';
goStep(1);
}
function closeInscription() {
document.getElementById('inscription-overlay').style.display = 'none';
document.body.style.overflow = '';
}
function goStep(n) {
if (n > currentStep) return; // only go back or stay
currentStep = n;
renderStep();
}
function renderStep() {
document.querySelectorAll('.reg-step').forEach((el, i) => {
el.style.display = (i + 1 === currentStep) ? 'block' : 'none';
});
// Update stepper dots
for (let i = 1; i <= 4; i++) {
const dot = document.getElementById('step-dot-' + i);
const circle = document.getElementById('step-circle-' + i);
const line = document.getElementById('step-line-' + i);
if (!dot) continue;
dot.className = 'step-item';
if (i < currentStep) { dot.classList.add('done'); if(circle) circle.textContent = ''; }
else if (i === currentStep) { dot.classList.add('active'); if(circle) circle.textContent = i; }
else { if(circle) circle.textContent = i; }
if (line && i < currentStep) line.classList.add('done');
else if (line) line.classList.remove('done');
}
}
function nextStep(from) {
if (from === 1) {
const prenom = document.getElementById('reg-prenom')?.value.trim();
const nom = document.getElementById('reg-nom')?.value.trim();
const tel = document.getElementById('reg-tel')?.value.trim();
const err = document.getElementById('step1-error');
if (!prenom || !nom || !tel) { if(err) err.style.display = 'block'; return; }
if(err) err.style.display = 'none';
currentStep = 2;
} else if (from === 2) {
if (!pwdValid || !pwdMatch || !consentsOk) return;
currentStep = 3;
} else if (from === 3) {
const err = document.getElementById('step3-error');
if (!uploadedDocs['doc-id'] || !uploadedDocs['doc-cv']) { if(err) err.style.display = 'block'; return; }
if(err) err.style.display = 'none';
// fill recap
const prenom = document.getElementById('reg-prenom')?.value || '';
const nom = document.getElementById('reg-nom')?.value || '';
const rn = document.getElementById('recap-nom'); if(rn) rn.textContent = prenom + ' ' + nom;
const docs = Object.keys(uploadedDocs).map(k => ({ 'doc-id':'Pièce d\'identité','doc-cv':'CV','doc-attestation':'Attestation' }[k])).filter(Boolean).join(', ');
const rd = document.getElementById('recap-docs'); if(rd) rd.textContent = docs || '—';
currentStep = 4;
// Add to admin queue
addDossier({ prenom, nom, email: 'jean.dupont@email.fr', formation: 'Excel Avancé & Power BI', docs: Object.keys(uploadedDocs).length, status: 'complet' });
}
renderStep();
document.querySelector('#inscription-overlay').scrollTo({ top: 0, behavior: 'smooth' });
}
function prevStep(from) {
currentStep = from - 1;
renderStep();
}
function validateStep1() {
const prenom = document.getElementById('reg-prenom')?.value.trim();
const nom = document.getElementById('reg-nom')?.value.trim();
const tel = document.getElementById('reg-tel')?.value.trim();
step1Valid = !!(prenom && nom && tel);
const err = document.getElementById('step1-error');
if (step1Valid && err) err.style.display = 'none';
}
function checkPassword() {
const pwd = document.getElementById('reg-pwd')?.value || '';
const rules = {
'rule-len': pwd.length >= 8,
'rule-upper': /[A-Z]/.test(pwd),
'rule-num': /[0-9]/.test(pwd),
'rule-special': /[^a-zA-Z0-9]/.test(pwd)
};
let score = Object.values(rules).filter(Boolean).length;
Object.entries(rules).forEach(([id, ok]) => {
const el = document.getElementById(id);
if (el) { el.style.color = ok ? 'var(--green)' : 'var(--muted-2)'; el.textContent = (ok ? '✓ ' : '○ ') + el.textContent.replace(/^[✓○] /, ''); }
});
const bars = ['pb1','pb2','pb3','pb4'];
const colors = ['var(--red)','var(--amber)','var(--amber)','var(--green)'];
bars.forEach((id, i) => {
const el = document.getElementById(id);
if (el) el.style.background = i < score ? colors[score - 1] : 'var(--border)';
});
const labels = ['','Trop faible','Faible','Bien','Fort ✓'];
const lbl = document.getElementById('pwd-strength-label');
if (lbl) { lbl.textContent = labels[score] || 'Entrez votre mot de passe'; lbl.style.color = score === 4 ? 'var(--green)' : score >= 2 ? 'var(--amber)' : 'var(--red)'; }
pwdValid = score >= 3;
checkPwdMatch();
updateStep2Btn();
}
function checkPwdMatch() {
const pwd = document.getElementById('reg-pwd')?.value || '';
const pwd2 = document.getElementById('reg-pwd2')?.value || '';
const msg = document.getElementById('pwd-match-msg');
if (!pwd2) { if(msg) msg.style.display = 'none'; pwdMatch = false; updateStep2Btn(); return; }
pwdMatch = pwd === pwd2;
if (msg) {
msg.style.display = 'block';
msg.textContent = pwdMatch ? '✓ Les mots de passe correspondent' : '✗ Les mots de passe ne correspondent pas';
msg.style.color = pwdMatch ? 'var(--green)' : 'var(--red)';
}
updateStep2Btn();
}
function checkConsents() {
const cgu = document.getElementById('cgu-check')?.checked;
const rgpd = document.getElementById('rgpd-check')?.checked;
consentsOk = !!(cgu && rgpd);
updateStep2Btn();
}
function updateStep2Btn() {
const btn = document.getElementById('btn-step2');
if (btn) btn.disabled = !(pwdValid && pwdMatch && consentsOk);
}
function togglePwd(id, btn) {
const input = document.getElementById(id);
if (!input) return;
input.type = input.type === 'password' ? 'text' : 'password';
btn.textContent = input.type === 'password' ? '👁' : '🙈';
}
function handleDocUpload(input, cardId, statusId) {
const file = input.files[0];
if (!file) return;
const card = document.getElementById(cardId);
const status = document.getElementById(statusId);
if (card) card.classList.add('uploaded');
if (status) {
status.style.display = 'block';
status.textContent = `✓ ${file.name} (${(file.size/1024).toFixed(0)} Ko) — En attente de validation`;
}
uploadedDocs[cardId] = { name: file.name, size: file.size };
const err = document.getElementById('step3-error');
if (uploadedDocs['doc-id'] && uploadedDocs['doc-cv'] && err) err.style.display = 'none';
}
// ─────────────────────────────────────────
// DOSSIERS DE VALIDATION ADMIN
// ─────────────────────────────────────────
const dossiers = [
{ id:1, prenom:'Jean', nom:'Dupont', email:'jean.dupont@email.fr', formation:'Excel Avancé & Power BI', role:'Apprenant', soumisLe:'25 mars 2026 · 09h14', docs:2, status:'complet', urgent:false, note:'' },
{ id:2, prenom:'Claire', nom:'Martin', email:'claire.martin@email.fr', formation:'Management Niveau 1', role:'Apprenant', soumisLe:'25 mars 2026 · 11h32', docs:2, status:'complet', urgent:false, note:'' },
{ id:3, prenom:'Paul', nom:'Bernard', email:'paul.bernard@email.fr', formation:'IA & Productivité', role:'Apprenant', soumisLe:'24 mars 2026 · 16h45', docs:1, status:'incomplet', urgent:true, note:'CV manquant' },
{ id:4, prenom:'Sophie', nom:'Renard', email:'sophie.renard@email.fr', formation:'Cybersécurité', role:'Apprenant', soumisLe:'24 mars 2026 · 14h20', docs:1, status:'incomplet', urgent:false, note:'Pièce d\'identité manquante' },
{ id:5, prenom:'Hugo', nom:'Simon', email:'hugo.simon@email.fr', formation:'Excel Avancé & Power BI', role:'Apprenant', soumisLe:'23 mars 2026 · 10h05', docs:3, status:'valide', urgent:false, note:'' },
{ id:6, prenom:'Léa', nom:'Moreau', email:'lea.moreau@email.fr', formation:'Prise de parole', role:'Apprenant', soumisLe:'22 mars 2026 · 15h50', docs:2, status:'valide', urgent:false, note:'' },
{ id:7, prenom:'Marc', nom:'Durand', email:'marc.durand@email.fr', formation:'Droit du travail', role:'Apprenant', soumisLe:'20 mars 2026 · 09h00', docs:2, status:'refuse', urgent:false, note:'Dossier non conforme' },
];
let currentDossierFilter = 'all';
function addDossier(d) {
const now = new Date();
dossiers.unshift({
id: Date.now(), prenom: d.prenom, nom: d.nom, email: d.email,
formation: d.formation, role: 'Apprenant',
soumisLe: now.toLocaleDateString('fr-FR',{day:'numeric',month:'long',year:'numeric'}) + ' · ' + now.toLocaleTimeString('fr-FR',{hour:'2-digit',minute:'2-digit'}),
docs: d.docs, status: d.status, urgent: false, note: ''
});
renderDossiers();
updateDossierKpis();
const badge = document.getElementById('badge-inscriptions');
if (badge) badge.textContent = dossiers.filter(d => d.status === 'pending' || d.status === 'complet').length;
}
function filterDossiers(filter) {
currentDossierFilter = filter;
['all','pending','complet','incomplet','valide','refuse'].forEach(f => {
const el = document.getElementById('itab-' + f);
if (el) { el.className = 'sess-tab' + (f === filter ? ' active' : ''); }
});
renderDossiers();
}
function renderDossiers() {
const container = document.getElementById('dossiers-list');
if (!container) return;
const filtered = currentDossierFilter === 'all' ? dossiers : dossiers.filter(d => d.status === currentDossierFilter);
const statusCfg = {
complet: { tag:'tag-blue', icon:'📋', label:'Complet', actions: true },
incomplet: { tag:'tag-amber', icon:'⚠️', label:'Incomplet', actions: false },
pending: { tag:'tag-amber', icon:'⏳', label:'En attente',actions: true },
valide: { tag:'tag-green', icon:'✓', label:'Validé', actions: false },
refuse: { tag:'tag-red', icon:'✗', label:'Refusé', actions: false },
};
container.innerHTML = filtered.map(d => {
const cfg = statusCfg[d.status] || statusCfg.pending;
const initials = (d.prenom[0] || '') + (d.nom[0] || '');
return `<div class="dossier-card${d.urgent ? ' urgent' : ''}" onclick="openDrawer(${d.id})">
<div style="display:flex;align-items:center;gap:16px">
<div style="width:44px;height:44px;border-radius:50%;background:var(--ink);color:white;display:flex;align-items:center;justify-content:center;font-size:15px;font-weight:600;flex-shrink:0">${initials}</div>
<div style="flex:1">
<div style="display:flex;align-items:center;gap:10px;margin-bottom:4px">
<span style="font-size:15px;font-weight:500">${d.prenom} ${d.nom}</span>
<span class="tag ${cfg.tag}">${cfg.icon} ${cfg.label}</span>
${d.urgent ? '<span class="tag tag-red">🔴 Urgent</span>' : ''}
</div>
<div style="font-size:12.5px;color:var(--muted)">
${d.email} · <span class="tag tag-surface" style="font-size:11px">${d.formation}</span> · ${d.docs} document${d.docs > 1 ? 's' : ''} joint${d.docs > 1 ? 's' : ''}
</div>
${d.note ? `<div style="font-size:12px;color:var(--amber);margin-top:4px">⚠ ${d.note}</div>` : ''}
</div>
<div style="text-align:right;flex-shrink:0">
<div style="font-size:11.5px;color:var(--muted-2);margin-bottom:8px">${d.soumisLe}</div>
${cfg.actions ? `
<div style="display:flex;gap:6px;justify-content:flex-end">
<button class="btn btn-xs" onclick="event.stopPropagation();refuserDossier(${d.id})">✗ Refuser</button>
<button class="btn btn-xs btn-solid" onclick="event.stopPropagation();validerDossier(${d.id})">✓ Valider</button>
</div>` : d.status === 'valide' ? '<span style="font-size:12px;color:var(--green)">✓ Accès activé</span>' : '<span style="font-size:12px;color:var(--red)">✗ Accès refusé</span>'}
</div>
</div>
</div>`;
}).join('') || '<div style="padding:40px;text-align:center;color:var(--muted);font-size:13px">Aucun dossier dans cette catégorie.</div>';
}
function updateDossierKpis() {
const pending = dossiers.filter(d => d.status === 'pending' || d.status === 'complet').length;
const valides = dossiers.filter(d => d.status === 'valide').length;
const refuses = dossiers.filter(d => d.status === 'refuse').length;
const incomplets = dossiers.filter(d => d.status === 'incomplet').length;
const set = (id, v) => { const el = document.getElementById(id); if(el) el.textContent = v; };
set('kpi-attente', pending); set('kpi-valides', valides); set('kpi-refus', refuses); set('kpi-incomplets', incomplets);
// update filter tabs
const counts = { all: dossiers.length, pending: pending, complet: dossiers.filter(d=>d.status==='complet').length, incomplet: incomplets, valide: valides, refuse: refuses };
const labels = { all:`Tous (${dossiers.length})`, pending:`En attente (${pending})`, complet:`Complets (${counts.complet})`, incomplet:`Incomplets (${incomplets})`, valide:`Validés (${valides})`, refuse:`Refusés (${refuses})` };
Object.entries(labels).forEach(([k,v]) => { const el = document.getElementById('itab-'+k); if(el) el.textContent = v; });
const badge = document.getElementById('badge-inscriptions'); if(badge) badge.textContent = pending;
}
function validerDossier(id) {
const d = dossiers.find(x => x.id === id); if(!d) return;
d.status = 'valide';
renderDossiers(); updateDossierKpis();
closeDrawer();
showToast(`✓ ${d.prenom} ${d.nom} — Accès activé`);
}
function refuserDossier(id) {
const d = dossiers.find(x => x.id === id); if(!d) return;
const raison = prompt(`Motif de refus pour ${d.prenom} ${d.nom} (sera inclus dans l'email) :`, 'Dossier incomplet ou non conforme');
if (raison === null) return;
d.status = 'refuse'; d.note = raison;
renderDossiers(); updateDossierKpis();
closeDrawer();
showToast(`✗ ${d.prenom} ${d.nom} — Dossier refusé`);
}
function validerTous() {
const pending = dossiers.filter(d => d.status === 'complet');
if (!pending.length) { showToast('✓ Aucun dossier complet en attente'); return; }
if (!confirm(`Valider les ${pending.length} dossiers complets ?`)) return;
pending.forEach(d => d.status = 'valide');
renderDossiers(); updateDossierKpis();
showToast(`✓ ${pending.length} dossiers validés — accès activés`);
}
function openDrawer(id) {
const d = dossiers.find(x => x.id === id); if(!d) return;
const drawer = document.getElementById('dossier-drawer');
const backdrop = document.getElementById('drawer-backdrop');
const title = document.getElementById('drawer-title');
const body = document.getElementById('drawer-body');
if (!drawer) return;
title.textContent = `${d.prenom} ${d.nom}`;
const statusCfg = { complet:['tag-blue','Complet'], incomplet:['tag-amber','Incomplet'], pending:['tag-amber','En attente'], valide:['tag-green','Validé'], refuse:['tag-red','Refusé'] };
const [tagCls, label] = statusCfg[d.status] || ['tag-surface','Inconnu'];
body.innerHTML = `
<div style="display:flex;align-items:center;gap:12px;margin-bottom:24px">
<div style="width:52px;height:52px;border-radius:50%;background:var(--ink);color:white;display:flex;align-items:center;justify-content:center;font-size:17px;font-weight:600">${d.prenom[0]}${d.nom[0]}</div>
<div>
<div style="font-size:15px;font-weight:500">${d.prenom} ${d.nom}</div>
<div style="font-size:12.5px;color:var(--muted)">${d.email}</div>
<span class="tag ${tagCls}" style="margin-top:6px;display:inline-flex">${label}</span>
</div>
</div>
<div style="display:flex;flex-direction:column;gap:10px;margin-bottom:24px">
<div style="display:flex;justify-content:space-between;padding:10px 0;border-bottom:1px solid var(--border)"><span style="font-size:12px;color:var(--muted)">Formation</span><span style="font-size:13px;font-weight:500">${d.formation}</span></div>
<div style="display:flex;justify-content:space-between;padding:10px 0;border-bottom:1px solid var(--border)"><span style="font-size:12px;color:var(--muted)">Rôle</span><span style="font-size:13px">${d.role}</span></div>
<div style="display:flex;justify-content:space-between;padding:10px 0;border-bottom:1px solid var(--border)"><span style="font-size:12px;color:var(--muted)">Soumis le</span><span style="font-size:13px">${d.soumisLe}</span></div>
<div style="display:flex;justify-content:space-between;padding:10px 0;border-bottom:1px solid var(--border)"><span style="font-size:12px;color:var(--muted)">Documents</span><span style="font-size:13px">${d.docs} fichier${d.docs>1?'s':''}</span></div>
${d.note ? `<div style="display:flex;justify-content:space-between;padding:10px 0;border-bottom:1px solid var(--border)"><span style="font-size:12px;color:var(--muted)">Note</span><span style="font-size:13px;color:var(--amber)">${d.note}</span></div>` : ''}
</div>
<div style="margin-bottom:24px">
<div style="font-size:11px;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:.07em;margin-bottom:12px">Documents joints</div>
${d.docs >= 1 ? `<div style="display:flex;align-items:center;gap:10px;padding:10px 14px;background:var(--surface);border-radius:8px;margin-bottom:8px"><span style="font-size:18px">🪪</span><div><div style="font-size:13px;font-weight:500">Pièce d'identité</div><div style="font-size:11.5px;color:var(--muted)">PDF · ${(Math.random()*2+0.5).toFixed(1)} Mo</div></div><button class="btn btn-xs" style="margin-left:auto">Voir</button></div>` : ''}
${d.docs >= 2 ? `<div style="display:flex;align-items:center;gap:10px;padding:10px 14px;background:var(--surface);border-radius:8px;margin-bottom:8px"><span style="font-size:18px">📄</span><div><div style="font-size:13px;font-weight:500">CV</div><div style="font-size:11.5px;color:var(--muted)">PDF · ${(Math.random()*1+0.3).toFixed(1)} Mo</div></div><button class="btn btn-xs" style="margin-left:auto">Voir</button></div>` : ''}
${d.docs >= 3 ? `<div style="display:flex;align-items:center;gap:10px;padding:10px 14px;background:var(--surface);border-radius:8px"><span style="font-size:18px">📋</span><div><div style="font-size:13px;font-weight:500">Attestation employeur</div><div style="font-size:11.5px;color:var(--muted)">PDF · ${(Math.random()*1+0.2).toFixed(1)} Mo</div></div><button class="btn btn-xs" style="margin-left:auto">Voir</button></div>` : ''}
${d.status === 'incomplet' ? `<div style="margin-top:10px;padding:10px 14px;background:var(--amber-bg);border-left:3px solid var(--amber);border-radius:6px;font-size:12.5px;color:var(--amber)">⚠ ${d.note || 'Document(s) manquant(s)'}</div>` : ''}
</div>
<div style="margin-bottom:20px">
<div style="font-size:11px;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:.07em;margin-bottom:10px">Note interne</div>
<textarea class="field-input" rows="3" placeholder="Ajouter une note visible uniquement par l'administration…" style="margin-bottom:10px">${d.note||''}</textarea>
</div>
${d.status === 'complet' || d.status === 'pending' ? `
<div style="display:flex;flex-direction:column;gap:8px">
<button class="btn btn-solid" style="justify-content:center" onclick="validerDossier(${d.id})">✓ Valider — Activer l'accès</button>
<button class="btn" style="justify-content:center;color:var(--red)" onclick="refuserDossier(${d.id})">✗ Refuser le dossier</button>
<button class="btn btn-sm" style="justify-content:center" onclick="showToast('📧 Email de relance envoyé')">📧 Demander documents manquants</button>
</div>` : d.status === 'valide' ? `<div style="padding:14px;background:var(--green-bg);border-radius:var(--radius-sm);text-align:center;font-size:13px;color:var(--green)">✓ Compte actif — accès à la plateforme activé</div>` : `<div style="padding:14px;background:var(--red-bg);border-radius:var(--radius-sm);text-align:center;font-size:13px;color:var(--red)">✗ Dossier refusé</div>`}
`;
drawer.style.display = 'block';
backdrop.style.display = 'block';
}
function closeDrawer() {
const drawer = document.getElementById('dossier-drawer');
const backdrop = document.getElementById('drawer-backdrop');
if(drawer) drawer.style.display = 'none';
if(backdrop) backdrop.style.display = 'none';
}
// dossiers init — wired in main init
// ─────────────────────────────────────────
// UTILISATEURS & RÔLES MODULE
// ─────────────────────────────────────────
const utilisateurs = [
{ id:1, prenom:'Marie', nom:'Lambert', email:'m.lambert@formapro.fr', role:'admin', status:'actif', lastSeen:'Aujourd\'hui', avatar:'ML', formation:'' },
{ id:2, prenom:'Thomas', nom:'Girard', email:'t.girard@formapro.fr', role:'admin', status:'actif', lastSeen:'Hier', avatar:'TG', formation:'' },
{ id:3, prenom:'Pierre', nom:'Leblanc', email:'p.leblanc@formapro.fr', role:'formateur', status:'actif', lastSeen:'Aujourd\'hui', avatar:'PL', formation:'Excel Avancé & Power BI' },
{ id:4, prenom:'Anne', nom:'Moreau', email:'a.moreau@formapro.fr', role:'formateur', status:'actif', lastSeen:'22 mars', avatar:'AM', formation:'Management Niveau 1' },
{ id:5, prenom:'Laura', nom:'Simon', email:'l.simon@formapro.fr', role:'formateur', status:'actif', lastSeen:'24 mars', avatar:'LS', formation:'IA & Productivité' },
{ id:6, prenom:'Charles', nom:'Faure', email:'c.faure@formapro.fr', role:'formateur', status:'actif', lastSeen:'23 mars', avatar:'CF', formation:'Droit du travail' },
{ id:7, prenom:'Julie', nom:'Dupont', email:'j.dupont@email.fr', role:'apprenant', status:'actif', lastSeen:'Aujourd\'hui', avatar:'JD', formation:'Excel Avancé & Power BI' },
{ id:8, prenom:'Antoine', nom:'Martin', email:'a.martin@email.fr', role:'apprenant', status:'actif', lastSeen:'Hier', avatar:'AM', formation:'Management Niveau 1' },
{ id:9, prenom:'Sophie', nom:'Bernard', email:'s.bernard@email.fr', role:'apprenant', status:'actif', lastSeen:'23 mars', avatar:'SB', formation:'IA & Productivité' },
{ id:10, prenom:'Lucas', nom:'Roche', email:'l.roche@email.fr', role:'apprenant', status:'inactif', lastSeen:'11 mars', avatar:'LR', formation:'Excel Avancé & Power BI' },
{ id:11, prenom:'Emma', nom:'Lefebvre', email:'e.lefebvre@email.fr', role:'apprenant', status:'actif', lastSeen:'24 mars', avatar:'EL', formation:'Prise de parole' },
{ id:12, prenom:'Thomas', nom:'Renard', email:'t.renard@email.fr', role:'apprenant', status:'actif', lastSeen:'Hier', avatar:'TR', formation:'Excel Avancé & Power BI' },
{ id:13, prenom:'Hugo', nom:'Simon', email:'h.simon@email.fr', role:'apprenant', status:'actif', lastSeen:'25 mars', avatar:'HS', formation:'Excel Avancé & Power BI' },
{ id:14, prenom:'Léa', nom:'Moreau', email:'l.moreau@email.fr', role:'apprenant', status:'actif', lastSeen:'24 mars', avatar:'LM', formation:'Management Niveau 1' },
{ id:15, prenom:'Félix', nom:'Garcia', email:'f.garcia@email.fr', role:'apprenant', status:'actif', lastSeen:'25 mars', avatar:'FG', formation:'IA & Productivité' },
{ id:16, prenom:'Alice', nom:'Robin', email:'a.robin@email.fr', role:'apprenant', status:'actif', lastSeen:'23 mars', avatar:'AR', formation:'Prise de parole' },
{ id:17, prenom:'Marc', nom:'Durand', email:'m.durand@email.fr', role:'apprenant', status:'suspended','lastSeen':'18 mars', avatar:'MD', formation:'' },
{ id:18, prenom:'Chloé', nom:'Petit', email:'c.petit@email.fr', role:'apprenant', status:'actif', lastSeen:'25 mars', avatar:'CP', formation:'Cybersécurité' },
];
const avatarColors = { admin:'#0a0a0a', formateur:'#1d4ed8', apprenant:'#4b5563', suspended:'#9ca3af' };
const permissions = [
{ section:'Navigation', key:'voir_dashboard', label:'Voir le tableau de bord', admin:true, formateur:true, apprenant:false },
{ section:'Navigation', key:'voir_catalogue', label:'Voir le catalogue', admin:true, formateur:true, apprenant:true },
{ section:'Navigation', key:'voir_analytics', label:'Accéder aux analytics', admin:true, formateur:true, apprenant:false },
{ section:'Navigation', key:'voir_admin', label:'Accéder à l\'administration', admin:true, formateur:false, apprenant:false },
{ section:'Formations', key:'creer_formation', label:'Créer une formation', admin:true, formateur:false, apprenant:false },
{ section:'Formations', key:'editer_formation', label:'Modifier une formation', admin:true, formateur:true, apprenant:false },
{ section:'Formations', key:'suppr_formation', label:'Supprimer une formation', admin:true, formateur:false, apprenant:false },
{ section:'Sessions', key:'creer_session', label:'Créer une session', admin:true, formateur:true, apprenant:false },
{ section:'Sessions', key:'voir_emarg', label:'Gérer l\'émargement', admin:true, formateur:true, apprenant:false },
{ section:'Sessions', key:'inscrire_session', label:'S\'inscrire à une session', admin:true, formateur:false, apprenant:true },
{ section:'Apprenants', key:'voir_apprenants', label:'Voir la liste des apprenants', admin:true, formateur:true, apprenant:false },
{ section:'Apprenants', key:'noter_apprenants', label:'Évaluer les apprenants', admin:true, formateur:true, apprenant:false },
{ section:'Apprenants', key:'valider_inscription',label:'Valider les inscriptions', admin:true, formateur:false, apprenant:false },
{ section:'Données', key:'export_data', label:'Exporter les données', admin:true, formateur:true, apprenant:false },
{ section:'Données', key:'voir_certifs', label:'Voir les certifications', admin:true, formateur:true, apprenant:true },
{ section:'Données', key:'telecharger_certif',label:'Télécharger ses certifications',admin:true, formateur:false, apprenant:true },
{ section:'Compte', key:'changer_pwd', label:'Changer son mot de passe', admin:true, formateur:true, apprenant:true },
{ section:'Compte', key:'gerer_utilisateurs',label:'Gérer les utilisateurs', admin:true, formateur:false, apprenant:false },
];
const auditLog = [
{ type:'role', color:'#1d4ed8', time:'Aujourd\'hui 11:42', actor:'Marie Lambert', action:'a modifié le rôle de', target:'Lucas Roche', detail:'Apprenant → Suspendu' },
{ type:'user', color:'#1a7f4f', time:'Aujourd\'hui 10:15', actor:'Marie Lambert', action:'a créé le compte de', target:'Chloé Petit', detail:'Rôle: Apprenant' },
{ type:'login', color:'#6b7280', time:'Aujourd\'hui 09:03', actor:'Pierre Leblanc',action:'s\'est connecté', target:'', detail:'IP: 192.168.1.42' },
{ type:'perm', color:'#b45309', time:'Hier 17:30', actor:'Marie Lambert', action:'a modifié les permissions', target:'Formateur', detail:'+ noter_apprenants' },
{ type:'user', color:'#1a7f4f', time:'Hier 14:22', actor:'Thomas Girard', action:'a réactivé le compte de', target:'Hugo Simon', detail:'' },
{ type:'login', color:'#6b7280', time:'Hier 09:15', actor:'Anne Moreau', action:'s\'est connectée', target:'', detail:'IP: 10.0.0.8' },
{ type:'role', color:'#1d4ed8', time:'24 mars 16:10', actor:'Marie Lambert', action:'a promu', target:'Laura Simon', detail:'Apprenant → Formateur' },
{ type:'data', color:'#7c3aed', time:'24 mars 11:00', actor:'Marie Lambert', action:'a exporté les données de', target:'Analytics Mars','detail':'CSV · 2 340 lignes' },
{ type:'user', color:'#c0392b', time:'23 mars 15:45', actor:'Marie Lambert', action:'a suspendu le compte de', target:'Marc Durand', detail:'Motif: Dossier non conforme' },
{ type:'login', color:'#6b7280', time:'23 mars 09:00', actor:'Julie Dupont', action:'s\'est connectée', target:'', detail:'IP: 82.64.12.5' },
];
let currentUtilTab = 'users';
let selectedRole = 'admin';
let currentAuditFilter = '';
function switchUtilTab(tab) {
currentUtilTab = tab;
['users','roles','matrix','audit'].forEach(t => {
const el = document.getElementById('upanel-' + t);
const tab_el = document.getElementById('utab-' + t);
if (el) el.style.display = t === tab ? 'block' : 'none';
if (tab_el) { tab_el.className = 'sess-tab' + (t === tab ? ' active' : ''); }
});
if (tab === 'matrix') renderMatrix();
if (tab === 'audit') renderAudit();
if (tab === 'roles') renderRolePerms(selectedRole);
}
// ── USERS LIST ──
function filterUsers() {
const q = (document.getElementById('user-search')?.value || '').toLowerCase();
const role = document.getElementById('role-filter')?.value || '';
renderUsers(utilisateurs.filter(u =>
(!q || (u.prenom + ' ' + u.nom + ' ' + u.email).toLowerCase().includes(q)) &&
(!role || u.role === role || (role === 'suspended' && u.status === 'suspended'))
));
}
function renderUsers(list) {
list = list || utilisateurs;
const container = document.getElementById('users-list');
const empty = document.getElementById('users-empty');
if (!container) return;
if (!list.length) { container.innerHTML = ''; empty.style.display = 'block'; return; }
empty.style.display = 'none';
const roleLabel = { admin:'Admin', formateur:'Formateur', apprenant:'Apprenant' };
const roleCls = { admin:'role-admin', formateur:'role-formateur', apprenant:'role-apprenant' };
container.innerHTML = list.map(u => {
const isSuspended = u.status === 'suspended';
const badgeCls = isSuspended ? 'role-suspended' : (roleCls[u.role] || 'role-apprenant');
const badgeTxt = isSuspended ? 'Suspendu' : (roleLabel[u.role] || u.role);
const color = isSuspended ? '#9ca3af' : (avatarColors[u.role] || '#4b5563');
return `<div class="user-row" onclick="openUtilDrawer(${u.id})">
<input type="checkbox" class="user-check" data-id="${u.id}" style="accent-color:var(--ink);flex-shrink:0" onclick="event.stopPropagation();updateBulkBar()" >
<div class="user-avatar-sm" style="background:${color}">${u.avatar}</div>
<div style="flex:1;min-width:0">
<div style="font-size:13.5px;font-weight:500;margin-bottom:2px">${u.prenom} ${u.nom}</div>
<div style="font-size:12px;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis">${u.email}${u.formation ? ' · ' + u.formation : ''}</div>
</div>
<span class="role-badge ${badgeCls}" style="flex-shrink:0;margin-right:24px">${badgeTxt}</span>
<span style="font-size:12px;color:var(--muted-2);white-space:nowrap;margin-right:20px">${u.lastSeen}</span>
<div style="display:flex;gap:6px;flex-shrink:0" onclick="event.stopPropagation()">
<button class="btn btn-xs" onclick="openUtilDrawer(${u.id})">Éditer</button>
${isSuspended
? `<button class="btn btn-xs" style="color:var(--green)" onclick="unsuspendUser(${u.id})">Réactiver</button>`
: `<button class="btn btn-xs" style="color:var(--red)" onclick="suspendUser(${u.id})">Suspendre</button>`}
</div>
</div>`;
}).join('');
updateKpis();
}
function updateKpis() {
const set = (id,v) => { const el=document.getElementById(id); if(el) el.textContent=v; };
set('u-total', utilisateurs.length);
set('u-admins', utilisateurs.filter(u=>u.role==='admin').length);
set('u-formateurs',utilisateurs.filter(u=>u.role==='formateur').length);
set('u-apprenants',utilisateurs.filter(u=>u.role==='apprenant').length);
set('count-admin', utilisateurs.filter(u=>u.role==='admin').length + ' utilisateurs');
set('count-formateur', utilisateurs.filter(u=>u.role==='formateur').length + ' utilisateurs');
set('count-apprenant', utilisateurs.filter(u=>u.role==='apprenant').length + ' utilisateurs');
}
function toggleSelectAll(cb) {
document.querySelectorAll('.user-check').forEach(c => c.checked = cb.checked);
updateBulkBar();
}
function updateBulkBar() {
const checked = document.querySelectorAll('.user-check:checked');
const bar = document.getElementById('bulk-bar');
const cnt = document.getElementById('bulk-count');
if (!bar) return;
if (checked.length > 0) {
bar.style.display = 'flex';
cnt.textContent = checked.length + ' utilisateur' + (checked.length > 1 ? 's' : '') + ' sélectionné' + (checked.length > 1 ? 's' : '');
} else { bar.style.display = 'none'; }
}
function getSelectedIds() {
return [...document.querySelectorAll('.user-check:checked')].map(c => parseInt(c.dataset.id));
}
function bulkChangeRole() {
const ids = getSelectedIds(); if (!ids.length) return;
const role = prompt('Nouveau rôle (admin / formateur / apprenant) :');
if (!role || !['admin','formateur','apprenant'].includes(role)) return;
ids.forEach(id => { const u = utilisateurs.find(x=>x.id===id); if(u) { u.role = role; if(u.status==='suspended') u.status='actif'; } });
filterUsers(); addAuditEntry('role', '#1d4ed8', `a modifié le rôle de ${ids.length} utilisateur(s)`, '', `→ ${role}`);
showToast(`✓ Rôle mis à jour pour ${ids.length} utilisateur(s)`);
}
function bulkSuspend() {
const ids = getSelectedIds(); if (!ids.length) return;
ids.forEach(id => { const u = utilisateurs.find(x=>x.id===id); if(u && u.role !== 'admin') u.status = 'suspended'; });
filterUsers(); showToast(`⏸ ${ids.length} compte(s) suspendu(s)`);
}
function bulkUnsuspend() {
const ids = getSelectedIds(); if (!ids.length) return;
ids.forEach(id => { const u = utilisateurs.find(x=>x.id===id); if(u) u.status = 'actif'; });
filterUsers(); showToast(`▶ ${ids.length} compte(s) réactivé(s)`);
}
function suspendUser(id) {
const u = utilisateurs.find(x=>x.id===id); if(!u) return;
if (u.role === 'admin') { showToast('⚠ Impossible de suspendre un administrateur'); return; }
u.status = 'suspended';
filterUsers(); addAuditEntry('user','#c0392b','a suspendu le compte de', u.prenom+' '+u.nom, '');
showToast(`⏸ ${u.prenom} ${u.nom} suspendu`);
}
function unsuspendUser(id) {
const u = utilisateurs.find(x=>x.id===id); if(!u) return;
u.status = 'actif';
filterUsers(); addAuditEntry('user','#1a7f4f','a réactivé le compte de', u.prenom+' '+u.nom, '');
showToast(`▶ ${u.prenom} ${u.nom} réactivé`);
}
// ── USER DRAWER ──
function openUtilDrawer(id) {
const u = utilisateurs.find(x=>x.id===id); if(!u) return;
const color = u.status==='suspended' ? '#9ca3af' : (avatarColors[u.role]||'#4b5563');
const roleLabel = { admin:'Administrateur', formateur:'Formateur', apprenant:'Apprenant' };
document.getElementById('util-drawer-title').textContent = u.prenom + ' ' + u.nom;
document.getElementById('util-drawer-body').innerHTML = `
<div style="display:flex;flex-direction:column;align-items:center;text-align:center;padding-bottom:24px;border-bottom:1px solid var(--border);margin-bottom:24px">
<div style="width:64px;height:64px;border-radius:50%;background:${color};color:white;display:flex;align-items:center;justify-content:center;font-size:20px;font-weight:600;margin-bottom:12px">${u.avatar}</div>
<div style="font-size:16px;font-weight:500;margin-bottom:3px">${u.prenom} ${u.nom}</div>
<div style="font-size:13px;color:var(--muted);margin-bottom:10px">${u.email}</div>
<span class="role-badge ${u.status==='suspended'?'role-suspended':(u.role==='admin'?'role-admin':u.role==='formateur'?'role-formateur':'role-apprenant')}">${u.status==='suspended'?'Suspendu':(roleLabel[u.role]||u.role)}</span>
</div>
<div style="display:flex;flex-direction:column;gap:14px;margin-bottom:24px">
<div class="field-group"><div class="field-label">Prénom</div><input class="field-input" id="edit-prenom" value="${u.prenom}"></div>
<div class="field-group"><div class="field-label">Nom</div><input class="field-input" id="edit-nom" value="${u.nom}"></div>
<div class="field-group"><div class="field-label">Email</div><input class="field-input" id="edit-email" value="${u.email}"></div>
<div class="field-group"><div class="field-label">Rôle</div>
<select class="field-input" id="edit-role">
<option value="apprenant" ${u.role==='apprenant'?'selected':''}>Apprenant</option>
<option value="formateur" ${u.role==='formateur'?'selected':''}>Formateur</option>
<option value="admin" ${u.role==='admin'?'selected':''}>Administrateur</option>
</select>
</div>
<div class="field-group"><div class="field-label">Dernière activité</div><input class="field-input" value="${u.lastSeen}" readonly style="background:var(--surface-2);color:var(--muted)"></div>
</div>
<div style="display:flex;flex-direction:column;gap:8px">
<button class="btn btn-solid" style="justify-content:center" onclick="saveUserEdit(${u.id})">💾 Enregistrer</button>
${u.status==='suspended'
? `<button class="btn" style="justify-content:center;color:var(--green)" onclick="unsuspendUser(${u.id});closeUtilDrawer()">▶ Réactiver le compte</button>`
: `<button class="btn" style="justify-content:center;color:var(--red)" onclick="suspendUser(${u.id});closeUtilDrawer()">⏸ Suspendre le compte</button>`}
<button class="btn btn-sm" style="justify-content:center" onclick="showToast('📧 Email de réinitialisation envoyé')">📧 Réinitialiser le mot de passe</button>
</div>`;
document.getElementById('util-drawer').style.display = 'block';
document.getElementById('util-backdrop').style.display = 'block';
}
function saveUserEdit(id) {
const u = utilisateurs.find(x=>x.id===id); if(!u) return;
const oldRole = u.role;
u.prenom = document.getElementById('edit-prenom')?.value || u.prenom;
u.nom = document.getElementById('edit-nom')?.value || u.nom;
u.email = document.getElementById('edit-email')?.value || u.email;
u.role = document.getElementById('edit-role')?.value || u.role;
if (oldRole !== u.role) addAuditEntry('role','#1d4ed8','a modifié le rôle de',u.prenom+' '+u.nom, oldRole+' → '+u.role);
filterUsers(); closeUtilDrawer();
showToast(`✓ ${u.prenom} ${u.nom} mis à jour`);
}
function closeUtilDrawer() {
document.getElementById('util-drawer').style.display = 'none';
document.getElementById('util-backdrop').style.display = 'none';
}
// ── ADD USER MODAL ──
function openAddUser() {
const modal = document.getElementById('add-user-modal');
if (modal) { modal.style.display = 'flex'; }
}
function closeAddUser() {
const modal = document.getElementById('add-user-modal');
if (modal) modal.style.display = 'none';
}
function confirmAddUser() {
const prenom = document.getElementById('add-prenom')?.value.trim();
const nom = document.getElementById('add-nom')?.value.trim();
const email = document.getElementById('add-email')?.value.trim();
const role = document.getElementById('add-role')?.value || 'apprenant';
const formation= document.getElementById('add-formation')?.value || '';
if (!prenom || !nom || !email) { showToast('⚠ Remplissez tous les champs obligatoires'); return; }
const initials = (prenom[0]+nom[0]).toUpperCase();
utilisateurs.push({ id: Date.now(), prenom, nom, email, role, status:'actif', lastSeen:'À l\'instant', avatar: initials, formation });
filterUsers(); closeAddUser();
addAuditEntry('user','#1a7f4f','a créé le compte de', prenom+' '+nom, 'Rôle: '+role);
showToast(`✓ ${prenom} ${nom} ajouté · Invitation envoyée`);
['add-prenom','add-nom','add-email'].forEach(id => { const el=document.getElementById(id); if(el) el.value=''; });
}
// ── ROLE PERMISSIONS EDITOR ──
const rolePermsState = {};
permissions.forEach(p => {
rolePermsState[p.key] = { admin: p.admin, formateur: p.formateur, apprenant: p.apprenant };
});
function selectRole(role, card) {
selectedRole = role;
document.querySelectorAll('.role-card').forEach(c => c.classList.remove('selected'));
if (card) card.classList.add('selected');
renderRolePerms(role);
}
function renderRolePerms(role) {
const el = document.getElementById('role-perms-list');
const title = document.getElementById('role-detail-title');
if (!el) return;
const labels = { admin:'Administrateur', formateur:'Formateur', apprenant:'Apprenant' };
if (title) title.textContent = 'Rôle : ' + (labels[role] || role);
let lastSection = '';
el.innerHTML = permissions.map(p => {
const sectionHeader = p.section !== lastSection
? `<div style="font-size:10px;font-weight:700;color:var(--muted-2);text-transform:uppercase;letter-spacing:.1em;padding:12px 0 6px">${(lastSection=p.section, p.section)}</div>`
: '';
const checked = rolePermsState[p.key]?.[role] ?? p[role];
return `${sectionHeader}
<div style="display:flex;align-items:center;gap:12px;padding:10px 14px;background:var(--surface);border-radius:8px">
<div style="flex:1;font-size:13px">${p.label}</div>
<div class="toggle ${checked ? 'on' : ''}" onclick="togglePerm('${p.key}','${role}',this)" style="flex-shrink:0"></div>
</div>`;
}).join('');
}
function togglePerm(key, role, el) {
el.classList.toggle('on');
rolePermsState[key][role] = el.classList.contains('on');
addAuditEntry('perm','#b45309','a modifié la permission', key, (el.classList.contains('on')?'+':'-')+' pour '+role);
}
function saveRolePerms() {
showToast('💾 Permissions du rôle sauvegardées');
}
// ── PERMISSIONS MATRIX ──
function renderMatrix() {
const body = document.getElementById('perm-matrix-body');
if (!body) return;
let lastSection = '';
body.innerHTML = permissions.map(p => {
const sectionRow = p.section !== lastSection
? `<tr><td colspan="4" style="padding:12px 16px 4px;font-size:10px;font-weight:700;color:var(--muted-2);text-transform:uppercase;letter-spacing:.1em;background:var(--surface-2)">${(lastSection=p.section, p.section)}</td></tr>`
: '';
const mk = (role) => {
const val = rolePermsState[p.key]?.[role] ?? p[role];
return `<td><input type="checkbox" class="perm-check" data-key="${p.key}" data-role="${role}" ${val ? 'checked' : ''} style="accent-color:var(--ink);width:16px;height:16px;cursor:pointer" onchange="matrixToggle(this)"></td>`;
};
return `${sectionRow}<tr class="perm-row"><td style="padding:11px 16px">${p.label}</td>${mk('admin')}${mk('formateur')}${mk('apprenant')}</tr>`;
}).join('');
}
function matrixToggle(cb) {
const key = cb.dataset.key, role = cb.dataset.role;
rolePermsState[key][role] = cb.checked;
addAuditEntry('perm','#b45309','a modifié la permission',key,(cb.checked?'✓ activée':'✗ désactivée')+' pour '+role);
}
function saveMatrix() {
showToast('💾 Matrice de permissions sauvegardée');
}
// ── AUDIT LOG ──
function addAuditEntry(type, color, action, target, detail) {
const now = new Date();
auditLog.unshift({ type, color, time: 'Aujourd\'hui '+now.toLocaleTimeString('fr-FR',{hour:'2-digit',minute:'2-digit'}), actor:'Marie Lambert', action, target, detail });
if (currentUtilTab === 'audit') renderAudit();
}
function filterAudit(type) {
currentAuditFilter = type;
renderAudit();
}
function renderAudit() {
const el = document.getElementById('audit-list');
if (!el) return;
const list = currentAuditFilter ? auditLog.filter(a => a.type === currentAuditFilter) : auditLog;
el.innerHTML = list.map(a =>
`<div class="audit-row">
<div class="audit-dot" style="background:${a.color}"></div>
<div class="audit-time">${a.time}</div>
<div class="audit-txt"><span class="audit-actor">${a.actor}</span> ${a.action}${a.target ? ' <strong>'+a.target+'</strong>' : ''}${a.detail ? ' <span style="color:var(--muted);font-size:12px">— '+a.detail+'</span>' : ''}</div>
</div>`
).join('') || '<div style="text-align:center;color:var(--muted);padding:20px">Aucune entrée.</div>';
}
function exportAudit() {
const rows = ['Heure,Acteur,Action,Cible,Détail'];
auditLog.forEach(a => rows.push(`"${a.time}","${a.actor}","${a.action}","${a.target}","${a.detail}"`));
const blob = new Blob([rows.join('\n')], {type:'text/csv'});
const link = document.createElement('a'); link.href=URL.createObjectURL(blob); link.download='audit_log.csv'; link.click();
showToast('⬇ Audit log exporté');
}
// utilisateurs init — wired in main init
// ── Single consolidated DOMContentLoaded init ──
window.addEventListener('DOMContentLoaded', () => {
// Invitation form live preview wiring
const formation = document.getElementById('inv-formation');
const expiry = document.getElementById('inv-expiry');
const msg = document.getElementById('inv-msg');
if (formation) formation.addEventListener('change', updateSendPreview);
if (expiry) expiry.addEventListener('change', updateSendPreview);
if (msg) msg.addEventListener('input', updateSendPreview);
// Inscriptions (dossiers)
if (typeof renderDossiers === 'function') { renderDossiers(); updateDossierKpis(); }
// Utilisateurs
if (typeof filterUsers === 'function') { filterUsers(); }
// Invitations tracking
if (typeof renderTrackTable === 'function') { renderTrackTable(); }
});
</script>
<script>
// ─────────────────────────────────────────
// CHAT / MESSAGERIE MODULE
// ─────────────────────────────────────────
// ── Data ──
const chatChannels = [
{ id:'excel', name:'excel-avancé', desc:'Formation Excel Avancé & Power BI · 12 membres', members:12, color:'#3b82f6', unread:3 },
{ id:'management', name:'management', desc:'Management Niveau 1 · 8 membres', members:8, color:'#1a7f4f', unread:0 },
{ id:'ia', name:'ia-productivité', desc:'IA & Productivité · 15 membres', members:15, color:'#7c3aed', unread:1 },
{ id:'cyber', name:'cybersécurité', desc:'Cybersécurité Essentielle · 31 membres', members:31, color:'#b45309', unread:0 },
{ id:'parole', name:'prise-de-parole', desc:'Prise de parole en public · 10 membres', members:10, color:'#ec4899', unread:0 },
{ id:'general', name:'général', desc:'Canal ouvert à tous · 18 membres', members:18, color:'#6b7280', unread:2 },
];
const dmUsers = [
{ id:'pl', name:'Pierre Leblanc', role:'Formateur', avatar:'PL', color:'#1d4ed8', online:true, unread:1 },
{ id:'am', name:'Anne Moreau', role:'Formateur', avatar:'AM', color:'#1a7f4f', online:false, unread:0 },
{ id:'jd', name:'Julie Dupont', role:'Apprenant', avatar:'JD', color:'#4b5563', online:true, unread:0 },
{ id:'lr', name:'Lucas Roche', role:'Apprenant', avatar:'LR', color:'#9ca3af', online:false, unread:2 },
{ id:'sb', name:'Sophie Bernard', role:'Apprenant', avatar:'SB', color:'#4b5563', online:true, unread:0 },
];
// Messages keyed by channel/dm id
const chatMessages = {
excel: [
{ id:1, from:'Pierre Leblanc', avatar:'PL', color:'#1d4ed8', mine:false, time:'Hier 14:30', text:'Bonjour à tous ! N\'oubliez pas de télécharger le fichier de données pour la séance de demain.' },
{ id:2, from:'Julie Dupont', avatar:'JD', color:'#4b5563', mine:false, time:'Hier 14:45', text:'C\'est fait ! Question sur les TCD — faut-il déjà avoir lu le module 2 ?' },
{ id:3, from:'Pierre Leblanc', avatar:'PL', color:'#1d4ed8', mine:false, time:'Hier 14:52', text:'Oui idéalement, mais on reprendra les bases ensemble au début de la session 👍' },
{ id:4, from:'Antoine Martin', avatar:'AM', color:'#4b5563', mine:false, time:'Hier 17:10', text:'Parfait merci ! À demain.' },
{ id:5, from:'Moi', avatar:'ML', color:'#0a0a0a', mine:true, time:'Aujourd\'hui 09:15', text:'Bonjour à tous, un rappel : la session commence à 14h00 en Salle Émeraude. Bon courage !' },
{ id:6, from:'Sophie Bernard', avatar:'SB', color:'#4b5563', mine:false, time:'Aujourd\'hui 09:32', text:'Merci Marie ! On sera là 😊' },
{ id:7, from:'Lucas Roche', avatar:'LR', color:'#9ca3af', mine:false, time:'Aujourd\'hui 10:05', text:'Est-ce que j\'ai encore accès aux ressources du module 1 ?' },
{ id:8, from:'Moi', avatar:'ML', color:'#0a0a0a', mine:true, time:'Aujourd\'hui 10:08', text:'Oui Lucas, tout est accessible dans l\'onglet Lecteur. N\'hésite pas si tu as des questions.' },
],
management: [
{ id:1, from:'Anne Moreau', avatar:'AM', color:'#1a7f4f', mine:false, time:'Lundi 10:00', text:'Bonjour à tous ! Le support du Module 3 est disponible dans les ressources.' },
{ id:2, from:'Moi', avatar:'ML', color:'#0a0a0a', mine:true, time:'Lundi 10:15', text:'Merci Anne ! Le quiz de fin de module sera ouvert vendredi.' },
],
ia: [
{ id:1, from:'Laura Simon', avatar:'LS', color:'#7c3aed', mine:false, time:'Mardi 11:00', text:'Nouvelle leçon disponible : "Automatiser avec ChatGPT" — 35 minutes.' },
{ id:2, from:'Emma Lefebvre', avatar:'EL', color:'#4b5563', mine:false, time:'Mardi 14:22', text:'Super ! J\'ai hâte de la voir 🎉' },
{ id:3, from:'Moi', avatar:'ML', color:'#0a0a0a', mine:true, time:'Mercredi 09:00', text:'Bravo à tous pour votre progression sur ce module ! 78% de complétion en moyenne.' },
],
cyber: [
{ id:1, from:'Charles Faure', avatar:'CF', color:'#b45309', mine:false, time:'23 mars 08:30', text:'Quiz disponible jusqu\'au 31 mars. Bonne chance à tous !' },
],
parole: [],
general: [
{ id:1, from:'Moi', avatar:'ML', color:'#0a0a0a', mine:true, time:'22 mars 09:00', text:'🎉 Bienvenue sur FormaPro ! Ce canal est ouvert à tous les membres de la plateforme.' },
{ id:2, from:'Julie Dupont', avatar:'JD', color:'#4b5563', mine:false, time:'22 mars 09:15', text:'Merci pour l\'accueil ! La plateforme est vraiment bien faite 😊' },
{ id:3, from:'Pierre Leblanc', avatar:'PL', color:'#1d4ed8', mine:false, time:'22 mars 10:00', text:'Hâte de commencer les formations avec vous tous !' },
],
dm_pl: [
{ id:1, from:'Pierre Leblanc', avatar:'PL', color:'#1d4ed8', mine:false, time:'Hier 16:00', text:'Marie, j\'ai finalisé le programme du module 4. Je vous envoie le PDF dès que possible.' },
{ id:2, from:'Moi', avatar:'ML', color:'#0a0a0a', mine:true, time:'Hier 16:10', text:'Parfait Pierre, merci ! Envoyez-le quand vous êtes prêt.' },
{ id:3, from:'Pierre Leblanc', avatar:'PL', color:'#1d4ed8', mine:false, time:'Aujourd\'hui 08:45', text:'Voici le document 📎', file:{ name:'Programme_Module4.pdf', size:'1.2 Mo' } },
],
dm_am: [],
dm_jd: [
{ id:1, from:'Julie Dupont', avatar:'JD', color:'#4b5563', mine:false, time:'Aujourd\'hui 08:00', text:'Bonjour ! Je voulais vous remercier pour les ressources supplémentaires envoyées hier.' },
{ id:2, from:'Moi', avatar:'ML', color:'#0a0a0a', mine:true, time:'Aujourd\'hui 08:05', text:'Avec plaisir Julie, bon courage pour la suite !' },
],
dm_lr: [
{ id:1, from:'Lucas Roche', avatar:'LR', color:'#9ca3af', mine:false, time:'Il y a 2h', text:'Bonjour, j\'ai du mal avec le module DAX, est-ce qu\'on peut planifier un point ?' },
{ id:2, from:'Lucas Roche', avatar:'LR', color:'#9ca3af', mine:false, time:'Il y a 1h', text:'Pas de souci si vous êtes occupée, je peux aussi revoir les vidéos.' },
],
dm_sb: [],
};
let currentChatId = 'excel';
let chatOpen = false;
let typingTimer = null;
// ── Toggle panel ──
function toggleChat() {
chatOpen = !chatOpen;
const panel = document.getElementById('chat-panel');
const bubble = document.getElementById('chat-bubble-btn');
panel.classList.toggle('open', chatOpen);
// Rotate bubble icon slightly when open
if (bubble) bubble.style.transform = chatOpen ? 'scale(1.08)' : '';
if (chatOpen) {
renderChannelList();
openChatChannel(currentChatId);
}
}
// ── Render channel sidebar ──
function renderChannelList() {
const el = document.getElementById('chat-channels-list');
if (!el) return;
const chanHTML = chatChannels.map(ch => `
<div class="chat-channel-item ${ch.id === currentChatId ? 'active' : ''}" onclick="openChatChannel('${ch.id}')">
<div class="ch-dot" style="background:${ch.color}"></div>
<span style="overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1"># ${ch.name}</span>
${ch.unread ? `<span class="ch-unread">${ch.unread}</span>` : ''}
</div>`).join('');
const dmHTML = dmUsers.map(u => `
<div class="chat-channel-item ${('dm_'+u.id) === currentChatId ? 'active' : ''}" onclick="openChatChannel('dm_${u.id}')">
<div style="width:7px;height:7px;border-radius:50%;background:${u.online ? 'var(--green)' : 'var(--border-2)'};flex-shrink:0"></div>
<span style="overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1;font-size:12px">${u.name}</span>
${u.unread ? `<span class="ch-unread">${u.unread}</span>` : ''}
</div>`).join('');
el.innerHTML = `
<div class="chat-section-lbl">Canaux</div>
${chanHTML}
<div class="chat-section-lbl" style="margin-top:8px">Messages directs</div>
${dmHTML}`;
updateGlobalBadge();
}
// ── Open a channel or DM ──
function openChatChannel(id) {
currentChatId = id;
const nameEl = document.getElementById('chat-chan-name');
const descEl = document.getElementById('chat-chan-desc');
if (id.startsWith('dm_')) {
const uid = id.replace('dm_','');
const user = dmUsers.find(u => u.id === uid);
if (nameEl) nameEl.textContent = user ? user.name : id;
if (descEl) descEl.textContent = user ? (user.role + (user.online ? ' · En ligne' : ' · Hors ligne')) : '';
// clear unread
if (user) user.unread = 0;
} else {
const ch = chatChannels.find(c => c.id === id);
if (nameEl) nameEl.textContent = ch ? '# ' + ch.name : id;
if (descEl) descEl.textContent = ch ? ch.desc : '';
if (ch) ch.unread = 0;
}
renderChannelList();
renderMessages();
scrollToBottom();
document.getElementById('chat-input')?.focus();
}
// ── Render messages ──
function renderMessages() {
const el = document.getElementById('chat-messages-area');
if (!el) return;
const msgs = chatMessages[currentChatId] || [];
if (!msgs.length) {
el.innerHTML = `<div style="text-align:center;padding:40px 16px;color:var(--muted-2);font-size:12.5px">
<div style="font-size:28px;margin-bottom:10px">💬</div>
Aucun message pour l'instant.<br>Soyez le premier à écrire !
</div>`;
return;
}
let lastDate = '';
el.innerHTML = msgs.map(m => {
let dayHeader = '';
const msgDay = m.time.includes('Aujourd') ? 'Aujourd\'hui' : m.time.includes('Hier') ? 'Hier' : m.time.split(' ')[0];
if (msgDay !== lastDate) {
lastDate = msgDay;
dayHeader = `<div class="chat-day-sep"><span>${msgDay}</span></div>`;
}
const bubble = m.file
? `<div class="chat-file-bubble" onclick="showToast('📎 Téléchargement de ${m.file.name}')">
<span style="font-size:18px">📄</span>
<div><div style="font-weight:500;font-size:12px">${m.file.name}</div><div style="font-size:11px;color:var(--muted)">${m.file.size}</div></div>
<svg style="margin-left:auto;opacity:.4" width="12" height="12" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="2"><path d="M10 2v12M4 10l6 6 6-6"/></svg>
</div>`
: `<div class="chat-msg-bubble">${escHtml(m.text)}</div>`;
return `${dayHeader}
<div class="chat-msg ${m.mine ? 'mine' : ''}">
${!m.mine ? `<div class="chat-msg-av" style="background:${m.color}">${m.avatar}</div>` : ''}
<div class="chat-msg-body">
${!m.mine ? `<div class="chat-msg-meta">${m.from} · <span style="font-size:10px">${m.time.includes(' ') ? m.time.split(' ').slice(-1)[0] : m.time}</span></div>` : ''}
${bubble}
${m.mine ? `<div class="chat-msg-meta" style="margin-top:3px">${m.time.split(' ').slice(-1)[0]}</div>` : ''}
</div>
</div>`;
}).join('');
}
function escHtml(s) {
return s.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/\n/g,'<br>');
}
function scrollToBottom() {
const el = document.getElementById('chat-messages-area');
if (el) setTimeout(() => el.scrollTop = el.scrollHeight, 50);
}
// ── Send message ──
function sendChatMessage() {
const input = document.getElementById('chat-input');
if (!input) return;
const text = input.value.trim();
if (!text) return;
if (!chatMessages[currentChatId]) chatMessages[currentChatId] = [];
const now = new Date();
chatMessages[currentChatId].push({
id: Date.now(),
from: 'Moi', avatar: 'ML', color: '#0a0a0a', mine: true,
time: 'Aujourd\'hui ' + now.toLocaleTimeString('fr-FR',{hour:'2-digit',minute:'2-digit'}),
text
});
input.value = '';
input.style.height = '';
renderMessages();
scrollToBottom();
simulateReply();
}
function handleChatKey(e) {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
sendChatMessage();
}
}
function autoResizeInput(ta) {
ta.style.height = 'auto';
ta.style.height = Math.min(ta.scrollHeight, 100) + 'px';
showTyping();
}
// ── Simulated reply ──
const autoReplies = {
excel: ['Merci !', 'Super, à bientôt 👍', 'On note, merci Marie.', 'Parfait !', 'Ok, reçu 5/5 !'],
management: ['Bien reçu !', 'Merci pour l\'info.'],
ia: ['Génial, merci ! 🙌', 'Super nouvelle !'],
general: ['Merci Marie !', '👍', 'Super !'],
dm_pl: ['Je vous transmets ça rapidement.', 'Bien reçu, merci !'],
dm_jd: ['Avec plaisir ! Bonne continuation 😊', 'Merci beaucoup !'],
dm_lr: ['D\'accord, merci.', 'Bien reçu !'],
};
function simulateReply() {
const replies = autoReplies[currentChatId];
if (!replies || Math.random() > 0.55) return;
let sender;
if (currentChatId.startsWith('dm_')) {
const uid = currentChatId.replace('dm_','');
sender = dmUsers.find(u => u.id === uid);
if (!sender) return;
} else {
const ch = chatChannels.find(c => c.id === currentChatId);
const senderMap = { excel:{ from:'Pierre Leblanc', avatar:'PL', color:'#1d4ed8' }, management:{ from:'Anne Moreau', avatar:'AM', color:'#1a7f4f' }, ia:{ from:'Laura Simon', avatar:'LS', color:'#7c3aed' }, general:{ from:'Julie Dupont', avatar:'JD', color:'#4b5563' } };
sender = senderMap[currentChatId];
if (!sender) return;
}
// show typing indicator
const typEl = document.getElementById('chat-typing');
const senderName = currentChatId.startsWith('dm_') ? sender.name : sender.from;
if (typEl) typEl.textContent = senderName + ' est en train d\'écrire…';
const delay = 1200 + Math.random() * 1400;
setTimeout(() => {
if (typEl) typEl.textContent = '';
if (!chatMessages[currentChatId]) chatMessages[currentChatId] = [];
const reply = replies[Math.floor(Math.random() * replies.length)];
const now = new Date();
chatMessages[currentChatId].push({
id: Date.now(),
from: currentChatId.startsWith('dm_') ? sender.name : sender.from,
avatar: currentChatId.startsWith('dm_') ? sender.avatar : sender.avatar,
color: currentChatId.startsWith('dm_') ? sender.color : sender.color,
mine: false,
time: 'Aujourd\'hui ' + now.toLocaleTimeString('fr-FR',{hour:'2-digit',minute:'2-digit'}),
text: reply
});
renderMessages();
scrollToBottom();
}, delay);
}
function showTyping() {
clearTimeout(typingTimer);
typingTimer = setTimeout(() => {
const t = document.getElementById('chat-typing');
if (t) t.textContent = '';
}, 2000);
}
// ── Global unread badge ──
function updateGlobalBadge() {
const totalUnread = chatChannels.reduce((s,c) => s+c.unread, 0) + dmUsers.reduce((s,u) => s+u.unread, 0);
const badge = document.getElementById('chat-notif-badge');
if (badge) badge.style.display = totalUnread > 0 ? 'block' : 'none';
}
// Init on load
window.addEventListener('DOMContentLoaded', () => {
updateGlobalBadge();
});
</script>
<script>
// ─────────────────────────────────────────
// LOGIN MODULE
// ─────────────────────────────────────────
const demoAccounts = {
admin: { email:'admin@litd.fr', pwd:'Admin2026!', name:'Marie Lambert', role:'Administratrice', initials:'ML', color:'#0066cc', isAdmin:true, isFormateur:false, isApprenant:false },
formateur: { email:'formateur@litd.fr', pwd:'Formateur1!', name:'Pierre Leblanc', role:'Formateur', initials:'PL', color:'#ff6633', isAdmin:false, isFormateur:true, isApprenant:false },
apprenant: { email:'apprenant@litd.fr', pwd:'Apprenant1!', name:'Julie Dupont', role:'Apprenant', initials:'JD', color:'#4b5563', isAdmin:false, isFormateur:false, isApprenant:true },
};
let currentUser = null;
function loginSubmit() {
const email = document.getElementById('login-email')?.value.trim();
const pwd = document.getElementById('login-pwd')?.value;
const btn = document.getElementById('login-btn');
const errEl = document.getElementById('login-error-msg');
if (!email || !pwd) {
showLoginError('Veuillez remplir tous les champs.'); return;
}
// Animate button
btn.disabled = true;
btn.textContent = 'Connexion en cours…';
setTimeout(() => {
// Check demo accounts
const match = Object.values(demoAccounts).find(a => a.email === email && a.pwd === pwd);
if (match) {
currentUser = match;
doLogin(match);
} else if (email.includes('@') && pwd.length >= 6) {
// Accept any plausible credential as admin for demo
currentUser = { ...demoAccounts.admin, email };
doLogin(currentUser);
} else {
btn.disabled = false;
btn.textContent = 'Se connecter';
showLoginError('Email ou mot de passe incorrect. Essayez un accès démo ci-dessous.');
document.getElementById('login-email')?.classList.add('error');
document.getElementById('login-pwd')?.classList.add('error');
}
}, 900);
}
function loginAs(role) {
const acc = demoAccounts[role];
if (!acc) return;
currentUser = acc;
// Fill fields for visual feedback
const eEl = document.getElementById('login-email');
const pEl = document.getElementById('login-pwd');
if (eEl) eEl.value = acc.email;
if (pEl) pEl.value = acc.pwd;
const btn = document.getElementById('login-btn');
btn.disabled = true; btn.textContent = 'Connexion…';
setTimeout(() => doLogin(acc), 600);
}
function doLogin(user) {
// Mettre à jour l'interface
document.querySelectorAll('.user-name').forEach(el => el.textContent = user.name);
document.querySelectorAll('.user-role').forEach(el => el.textContent = user.role);
document.querySelectorAll('.user-avatar, .topbar-avatar').forEach(el => {
el.textContent = user.initials;
el.style.background = user.color;
});
// Appliquer le rôle sur le body pour masquer/afficher les éléments
document.body.classList.remove('role-admin','role-formateur','role-apprenant');
if (user.isAdmin) document.body.classList.add('role-admin');
else if (user.isFormateur) document.body.classList.add('role-formateur');
else document.body.classList.add('role-apprenant');
// Masquer l'écran de connexion
const screen = document.getElementById('login-screen');
screen.style.transition = 'opacity 0.4s ease, transform 0.4s ease';
screen.style.opacity = '0';
screen.style.transform = 'scale(1.02)';
setTimeout(() => {
screen.style.display = 'none';
// Redirection selon le rôle
if (user.isApprenant) nav('apprenant');
else nav('dashboard');
// Charger le catalogue dynamique
setTimeout(() => { if(typeof renderCatalogueDyn==='function') renderCatalogueDyn(); }, 200);
addNotification({
type: 'system', icon: '👋', iconBg: '#fff4f0',
title: 'Bienvenue, ' + user.name.split(' ')[0] + ' !',
desc: 'Connecté en tant que ' + user.role + '. Bonne journée !',
time: 'À l\'instant', unread: true
});
renderNotifications();
updateNotifBadge();
showToast('✓ Connecté en tant que ' + user.name);
}, 380);
}
function clearLoginError() {
document.getElementById('login-error-msg').style.display = 'none';
document.getElementById('login-email')?.classList.remove('error');
document.getElementById('login-pwd')?.classList.remove('error');
}
function showLoginError(msg) {
const el = document.getElementById('login-error-msg');
el.textContent = msg; el.style.display = 'block';
}
function toggleLoginPwd() {
const input = document.getElementById('login-pwd');
const btn = document.querySelector('.login-pwd-toggle');
if (!input) return;
input.type = input.type === 'password' ? 'text' : 'password';
if (btn) btn.textContent = input.type === 'password' ? '👁' : '🙈';
}
function showForgot() {
document.getElementById('login-form-panel').style.display = 'none';
document.getElementById('forgot-form-panel').style.display = 'block';
}
function showLogin() {
document.getElementById('forgot-form-panel').style.display = 'none';
document.getElementById('login-form-panel').style.display = 'block';
document.getElementById('forgot-success-msg').style.display = 'none';
document.getElementById('forgot-error-msg').style.display = 'none';
}
function forgotSubmit() {
const email = document.getElementById('forgot-email')?.value.trim();
const succ = document.getElementById('forgot-success-msg');
const err = document.getElementById('forgot-error-msg');
if (!email || !email.includes('@')) {
err.textContent = 'Veuillez entrer une adresse email valide.';
err.style.display = 'block'; succ.style.display = 'none'; return;
}
err.style.display = 'none';
succ.textContent = `Un lien de réinitialisation a été envoyé à ${email}. Vérifiez votre boîte mail.`;
succ.style.display = 'block';
}
// ─────────────────────────────────────────
// NOTIFICATIONS MODULE
// ─────────────────────────────────────────
const notifications = [
{ id:1, type:'formation', icon:'📚', iconBg:'#eff6ff', title:'Nouvelle leçon disponible', desc:'Module 3 — "Langage DAX" est maintenant accessible dans Excel Avancé.', time:'Il y a 5 min', unread:true },
{ id:2, type:'session', icon:'📅', iconBg:'#f0faf5', title:'Session dans 2 heures', desc:'Excel Avancé — Groupe A · Aujourd\'hui 14h00 · Salle Émeraude', time:'Il y a 10 min', unread:true },
{ id:3, type:'message', icon:'💬', iconBg:'#fdf4ff', title:'Nouveau message de Pierre Leblanc', desc:'"Voici le document Programme_Module4.pdf pour la prochaine session."', time:'Il y a 23 min', unread:true },
{ id:4, type:'system', icon:'✅', iconBg:'#f0faf5', title:'3 certifications délivrées', desc:'Julie Dupont, Antoine Martin et Thomas Renard ont obtenu leur certification.', time:'Il y a 1h', unread:true },
{ id:5, type:'alert', icon:'⚠️', iconBg:'#fffbeb', title:'Apprenant inactif', desc:'Lucas Roche n\'a pas consulté sa formation depuis 14 jours.', time:'Il y a 2h', unread:false },
{ id:6, type:'session', icon:'🔴', iconBg:'#fef2f2', title:'Places insuffisantes', desc:'Session Management du 2 avril — seulement 2 places restantes.', time:'Il y a 3h', unread:false },
{ id:7, type:'formation', icon:'🎓', iconBg:'#eff6ff', title:'Quiz complété', desc:'Sophie Bernard a obtenu 16/20 au quiz du Module 2 — IA & Productivité.', time:'Hier 16:30', unread:false },
{ id:8, type:'system', icon:'👤', iconBg:'#f8f8f8', title:'Nouveau dossier d\'inscription', desc:'Jean Dupont a soumis son dossier d\'inscription — en attente de validation.', time:'Hier 11:00', unread:false },
{ id:9, type:'message', icon:'💬', iconBg:'#fdf4ff', title:'Nouveau message de Lucas Roche', desc:'"Bonjour, j\'ai du mal avec DAX, est-ce qu\'on peut planifier un point ?"', time:'Hier 10:05', unread:false },
{ id:10, type:'system', icon:'📤', iconBg:'#f8f8f8', title:'Export OPCO transmis', desc:'Le dossier de financement du Groupe Excel A a été envoyé à l\'OPCO.', time:'23 mars 11:00', unread:false },
];
let notifPanelOpen = false;
let currentNotifTab = 'all';
function toggleNotifPanel() {
notifPanelOpen = !notifPanelOpen;
const panel = document.getElementById('notif-panel');
if (!panel) return;
panel.classList.toggle('open', notifPanelOpen);
if (notifPanelOpen) renderNotifications();
// Close on outside click
if (notifPanelOpen) {
setTimeout(() => document.addEventListener('click', closeNotifOutside), 0);
}
}
function closeNotifOutside(e) {
const panel = document.getElementById('notif-panel');
const btn = document.getElementById('notif-bell-btn');
if (panel && !panel.contains(e.target) && btn && !btn.contains(e.target)) {
notifPanelOpen = false;
panel.classList.remove('open');
document.removeEventListener('click', closeNotifOutside);
}
}
function switchNotifTab(tab) {
currentNotifTab = tab;
['all','unread','system'].forEach(t => {
const el = document.getElementById('ntab-' + t);
if (el) el.className = 'notif-tab' + (t === tab ? ' active' : '');
});
renderNotifications();
}
function renderNotifications() {
const el = document.getElementById('notif-list');
if (!el) return;
let list = [...notifications];
if (currentNotifTab === 'unread') list = list.filter(n => n.unread);
if (currentNotifTab === 'system') list = list.filter(n => n.type === 'system');
if (!list.length) {
el.innerHTML = '<div style="padding:32px;text-align:center;color:var(--muted-2);font-size:13px">Aucune notification.</div>';
return;
}
el.innerHTML = list.map(n => `
<div class="notif-item ${n.unread ? 'unread' : ''}" onclick="readNotif(${n.id})">
<div class="notif-icon" style="background:${n.iconBg}">${n.icon}</div>
<div class="notif-body">
<div class="notif-title">${n.title}</div>
<div class="notif-desc">${n.desc}</div>
<div class="notif-time">${n.time}</div>
</div>
${n.unread ? '<div style="width:7px;height:7px;border-radius:50%;background:var(--blue);flex-shrink:0;margin-top:4px"></div>' : ''}
</div>`).join('');
}
function readNotif(id) {
const n = notifications.find(x => x.id === id);
if (n) n.unread = false;
renderNotifications();
updateNotifBadge();
}
function markAllRead() {
notifications.forEach(n => n.unread = false);
renderNotifications();
updateNotifBadge();
showToast('✓ Toutes les notifications lues');
}
function addNotification(n) {
n.id = Date.now();
notifications.unshift(n);
}
function updateNotifBadge() {
const count = notifications.filter(n => n.unread).length;
const el = document.getElementById('notif-count');
if (!el) return;
if (count > 0) {
el.textContent = count > 9 ? '9+' : count;
el.style.display = 'flex';
} else {
el.style.display = 'none';
}
}
// Simulate incoming notification every ~45s
function simulateIncomingNotif() {
const pool = [
{ type:'message', icon:'💬', iconBg:'#fdf4ff', title:'Nouveau message', desc:'Anne Moreau vous a envoyé un message.', unread:true },
{ type:'session', icon:'📅', iconBg:'#f0faf5', title:'Rappel de session', desc:'Management Module 3 — demain à 09h00.', unread:true },
{ type:'formation',icon:'🎓', iconBg:'#eff6ff', title:'Progression mise à jour', desc:'Emma Lefebvre a complété le module 2.', unread:true },
{ type:'alert', icon:'⚠️', iconBg:'#fffbeb', title:'Quiz disponible', desc:'Le quiz du module Cybersécurité expire dans 3 jours.',unread:true },
];
const n = pool[Math.floor(Math.random() * pool.length)];
n.time = 'À l\'instant';
addNotification(n);
updateNotifBadge();
if (notifPanelOpen) renderNotifications();
// Show a subtle toast
showToast('🔔 ' + n.title);
}
// Init
window.addEventListener('DOMContentLoaded', () => {
updateNotifBadge();
// Simulate one notification after 30s
setTimeout(simulateIncomingNotif, 30000);
});
</script>
<script>
// ─────────────────────────────────────────
// PARAMÈTRES
// ─────────────────────────────────────────
function switchSettings(sec) {
['profile','security','notifs','apparence','danger'].forEach(s => {
const nav = document.getElementById('snav-'+s);
const sec_el = document.getElementById('ssec-'+s);
if (nav) nav.className = 'settings-nav-item' + (s===sec?' active':'');
if (sec_el) sec_el.className = 'settings-section' + (s===sec?' active':'');
});
}
function checkPwdStrengthSettings(input) {
const v = input.value;
const score = [v.length>=8, /[A-Z]/.test(v), /[0-9]/.test(v), /[^a-zA-Z0-9]/.test(v)].filter(Boolean).length;
const labels = ['','Trop faible','Faible','Bien','Fort ✓'];
const colors = ['','var(--red)','var(--amber)','var(--amber)','var(--green)'];
const el = document.getElementById('pwd-strength-settings');
if (el) { el.textContent = labels[score] || ''; el.style.color = colors[score] || ''; }
}
// ─────────────────────────────────────────
// QUIZ MODULE
// ─────────────────────────────────────────
const quizData = {
excel3: {
title: 'Module 3 — Introduction à Power BI',
formation: 'Excel Avancé & Power BI',
questions: [
{ q: 'Quel outil permet de transformer et nettoyer les données avant leur chargement dans Power BI ?', opts: ['Power Pivot','Power Query','DAX Studio','Power View'], correct: 1, expl: 'Power Query est l\'outil ETL intégré à Power BI qui permet de transformer les données avant chargement.' },
{ q: 'Que signifie DAX dans le contexte de Power BI ?', opts: ['Data Analysis Expressions','Database Access Extension','Digital Analytics XML','Data Aggregation eXtension'], correct: 0, expl: 'DAX signifie Data Analysis Expressions — c\'est le langage de formules utilisé dans Power BI.' },
{ q: 'Quelle est la différence entre un mode Import et un mode DirectQuery ?', opts: ['Import est plus lent','DirectQuery importe les données en local','Import charge les données en mémoire, DirectQuery interroge la source en direct','Il n\'y a pas de différence'], correct: 2, expl: 'En mode Import, les données sont copiées dans le modèle. En DirectQuery, chaque requête interroge directement la source.' },
{ q: 'Une mesure DAX est calculée :', opts: ['Une seule fois à l\'import','À chaque actualisation du rapport','À la volée selon le contexte de filtre','Par l\'utilisateur manuellement'], correct: 2, expl: 'Les mesures DAX sont calculées dynamiquement selon le contexte de filtre (sélections, segments, etc.).' },
{ q: 'Quelle fonction DAX calcule la somme d\'une colonne ?', opts: ['COUNT()','AVERAGE()','SUM()','SUMX()'], correct: 2, expl: 'SUM() calcule la somme de toutes les valeurs d\'une colonne. SUMX() permet une somme avec une expression par ligne.' },
]
},
excel1: { title: 'Module 1 — Fonctions avancées', formation: 'Excel Avancé', questions: [] },
ia1: { title: 'Module 1 — Bases de l\'IA', formation: 'IA & Productivité', questions: [] },
};
let currentQuiz = null;
let currentQuizState = { qIndex: 0, answers: [], score: 0, answered: false };
function loadQuiz(id) {
currentQuiz = id;
const quiz = quizData[id];
if (!quiz) return;
// Update active state in list
document.querySelectorAll('.quiz-list-item').forEach(el => {
if (!el.classList.contains('locked') && !el.classList.contains('done')) el.classList.remove('active');
});
const item = document.getElementById('qitem-' + id);
if (item) item.classList.add('active');
const startScreen = document.getElementById('quiz-start-screen');
const qScreen = document.getElementById('quiz-question-screen');
const rScreen = document.getElementById('quiz-results-screen');
// If quiz has no questions (done), show results
if (!quiz.questions.length) {
const scores = { excel1: {got:18, total:20}, excel2: {got:16, total:20}, ia1: {got:15, total:20} };
const s = scores[id] || {got:0, total:20};
startScreen.style.display = 'none';
qScreen.style.display = 'none';
rScreen.style.display = 'block';
showQuizResults(s.got, s.total, true);
return;
}
startScreen.style.display = 'block';
qScreen.style.display = 'none';
rScreen.style.display = 'none';
startScreen.querySelector('.card-title, [style*="Playfair"]') && null;
// Update start screen text
startScreen.innerHTML = `
<div style="font-size:48px;margin-bottom:16px">📝</div>
<div style="font-family:'Playfair Display',serif;font-size:22px;font-weight:400;margin-bottom:8px">${quiz.title}</div>
<div style="font-size:13.5px;color:var(--muted);max-width:320px;margin:0 auto 28px;line-height:1.7">${quiz.formation} · ${quiz.questions.length} questions · ~${Math.ceil(quiz.questions.length*1.5)} minutes<br>Lisez bien chaque question avant de répondre.</div>
<button class="btn btn-solid" onclick="startQuiz('${id}')">Commencer →</button>`;
}
function startQuiz(id) {
currentQuiz = id;
currentQuizState = { qIndex: 0, answers: [], score: 0, answered: false };
document.getElementById('quiz-start-screen').style.display = 'none';
document.getElementById('quiz-results-screen').style.display = 'none';
document.getElementById('quiz-question-screen').style.display = 'block';
renderQuestion();
}
function renderQuestion() {
const quiz = quizData[currentQuiz];
const state = currentQuizState;
const q = quiz.questions[state.qIndex];
const total = quiz.questions.length;
const pct = ((state.qIndex) / total * 100).toFixed(0);
const letters = ['A','B','C','D'];
document.getElementById('quiz-question-screen').innerHTML = `
<div class="quiz-question-card">
<div class="q-progress-bar"><div class="q-progress-fill" style="width:${pct}%"></div></div>
<div class="q-num">Question ${state.qIndex+1} / ${total}</div>
<div class="q-text">${q.q}</div>
<div class="q-options">
${q.opts.map((opt,i) => `
<div class="q-option" id="opt-${i}" onclick="selectOption(${i})">
<div class="q-opt-letter">${letters[i]}</div>
<span>${opt}</span>
</div>`).join('')}
</div>
<div class="q-feedback" id="q-feedback"></div>
<div style="display:flex;justify-content:flex-end">
<button class="btn btn-solid" id="q-next-btn" onclick="nextQuestion()" disabled>
${state.qIndex < total-1 ? 'Question suivante →' : 'Voir les résultats →'}
</button>
</div>
</div>`;
state.answered = false;
}
function selectOption(idx) {
if (currentQuizState.answered) return;
currentQuizState.answered = true;
const quiz = quizData[currentQuiz];
const q = quiz.questions[currentQuizState.qIndex];
const correct = q.correct;
currentQuizState.answers.push({ chosen: idx, correct });
if (idx === correct) currentQuizState.score++;
// Visual feedback
document.querySelectorAll('.q-option').forEach(el => el.classList.add('disabled'));
document.getElementById('opt-'+idx)?.classList.add(idx === correct ? 'correct' : 'wrong');
if (idx !== correct) document.getElementById('opt-'+correct)?.classList.add('correct');
const fb = document.getElementById('q-feedback');
fb.className = 'q-feedback show ' + (idx===correct ? 'ok' : 'ko');
fb.innerHTML = (idx===correct ? '✓ Bonne réponse ! ' : '✗ Mauvaise réponse. ') + q.expl;
document.getElementById('q-next-btn').disabled = false;
}
function nextQuestion() {
currentQuizState.qIndex++;
if (currentQuizState.qIndex >= quizData[currentQuiz].questions.length) {
finishQuiz();
} else {
renderQuestion();
}
}
function finishQuiz() {
const total = quizData[currentQuiz].questions.length;
document.getElementById('quiz-question-screen').style.display = 'none';
document.getElementById('quiz-results-screen').style.display = 'block';
showQuizResults(currentQuizState.score, total, false);
// Mark as done in list
const item = document.getElementById('qitem-' + currentQuiz);
if (item) { item.classList.remove('active'); item.classList.add('done'); }
// Add XP notification
addNotification({ type:'system', icon:'🎯', iconBg:'#f0faf5', title:'Quiz complété !', desc:`Score : ${currentQuizState.score}/${total} — +${currentQuizState.score*20} XP gagnés`, time:'À l\'instant', unread:true });
updateNotifBadge();
showToast(`🎯 Quiz terminé — ${currentQuizState.score}/${total}`);
}
function showQuizResults(score, total, isPast) {
const pct = Math.round(score/total*100);
const mention = pct>=90?'Excellent 🏆':pct>=75?'Très bien 🎓':pct>=60?'Bien 👍':'À revoir 📖';
const color = pct>=75?'var(--green)':pct>=60?'var(--amber)':'var(--red)';
const circumference = 2 * Math.PI * 45;
const offset = circumference * (1 - pct/100);
document.getElementById('quiz-results-screen').innerHTML = `
<div class="quiz-question-card quiz-results">
<div class="result-score-ring">
<svg width="120" height="120" viewBox="0 0 120 120" style="transform:rotate(-90deg)">
<circle cx="60" cy="60" r="45" fill="none" stroke="var(--border)" stroke-width="10"/>
<circle cx="60" cy="60" r="45" fill="none" stroke="${color}" stroke-width="10"
stroke-dasharray="${circumference}" stroke-dashoffset="${offset}"
stroke-linecap="round" style="transition:stroke-dashoffset 1s ease"/>
</svg>
<div class="result-score-text">
<div class="result-score-val" style="color:${color}">${score}</div>
<div class="result-score-tot">/ ${total}</div>
</div>
</div>
<div style="font-family:'Playfair Display',serif;font-size:24px;font-weight:400;margin-bottom:6px">${mention}</div>
<div style="font-size:14px;color:var(--muted);margin-bottom:28px">${pct}% de bonnes réponses${isPast?' (résultat enregistré)':''}</div>
<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:12px;max-width:380px;margin:0 auto 28px">
<div style="background:var(--green-bg);border-radius:var(--radius-sm);padding:14px;text-align:center">
<div style="font-family:'Playfair Display',serif;font-size:24px;color:var(--green)">${score}</div>
<div style="font-size:11px;color:var(--green)">Correctes</div>
</div>
<div style="background:var(--red-bg);border-radius:var(--radius-sm);padding:14px;text-align:center">
<div style="font-family:'Playfair Display',serif;font-size:24px;color:var(--red)">${total-score}</div>
<div style="font-size:11px;color:var(--red)">Incorrectes</div>
</div>
<div style="background:var(--blue-bg);border-radius:var(--radius-sm);padding:14px;text-align:center">
<div style="font-family:'Playfair Display',serif;font-size:24px;color:var(--blue)">+${score*20}</div>
<div style="font-size:11px;color:var(--blue)">XP gagnés</div>
</div>
</div>
<div style="display:flex;gap:10px;justify-content:center">
${!isPast ? `<button class="btn btn-sm" onclick="startQuiz('${currentQuiz}')">↺ Réessayer</button>` : ''}
<button class="btn btn-sm btn-solid" onclick="nav('apprenant2')">🏆 Voir mes badges</button>
</div>
</div>`;
}
function switchQuizView(v) {
if (v === 'results') showToast('📊 Résultats : Excel M1: 18/20 · Excel M2: 16/20 · IA M1: 15/20');
}
// Init nav hooks
window.addEventListener('DOMContentLoaded', () => {
// nothing extra needed
});
// ─────────────────────────────────────────
// DARK MODE — RIPPLE TRANSITION
// ─────────────────────────────────────────
let darkMode = localStorage.getItem('formapro-dark') === 'true';
let rippling = false;
function applyDarkMode(on, animate) {
darkMode = on;
localStorage.setItem('formapro-dark', on);
const btn = document.getElementById('dark-toggle-btn');
if (btn) btn.textContent = on ? '☀️' : '🌙';
const st = document.getElementById('dark-mode-settings-toggle');
if (st) st.classList.toggle('on', on);
if (!animate) {
document.documentElement.classList.toggle('dark', on);
return;
}
playRipple(on);
}
function toggleDarkMode() {
if (rippling) return;
applyDarkMode(!darkMode, true);
}
function playRipple(goingDark) {
rippling = true;
// Get button position for ripple origin
const btn = document.getElementById('dark-toggle-btn');
const rect = btn ? btn.getBoundingClientRect() : { left: window.innerWidth - 80, top: 29, width: 36, height: 36 };
const originX = rect.left + rect.width / 2;
const originY = rect.top + rect.height / 2;
// Size needed to cover full screen from that origin
const maxDist = Math.hypot(
Math.max(originX, window.innerWidth - originX),
Math.max(originY, window.innerHeight - originY)
);
const finalDiam = maxDist * 2.1;
// Create ripple element
const ripple = document.createElement('div');
ripple.id = 'dark-ripple';
ripple.style.cssText = `
position: fixed;
width: ${finalDiam}px;
height: ${finalDiam}px;
left: ${originX - finalDiam / 2}px;
top: ${originY - finalDiam / 2}px;
border-radius: 50%;
pointer-events: none;
z-index: 99999;
transform: scale(0);
transform-origin: center center;
will-change: transform;
background: ${goingDark ? '#0a0a0a' : '#f8f8f8'};
`;
document.body.appendChild(ripple);
// EXPAND animation
const EXPAND_MS = 520;
const HOLD_MS = 60;
const SHRINK_MS = 400;
// Use Web Animations API for buttery smooth ripple
const expandAnim = ripple.animate([
{ transform: 'scale(0)', opacity: 1 },
{ transform: 'scale(1)', opacity: 1 },
], {
duration: EXPAND_MS,
easing: 'cubic-bezier(0.22, 0.61, 0.36, 1)',
fill: 'forwards',
});
expandAnim.onfinish = () => {
// Flip the actual theme at full coverage
document.documentElement.classList.toggle('dark', goingDark);
setTimeout(() => {
// SHRINK back out (ripple recedes, revealing new theme underneath)
const shrinkAnim = ripple.animate([
{ transform: 'scale(1)', opacity: 1 },
{ transform: 'scale(0)', opacity: 0.6 },
], {
duration: SHRINK_MS,
easing: 'cubic-bezier(0.55, 0, 1, 0.45)',
fill: 'forwards',
});
shrinkAnim.onfinish = () => {
ripple.remove();
rippling = false;
};
}, HOLD_MS);
};
}
// ── Init ──
(function initDark() {
const saved = localStorage.getItem('formapro-dark');
if (saved !== null) {
applyDarkMode(saved === 'true', false);
} else if (window.matchMedia?.('(prefers-color-scheme: dark)').matches) {
applyDarkMode(true, false);
}
})();
</script>
<!-- ═══ SCRIPT GESTION DES FORMATIONS ═══ -->
<script>
// ── Données persistantes ──
let formations = JSON.parse(localStorage.getItem('formapro_formations') || '[]');
let _editId = null;
let _modCount = 0;
let _resCount = 0;
function saveFormations() {
localStorage.setItem('formapro_formations', JSON.stringify(formations));
// badge sidebar
const b = document.getElementById('badge-gestion');
if (b) b.textContent = formations.length;
}
// ── Compteurs globaux ──
let _chCount = 0, _mCount = 0;
// ── Modal ──
function openFormModal(id) {
_editId = id || null;
_chCount = 0; _mCount = 0;
document.getElementById('ch-list').innerHTML = '';
document.getElementById('fm-edit-id').value = id || '';
document.getElementById('modal-ttl').textContent = id ? 'Modifier la formation' : 'Nouvelle formation';
if (id) {
const f = formations.find(x => x.id === id);
if (f) {
document.getElementById('fm-titre').value = f.titre || '';
document.getElementById('fm-emoji').value = f.emoji || '📚';
document.getElementById('fm-domaine').value = f.domaine || '';
document.getElementById('fm-duree').value = f.duree || '';
document.getElementById('fm-prix').value = f.prix || '';
document.getElementById('fm-modalite').value = f.modalite || 'Présentiel';
document.getElementById('fm-niveau').value = f.niveau || 'Tous niveaux';
document.getElementById('fm-desc').value = f.desc || '';
document.getElementById('fm-cpf').checked = !!f.cpf;
document.getElementById('fm-opco').checked = !!f.opco;
document.getElementById('fm-plan').checked = !!f.plan;
document.getElementById('fm-certif').checked = !!f.certif;
document.getElementById('fm-formateur').value = f.formateur || '';
document.getElementById('fm-form-bio').value = f.formBio || '';
(f.chapitres || []).forEach(ch => addChapitre(ch));
}
} else {
['fm-titre','fm-duree','fm-prix','fm-desc','fm-formateur','fm-form-bio'].forEach(fid => document.getElementById(fid).value = '');
document.getElementById('fm-emoji').value = '📚';
['fm-cpf','fm-opco','fm-plan','fm-certif'].forEach(fid => document.getElementById(fid).checked = false);
}
document.getElementById('form-modal').classList.add('open');
}
function closeFormModal() {
document.getElementById('form-modal').classList.remove('open');
}
window.addEventListener('DOMContentLoaded', function() {
const fm = document.getElementById('form-modal');
if (fm) fm.addEventListener('click', function(e) { if (e.target === this) closeFormModal(); });
});
// ════════════════════════════════════════
// ── BUILDER CHAPITRES ──
// ════════════════════════════════════════
function addChapitre(data) {
_chCount++;
const cid = 'ch_' + _chCount;
const nom = data?.nom || '';
const div = document.createElement('div');
div.className = 'ch-block';
div.id = cid;
div.innerHTML = `
<div class="ch-hdr" onclick="toggleChapitre('${cid}')">
<span class="ch-num">${String(_chCount).padStart(2,'0')}</span>
<span class="ch-title-preview" id="${cid}-preview">${nom || 'Nouveau chapitre'}</span>
<span class="ch-toggle open" id="${cid}-toggle">▶</span>
<button class="ch-del-btn" onclick="event.stopPropagation();deleteChapitre('${cid}')" title="Supprimer">×</button>
</div>
<div class="ch-body open" id="${cid}-body">
<div class="ch-fields">
<div class="fm-field">
<label class="fm-lbl">Nom du chapitre</label>
<input class="fm-inp" type="text" id="${cid}-nom" placeholder="ex. Introduction à Excel"
value="${esc(nom)}"
oninput="document.getElementById('${cid}-preview').textContent=this.value||'Nouveau chapitre'">
</div>
<div class="fm-field">
<label class="fm-lbl">Durée estimée</label>
<input class="fm-inp" type="text" id="${cid}-duree" placeholder="ex. 2h30" value="${esc(data?.duree||'')}">
</div>
<div class="fm-field ch-obj-field">
<label class="fm-lbl">Objectif pédagogique</label>
<input class="fm-inp" type="text" id="${cid}-objectif" placeholder="À la fin de ce chapitre, l'apprenant sera capable de…" value="${esc(data?.objectif||'')}">
</div>
</div>
<div style="font-size:11px;font-weight:700;color:var(--muted-2);text-transform:uppercase;letter-spacing:.08em;margin-bottom:8px">Modules</div>
<div class="subch-list" id="${cid}-modules"></div>
<button class="btn btn-sm" type="button" onclick="addSousChapitre('${cid}')">+ Ajouter un module</button>
</div>`;
document.getElementById('ch-list').appendChild(div);
(data?.modules || []).forEach(m => addSousChapitre(cid, m));
}
function toggleChapitre(cid) {
const body = document.getElementById(cid + '-body');
const tog = document.getElementById(cid + '-toggle');
const open = body.classList.toggle('open');
tog.classList.toggle('open', open);
}
function deleteChapitre(cid) {
if (!confirm('Supprimer ce chapitre et tous ses modules ?')) return;
document.getElementById(cid)?.remove();
}
// ════════════════════════════════════════
// ── BUILDER SOUS-CHAPITRES (MODULES) ──
// ════════════════════════════════════════
function addSousChapitre(chId, data) {
_mCount++;
const mid = 'mod_' + _mCount;
const nom = data?.nom || '';
const ctype = data?.contentType || 'upload';
const container = document.getElementById(chId + '-modules');
if (!container) return;
const idx = container.children.length + 1;
const div = document.createElement('div');
div.className = 'subch-block';
div.id = mid;
div.innerHTML = `
<div class="subch-hdr" onclick="toggleSousChapitre('${mid}')">
<span class="subch-num">Module ${idx}</span>
<span class="subch-preview" id="${mid}-preview">${nom || 'Nouveau module'}</span>
<span class="subch-toggle open" id="${mid}-toggle">▶</span>
<button class="subch-del" onclick="event.stopPropagation();document.getElementById('${mid}').remove()" title="Supprimer">×</button>
</div>
<div class="subch-body open" id="${mid}-body">
<div class="subch-fields">
<div class="fm-field">
<label class="fm-lbl">Nom du module</label>
<input class="fm-inp" type="text" id="${mid}-nom" placeholder="ex. Connexion aux données"
value="${esc(nom)}"
oninput="document.getElementById('${mid}-preview').textContent=this.value||'Nouveau module'">
</div>
<div class="fm-field">
<label class="fm-lbl">Durée</label>
<input class="fm-inp" type="text" id="${mid}-duree" placeholder="ex. 45 min" value="${esc(data?.duree||'')}">
</div>
<div class="fm-field subch-obj-field">
<label class="fm-lbl">Objectif</label>
<input class="fm-inp" type="text" id="${mid}-objectif" placeholder="Ce que l'apprenant maîtrisera…" value="${esc(data?.objectif||'')}">
</div>
</div>
<div class="ctype-label">Type de contenu</div>
<div class="ctype-tabs">
<button class="ctype-tab ${ctype==='upload'?'active':''}" type="button" onclick="switchCtype('${mid}','upload')">📄 Fichier</button>
<button class="ctype-tab ${ctype==='lien'?'active':''}" type="button" onclick="switchCtype('${mid}','lien')">🔗 Lien / Intégration</button>
<button class="ctype-tab ${ctype==='scorm'?'active':''}" type="button" onclick="switchCtype('${mid}','scorm')">⚙️ SCORM</button>
</div>
<!-- PANEL : Fichier -->
<div class="ctype-panel ${ctype==='upload'?'active':''}" id="${mid}-panel-upload">
<label class="upload-area" for="${mid}-file-input" onclick="">
<div class="upload-area-icon">📎</div>
<div class="upload-area-text">Glissez un fichier ou cliquez pour choisir<br><span style="font-size:11px;opacity:.7">PDF, PowerPoint (.pptx), export Canva</span></div>
<input type="file" id="${mid}-file-input" accept=".pdf,.ppt,.pptx,.key" onchange="handleFileUpload('${mid}',this)">
</label>
<div class="upload-filename" id="${mid}-filename">${data?.upload?.nom ? '✅ ' + data.upload.nom : ''}</div>
<input type="hidden" id="${mid}-upload-nom" value="${esc(data?.upload?.nom||'')}">
<div class="doctype-pills">
${['PDF','PowerPoint','Canva'].map(t =>
`<span class="doctype-pill ${(data?.upload?.typeDoc||'PDF')===t?'active':''}"
id="${mid}-dtype-${t}" onclick="setDocType('${mid}','${t}')">${t}</span>`
).join('')}
</div>
<input type="hidden" id="${mid}-upload-type" value="${esc(data?.upload?.typeDoc||'PDF')}">
</div>
<!-- PANEL : Lien / Intégration -->
<div class="ctype-panel ${ctype==='lien'?'active':''}" id="${mid}-panel-lien">
<div class="fm-field" style="margin-bottom:10px">
<label class="fm-lbl">URL de la ressource</label>
<input class="fm-inp" type="url" id="${mid}-lien-url" placeholder="https://www.canva.com/…" value="${esc(data?.lien?.url||'')}"
oninput="previewEmbed('${mid}',this.value)">
</div>
<div class="service-pills">
${['Canva','Genially','Prezi','YouTube','Vimeo','Autre'].map(s =>
`<span class="service-pill ${(data?.lien?.service||'Canva')===s?'active':''}"
id="${mid}-svc-${s}" onclick="setService('${mid}','${s}')">${s}</span>`
).join('')}
</div>
<input type="hidden" id="${mid}-lien-service" value="${esc(data?.lien?.service||'Canva')}">
<div class="embed-preview ${data?.lien?.url?'visible':''}" id="${mid}-embed-preview">
${data?.lien?.url ? `<iframe src="${data.lien.url}" style="width:100%;height:100%;border:none" allowfullscreen></iframe>` : '🔗 Aperçu de l\'intégration'}
</div>
</div>
<!-- PANEL : SCORM -->
<div class="ctype-panel ${ctype==='scorm'?'active':''}" id="${mid}-panel-scorm">
<label class="scorm-zone" for="${mid}-scorm-input">
<div class="scorm-zone-icon">⚙️</div>
<div class="scorm-zone-text">Déposez votre package SCORM (.zip)<br><span style="font-size:11px;opacity:.8">Compatible SCORM 1.2 et SCORM 2004</span></div>
<input type="file" id="${mid}-scorm-input" accept=".zip" onchange="handleScormUpload('${mid}',this)">
</label>
<div class="scorm-filename" id="${mid}-scorm-name">${data?.scorm?.nom ? '✅ ' + data.scorm.nom : ''}</div>
<input type="hidden" id="${mid}-scorm-nom" value="${esc(data?.scorm?.nom||'')}">
<div class="scorm-meta" style="margin-top:10px">
<div class="fm-field" style="flex:1">
<label class="fm-lbl">Version SCORM</label>
<select class="fm-sel" id="${mid}-scorm-ver" style="font-size:12px;padding:7px 10px">
<option ${(data?.scorm?.version||'1.2')==='1.2'?'selected':''}>1.2</option>
<option ${data?.scorm?.version==='2004'?'selected':''}>2004</option>
</select>
</div>
<div class="fm-field" style="flex:2">
<label class="fm-lbl">Point d'entrée (index.html)</label>
<input class="fm-inp" type="text" id="${mid}-scorm-entry" placeholder="index.html" style="font-size:12px" value="${esc(data?.scorm?.entry||'index.html')}">
</div>
</div>
</div>
</div>`;
container.appendChild(div);
}
function toggleSousChapitre(mid) {
const body = document.getElementById(mid + '-body');
const tog = document.getElementById(mid + '-toggle');
const open = body.classList.toggle('open');
tog.classList.toggle('open', open);
}
// ── Switcher de type de contenu ──
function switchCtype(mid, type) {
['upload','lien','scorm'].forEach(t => {
document.getElementById(mid + '-panel-' + t)?.classList.toggle('active', t === type);
});
const tabs = document.querySelectorAll(`#${mid}-body .ctype-tab`);
tabs.forEach(tab => tab.classList.toggle('active', tab.textContent.toLowerCase().includes(
type === 'upload' ? 'fich' : type === 'lien' ? 'lien' : 'scorm'
)));
document.getElementById(mid + '-ctype') && (document.getElementById(mid + '-ctype').value = type);
}
// ── Upload fichier ──
function handleFileUpload(mid, input) {
const file = input.files[0];
if (!file) return;
document.getElementById(mid + '-filename').textContent = '✅ ' + file.name;
document.getElementById(mid + '-upload-nom').value = file.name;
// Détecter le type
const ext = file.name.split('.').pop().toLowerCase();
const typeMap = { pdf: 'PDF', ppt: 'PowerPoint', pptx: 'PowerPoint', key: 'Canva' };
const detected = typeMap[ext] || 'PDF';
setDocType(mid, detected);
}
function setDocType(mid, type) {
['PDF','PowerPoint','Canva'].forEach(t => {
document.getElementById(mid + '-dtype-' + t)?.classList.toggle('active', t === type);
});
document.getElementById(mid + '-upload-type').value = type;
}
// ── Embed preview ──
function previewEmbed(mid, url) {
const box = document.getElementById(mid + '-embed-preview');
if (!box) return;
if (!url) { box.classList.remove('visible'); box.innerHTML = '🔗 Aperçu de l\'intégration'; return; }
box.classList.add('visible');
box.innerHTML = `<iframe src="${url}" style="width:100%;height:100%;border:none" allowfullscreen loading="lazy"></iframe>`;
}
function setService(mid, svc) {
document.querySelectorAll(`#${mid}-panel-lien .service-pill`).forEach(p =>
p.classList.toggle('active', p.textContent === svc)
);
document.getElementById(mid + '-lien-service').value = svc;
}
// ── Upload SCORM ──
function handleScormUpload(mid, input) {
const file = input.files[0];
if (!file) return;
document.getElementById(mid + '-scorm-name').textContent = '✅ ' + file.name;
document.getElementById(mid + '-scorm-nom').value = file.name;
}
// ── Helper escape ──
function esc(str) { return (str||'').replace(/"/g,'"').replace(/</g,'<'); }
// ── Collecte des chapitres pour la sauvegarde ──
function collectChapitres() {
const chapitres = [];
document.querySelectorAll('#ch-list .ch-block').forEach(chEl => {
const cid = chEl.id;
const modules = [];
chEl.querySelectorAll('.subch-block').forEach(mEl => {
const mid = mEl.id;
// Déterminer le type actif
let ctype = 'upload';
if (document.getElementById(mid + '-panel-lien')?.classList.contains('active')) ctype = 'lien';
if (document.getElementById(mid + '-panel-scorm')?.classList.contains('active')) ctype = 'scorm';
modules.push({
id: mid,
nom: document.getElementById(mid + '-nom')?.value?.trim() || '',
objectif: document.getElementById(mid + '-objectif')?.value?.trim() || '',
duree: document.getElementById(mid + '-duree')?.value?.trim() || '',
contentType: ctype,
upload: {
nom: document.getElementById(mid + '-upload-nom')?.value || '',
typeDoc: document.getElementById(mid + '-upload-type')?.value || 'PDF',
},
lien: {
url: document.getElementById(mid + '-lien-url')?.value?.trim() || '',
service: document.getElementById(mid + '-lien-service')?.value || 'Canva',
},
scorm: {
nom: document.getElementById(mid + '-scorm-nom')?.value || '',
version: document.getElementById(mid + '-scorm-ver')?.value || '1.2',
entry: document.getElementById(mid + '-scorm-entry')?.value || 'index.html',
}
});
});
chapitres.push({
id: cid,
nom: document.getElementById(cid + '-nom')?.value?.trim() || '',
objectif: document.getElementById(cid + '-objectif')?.value?.trim() || '',
duree: document.getElementById(cid + '-duree')?.value?.trim() || '',
modules
});
});
return chapitres;
}
// ── Sauvegarder ──
function saveFormation() {
const titre = document.getElementById('fm-titre').value.trim();
if (!titre) { alert('Le titre est obligatoire.'); document.getElementById('fm-titre').focus(); return; }
const chapitres = collectChapitres();
const totalModules = chapitres.reduce((s,c) => s + c.modules.length, 0);
const isAdminRole = document.body.classList.contains('role-admin');
const f = {
id: _editId || Date.now().toString(),
publie: isAdminRole,
titre,
emoji: document.getElementById('fm-emoji').value || '📚',
domaine: document.getElementById('fm-domaine').value,
duree: document.getElementById('fm-duree').value,
prix: document.getElementById('fm-prix').value,
modalite: document.getElementById('fm-modalite').value,
niveau: document.getElementById('fm-niveau').value,
desc: document.getElementById('fm-desc').value,
cpf: document.getElementById('fm-cpf').checked,
opco: document.getElementById('fm-opco').checked,
plan: document.getElementById('fm-plan').checked,
certif: document.getElementById('fm-certif').checked,
formateur: document.getElementById('fm-formateur').value,
formBio: document.getElementById('fm-form-bio').value,
chapitres,
totalModules,
date: _editId ? (formations.find(x=>x.id===_editId)?.date || new Date().toLocaleDateString('fr-FR')) : new Date().toLocaleDateString('fr-FR')
};
if (_editId) {
const idx = formations.findIndex(x => x.id === _editId);
if (idx !== -1) formations[idx] = f; else formations.unshift(f);
} else {
formations.unshift(f);
}
saveFormations();
closeFormModal();
renderGestion();
// Recharger le catalogue dynamique si on y est
if (document.getElementById('view-catalogue').classList.contains('active')) renderCatalogueDyn();
}
// ── Publier une formation (admin uniquement) ──
function publierFormation(id) {
const f = formations.find(x => x.id === id);
if (!f) return;
f.publie = true;
saveFormations();
renderGestion();
renderCatalogueDyn();
showToast('✓ Formation publiée dans le catalogue');
}
// ── Supprimer ──
function deleteFormation(id) {
if (!confirm('Supprimer définitivement cette formation ?')) return;
formations = formations.filter(x => x.id !== id);
saveFormations();
renderGestion();
}
// ── Tag helpers ──
function modaliteTag(m) {
const map = { 'Présentiel': 'tag-green', 'Distanciel': 'tag-blue', 'E-learning': 'tag-blue', 'Hybride': 'tag-amber' };
return `<span class="tag ${map[m]||'tag-surface'}">${m}</span>`;
}
// ── Rendu vue Gestion ──
function renderGestion() {
const total = formations.length;
const heures = formations.reduce((s,f) => s + (parseInt(f.duree)||0), 0);
const cpf = formations.filter(f => f.cpf).length;
const mods = formations.reduce((s,f) => s + (f.totalModules||0), 0);
const set = (id,v) => { const el=document.getElementById(id); if(el) el.textContent=v; };
set('gst-total', total); set('gst-heures', heures?heures+'h':'—'); set('gst-cpf', cpf); set('gst-res', mods);
const badge = document.getElementById('badge-gestion'); if(badge) badge.textContent = total;
const container = document.getElementById('gestion-container'); if(!container) return;
if (!total) {
container.innerHTML = '<div class="gestion-empty-state"><div class="gestion-empty-icon">📚</div>'
+ '<div class="gestion-empty-text">Aucune formation pour l\'instant.<br>Cliquez sur <strong>"+ Nouvelle formation"</strong> pour commencer.</div>'
+ '<button class="btn btn-solid" onclick="openFormModal()">+ Créer ma première formation</button></div>';
return;
}
const modalMap = {'Présentiel':'tag-green','Distanciel':'tag-blue','E-learning':'tag-blue','Hybride':'tag-amber'};
const ctypeIcon = {upload:'📄',lien:'🔗',scorm:'⚙️'};
const isAdmin = document.body.classList.contains('role-admin');
function makeCard(f) {
var chSummary = '';
(f.chapitres||[]).forEach(function(ch) {
var mSummary = '';
(ch.modules||[]).forEach(function(m) {
mSummary += '<div style="display:flex;align-items:center;gap:6px;padding:4px 0;border-top:1px solid var(--border);margin-top:4px">'
+ '<span style="font-size:13px">'+(ctypeIcon[m.contentType||'upload']||'📄')+'</span>'
+ '<span style="font-size:12px;flex:1;color:var(--muted)">'+(m.nom||'Module sans nom')+'</span>'
+ (m.duree?'<span style="font-size:11px;color:var(--muted-2)">'+m.duree+'</span>':'')
+ '</div>';
});
chSummary += '<div style="margin-bottom:8px">'
+ '<div style="font-size:12px;font-weight:600;color:var(--ink);margin-bottom:2px">📖 '+(ch.nom||'Chapitre')+(ch.duree?' · '+ch.duree:'')+'</div>'
+ (ch.objectif?'<div style="font-size:11px;color:var(--muted);margin-bottom:4px;font-style:italic">'+ch.objectif+'</div>':'')
+ mSummary+'</div>';
});
var pubBadge = f.publie
? '<span class="tag" style="background:#e6f0ff;color:#0066cc;font-size:10px">● Publié</span>'
: '<span class="tag" style="background:#fff4f0;color:#ff6633;font-size:10px">⏳ En attente</span>';
var pubBtn = (!f.publie && isAdmin)
? '<button class="btn btn-xs" style="background:#fff4f0;color:#ff6633;border-color:#ff6633" onclick="publierFormation(\''+f.id+'\')">✓ Publier</button>'
: '';
var modalTag = '<span class="tag '+(modalMap[f.modalite]||'tag-surface')+'">'+(f.modalite||'')+'</span>';
return '<div class="gestion-cours-card" style="flex-direction:column;align-items:stretch;gap:0">'
+ '<div style="display:flex;align-items:center;gap:20px;margin-bottom:'+(f.chapitres&&f.chapitres.length?'16px':'0')+'">'
+ '<div class="gestion-cours-emoji">'+(f.emoji||'📚')+'</div>'
+ '<div class="gestion-cours-info">'
+ '<div class="gestion-cours-title">'+f.titre+'</div>'
+ '<div class="gestion-cours-meta">'
+ '<span>📂 '+(f.domaine||'')+'</span>'
+ (f.duree?'<span>⏱ '+f.duree+'h</span>':'')
+ (f.prix?'<span>💶 '+f.prix+'</span>':'')
+ (f.formateur?'<span>👤 '+f.formateur+'</span>':'')
+ (f.chapitres&&f.chapitres.length?'<span>📖 '+f.chapitres.length+' chapitre(s)</span>':'')
+ (f.totalModules?'<span>📋 '+f.totalModules+' module(s)</span>':'')
+ '</div>'
+ '<div style="display:flex;gap:6px;flex-wrap:wrap;margin-top:6px">'
+ modalTag
+ (f.cpf?'<span class="tag tag-ink">CPF</span>':'')
+ (f.opco?'<span class="tag tag-surface">OPCO</span>':'')
+ (f.certif?'<span class="tag tag-green">Certifiant</span>':'')
+ '<span class="tag tag-surface">'+(f.niveau||'Tous niveaux')+'</span>'
+ pubBadge
+ '</div></div>'
+ '<div class="gestion-cours-actions" style="align-self:flex-start;display:flex;flex-wrap:wrap;gap:6px">'
+ pubBtn
+ '<button class="btn btn-xs" onclick="openFormModal(\''+f.id+'\')">✏️ Modifier</button>'
+ '<button class="btn btn-xs btn-danger" onclick="deleteFormation(\''+f.id+'\')">🗑</button>'
+ '</div></div>'
+ (f.chapitres&&f.chapitres.length?'<div style="border-top:1px solid var(--border);padding-top:14px">'+chSummary+'</div>':'')
+ '</div>';
}
container.innerHTML = '<div class="gestion-cours-list">'+formations.map(makeCard).join('')+'</div>';
}
function renderCatalogueDyn() {
const container = document.getElementById('dyn-catalogue-cards');
if (!container) return;
const isAdm = document.body.classList.contains('role-admin');
const visible = isAdm ? formations : formations.filter(f => f.publie === true);
if (!visible.length) { container.innerHTML = ''; return; }
container.innerHTML = visible.map(f => {
const tagCls = { 'Distanciel':'tag-blue','E-learning':'tag-blue','Hybride':'tag-amber' }[f.modalite] || 'tag-green';
const badge = f.cpf ? '<span class="cat-card-cpf">CPF</span>' : (f.certif ? '<span class="cat-card-cpf" style="background:var(--green)">Certifiant</span>' : '');
return `
<div class="catalogue-card" onclick="nav('fiche')">
<div class="cat-card-thumb">${f.emoji||'📚'}${badge}</div>
<div class="cat-card-body">
<div class="cat-card-domain">${f.domaine}</div>
<div class="cat-card-name">${f.titre}</div>
<div style="display:flex;gap:6px;flex-wrap:wrap">
${f.duree ? `<span class="tag tag-surface">${f.duree}h</span>` : ''}
<span class="tag ${tagCls}">${f.modalite}</span>
</div>
<div class="cat-card-footer">
<div class="cat-price">${f.prix||'—'}</div>
</div>
</div>
</div>`;
}).join('');
}
// ── Injecter le conteneur dynamique dans la grille du catalogue ──
(function injectDynCatalogue() {
const grid = document.querySelector('#view-catalogue .catalogue-grid');
if (!grid) return;
const dyn = document.createElement('div');
dyn.id = 'dyn-catalogue-cards';
dyn.style.display = 'contents';
grid.prepend(dyn);
renderCatalogueDyn();
})();
// ── Init ──
saveFormations(); // met le badge à jour au chargement
</script>
<!-- ═══ MODAL SESSIONS ═══ -->
<div class="sess-modal-backdrop hidden" id="sess-modal-backdrop" onclick="if(event.target===this)closeSessModal()">
<div class="sess-modal">
<div class="sess-modal-hdr">
<div class="sess-modal-title" id="sess-modal-title">Nouvelle session</div>
<button class="sess-modal-x" onclick="closeSessModal()">✕</button>
</div>
<div class="sess-modal-bdy">
<div><label class="sf-lbl">Titre de la session *</label>
<input class="sf-inp" type="text" id="sf-titre" placeholder="ex. Excel Avancé — Groupe A"></div>
<div class="sf-g2">
<div><label class="sf-lbl">Formation liée</label>
<input class="sf-inp" type="text" id="sf-formation" placeholder="ex. Excel Avancé & Power BI"></div>
<div><label class="sf-lbl">Formateur</label>
<input class="sf-inp" type="text" id="sf-formateur" placeholder="ex. Pierre Leblanc"></div>
</div>
<div class="sf-g3">
<div><label class="sf-lbl">Date *</label>
<input class="sf-inp" type="date" id="sf-date"></div>
<div><label class="sf-lbl">Heure début</label>
<input class="sf-inp" type="time" id="sf-hdebut" value="09:00"></div>
<div><label class="sf-lbl">Heure fin</label>
<input class="sf-inp" type="time" id="sf-hfin" value="12:00"></div>
</div>
<div class="sf-g2">
<div><label class="sf-lbl">Lieu / Salle</label>
<input class="sf-inp" type="text" id="sf-lieu" placeholder="ex. Salle Émeraude"></div>
<div><label class="sf-lbl">Modalité</label>
<select class="sf-sel" id="sf-modalite">
<option>Présentiel</option><option>Distanciel</option><option>Hybride</option><option>E-learning</option>
</select></div>
</div>
<div class="sf-g3">
<div><label class="sf-lbl">Capacité max</label>
<input class="sf-inp" type="number" id="sf-capacite" value="12" min="1"></div>
<div><label class="sf-lbl">Inscrits</label>
<input class="sf-inp" type="number" id="sf-inscrits" value="0" min="0"></div>
<div><label class="sf-lbl">Statut</label>
<select class="sf-sel" id="sf-statut">
<option value="planifie">Planifié</option>
<option value="encours">En cours</option>
<option value="termine">Terminé</option>
<option value="annule">Annulé</option>
</select></div>
</div>
<div class="sf-g2">
<div><label class="sf-lbl">Couleur calendrier</label>
<div style="display:flex;gap:8px;align-items:center;margin-top:4px">
<input type="color" id="sf-color" value="#0066cc" style="width:38px;height:34px;border:1px solid var(--border);border-radius:8px;padding:2px;cursor:pointer">
<div style="display:flex;gap:6px">
<div style="width:22px;height:22px;border-radius:50%;background:#0066cc;cursor:pointer" onclick="document.getElementById('sf-color').value='#0066cc'" title="Bleu ITD"></div>
<div style="width:22px;height:22px;border-radius:50%;background:#ff6633;cursor:pointer" onclick="document.getElementById('sf-color').value='#ff6633'" title="Orange ITD"></div>
<div style="width:22px;height:22px;border-radius:50%;background:#1a7f4f;cursor:pointer" onclick="document.getElementById('sf-color').value='#1a7f4f'" title="Vert"></div>
<div style="width:22px;height:22px;border-radius:50%;background:#9d174d;cursor:pointer" onclick="document.getElementById('sf-color').value='#9d174d'" title="Rose"></div>
</div>
</div></div>
<div><label class="sf-lbl">Notes internes</label>
<input class="sf-inp" type="text" id="sf-notes" placeholder="Matériel, remarques…"></div>
</div>
</div>
<div class="sess-modal-ftr">
<button class="btn" onclick="closeSessModal()">Annuler</button>
<button class="btn btn-solid" onclick="saveSessModal()">💾 Enregistrer</button>
</div>
</div>
</div>
<script>
// ═══════════════════════════════════════
// MODULE SESSIONS — L'ITD Formation
// ═══════════════════════════════════════
const SESS_KEY = 'litd_sessions_v2';
function loadSessions() {
try { return JSON.parse(localStorage.getItem(SESS_KEY)) || getDefaultSessions(); }
catch(e) { return getDefaultSessions(); }
}
function saveSessions(arr) { localStorage.setItem(SESS_KEY, JSON.stringify(arr)); }
function getDefaultSessions() {
return [
{id:'s1',titre:'Excel Avancé — Groupe A',formation:'Excel Avancé & Power BI',formateur:'Pierre Leblanc',dateDebut:'2026-03-25',heureDebut:'14:00',heureFin:'17:00',lieu:'Salle Émeraude',modalite:'Présentiel',inscrits:12,capacite:12,statut:'encours',color:'#0066cc',notes:''},
{id:'s2',titre:'Management — Module 3',formation:'Management Niveau 1',formateur:'A. Moreau',dateDebut:'2026-03-26',heureDebut:'09:00',heureFin:'12:00',lieu:'Distanciel',modalite:'Distanciel',inscrits:8,capacite:15,statut:'planifie',color:'#ff6633',notes:''},
{id:'s3',titre:'Prise de parole — Init.',formation:'Prise de parole en public',formateur:'L. Simon',dateDebut:'2026-03-27',heureDebut:'10:00',heureFin:'13:00',lieu:'Salle Améthyste',modalite:'Hybride',inscrits:6,capacite:10,statut:'planifie',color:'#9d174d',notes:''},
{id:'s4',titre:'Droit du travail — Update',formation:'Droit du Travail',formateur:'C. Faure',dateDebut:'2026-03-28',heureDebut:'14:00',heureFin:'17:30',lieu:'Salle Rubis',modalite:'Présentiel',inscrits:10,capacite:12,statut:'planifie',color:'#0066cc',notes:''},
{id:'s5',titre:'Excel Avancé — Groupe B',formation:'Excel Avancé & Power BI',formateur:'Pierre Leblanc',dateDebut:'2026-03-31',heureDebut:'09:00',heureFin:'12:00',lieu:'Salle Émeraude',modalite:'Présentiel',inscrits:9,capacite:12,statut:'planifie',color:'#0066cc',notes:''},
{id:'s6',titre:'IA & Productivité — Gr. A',formation:'IA & Productivité',formateur:'L. Simon',dateDebut:'2026-04-14',heureDebut:'14:00',heureFin:'17:00',lieu:'Distanciel',modalite:'Distanciel',inscrits:11,capacite:20,statut:'planifie',color:'#ff6633',notes:''},
];
}
let sessions = loadSessions();
let calYear = new Date().getFullYear();
let calMonth = new Date().getMonth();
const MONTHS_FR = ['Janvier','Février','Mars','Avril','Mai','Juin','Juillet','Août','Septembre','Octobre','Novembre','Décembre'];
// ── Calendrier ──
function renderCalendar() {
const titleEl = document.getElementById('cal-month-title');
if (titleEl) titleEl.textContent = MONTHS_FR[calMonth] + ' ' + calYear;
const body = document.getElementById('cal-body');
if (!body) return;
const firstDay = new Date(calYear, calMonth, 1).getDay();
const offset = firstDay === 0 ? 6 : firstDay - 1;
const daysInMonth = new Date(calYear, calMonth + 1, 0).getDate();
const daysInPrev = new Date(calYear, calMonth, 0).getDate();
const today = new Date();
const todayStr = today.getFullYear() + '-' + String(today.getMonth()+1).padStart(2,'0') + '-' + String(today.getDate()).padStart(2,'0');
// Grouper les sessions par date
const sessMap = {};
sessions.forEach(s => {
if (!s.dateDebut) return;
if (!sessMap[s.dateDebut]) sessMap[s.dateDebut] = [];
sessMap[s.dateDebut].push(s);
});
let html = '';
for (let i = 0; i < 42; i++) {
let day, isOther = false;
if (i < offset) { day = daysInPrev - offset + i + 1; isOther = true; }
else if (i >= offset + daysInMonth) { day = i - offset - daysInMonth + 1; isOther = true; }
else { day = i - offset + 1; }
const dateStr = !isOther
? calYear + '-' + String(calMonth+1).padStart(2,'0') + '-' + String(day).padStart(2,'0')
: '';
const isToday = dateStr === todayStr;
let evHtml = '';
if (!isOther && sessMap[dateStr]) {
sessMap[dateStr].slice(0,3).forEach(s => {
evHtml += '<div class="cal-event" style="background:' + s.color + '22;color:' + s.color + ';font-weight:500;cursor:pointer" '
+ 'onclick="event.stopPropagation();openSessModal(\'' + s.id + '\')" '
+ 'title="' + s.titre + '">'
+ s.heureDebut + ' ' + s.titre + '</div>';
});
if (sessMap[dateStr].length > 3)
evHtml += '<div style="font-size:10px;color:var(--muted);padding:2px 6px">+' + (sessMap[dateStr].length-3) + ' autres</div>';
}
const dayNum = isToday
? '<div class="cal-date"><span class="cal-date-today">' + day + '</span></div>'
: '<div class="cal-date">' + day + '</div>';
const cls = 'cal-day' + (isOther?' other-month':'') + (isToday?' today':'');
const clickHandler = !isOther ? 'onclick="openSessModalForDate(\'' + dateStr + '\')"' : '';
html += '<div class="' + cls + '" ' + clickHandler + ' style="background:var(--white);padding:8px;min-height:90px;cursor:pointer">'
+ dayNum + evHtml + '</div>';
}
body.innerHTML = html;
}
function calNav(dir) {
calMonth += dir;
if (calMonth > 11) { calMonth = 0; calYear++; }
if (calMonth < 0) { calMonth = 11; calYear--; }
renderCalendar();
}
function calGoToday() {
const n = new Date();
calYear = n.getFullYear(); calMonth = n.getMonth();
renderCalendar();
}
// ── Liste ──
function renderSessions() {
const cont = document.getElementById('sess-list-container');
if (!cont) return;
const fStat = document.getElementById('sess-f-statut')?.value || '';
const fMod = document.getElementById('sess-f-modal')?.value || '';
const fSrch = (document.getElementById('sess-f-search')?.value || '').toLowerCase();
let list = sessions.filter(s => {
if (fStat && s.statut !== fStat) return false;
if (fMod && s.modalite !== fMod) return false;
if (fSrch && !s.titre.toLowerCase().includes(fSrch)
&& !s.formation.toLowerCase().includes(fSrch)
&& !s.formateur.toLowerCase().includes(fSrch)) return false;
return true;
}).sort((a,b) => (a.dateDebut+a.heureDebut).localeCompare(b.dateDebut+b.heureDebut));
if (!list.length) { cont.innerHTML = '<div style="padding:40px;text-align:center;color:var(--muted);font-size:13px">Aucune session trouvée.</div>'; return; }
const sCfg = {
planifie:{tag:'tag-surface',label:'Planifié'},
encours: {tag:'tag-blue', label:'En cours'},
termine: {tag:'tag-green', label:'Terminé'},
annule: {tag:'tag-red', label:'Annulé'},
};
cont.innerHTML = list.map(s => {
const cfg = sCfg[s.statut] || sCfg.planifie;
const df = s.dateDebut ? new Date(s.dateDebut+'T12:00').toLocaleDateString('fr-FR',{weekday:'short',day:'numeric',month:'short'}) : '—';
return '<div class="sess-card">'
+ '<div class="sess-card-color" style="background:' + s.color + '"></div>'
+ '<div class="sess-card-info">'
+ '<div class="sess-card-title">' + s.titre + '</div>'
+ '<div class="sess-card-meta">📅 ' + df + ' · ' + s.heureDebut + '–' + s.heureFin
+ ' · 📍 ' + s.lieu + ' · 👤 ' + s.formateur
+ ' · 👥 ' + s.inscrits + '/' + s.capacite + '</div>'
+ '<div style="display:flex;gap:6px;margin-top:6px;flex-wrap:wrap">'
+ '<span class="tag ' + cfg.tag + '">' + cfg.label + '</span>'
+ '<span class="tag tag-surface">' + s.modalite + '</span>'
+ '<span class="tag" style="background:' + s.color + '22;color:' + s.color + '">' + s.formation + '</span>'
+ '</div></div>'
+ '<div class="sess-card-actions">'
+ '<button class="btn btn-xs" onclick="openSessModal(\'' + s.id + '\')">✏️</button>'
+ '<button class="btn btn-xs" onclick="duplicateSess(\'' + s.id + '\')">⧉</button>'
+ '<button class="btn btn-xs btn-danger" onclick="deleteSess(\'' + s.id + '\')">🗑</button>'
+ '</div></div>';
}).join('');
}
// ── KPIs ──
function updateSessKpis() {
const today = new Date().toISOString().substring(0,10);
const set = (id,v) => { const el=document.getElementById(id); if(el) el.textContent=v; };
set('skpi-total', sessions.length);
set('skpi-encours', sessions.filter(s=>s.dateDebut===today&&s.statut!=='annule').length);
set('skpi-avenir', sessions.filter(s=>s.dateDebut>today&&s.statut==='planifie').length);
set('skpi-inscrits',sessions.reduce((sum,s)=>sum+(parseInt(s.inscrits)||0),0));
}
// ── Modal ──
let _sessEditId = null;
function openSessModal(id) {
_sessEditId = id || null;
const s = id ? sessions.find(x=>x.id===id) : null;
const el = document.getElementById('sess-modal-title');
if (el) el.textContent = s ? 'Modifier la session' : 'Nouvelle session';
const g = (fid,val) => { const e=document.getElementById(fid); if(e) e.value=val||''; };
g('sf-titre', s?.titre || '');
g('sf-formation',s?.formation || '');
g('sf-formateur',s?.formateur || '');
g('sf-date', s?.dateDebut || '');
g('sf-hdebut', s?.heureDebut|| '09:00');
g('sf-hfin', s?.heureFin || '12:00');
g('sf-lieu', s?.lieu || '');
g('sf-modalite', s?.modalite || 'Présentiel');
g('sf-capacite', s?.capacite || 12);
g('sf-inscrits', s?.inscrits || 0);
g('sf-statut', s?.statut || 'planifie');
g('sf-color', s?.color || '#0066cc');
g('sf-notes', s?.notes || '');
document.getElementById('sess-modal-backdrop').classList.remove('hidden');
}
function openSessModalForDate(dateStr) { openSessModal(null); setTimeout(()=>{ const e=document.getElementById('sf-date'); if(e)e.value=dateStr; },50); }
function closeSessModal() {
document.getElementById('sess-modal-backdrop').classList.add('hidden');
_sessEditId = null;
}
function saveSessModal() {
const g = id => document.getElementById(id)?.value?.trim() || '';
const titre = g('sf-titre');
if (!titre) { alert('Le titre est obligatoire.'); return; }
const s = {
id: _sessEditId || 's'+Date.now(),
titre,
formation: g('sf-formation'),
formateur: g('sf-formateur'),
dateDebut: g('sf-date'),
heureDebut: g('sf-hdebut'),
heureFin: g('sf-hfin'),
lieu: g('sf-lieu'),
modalite: g('sf-modalite'),
capacite: parseInt(g('sf-capacite'))||12,
inscrits: parseInt(g('sf-inscrits'))||0,
statut: g('sf-statut'),
color: g('sf-color')||'#0066cc',
notes: g('sf-notes'),
};
if (_sessEditId) {
const idx = sessions.findIndex(x=>x.id===_sessEditId);
if (idx!==-1) sessions[idx]=s; else sessions.unshift(s);
} else { sessions.unshift(s); }
saveSessions(sessions);
closeSessModal();
renderCalendar();
renderSessions();
updateSessKpis();
showToast(_sessEditId ? '✓ Session modifiée' : '✓ Session créée');
}
function duplicateSess(id) {
const s = sessions.find(x=>x.id===id);
if (!s) return;
sessions.unshift({...s, id:'s'+Date.now(), titre:s.titre+' (copie)', statut:'planifie'});
saveSessions(sessions);
renderCalendar(); renderSessions(); updateSessKpis();
showToast('⧉ Session dupliquée');
}
function deleteSess(id) {
const s = sessions.find(x=>x.id===id);
if (!s || !confirm('Supprimer "'+s.titre+'" ?')) return;
sessions = sessions.filter(x=>x.id!==id);
saveSessions(sessions);
renderCalendar(); renderSessions(); updateSessKpis();
showToast('🗑 Session supprimée');
}
window.addEventListener('DOMContentLoaded', () => { updateSessKpis(); });
</script>
</body>
</html>