ReflAgent / templates /index-old.html
OUAREDAEK's picture
Upload templates/index-old.html with huggingface_hub
5315d44 verified
Raw
History Blame Contribute Delete
94.9 kB
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dashboard Réflexif – ReflAgent</title>
<style>
/* ================= STYLES DE BASE ================= */
body {
font-family: 'Segoe UI', Arial, sans-serif;
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
margin: 0;
padding: 20px;
color: #333;
}
h2, h3, h4 {
color: #1976D2;
margin-top: 0;
}
h2 {
background: linear-gradient(90deg, #1976D2, #2196F3);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
padding: 10px 0;
border-bottom: 2px solid #e3f2fd;
margin-bottom: 25px;
}
h3 {
background: #e3f2fd;
padding: 12px 15px;
border-radius: 8px;
margin: 25px 0 15px;
display: flex;
align-items: center;
gap: 10px;
}
h3::before {
content: '';
display: inline-block;
width: 6px;
height: 24px;
background: #1976D2;
border-radius: 3px;
}
#reflection {
background: white;
padding: 30px;
border-radius: 20px;
max-width: 1400px;
margin: auto;
box-shadow: 0 10px 40px rgba(0,0,0,0.08);
border: 1px solid #e0e0e0;
}
/* ================= SYSTEME D'ONGLETS ================= */
.tabs-container {
margin-bottom: 30px;
}
.tabs {
display: flex;
background: #f8f9fa;
border-radius: 12px;
padding: 5px;
margin-bottom: 20px;
border: 1px solid #e0e0e0;
}
.tab-btn {
flex: 1;
padding: 15px 20px;
border: none;
background: transparent;
color: #666;
font-weight: 600;
font-size: 16px;
cursor: pointer;
border-radius: 8px;
transition: all 0.3s ease;
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
}
.tab-btn:hover {
background: rgba(25, 118, 210, 0.1);
color: #1976D2;
}
.tab-btn.active {
background: #1976D2;
color: white;
box-shadow: 0 3px 8px rgba(25, 118, 210, 0.2);
}
.tab-content {
display: none;
animation: fadeIn 0.5s ease;
}
.tab-content.active {
display: block;
}
/* ================= NAVIGATION ================= */
.nav {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30px;
background: linear-gradient(90deg, #e3f2fd, #f3e5f5);
padding: 15px 25px;
border-radius: 12px;
border: 1px solid #bbdefb;
}
#scenarioTitle {
font-size: 1.4em;
color: #333;
text-align: center;
flex-grow: 1;
margin: 0 20px;
}
button {
background: linear-gradient(135deg, #1976D2, #2196F3);
color: white;
border: none;
padding: 12px 25px;
border-radius: 10px;
cursor: pointer;
font-weight: 600;
font-size: 15px;
transition: all 0.3s ease;
box-shadow: 0 3px 8px rgba(25, 118, 210, 0.2);
display: flex;
align-items: center;
gap: 8px;
}
button:hover:not(:disabled) {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(25, 118, 210, 0.3);
}
button:active:not(:disabled) {
transform: translateY(0);
}
button:disabled {
opacity: 0.5;
cursor: not-allowed;
background: #b0bec5;
}
/* ================= BOUTON LECTURE CROISÉE ================= */
#crossReadersBtn {
background: linear-gradient(135deg, #43A047, #4CAF50) !important;
color: white;
border: none;
padding: 12px 25px;
border-radius: 10px;
cursor: pointer;
font-weight: 600;
font-size: 15px;
transition: all 0.3s ease;
box-shadow: 0 3px 8px rgba(67, 160, 71, 0.2);
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
width: 100%;
}
#crossReadersBtn:hover:not(:disabled) {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(67, 160, 71, 0.3);
background: linear-gradient(135deg, #388E3C, #43A047) !important;
}
#crossReadersBtn:active:not(:disabled) {
transform: translateY(0);
}
#crossReadersBtn:disabled {
opacity: 0.5;
cursor: not-allowed;
background: #b0bec5 !important;
}
/* ================= ONGLET 1: ÉVALUATEUR GDPR ================= */
#tab-evaluator .evaluator-info {
background: linear-gradient(135deg, #fff3e0, #ffe0b2);
padding: 25px;
border-radius: 15px;
border-left: 6px solid #ff9800;
margin: 20px 0;
}
#tab-evaluator .evaluator-info h4 {
color: #ff9800;
margin-top: 0;
margin-bottom: 20px;
display: flex;
align-items: center;
gap: 10px;
font-size: 20px;
}
.evaluator-field {
margin-bottom: 20px;
background: white;
padding: 20px;
border-radius: 10px;
border: 1px solid #FFE0B2;
}
.evaluator-field label {
display: block;
margin-bottom: 12px;
color: #333;
font-weight: 600;
font-size: 15px;
}
.evaluator-field input[type="text"],
.evaluator-field select {
width: 100%;
padding: 14px 18px;
border: 2px solid #e0e0e0;
border-radius: 8px;
font-size: 15px;
transition: all 0.3s;
box-sizing: border-box;
background: white;
}
.evaluator-field input[type="text"]:focus,
.evaluator-field select:focus {
outline: none;
border-color: #1976D2;
box-shadow: 0 0 0 3px rgba(25, 118, 210, 0.1);
}
.evaluator-field input:disabled,
.evaluator-field select:disabled {
background: #f5f5f5;
color: #999;
cursor: not-allowed;
}
.evaluator-field small {
display: block;
margin-top: 8px;
color: #666;
font-size: 13px;
font-style: italic;
}
/* Style pour les boutons radio GDPR */
.gdpr-options {
display: flex;
gap: 30px;
margin-top: 10px;
flex-wrap: wrap;
}
.gdpr-option {
display: flex;
align-items: center;
gap: 10px;
cursor: pointer;
padding: 12px 20px;
border-radius: 8px;
background: white;
border: 2px solid #e0e0e0;
transition: all 0.3s;
flex: 1;
min-width: 250px;
}
.gdpr-option:hover {
border-color: #1976D2;
background: #f0f7ff;
}
.gdpr-option input[type="radio"] {
width: 20px;
height: 20px;
cursor: pointer;
}
.gdpr-option span {
font-size: 15px;
font-weight: 500;
color: #333;
}
#anonymousMessage {
background: #E3F2FD;
padding: 18px;
border-radius: 10px;
margin-top: 20px;
border-left: 4px solid #1976D2;
animation: fadeIn 0.5s ease;
}
#anonymousMessage p {
margin: 0;
color: #1976D2;
font-size: 14px;
line-height: 1.5;
}
/* ================= ONGLET 2: DASHBOARD ÉTUDIANT ================= */
#tab-dashboard .manifest-item {
background: linear-gradient(135deg, #f9fbe7, #f0f4c3);
padding: 20px;
border-radius: 12px;
border-left: 6px solid #cddc39;
margin: 15px 0;
box-shadow: 0 3px 10px rgba(0,0,0,0.05);
}
.manifest-item b {
color: #689f38;
}
/* ================= COMPÉTENCES ================= */
.bar {
background: #f5f5f5;
border-radius: 12px;
height: 22px;
margin: 8px 0 20px;
overflow: hidden;
box-shadow: inset 0 2px 5px rgba(0,0,0,0.1);
}
.fill {
height: 100%;
border-radius: 12px;
color: white;
font-size: 13px;
text-align: right;
padding-right: 12px;
line-height: 22px;
font-weight: 600;
transition: width 0.5s ease;
position: relative;
overflow: hidden;
}
.fill::after {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.3));
}
/* ================= RADAR ================= */
#radar-container {
display: flex;
flex-wrap: wrap;
gap: 30px;
margin: 20px auto;
align-items: flex-start;
}
#radar {
flex: 1;
min-width: 500px;
max-width: 500px;
background: white;
border-radius: 15px;
padding: 20px;
box-shadow: 0 5px 20px rgba(0,0,0,0.08);
border: 1px solid #e0e0e0;
margin-left: auto;
}
/* ================= PROFILE-RADAR CONTAINER ================= */
.profile-radar-container {
display: flex;
gap: 30px;
margin: 20px 0;
align-items: flex-start;
}
.profile-radar-container .radar-bot-message {
flex: 1;
min-width: 300px;
max-width: 600px;
display: block !important;
}
.profile-radar-container #radar-container {
flex: 1;
min-width: 500px;
max-width: 500px;
margin-left: 0;
}
/* ================= GRILLE DES MÉTRIQUES RADAR ================= */
.radar-metrics-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
margin: 25px 0;
}
.metric-card {
background: white;
border-radius: 12px;
padding: 20px;
box-shadow: 0 4px 12px rgba(0,0,0,0.08);
border-left: 5px solid #1976D2;
transition: transform 0.3s, box-shadow 0.3s;
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
}
.metric-card:hover {
transform: translateY(-5px);
box-shadow: 0 8px 20px rgba(0,0,0,0.12);
}
.metric-icon {
font-size: 32px;
margin-bottom: 10px;
}
.metric-name {
font-weight: 600;
color: #333;
margin-bottom: 8px;
font-size: 16px;
}
.metric-value {
font-size: 28px;
font-weight: bold;
color: #1976D2;
margin: 5px 0;
}
.metric-value.high {
color: #43A047;
}
.metric-value.medium {
color: #FB8C00;
}
.metric-value.low {
color: #E53935;
}
.metric-description {
font-size: 13px;
color: #666;
margin-top: 8px;
line-height: 1.4;
}
/* ================= MESSAGE BOT RADAR ================= */
.radar-bot-message {
background: linear-gradient(135deg, #E3F2FD, #BBDEFB);
padding: 20px;
border-radius: 15px;
border-left: 6px solid #1976D2;
box-shadow: 0 4px 12px rgba(25, 118, 210, 0.1);
animation: fadeIn 0.5s ease-in;
}
.radar-bot-header {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 15px;
}
.radar-bot-icon {
font-size: 28px;
background: #1976D2;
color: white;
width: 50px;
height: 50px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
.radar-bot-title {
font-size: 18px;
font-weight: 600;
color: #1976D2;
margin: 0;
}
.radar-bot-content {
font-size: 16px;
line-height: 1.6;
color: #333;
margin: 0;
}
.radar-bot-insights {
background: white;
padding: 15px;
border-radius: 10px;
margin-top: 15px;
border: 1px solid #E3F2FD;
}
.radar-insight-item {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 8px;
padding: 8px 12px;
border-radius: 6px;
background: #F5F5F5;
transition: all 0.3s;
}
.radar-insight-item:hover {
background: #E3F2FD;
transform: translateX(5px);
}
.radar-insight-icon {
font-size: 20px;
width: 30px;
text-align: center;
}
.radar-insight-text {
flex-grow: 1;
font-size: 14px;
}
/* Légende radar */
.radar-legend {
display: flex;
justify-content: center;
gap: 25px;
margin: 15px 0 25px 0;
flex-wrap: wrap;
padding: 15px;
background: white;
border-radius: 10px;
border: 1px solid #e0e0e0;
box-shadow: 0 2px 8px rgba(0,0,0,0.05);
}
.legend-item {
display: flex;
align-items: center;
gap: 10px;
padding: 8px 15px;
background: #f8f9fa;
border-radius: 8px;
font-size: 14px;
font-weight: 500;
transition: transform 0.2s;
}
.legend-item:hover {
transform: translateY(-2px);
box-shadow: 0 3px 10px rgba(0,0,0,0.1);
}
.legend-color {
width: 20px;
height: 20px;
border-radius: 4px;
border: 2px solid white;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.legend-low {
background: linear-gradient(135deg, #e53935, #f44336);
}
.legend-medium {
background: linear-gradient(135deg, #fb8c00, #ff9800);
}
.legend-high {
background: linear-gradient(135deg, #43a047, #4caf50);
}
#radar-list {
list-style: none;
padding: 0;
margin: 15px 0;
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 10px;
}
#radar-list li {
background: #f8f9fa;
padding: 10px 15px;
border-radius: 8px;
font-size: 13px;
border-left: 4px solid #1976D2;
transition: all 0.3s;
display: flex;
justify-content: space-between;
align-items: center;
}
#radar-list li:hover {
background: #e3f2fd;
transform: translateX(5px);
}
#radar-list li .value {
font-weight: bold;
color: #1976D2;
font-size: 14px;
}
/* ================= HEATMAP ENRICHIE ================= */
.heatmap-and-indicators {
display: flex;
gap: 30px;
margin: 20px 0;
align-items: flex-start;
}
.heatmap-and-indicators .heatmap-analysis {
flex: 2;
min-width: 500px;
}
.heatmap-and-indicators .indicators-legend {
flex: 1;
min-width: 300px;
}
.heatmap-analysis {
background: white;
padding: 20px;
border-radius: 15px;
box-shadow: 0 5px 20px rgba(0,0,0,0.08);
border: 1px solid #e0e0e0;
margin: 10px auto;
}
.analysis-controls {
display: flex;
align-items: center;
gap: 15px;
margin-bottom: 20px;
padding: 15px;
background: #f8f9fa;
border-radius: 12px;
}
.analysis-controls label {
font-weight: 600;
color: #1976D2;
}
.analysis-controls select {
padding: 10px 20px;
border-radius: 8px;
border: 2px solid #1976D2;
background: white;
color: #333;
font-weight: 500;
cursor: pointer;
transition: all 0.3s;
flex-grow: 1;
max-width: 300px;
}
.analysis-controls select:hover {
border-color: #2196F3;
box-shadow: 0 0 0 3px rgba(33, 150, 243, 0.1);
}
#heatmap {
background: white;
border-radius: 15px;
padding: 15px;
box-shadow: 0 5px 20px rgba(0,0,0,0.08);
border: 1px solid #e0e0e0;
margin: 10px auto;
cursor: crosshair;
}
.enriched-legend {
background: white;
padding: 20px;
border-radius: 15px;
box-shadow: 0 5px 20px rgba(0,0,0,0.08);
border: 1px solid #e0e0e0;
margin-bottom: 20px;
}
.legend-section h5 {
color: #1976D2;
margin-bottom: 15px;
font-size: 16px;
display: flex;
align-items: center;
gap: 8px;
}
.legend-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 10px;
}
.legend-item-small {
display: flex;
align-items: center;
gap: 8px;
font-size: 13px;
}
.legend-color-small {
width: 20px;
height: 20px;
border-radius: 4px;
border: 2px solid white;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.persistence-indicators {
display: flex;
flex-direction: column;
gap: 12px;
}
.indicator {
display: flex;
align-items: center;
gap: 10px;
padding: 10px 15px;
background: white;
border-radius: 8px;
font-size: 14px;
border-left: 4px solid #1976D2;
}
.indicator-dot {
width: 12px;
height: 12px;
border-radius: 50%;
}
.persistence-analysis {
background: linear-gradient(135deg, #e3f2fd, #f3e5f5);
padding: 20px;
border-radius: 12px;
border-left: 6px solid #1976D2;
}
.persistence-analysis h5 {
color: #1976D2;
margin-top: 0;
margin-bottom: 15px;
font-size: 16px;
display: flex;
align-items: center;
gap: 8px;
}
.persistence-text {
font-size: 14px;
line-height: 1.5;
}
.persistence-text > div {
margin-bottom: 8px;
padding: 10px;
background: white;
border-radius: 8px;
border-left: 3px solid #1976D2;
}
/* ================= ONGLET 3: MESSAGE RÉFLEXIF ET ÉVALUATION ================= */
#tab-evaluation #reflagent {
background: linear-gradient(135deg, #e8f5e9, #c8e6c9);
padding: 25px;
border-radius: 15px;
border-left: 8px solid #43a047;
margin: 25px 0;
font-size: 16px;
line-height: 1.6;
position: relative;
overflow: hidden;
min-height: 100px;
display: flex;
align-items: center;
}
#tab-evaluation #reflagent::before {
content: '🤖';
position: absolute;
top: 15px;
right: 15px;
font-size: 40px;
opacity: 0.2;
}
/* ================= MÉTRIQUES DE CONFIANCE ================= */
#tab-evaluation hr {
border: none;
height: 2px;
background: linear-gradient(90deg, #1976D2, transparent);
margin: 40px 0;
}
.trust-item {
margin-bottom: 20px;
padding: 15px;
background: #f8f9fa;
border-radius: 12px;
transition: transform 0.3s;
}
.trust-item:hover {
transform: translateX(5px);
background: #f1f3f4;
}
.trust-item b {
display: block;
margin-bottom: 10px;
color: #1976D2;
font-size: 15px;
}
.stars {
display: flex;
gap: 8px;
margin: 10px 0;
}
.stars span {
font-size: 32px;
cursor: pointer;
color: #e0e0e0;
transition: all 0.2s;
text-shadow: none;
}
.stars span.active {
color: #ffd54f;
text-shadow: 0 2px 4px rgba(255, 213, 79, 0.3);
}
.stars span:hover {
transform: scale(1.2);
}
.stars span.inactive {
color: #e0e0e0;
text-shadow: none;
}
.stars span.inactive:hover {
color: #ffd54f;
}
/* ================= CHAMP COMMENTAIRE ================= */
textarea {
width: 100%;
min-height: 120px;
border-radius: 12px;
padding: 15px;
border: 2px solid #e0e0e0;
font-family: inherit;
font-size: 14px;
resize: vertical;
transition: all 0.3s;
margin-bottom: 20px;
}
textarea:focus {
outline: none;
border-color: #1976D2;
box-shadow: 0 0 0 3px rgba(25, 118, 210, 0.1);
}
/* ================= BARRE DE CONFIANCE ================= */
#trustBar {
height: 100%;
border-radius: 10px;
color: white;
font-size: 14px;
text-align: center;
line-height: 24px;
font-weight: bold;
transition: width 0.5s ease, background 0.5s ease;
position: relative;
overflow: hidden;
width: 0%;
background: #e0e0e0;
}
#trustBar::after {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(90deg,
transparent,
rgba(255,255,255,0.4),
transparent
);
animation: shimmer 2s infinite;
}
@keyframes shimmer {
0% { transform: translateX(-100%); }
100% { transform: translateX(100%); }
}
/* ================= MESSAGES ================= */
#saveTrustMsg {
padding: 10px 15px;
border-radius: 8px;
margin: 10px 0;
font-weight: 500;
text-align: center;
transition: all 0.3s;
min-height: 50px;
display: flex;
align-items: center;
justify-content: center;
}
/* ================= STATISTIQUES ================= */
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
margin: 20px 0;
}
.stat-item {
background: linear-gradient(135deg, #e3f2fd, #f3e5f5);
padding: 15px;
border-radius: 10px;
text-align: center;
transition: transform 0.3s;
}
.stat-item:hover {
transform: translateY(-3px);
box-shadow: 0 5px 15px rgba(0,0,0,0.1);
}
.stat-value {
font-size: 28px;
font-weight: bold;
color: #1976D2;
margin: 10px 0;
}
.stat-label {
font-size: 13px;
color: #666;
font-weight: 500;
}
/* ================= ANIMATIONS ================= */
@keyframes fadeIn {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes slideIn {
from { transform: translateX(100%); opacity: 0; }
to { transform: translateX(0); opacity: 1; }
}
@keyframes slideOut {
from { transform: translateX(0); opacity: 1; }
to { transform: translateX(100%); opacity: 0; }
}
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.05); }
100% { transform: scale(1); }
}
/* ================= RESPONSIVE ================= */
@media (max-width: 1200px) {
#reflection {
padding: 20px;
}
.profile-radar-container,
.heatmap-and-indicators {
flex-direction: column;
}
.profile-radar-container .radar-bot-message,
.profile-radar-container #radar-container,
.heatmap-and-indicators .heatmap-analysis,
.heatmap-and-indicators .indicators-legend {
min-width: 100%;
max-width: 100%;
}
#radar {
min-width: 100%;
max-width: 100%;
margin-left: 0;
}
.tabs {
flex-direction: column;
}
.tab-btn {
width: 100%;
}
}
@media (max-width: 768px) {
body {
padding: 10px;
}
.nav {
flex-direction: column;
gap: 15px;
text-align: center;
}
#scenarioTitle {
margin: 10px 0;
font-size: 1.2em;
}
button {
width: 100%;
justify-content: center;
}
.analysis-controls {
flex-direction: column;
align-items: stretch;
}
.radar-legend {
flex-direction: column;
align-items: center;
gap: 10px;
}
#radar-list {
grid-template-columns: 1fr;
}
.legend-grid {
grid-template-columns: repeat(2, 1fr);
}
.persistence-indicators {
flex-direction: column;
}
.gdpr-options {
flex-direction: column;
gap: 10px;
}
.gdpr-option {
min-width: 100%;
}
.radar-metrics-grid {
grid-template-columns: 1fr;
}
.heatmap-and-indicators .heatmap-analysis {
min-width: 100%;
}
}
/* ================= PROGRESS BAR CONTAINER ================= */
.progress-container {
width: 100%;
background: #f5f5f5;
border-radius: 12px;
height: 24px;
margin: 8px 0 20px;
overflow: hidden;
box-shadow: inset 0 2px 5px rgba(0,0,0,0.1);
}
/* ================= TOOLTIPS ================= */
.tooltip {
position: absolute;
background: rgba(25, 118, 210, 0.95);
color: white;
padding: 12px 18px;
border-radius: 10px;
font-size: 14px;
pointer-events: none;
opacity: 0;
transition: opacity 0.3s;
z-index: 1000;
max-width: 300px;
box-shadow: 0 6px 20px rgba(0,0,0,0.2);
border: 2px solid white;
backdrop-filter: blur(5px);
}
.tooltip::after {
content: '';
position: absolute;
top: 100%;
left: 50%;
margin-left: -10px;
border-width: 10px;
border-style: solid;
border-color: rgba(25, 118, 210, 0.95) transparent transparent transparent;
}
/* ================= SCROLLBAR ================= */
::-webkit-scrollbar {
width: 10px;
}
::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 5px;
}
::-webkit-scrollbar-thumb {
background: linear-gradient(135deg, #1976D2, #2196F3);
border-radius: 5px;
}
::-webkit-scrollbar-thumb:hover {
background: linear-gradient(135deg, #1565c0, #1976D2);
}
/* ================= LOADING ================= */
.loading {
display: inline-block;
width: 20px;
height: 20px;
border: 3px solid #f3f3f3;
border-top: 3px solid #1976D2;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* ================= NOTIFICATIONS ================= */
.notification {
position: fixed;
top: 20px;
right: 20px;
background: white;
padding: 18px;
border-radius: 12px;
box-shadow: 0 8px 25px rgba(0,0,0,0.15);
border-left: 6px solid #1976D2;
z-index: 10000;
max-width: 320px;
animation: slideIn 0.4s ease-out;
display: flex;
align-items: flex-start;
gap: 12px;
}
.notification-icon {
font-size: 28px;
flex-shrink: 0;
}
.notification-content {
flex-grow: 1;
}
.notification-title {
font-weight: bold;
color: #1976D2;
margin-bottom: 5px;
font-size: 15px;
}
.notification-message {
color: #555;
font-size: 13px;
line-height: 1.4;
}
.notification-close {
background: none;
border: none;
color: #999;
cursor: pointer;
padding: 0;
font-size: 20px;
line-height: 1;
}
</style>
</head>
<body>
<div id="reflection">
<!-- NAVIGATION SCÉNARIOS -->
<div class="nav">
<button id="prevScenario">⬅️ Scénario précédent</button>
<h3 id="scenarioTitle">Chargement...</h3>
<button id="nextScenario">Scénario suivant ➡️</button>
</div>
<!-- SYSTEME D'ONGLETS -->
<div class="tabs-container">
<div class="tabs">
<button class="tab-btn active" data-tab="tab-evaluator">
<span>👤</span> Évaluateur (RGPD)
</button>
<button class="tab-btn" data-tab="tab-dashboard">
<span>📊</span> Dashboard Étudiant
</button>
<button class="tab-btn" data-tab="tab-evaluation">
<span>🤖</span> Message Réflexif & Évaluation
</button>
</div>
<!-- ONGLET 1: INFORMATIONS ÉVALUATEUR GDPR -->
<div id="tab-evaluator" class="tab-content active">
<h3>👤 Informations de l'évaluateur (RGPD)</h3>
<div class="evaluator-info">
<!-- Consentement RGPD -->
<div class="evaluator-field">
<label for="gdprConsent">
<strong><span style="color: #1976D2; margin-right: 5px;">🔒</span> Consentement RGPD <span style="color: #e53935;">*</span></strong>
</label>
<div class="gdpr-options">
<label class="gdpr-option">
<input type="radio" name="gdprConsent" value="accept" id="gdprAccept" required>
<span>J'accepte de partager mes informations pour l'évaluation</span>
</label>
<label class="gdpr-option">
<input type="radio" name="gdprConsent" value="decline" id="gdprDecline">
<span>Je préfère rester anonyme</span>
</label>
</div>
<small>Veuillez accepter le consentement RGPD pour pouvoir remplir les champs d'identification</small>
</div>
<!-- Champs conditionnels (masqués par défaut) -->
<div id="evaluatorFields" style="display: none;">
<!-- Nom et prénom -->
<div class="evaluator-field">
<label for="evaluatorName">
<strong><span style="color: #1976D2; margin-right: 5px;">📝</span> Nom et prénom <span style="color: #e53935;">*</span></strong>
</label>
<input type="text"
id="evaluatorName"
placeholder="Ex: Dupont Jean ou Marie Curie"
maxlength="100"
disabled>
<small>Veuillez saisir votre nom et prénom</small>
</div>
<!-- Rôle -->
<div class="evaluator-field">
<label for="evaluatorRole"><strong><span style="color: #1976D2; margin-right: 5px;">👔</span> Rôle principal <span style="color: #e53935;">*</span></strong></label>
<select id="evaluatorRole" disabled>
<option value="">Sélectionnez votre rôle...</option>
<option value="enseignant">Enseignant / Formateur</option>
<option value="chercheur">Chercheur / Scientifique</option>
<option value="expert_pedago">Expert pédagogique</option>
<option value="etudiant_avance">Étudiant avancé / Doctorant</option>
<option value="conseiller">Conseiller pédagogique</option>
<option value="responsable">Responsable de formation</option>
<option value="autre">Autre professionnel de l'éducation</option>
</select>
<small>Sélectionnez votre rôle principal dans le domaine éducatif</small>
</div>
<!-- Niveau d'expertise en réflexivité -->
<div class="evaluator-field">
<label for="expertiseLevel"><strong><span style="color: #1976D2; margin-right: 5px;">🎓</span> Niveau d'expertise en réflexivité <span style="color: #e53935;">*</span></strong></label>
<select id="expertiseLevel" disabled>
<option value="">Sélectionnez votre niveau...</option>
<option value="debutant">Débutant - Je découvre la réflexivité</option>
<option value="intermediaire">Intermédiaire - J'utilise régulièrement des outils réflexifs</option>
<option value="avance">Avancé - Je conçois des dispositifs réflexifs</option>
<option value="expert">Expert - Je forme à la réflexivité pédagogique</option>
<option value="chercheur_reflex">Chercheur en réflexivité</option>
</select>
<small>Indiquez votre niveau de maîtrise des concepts et outils de réflexivité</small>
</div>
<!-- Niveau de maîtrise des messages réflexifs -->
<div class="evaluator-field">
<label for="messageMastery"><strong><span style="color: #1976D2; margin-right: 5px;">💬</span> Niveau de maîtrise des messages réflexifs <span style="color: #e53935;">*</span></strong></label>
<select id="messageMastery" disabled>
<option value="">Sélectionnez votre niveau...</option>
<option value="novice">Novice - Je commence à analyser des messages réflexifs</option>
<option value="intermediaire_msg">Intermédiaire - J'évalue régulièrement des feedbacks réflexifs</option>
<option value="avance_msg">Avancé - Je rédige des messages réflexifs pour autrui</option>
<option value="expert_msg">Expert - Je forme à la conception de messages réflexifs</option>
</select>
<small>Indiquez votre expérience avec l'analyse et la création de messages réflexifs</small>
</div>
</div>
<!-- Message pour utilisateurs anonymes -->
<div id="anonymousMessage" style="display: none;">
<p>
<strong><span style="margin-right: 5px;">🕵️</span> Évaluation anonyme :</strong> Vos évaluations seront enregistrées sans information personnelle.
Seules vos réponses aux métriques de confiance seront conservées.
</p>
</div>
<!-- Boutons d'action -->
<div style="display: flex; gap: 10px; margin-top: 20px;">
<button onclick="fillSampleEvaluator()" type="button" style="background: #666; flex: 1;" id="fillExampleBtn">
<span style="margin-right: 5px;">📋</span> Remplir exemple
</button>
<button onclick="clearEvaluatorFields()" type="button" style="background: #9e9e9e; flex: 1;" id="clearFieldsBtn">
<span style="margin-right: 5px;">🗑️</span> Effacer
</button>
</div>
</div>
</div>
<!-- ONGLET 2: DASHBOARD ÉTUDIANT -->
<div id="tab-dashboard" class="tab-content">
<h3>📝 Manifeste étudiant</h3>
<div id="manifest-list">
<div class="manifest-item">
<em>Chargement des données...</em>
</div>
</div>
<h3>📊 Progression par compétence</h3>
<div id="competence-bars">
<em>Chargement des compétences...</em>
</div>
<!-- LÉGENDE RADAR -->
<div class="radar-legend">
<div class="legend-item">
<div class="legend-color legend-low"></div>
<span>Faible (0-50%)</span>
</div>
<div class="legend-item">
<div class="legend-color legend-medium"></div>
<span>Moyen (50-70%)</span>
</div>
<div class="legend-item">
<div class="legend-color legend-high"></div>
<span>Élevé (70-100%)</span>
</div>
</div>
<h3>🕸️ Radar – Learning Analytics</h3>
<!-- Grille des métriques radar -->
<div class="radar-metrics-grid" id="radar-metrics-grid">
<div class="metric-card">
<div class="metric-icon">🚀</div>
<div class="metric-name">Démarrage des activités</div>
<div class="metric-value high">85%</div>
<div class="metric-description">Rapidité d'initiation des tâches</div>
</div>
<div class="metric-card">
<div class="metric-icon">🎓</div>
<div class="metric-name">Connaissances validées</div>
<div class="metric-value high">72%</div>
<div class="metric-description">Acquis démontrés et évalués</div>
</div>
<div class="metric-card">
<div class="metric-icon">📈</div>
<div class="metric-name">Niveau moyen de compétence</div>
<div class="metric-value medium">68%</div>
<div class="metric-description">Moyenne des compétences maîtrisées</div>
</div>
<div class="metric-card">
<div class="metric-icon">🎯</div>
<div class="metric-name">Taux d'erreur (inversé)</div>
<div class="metric-value low">35%</div>
<div class="metric-description">Précision et attention aux détails</div>
</div>
<div class="metric-card">
<div class="metric-icon">🏁</div>
<div class="metric-name">Achèvement des activités</div>
<div class="metric-value high">74%</div>
<div class="metric-description">Terminaison des tâches initiées</div>
</div>
<div class="metric-card">
<div class="metric-icon">📚</div>
<div class="metric-name">Interactions avec les ressources</div>
<div class="metric-value high">80%</div>
<div class="metric-description">Utilisation des supports d'apprentissage</div>
</div>
<div class="metric-card">
<div class="metric-icon">🔧</div>
<div class="metric-name">Domaines de compétence</div>
<div class="metric-value high">78%</div>
<div class="metric-description">Étendue des compétences couvertes</div>
</div>
</div>
<!-- Conteneur Profil + Radar côte à côte -->
<div class="profile-radar-container">
<!-- Message Bot Radar Contextuel -->
<div id="radar-bot-message" class="radar-bot-message">
<div class="radar-bot-header">
<div class="radar-bot-icon">🎯</div>
<h4 class="radar-bot-title">Analyse de Profil</h4>
</div>
<p class="radar-bot-content" id="radar-bot-content">Analyse en cours...</p>
<div class="radar-bot-insights">
<div id="radar-bot-insights">
<!-- Les insights seront ajoutés dynamiquement -->
</div>
</div>
</div>
<!-- Container Radar -->
<div id="radar-container">
<canvas id="radar" width="700" height="700"></canvas>
</div>
</div>
<div id="radar-tooltip" class="tooltip"></div>
<!-- ================= HEATMAP ENRICHIE ================= -->
<div class="heatmap-and-indicators">
<div class="heatmap-analysis">
<h3>🔥 Heatmap d'activité - Analyse de Persévérance</h3>
<div class="analysis-controls">
<label for="persistenceMode">Mode d'analyse :</label>
<select id="persistenceMode" onchange="changePersistenceAnalysis()">
<option value="regularity">📊 Régularité du travail</option>
<option value="effortPattern">🔄 Schéma d'effort</option>
<option value="deadline">⏰ Gestion des échéances</option>
<option value="combined">🎯 Vue combinée</option>
</select>
</div>
<!-- Canvas pour le heatmap -->
<canvas id="heatmap" width="800" height="300"></canvas>
</div>
<div class="indicators-legend">
<!-- Légende des niveaux d'activité -->
<div class="enriched-legend">
<div class="legend-section">
<h5>📈 Niveaux d'activité</h5>
<div class="legend-grid">
<div class="legend-item-small">
<div class="legend-color-small" style="background:#EBEDF0;"></div>
<span>Aucune activité</span>
</div>
<div class="legend-item-small">
<div class="legend-color-small" style="background:#C6E48B;"></div>
<span>Faible activité</span>
</div>
<div class="legend-item-small">
<div class="legend-color-small" style="background:#7BC96F;"></div>
<span>Activité modérée</span>
</div>
<div class="legend-item-small">
<div class="legend-color-small" style="background:#239A3B;"></div>
<span>Activité élevée</span>
</div>
<div class="legend-item-small">
<div class="legend-color-small" style="background:#196127;"></div>
<span>Activité intense</span>
</div>
</div>
</div>
</div>
<!-- Analyse de persévérance -->
<div class="persistence-analysis" id="persistenceAnalysis">
<h5>🎯 Indicateurs de Persévérance</h5>
<div class="persistence-indicators">
<div class="indicator">
<span class="indicator-dot" style="background:#FF6B6B;"></span>
<span>Régularité : <strong id="regularityScore">-</strong></span>
</div>
<div class="indicator">
<span class="indicator-dot" style="background:#4ECDC4;"></span>
<span>Schéma d'effort : <strong id="effortPattern">-</strong></span>
</div>
<div class="indicator">
<span class="indicator-dot" style="background:#FFD166;"></span>
<span>Gestion échéances : <strong id="deadlineScore">-</strong></span>
</div>
</div>
<div class="persistence-text" style="margin-top: 15px;">
<div><strong>Régularité :</strong> <span id="regularityText">Analyse en cours...</span></div>
<div><strong>Schéma d'effort :</strong> <span id="patternText">Analyse en cours...</span></div>
</div>
</div>
</div>
</div>
</div>
<!-- ONGLET 3: MESSAGE RÉFLEXIF ET ÉVALUATION -->
<div id="tab-evaluation" class="tab-content">
<h3>🤖 ReflAgent – Analyse & accompagnement</h3>
<div id="reflagent">
<em>Le message d'analyse apparaîtra ici après chargement...</em>
</div>
<hr>
<h3>🧑‍⚖️ Volet Expert – Métriques de Confiance</h3>
<p style="color: #666; margin-bottom: 20px; font-style: italic;">
Évaluez votre confiance dans l'analyse de ReflAgent pour ce scénario :
</p>
<!-- MÉTRIQUES TRADUITES EN FRANÇAIS -->
<div class="trust-item">
<b>Authenticité Réflexive (AR)</b>
<div class="stars" data-metric="AR">
<!-- Les étoiles seront ajoutées par JavaScript -->
</div>
<small style="color: #666; display: block; margin-top: 5px;">
L'analyse reflète-t-elle fidèlement la situation réelle de l'étudiant ?
</small>
</div>
<div class="trust-item">
<b>Alignement Empathique (AE)</b>
<div class="stars" data-metric="AE">
<!-- Les étoiles seront ajoutées par JavaScript -->
</div>
<small style="color: #666; display: block; margin-top: 5px;">
L'agent comprend-il les émotions, motivations et difficultés de l'étudiant ?
</small>
</div>
<div class="trust-item">
<b>Encouragement Sensible au Risque (ESR)</b>
<div class="stars" data-metric="ESR">
<!-- Les étoiles seront ajoutées par JavaScript -->
</div>
<small style="color: #666; display: block; margin-top: 5px;">
Les encouragements tiennent-ils compte des risques et difficultés identifiés ?
</small>
</div>
<div class="trust-item">
<b>Support aux Défis Mentaux (SDM)</b>
<div class="stars" data-metric="SDM">
<!-- Les étoiles seront ajoutées par JavaScript -->
</div>
<small style="color: #666; display: block; margin-top: 5px;">
L'agent aide-t-il l'étudiant à développer sa pensée critique et ses compétences cognitives ?
</small>
</div>
<div class="trust-item">
<b>Support Métacognitif (SM)</b>
<div class="stars" data-metric="SM">
<!-- Les étoiles seront ajoutées par JavaScript -->
</div>
<small style="color: #666; display: block; margin-top: 5px;">
L'agent aide-t-il l'étudiant à réfléchir sur ses propres stratégies d'apprentissage ?
</small>
</div>
<h4>💬 Commentaire expert</h4>
<textarea id="trustComment" placeholder="Ajoutez vos observations, suggestions ou critiques concernant l'analyse de ReflAgent..."></textarea>
<div style="display: flex; justify-content: space-between; align-items: center; margin: 25px 0;">
<div style="flex-grow: 1; margin-right: 20px;">
<h4 style="margin-bottom: 10px;">🔒 Indice global de confiance</h4>
<div class="progress-container">
<div class="fill" id="trustBar">0%</div>
</div>
<div style="display: flex; justify-content: space-between; margin-top: 5px; font-size: 12px; color: #666;">
<span>Faible confiance</span>
<span>Confidence moyenne</span>
<span>Forte confiance</span>
</div>
</div>
<div style="display: flex; flex-direction: column; gap: 10px; align-items: flex-end;">
<button id="saveTrustBtn" style="height: fit-content; width: 100%;">💾 Sauvegarder l'évaluation</button>
<button id="crossReadersBtn" style="height: fit-content; width: 100%; background: linear-gradient(135deg, #43A047, #4CAF50);">
📊 Lecture croisée
</button>
</div>
</div>
<div id="saveTrustMsg"></div>
<!-- Statistiques rapides -->
<div class="stats-grid">
<div class="stat-item">
<div class="stat-label">Scénario actuel</div>
<div class="stat-value" id="currentScenarioNum">-</div>
</div>
<div class="stat-item">
<div class="stat-label">Confiance moyenne</div>
<div class="stat-value" id="avgTrustScore">0%</div>
</div>
<div class="stat-item">
<div class="stat-label">Niveau de risque</div>
<div class="stat-value" id="riskLevel">-</div>
</div>
<div class="stat-item">
<div class="stat-label">Activité moyenne</div>
<div class="stat-value" id="avgActivity">-</div>
</div>
</div>
</div>
</div>
</div>
<!-- Script principal -->
<script>
// ================= VARIABLES =================
let scenarios = [];
let currentScenarioIndex = 0;
let radarMetrics = [];
// Métriques de radar par défaut (sans abréviations)
const DEFAULT_RADAR_METRICS = [
{ label: "Démarrage des activités", value: 85 },
{ label: "Connaissances validées", value: 72 },
{ label: "Niveau moyen de compétence", value: 68 },
{ label: "Taux d'erreur (inversé)", value: 35 },
{ label: "Achèvement des activités", value: 74 },
{ label: "Interactions avec les ressources", value: 80 },
{ label: "Domaines de compétence", value: 78 }
];
// Noms des métriques traduits en français
const trustMetrics = {
"AR": 0, // Authenticité Réflexive
"AE": 0, // Alignement Empathique
"ESR": 0, // Encouragement Sensible au Risque
"SDM": 0, // Support aux Défis Mentaux
"SM": 0 // Support Métacognitif
};
const trustWeights = {
"AR": 0.30,
"AE": 0.20,
"ESR": 0.20,
"SDM": 0.10,
"SM": 0.20
};
// ================= INIT =================
document.addEventListener("DOMContentLoaded", () => {
console.log("✅ DOM chargé, initialisation...");
loadScenarios();
initTrustStars();
initTrustSave();
initTabs();
initGDPR();
// Initialiser la barre de confiance à 0%
updateTrustBar();
});
// ================= SYSTEME D'ONGLETS =================
function initTabs() {
const tabBtns = document.querySelectorAll('.tab-btn');
const tabContents = document.querySelectorAll('.tab-content');
tabBtns.forEach(btn => {
btn.addEventListener('click', () => {
const tabId = btn.getAttribute('data-tab');
// Retirer la classe active de tous les boutons et contenus
tabBtns.forEach(b => b.classList.remove('active'));
tabContents.forEach(c => c.classList.remove('active'));
// Ajouter la classe active au bouton et au contenu correspondant
btn.classList.add('active');
document.getElementById(tabId).classList.add('active');
});
});
}
// ================= GDPR =================
function initGDPR() {
const gdprAccept = document.getElementById('gdprAccept');
const gdprDecline = document.getElementById('gdprDecline');
const evaluatorFields = document.getElementById('evaluatorFields');
const anonymousMessage = document.getElementById('anonymousMessage');
// Activer/désactiver les champs selon le choix GDPR
function updateGDPRFields() {
if (gdprAccept.checked) {
// Afficher les champs et les activer
evaluatorFields.style.display = 'block';
anonymousMessage.style.display = 'none';
// Activer les champs
document.getElementById('evaluatorName').disabled = false;
document.getElementById('evaluatorRole').disabled = false;
document.getElementById('expertiseLevel').disabled = false;
document.getElementById('messageMastery').disabled = false;
} else if (gdprDecline.checked) {
// Cacher les champs et afficher le message anonyme
evaluatorFields.style.display = 'none';
anonymousMessage.style.display = 'block';
// Désactiver et vider les champs
document.getElementById('evaluatorName').disabled = true;
document.getElementById('evaluatorName').value = '';
document.getElementById('evaluatorRole').disabled = true;
document.getElementById('evaluatorRole').value = '';
document.getElementById('expertiseLevel').disabled = true;
document.getElementById('expertiseLevel').value = '';
document.getElementById('messageMastery').disabled = true;
document.getElementById('messageMastery').value = '';
}
}
// Écouter les changements
gdprAccept.addEventListener('change', updateGDPRFields);
gdprDecline.addEventListener('change', updateGDPRFields);
// Initialiser
updateGDPRFields();
}
// ================= CHARGEMENT JSON =================
function loadScenarios() {
console.log("Chargement des scénarios...");
fetch("/static/scenarios.json")
.then(r => {
if (!r.ok) throw new Error("Erreur de chargement du fichier JSON");
return r.json();
})
.then(data => {
console.log("Scénarios chargés:", data.scenarios.length);
scenarios = data.scenarios;
loadScenario(0);
})
.catch(error => {
console.error("Erreur:", error);
document.getElementById("manifest-list").innerHTML =
`<div class="manifest-item" style="background:#FFEBEE;color:#C62828;">
Erreur de chargement des scénarios. Vérifiez que le fichier scenarios.json existe.
</div>`;
});
}
// ================= CHARGER SCÉNARIO =================
function loadScenario(index) {
console.log("Chargement scénario", index);
if (index < 0 || index >= scenarios.length) return;
currentScenarioIndex = index;
const s = scenarios[index];
// Mettre à jour le titre
const titleElement = document.getElementById("scenarioTitle");
titleElement.textContent = `Scénario ${s.id}${s.title}`;
// Mettre à jour le manifeste
document.getElementById("manifest-list").innerHTML = `
<div class="manifest-item">
<b>Type de Manifest :</b> ${s.manifest.type}<br>
<b>Manifeste (écrit par l'étudiant) :</b> ${s.manifest.manifeste}<br>
<b>Description du Profil :</b> ${s.manifest.description}
</div>`;
// Version minimaliste des compétences
const c = document.getElementById("competence-bars");
c.innerHTML = "";
const columnsContainer = document.createElement("div");
columnsContainer.style.display = "flex";
columnsContainer.style.flexWrap = "wrap";
columnsContainer.style.gap = "20px";
const columnGroups = [4, 4, 3, 3];
let currentIndex = 0;
columnGroups.forEach((groupSize) => {
const column = document.createElement("div");
column.style.flex = "1";
column.style.minWidth = "200px";
for (let i = 0; i < groupSize && currentIndex < s.competences.length; i++) {
const cp = s.competences[currentIndex];
const compItem = document.createElement("div");
compItem.style.marginBottom = "15px";
let color;
if (cp.value < 50) color = '#E53935';
else if (cp.value < 70) color = '#FB8C00';
else color = '#43A047';
compItem.innerHTML = `
<div style="margin-bottom: 5px; font-weight: bold; font-size: 14px;">
${cp.label}
</div>
<div style="height: 20px; background: #e0e0e0; border-radius: 10px; overflow: hidden;">
<div style="height: 100%; width: ${cp.value}%; background: ${color};
display: flex; align-items: center; justify-content: center; color: white; font-weight: bold; font-size: 12px;">
${cp.value}%
</div>
</div>
`;
column.appendChild(compItem);
currentIndex++;
}
columnsContainer.appendChild(column);
});
c.appendChild(columnsContainer);
// Mettre à jour le radar avec les nouvelles métriques
if (s.radar && s.radar.length > 0) {
radarMetrics = s.radar;
} else {
// Utiliser les métriques par défaut avec variation pour chaque scénario
radarMetrics = DEFAULT_RADAR_METRICS.map(metric => {
// Variation de ±15% selon l'index du scénario pour rendre les données plus réalistes
const variation = (index % 7) * 5 - 15; // -15 à +15
const newValue = Math.max(10, Math.min(95, metric.value + variation));
return {
label: metric.label,
value: newValue
};
});
}
// Mettre à jour la grille des métriques radar
updateRadarMetricsGrid();
// Mettre à jour le graphique radar
drawRadar();
// Mettre à jour le message bot radar (CONTEXTUEL)
updateRadarBotMessage(s);
// Mettre à jour la heatmap enrichie
drawEnrichedHeatmap(s);
// Mettre à jour le message ReflAgent
document.getElementById("reflagent").textContent = s.reflAgent;
// Mettre à jour les boutons de navigation
updateNavButtons();
// Mettre à jour les statistiques
updateStats();
}
// ================= MISE À JOUR DE LA GRILLE DES MÉTRIQUES RADAR =================
function updateRadarMetricsGrid() {
const grid = document.getElementById("radar-metrics-grid");
if (!grid) return;
grid.innerHTML = "";
radarMetrics.forEach(metric => {
// Déterminer la classe de couleur selon la valeur
let colorClass = "";
if (metric.value >= 70) colorClass = "high";
else if (metric.value >= 40) colorClass = "medium";
else colorClass = "low";
// Déterminer l'icône selon le type de métrique
let icon = "📊";
if (metric.label.includes("Démarrage")) icon = "🚀";
else if (metric.label.includes("Connaissances")) icon = "🎓";
else if (metric.label.includes("Niveau moyen")) icon = "📈";
else if (metric.label.includes("Taux d'erreur")) icon = "🎯";
else if (metric.label.includes("Achèvement")) icon = "🏁";
else if (metric.label.includes("Interactions")) icon = "📚";
else if (metric.label.includes("Domaines")) icon = "🔧";
// Déterminer la description
let description = "";
if (metric.label.includes("Démarrage")) description = "Rapidité d'initiation des tâches";
else if (metric.label.includes("Connaissances")) description = "Acquis démontrés et évalués";
else if (metric.label.includes("Niveau moyen")) description = "Moyenne des compétences maîtrisées";
else if (metric.label.includes("Taux d'erreur")) description = "Précision et attention aux détails";
else if (metric.label.includes("Achèvement")) description = "Terminaison des tâches initiées";
else if (metric.label.includes("Interactions")) description = "Utilisation des supports d'apprentissage";
else if (metric.label.includes("Domaines")) description = "Étendue des compétences couvertes";
const metricCard = document.createElement("div");
metricCard.className = "metric-card";
metricCard.innerHTML = `
<div class="metric-icon">${icon}</div>
<div class="metric-name">${metric.label}</div>
<div class="metric-value ${colorClass}">${metric.value}%</div>
<div class="metric-description">${description}</div>
`;
grid.appendChild(metricCard);
});
}
// ================= NAVIGATION =================
document.getElementById("prevScenario").onclick = () =>
loadScenario(currentScenarioIndex - 1);
document.getElementById("nextScenario").onclick = () =>
loadScenario(currentScenarioIndex + 1);
function updateNavButtons() {
document.getElementById("prevScenario").disabled =
currentScenarioIndex === 0;
document.getElementById("nextScenario").disabled =
currentScenarioIndex === scenarios.length - 1;
}
// ================= BOUTON LECTURE CROISÉE =================
document.getElementById('crossReadersBtn').onclick = () => {
console.log('Navigation vers la page de lecture croisée...');
// Animation de transition
const reflectionDiv = document.getElementById('reflection');
if (reflectionDiv) {
reflectionDiv.style.opacity = '0.7';
reflectionDiv.style.transition = 'opacity 0.3s ease';
}
// Redirection
setTimeout(() => {
window.location.href = 'cross_reading/cross_readers.html';
}, 300);
};
// ================= MESSAGE BOT RADAR CONTEXTUEL =================
function updateRadarBotMessage(scenario) {
const radarBotMessage = document.getElementById("radar-bot-message");
const radarBotContent = document.getElementById("radar-bot-content");
const radarBotInsights = document.getElementById("radar-bot-insights");
if (!radarBotMessage || !radarBotContent || !radarBotInsights) return;
// Messages pour chaque profil basé sur le titre du scénario
const profileMessages = {
"Étudiant engagé": {
title: "Étudiant Engagé et Motivé",
message: "🎯 Ton engagement est impressionnant ! Tu démontres une forte volonté d'apprendre. Continue à canaliser cette énergie vers des objectifs clairs.",
insights: [
{ icon: "🔥", text: "Motivation élevée et constante" },
{ icon: "🎯", text: "Objectifs d'apprentissage bien définis" },
{ icon: "📚", text: "Implication active dans les activités" },
{ icon: "💪", text: "Persévérance face aux difficultés" }
]
},
"Étudiant orienté vers le succès": {
title: "Étudiant Orienté Réussite",
message: "🏆 Tu vises l'excellence avec détermination. Ta motivation par le succès est un atout, mais attention à ne pas négliger le processus d'apprentissage.",
insights: [
{ icon: "⭐", text: "Fort désir de réussite et de reconnaissance" },
{ icon: "🎯", text: "Objectifs ambitieux et clairs" },
{ icon: "📈", text: "Performance supérieure aux attentes" },
{ icon: "⚖️", text: "Équilibre entre résultats et apprentissage à trouver" }
]
},
"Étudiant motivé": {
title: "Étudiant Nouvellement Motivé",
message: "🚀 Ta motivation est un excellent point de départ ! Tu découvres les joies de l'apprentissage. Capitalise sur cet élan pour construire des bases solides.",
insights: [
{ icon: "🌟", text: "Motivation récente et prometteuse" },
{ icon: "🧱", text: "Construction des bases en cours" },
{ icon: "📖", text: "Curiosité et ouverture d'esprit" },
{ icon: "🌱", text: "Potentiel de croissance important" }
]
},
"Étudiant surmotivé": {
title: "Étudiant Surmotivé",
message: "💥 Ton niveau d'investissement est exceptionnel, mais attention au risque de surmenage. L'excellence durable nécessite un rythme soutenable.",
insights: [
{ icon: "⚡", text: "Investissement maximal dans toutes les activités" },
{ icon: "⚠️", text: "Risque de burnout à surveiller" },
{ icon: "🎯", text: "Perfectionnisme parfois excessif" },
{ icon: "⚖️", text: "Équilibre vie étudiante/privée à préserver" }
]
},
"Étudiant stratégique": {
title: "Étudiant Stratégique",
message: "♟️ Tu abordes l'apprentissage avec intelligence et planification. Tes méthodes efficaces montrent une bonne compréhension des enjeux académiques.",
insights: [
{ icon: "🧠", text: "Approche réfléchie et planifiée" },
{ icon: "🎯", text: "Optimisation des efforts pour des résultats maximaux" },
{ icon: "📊", text: "Analyse des retours pour amélioration" },
{ icon: "⚡", text: "Efficacité démontrée dans l'apprentissage" }
]
},
"Étudiant en amélioration": {
title: "Étudiant en Phase d'Amélioration",
message: "📈 Tu progresses régulièrement et montre une belle capacité d'adaptation. Continue à identifier tes points faibles pour les transformer en forces.",
insights: [
{ icon: "📈", text: "Progression constante et mesurable" },
{ icon: "🔄", text: "Capacité d'adaptation et de remise en question" },
{ icon: "🎯", text: "Objectifs d'amélioration clairement identifiés" },
{ icon: "💪", text: "Persévérance dans l'effort d'apprentissage" }
]
},
"Étudiant en difficulté": {
title: "Étudiant Rencontrant des Difficultés",
message: "🆘 Tu fais face à des obstacles dans ton apprentissage. C'est normal d'avoir des difficultés - l'important est d'identifier les causes et de chercher du soutien.",
insights: [
{ icon: "⚠️", text: "Difficultés persistantes dans certains domaines" },
{ icon: "🆘", text: "Besoin d'un soutien adapté identifié" },
{ icon: "💭", text: "Réflexion sur les causes des difficultés" },
{ icon: "🤝", text: "Ouverture à l'aide et au soutien" }
]
},
"Étudiant évitant l'échec": {
title: "Étudiant Évitant l'Échec",
message: "🛡️ Tu sembles éviter les situations qui pourraient mener à l'échec. Rappelle-toi que l'échec fait partie de l'apprentissage et peut être une source de croissance.",
insights: [
{ icon: "🛡️", text: "Stratégie de protection face aux risques d'échec" },
{ icon: "🎯", text: "Choix de tâches sécurisantes et maîtrisées" },
{ icon: "⚠️", text: "Évitement des défis perçus comme risqués" },
{ icon: "🌱", text: "Potentiel de dépassement de ses limites" }
]
},
"Étudiant à risque": {
title: "Étudiant à Risque de Décrochage",
message: "🚨 Certains indicateurs suggèrent un risque de décrochage. Il est important d'agir maintenant pour retrouver un parcours d'apprentissage stable et épanouissant.",
insights: [
{ icon: "🚨", text: "Indicateurs de risque identifiés" },
{ icon: "⚠️", text: "Engagement fluctuant ou en baisse" },
{ icon: "🆘", text: "Besoin urgent de soutien et d'accompagnement" },
{ icon: "🔍", text: "Identification des causes du désengagement" }
]
},
"Étudiant en progression": {
title: "Étudiant en Progression Continue",
message: "📊 Tu montres une belle progression dans tes apprentissages. Continue à capitaliser sur tes réussites pour maintenir cette dynamique positive.",
insights: [
{ icon: "📊", text: "Amélioration mesurable des compétences" },
{ icon: "🎯", text: "Objectifs de progression atteints" },
{ icon: "🚀", text: "Dynamique d'apprentissage positive" },
{ icon: "💡", text: "Acquisition de nouvelles stratégies efficaces" }
]
},
"Étudiant acceptant l'échec": {
title: "Étudiant Acceptant l'Échec",
message: "😔 Tu sembles avoir accepté certaines difficultés comme inévitables. Pourtant, chaque obstacle peut être surmonté avec la bonne approche et du soutien.",
insights: [
{ icon: "😔", text: "Acceptation passive des difficultés" },
{ icon: "⚠️", text: "Faible confiance dans sa capacité à progresser" },
{ icon: "🔄", text: "Besoin de redynamiser la motivation" },
{ icon: "🌟", text: "Potentiel non exploité identifié" }
]
},
"Étudiant inactif": {
title: "Étudiant Inactif",
message: "⏸️ Ton activité d'apprentissage est très limitée. Le premier pas est souvent le plus difficile - commence par de petites actions pour relancer ta motivation.",
insights: [
{ icon: "⏸️", text: "Activité d'apprentissage très réduite" },
{ icon: "🚪", text: "Éloignement du parcours éducatif" },
{ icon: "🆘", text: "Besoin d'un accompagnement renforcé" },
{ icon: "🔓", text: "Potentiel de réengagement à débloquer" }
]
},
"Étudiant non engagé": {
title: "Étudiant Non Engagé",
message: "😐 Tu sembles peu engagé dans ton parcours d'apprentissage. Identifier ce qui pourrait te motiver à nouveau serait un premier pas important.",
insights: [
{ icon: "😐", text: "Faible implication dans les activités" },
{ icon: "🎯", text: "Objectifs personnels à redéfinir" },
{ icon: "🔄", text: "Besoin de retrouver du sens à l'apprentissage" },
{ icon: "🌟", text: "Redécouverte des motivations intrinsèques" }
]
},
"Étudiant persévérant": {
title: "Étudiant Persévérant",
message: "🏅 Ta persévérance face aux difficultés est remarquable. Continue à avancer pas à pas - chaque effort contribue à ton apprentissage à long terme.",
insights: [
{ icon: "🏅", text: "Persévérance exceptionnelle face aux défis" },
{ icon: "🐢", text: "Approche patiente et régulière" },
{ icon: "📈", text: "Progrès constants malgré les obstacles" },
{ icon: "💪", text: "Résilience et ténacité démontrées" }
]
},
"Étudiant actif": {
title: "Étudiant Actif et Impliqué",
message: "🎉 Tu es activement engagé dans ton apprentissage avec une bonne participation. Continue à maintenir ce niveau d'implication pour maximiser tes progrès.",
insights: [
{ icon: "🎉", text: "Participation active et régulière" },
{ icon: "🤝", text: "Implication dans les activités collaboratives" },
{ icon: "💡", text: "Contributions pertinentes et réfléchies" },
{ icon: "🚀", text: "Dynamisme dans les apprentissages" }
]
}
};
// Récupérer le message pour le profil ou utiliser un message par défaut
const profileMessage = profileMessages[scenario.title] || {
title: "Profil d'Apprentissage",
message: "Analyse de votre profil d'apprentissage en cours...",
insights: [
{ icon: "📊", text: "Évaluation des indicateurs d'apprentissage" },
{ icon: "🎯", text: "Identification des forces et axes d'amélioration" },
{ icon: "🚀", text: "Recommandations personnalisées" }
]
};
// Mettre à jour l'interface
radarBotMessage.style.display = 'block';
radarBotContent.innerHTML = `<strong>${profileMessage.title}</strong><br>${profileMessage.message}`;
// Mettre à jour les insights
radarBotInsights.innerHTML = '';
profileMessage.insights.forEach(insight => {
const insightElement = document.createElement('div');
insightElement.className = 'radar-insight-item';
insightElement.innerHTML = `
<div class="radar-insight-icon">${insight.icon}</div>
<div class="radar-insight-text">${insight.text}</div>
`;
radarBotInsights.appendChild(insightElement);
});
}
// ================= RADAR =================
function drawRadar() {
const canvas = document.getElementById("radar");
if (!canvas) {
console.error("Canvas radar non trouvé!");
return;
}
const ctx = canvas.getContext("2d");
const cx = canvas.width / 2;
const cy = canvas.height / 2;
const radius = 220;
const n = radarMetrics.length;
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Dessiner le radar
for (let i = 1; i <= 5; i++) {
ctx.beginPath();
ctx.arc(cx, cy, (radius / 5) * i, 0, 2 * Math.PI);
ctx.strokeStyle = "#e0e0e0";
ctx.stroke();
}
// Dessiner les axes
radarMetrics.forEach((m, i) => {
const angle = (2 * Math.PI * i) / n - Math.PI / 2;
ctx.beginPath();
ctx.moveTo(cx, cy);
ctx.lineTo(cx + radius * Math.cos(angle), cy + radius * Math.sin(angle));
ctx.strokeStyle = "#b0bec5";
ctx.stroke();
// Labels
const tx = cx + (radius + 35) * Math.cos(angle);
const ty = cy + (radius + 35) * Math.sin(angle);
ctx.font = "12px Arial";
ctx.fillStyle = "#000";
ctx.textAlign = angle > Math.PI / 2 || angle < -Math.PI / 2 ? "right" : "left";
ctx.textBaseline = "middle";
ctx.fillText(`${m.label} : ${m.value}%`, tx, ty);
});
// Dessiner le polygone
ctx.beginPath();
radarMetrics.forEach((m, i) => {
const angle = (2 * Math.PI * i) / n - Math.PI / 2;
const r = radius * (m.value / 100);
const x = cx + r * Math.cos(angle);
const y = cy + r * Math.sin(angle);
i === 0 ? ctx.moveTo(x, y) : ctx.lineTo(x, y);
});
ctx.closePath();
ctx.fillStyle = "rgba(25,118,210,0.25)";
ctx.fill();
ctx.strokeStyle = "#1976D2";
ctx.lineWidth = 2;
ctx.stroke();
// Dessiner les points
radarMetrics.forEach((m, i) => {
const angle = (2 * Math.PI * i) / n - Math.PI / 2;
const r = radius * (m.value / 100);
const x = cx + r * Math.cos(angle);
const y = cy + r * Math.sin(angle);
ctx.beginPath();
ctx.arc(x, y, 4, 0, 2 * Math.PI);
ctx.fillStyle = "#1976D2";
ctx.fill();
});
}
// ================= HEATMAP ENRICHIE =================
function drawEnrichedHeatmap(scenario) {
const canvas = document.getElementById("heatmap");
if (!canvas) return;
const ctx = canvas.getContext("2d");
const cellSize = 25;
const padding = 2;
const days = ["L", "M", "M", "J", "V", "S", "D"];
const weeks = 4;
const totalDays = weeks * 7;
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Titre enrichi
const scenarioTitles = {
1: {
title: "Vous êtes engagé mais désorganisé",
emoji: "😅",
pattern: "Effort concentré avant échéances"
},
2: {
title: "Vous êtes actif mais peu efficace",
emoji: "😰",
pattern: "Démarrage lent avec accélération tardive"
},
3: {
title: "Vous êtes autonome et stratégique",
emoji: "😊",
pattern: "Travail régulier et équilibré"
}
};
const scenarioInfo = scenarioTitles[scenario.id] || {
title: "Votre activité",
emoji: "📊",
pattern: "Schéma à analyser"
};
ctx.font = "bold 18px Arial";
ctx.fillStyle = "#1976D2";
ctx.textAlign = "center";
ctx.fillText(`${scenarioInfo.emoji} ${scenarioInfo.title}`, canvas.width / 2, 30);
ctx.font = "14px Arial";
ctx.fillStyle = "#666";
ctx.fillText(`Schéma détecté : ${scenarioInfo.pattern}`, canvas.width / 2, 55);
// Générer les données d'activité selon le scénario
const activityData = generatePersistenceData(scenario.id, totalDays);
// Dessiner la heatmap
const colors = ["#EBEDF0", "#C6E48B", "#7BC96F", "#239A3B", "#196127"];
for (let day = 0; day < totalDays; day++) {
const week = Math.floor(day / 7);
const weekday = day % 7;
const x = 45 + week * (cellSize * 7 + padding * 7) + weekday * (cellSize + padding);
const y = 80 + weekday * (cellSize + padding);
const intensity = activityData[day];
// Dessiner la cellule
ctx.fillStyle = colors[intensity];
ctx.fillRect(x, y, cellSize, cellSize);
// Bordure avec indication des échéances (vendredi = jour important)
if (weekday === 4) { // Vendredi
ctx.strokeStyle = "#FF6B6B";
ctx.lineWidth = 2;
} else {
ctx.strokeStyle = intensity > 0 ? "#239A3B" : "#eee";
ctx.lineWidth = 1;
}
ctx.strokeRect(x, y, cellSize, cellSize);
// Indicateur de persévérance (points colorés)
if (intensity >= 3) {
// Point rouge pour forte activité (indice possible cram)
ctx.fillStyle = intensity === 4 ? "#FF6B6B" : "#FFD166";
ctx.beginPath();
ctx.arc(x + cellSize - 5, y + 5, 3, 0, Math.PI * 2);
ctx.fill();
}
}
// Analyser et afficher les métriques de persévérance
analyzePersistenceMetrics(activityData, scenario.id);
}
// Générer des données de persévérance selon le scénario
function generatePersistenceData(scenarioId, totalDays) {
const data = new Array(totalDays).fill(0);
switch(scenarioId) {
case 1: // Scénario désorganisé - Effort concentré avant échéances
for (let i = 0; i < totalDays; i++) {
const weekday = i % 7;
// Effort concentré les jeudis et vendredis (jours 3 et 4)
if (weekday === 3 || weekday === 4) { // Jeudi et vendredi
data[i] = Math.floor(Math.random() * 2) + 3; // 3-4 (forte activité)
} else if (weekday === 5 || weekday === 6) { // Weekend
data[i] = Math.floor(Math.random() * 2); // 0-1 (très faible)
} else {
data[i] = Math.floor(Math.random() * 2) + 1; // 1-2 (faible)
}
}
break;
case 2: // Scénario peu efficace - Démarrage lent, accélération tardive
for (let i = 0; i < totalDays; i++) {
const progress = i / totalDays;
if (progress < 0.3) {
data[i] = Math.floor(Math.random() * 2); // 0-1 (début très faible)
} else if (progress > 0.7) {
data[i] = Math.floor(Math.random() * 2) + 3; // 3-4 (fin forte)
} else {
data[i] = Math.floor(Math.random() * 2) + 1; // 1-2 (milieu faible)
}
}
break;
case 3: // Scénario stratégique - Travail régulier
for (let i = 0; i < totalDays; i++) {
const weekday = i % 7;
// Travail régulier et équilibré toute la semaine
if (weekday === 5 || weekday === 6) { // Weekend léger
data[i] = Math.floor(Math.random() * 2); // 0-1
} else {
data[i] = Math.floor(Math.random() * 2) + 2; // 2-3 (régulier)
}
}
break;
default:
// Pattern par défaut
for (let i = 0; i < totalDays; i++) {
data[i] = Math.floor(Math.random() * 5); // 0-4 aléatoire
}
}
return data;
}
// Analyser les métriques de persévérance
function analyzePersistenceMetrics(activityData, scenarioId) {
const totalDays = activityData.length;
const weeks = Math.ceil(totalDays / 7);
// 1. Calculer la régularité (écart-type)
const avgActivity = activityData.reduce((a, b) => a + b, 0) / totalDays;
const variance = activityData.reduce((a, b) => a + Math.pow(b - avgActivity, 2), 0) / totalDays;
const stdDev = Math.sqrt(variance);
const regularity = Math.max(0, 100 - stdDev * 25); // Score de 0-100
// 2. Identifier le schéma d'effort
let pattern = "Indéterminé";
let patternDescription = "";
let patternColor = "#666";
// Analyser la distribution par semaine
const weeklyPatterns = [];
for (let w = 0; w < weeks; w++) {
const weekData = activityData.slice(w * 7, Math.min((w + 1) * 7, totalDays));
if (weekData.length < 5) continue;
const weekAvg = weekData.reduce((a, b) => a + b, 0) / weekData.length;
// Vérifier les patterns
if (weekData[4] > weekAvg * 1.5 && weekData[3] > weekAvg * 1.3) {
weeklyPatterns.push("deadline"); // Pic jeudi/vendredi
} else if (weekData[0] < weekAvg * 0.7 && weekData[4] > weekAvg * 1.2) {
weeklyPatterns.push("acceleration"); // Démarrage lent, fin forte
} else if (Math.max(...weekData) - Math.min(...weekData) < 1.5) {
weeklyPatterns.push("regular"); // Régulier
} else if (weekData[5] < weekAvg * 0.5 && weekData[6] < weekAvg * 0.5) {
weeklyPatterns.push("weekend_break"); // Pause weekend
}
}
// Déterminer le pattern dominant
let dominantPattern = "regular"; // Par défaut
if (weeklyPatterns.length > 0) {
const patternCounts = weeklyPatterns.reduce((acc, val) => {
acc[val] = (acc[val] || 0) + 1;
return acc;
}, {});
dominantPattern = Object.keys(patternCounts).reduce((a, b) =>
patternCounts[a] > patternCounts[b] ? a : b, "regular"
);
}
// Associer les descriptions
const patternMap = {
"regular": {
name: "Travail régulier",
desc: "Effort équilibré tout au long de la semaine avec une bonne constance",
color: "#43A047"
},
"deadline": {
name: "Effort concentré avant échéances",
desc: "Pic d'activité marqué les jeudis et vendredis, typique du 'cramming'",
color: "#E53935"
},
"acceleration": {
name: "Accélération progressive",
desc: "Démarrage lent en début de semaine avec accélération avant les deadlines",
color: "#FB8C00"
},
"weekend_break": {
name: "Pause weekend marquée",
desc: "Activité réduite le weekend avec reprise en début de semaine",
color: "#2196F3"
}
};
if (patternMap[dominantPattern]) {
pattern = patternMap[dominantPattern].name;
patternDescription = patternMap[dominantPattern].desc;
patternColor = patternMap[dominantPattern].color;
}
// 3. Calculer la gestion des échéances
const deadlineDays = activityData.filter((_, index) => index % 7 === 3 || index % 7 === 4);
const nonDeadlineDays = activityData.filter((_, index) => index % 7 !== 3 && index % 7 !== 4);
const deadlineAvg = deadlineDays.length > 0 ?
deadlineDays.reduce((a, b) => a + b, 0) / deadlineDays.length : 0;
const nonDeadlineAvg = nonDeadlineDays.length > 0 ?
nonDeadlineDays.reduce((a, b) => a + b, 0) / nonDeadlineDays.length : 0;
let deadlineScore = 50;
if (deadlineAvg > 0 && nonDeadlineAvg > 0) {
const ratio = deadlineAvg / nonDeadlineAvg;
if (ratio > 1.5) {
deadlineScore = Math.min(100, 30 + (ratio - 1.5) * 40);
} else if (ratio < 0.8) {
deadlineScore = Math.max(0, 70 - (0.8 - ratio) * 50);
} else {
deadlineScore = 65 + Math.random() * 20;
}
}
// Mettre à jour l'interface
document.getElementById("regularityScore").textContent = Math.round(regularity) + "%";
document.getElementById("regularityScore").style.color = regularity > 70 ? "#43A047" : regularity > 40 ? "#FB8C00" : "#E53935";
document.getElementById("effortPattern").textContent = pattern;
document.getElementById("effortPattern").style.color = patternColor;
document.getElementById("deadlineScore").textContent = Math.round(deadlineScore) + "%";
document.getElementById("deadlineScore").style.color = deadlineScore > 70 ? "#43A047" : deadlineScore > 40 ? "#FB8C00" : "#E53935";
// Mettre à jour l'analyse textuelle
document.getElementById("regularityText").textContent =
regularity > 75 ? "Excellente régularité ! Travail stable et constant dans le temps" :
regularity > 60 ? "Bonne régularité avec quelques variations acceptables" :
regularity > 40 ? "Régularité moyenne, rythme de travail avec des hauts et des bas" :
"Faible régularité, rythme de travail très irrégulier avec des pics et des creux";
document.getElementById("patternText").textContent = patternDescription;
}
// Changer le mode d'analyse
function changePersistenceAnalysis() {
const mode = document.getElementById("persistenceMode").value;
const currentScenario = scenarios[currentScenarioIndex];
// Mettre à jour l'affichage selon le mode
const heatmapCanvas = document.getElementById("heatmap");
const ctx = heatmapCanvas.getContext("2d");
// Effacer et redessiner avec des annotations spécifiques
drawEnrichedHeatmap(currentScenario);
// Ajouter des annotations spécifiques au mode
ctx.font = "14px Arial";
ctx.fillStyle = "#1976D2";
ctx.textAlign = "center";
switch(mode) {
case "regularity":
ctx.fillText("📊 Analyse de la régularité - Écart-type des activités", heatmapCanvas.width / 2, 280);
break;
case "effortPattern":
ctx.fillText("🔄 Identification du schéma d'effort dominant", heatmapCanvas.width / 2, 280);
break;
case "deadline":
ctx.fillText("⏰ Focus sur la gestion des échéances (jeudis/vendredis)", heatmapCanvas.width / 2, 280);
break;
case "combined":
ctx.fillText("🎯 Vue combinée : Régularité + Schéma + Échéances", heatmapCanvas.width / 2, 280);
break;
}
}
// ================= MÉTRIQUES DE CONFIANCE =================
function initTrustStars(){
console.log("Initialisation des étoiles...");
// Réinitialiser toutes les métriques
for (const key in trustMetrics) {
trustMetrics[key] = 0;
}
let starsCreated = 0;
document.querySelectorAll(".stars").forEach(div => {
const metric = div.dataset.metric;
console.log(`Création des étoiles pour: ${metric}`);
// Vérifier que la métrique existe
if (!trustMetrics.hasOwnProperty(metric)) {
console.error(`Métrique inconnue: ${metric}`);
return;
}
// Vider le conteneur
div.innerHTML = '';
// Créer 5 étoiles
for (let i = 1; i <= 5; i++) {
const star = document.createElement("span");
star.textContent = "★";
star.classList.add("inactive");
star.style.cursor = "pointer";
star.title = `${i} étoile${i > 1 ? 's' : ''}`;
// Stocker la valeur dans un attribut data
star.dataset.value = i;
star.onclick = function() {
const value = parseInt(this.dataset.value);
console.log(`Clic sur ${metric}: ${value} étoiles`);
// Mettre à jour la valeur de la métrique
trustMetrics[metric] = value;
// Mettre à jour l'affichage des étoiles dans ce groupe
const allStarsInGroup = this.parentElement.children;
for (let j = 0; j < allStarsInGroup.length; j++) {
const starElem = allStarsInGroup[j];
const starValue = parseInt(starElem.dataset.value);
if (starValue <= value) {
starElem.classList.remove("inactive");
starElem.classList.add("active");
} else {
starElem.classList.add("inactive");
starElem.classList.remove("active");
}
}
// Mettre à jour la barre de progression
updateTrustBar();
};
div.appendChild(star);
starsCreated++;
}
});
console.log(`${starsCreated} étoiles créées pour ${Object.keys(trustMetrics).length} métriques`);
console.log("Métriques initialisées:", trustMetrics);
}
function updateTrustBar(){
console.log("=== MISE À JOUR BARRE DE CONFIANCE ===");
let scoreTotal = 0;
let scoreMaxPossible = 0;
let details = [];
// Calcul détaillé
for (const metric in trustMetrics) {
const valeur = trustMetrics[metric];
const poids = trustWeights[metric];
const contribution = valeur * poids;
const maxContribution = 5 * poids;
scoreTotal += contribution;
scoreMaxPossible += maxContribution;
details.push({
trique: metric,
valeur: valeur,
poids: poids,
contribution: contribution.toFixed(2),
max: maxContribution.toFixed(2)
});
}
console.log("Détails du calcul:");
details.forEach(d => {
console.log(`${d.métrique}: ${d.valeur}/5 × ${d.poids} = ${d.contribution} / ${d.max}`);
});
console.log(`Total: ${scoreTotal.toFixed(2)} / ${scoreMaxPossible.toFixed(2)}`);
// Calcul du pourcentage
let pourcentage = 0;
if (scoreMaxPossible > 0) {
pourcentage = Math.round((scoreTotal / scoreMaxPossible) * 100);
}
console.log(`Pourcentage calculé: ${pourcentage}%`);
// Mettre à jour la barre de progression
const barre = document.getElementById("trustBar");
if (barre) {
barre.style.width = pourcentage + "%";
barre.textContent = pourcentage + "%";
// Changer la couleur selon le niveau
if (pourcentage < 40) {
barre.style.background = "#E53935"; // Rouge - Faible
barre.title = `Faible confiance (${pourcentage}%)`;
} else if (pourcentage < 70) {
barre.style.background = "#FB8C00"; // Orange - Moyen
barre.title = `Confiance moyenne (${pourcentage}%)`;
} else {
barre.style.background = "#43A047"; // Vert - Fort
barre.title = `Forte confiance (${pourcentage}%)`;
}
console.log(`Barre mise à jour: ${pourcentage}% (width: ${pourcentage}%)`);
} else {
console.error("Élément #trustBar non trouvé!");
}
// Mettre à jour la statistique de confiance moyenne
const avgTrustElem = document.getElementById("avgTrustScore");
if (avgTrustElem) {
avgTrustElem.textContent = pourcentage + "%";
console.log(`Statistique moyenne mise à jour: ${pourcentage}%`);
}
// Afficher un résumé dans la console
console.log("=== RÉSUMÉ FINAL ===");
console.log(`Indice global de confiance: ${pourcentage}%`);
console.log("Valeurs des métriques:", JSON.stringify(trustMetrics, null, 2));
console.log("====================");
return pourcentage;
}
function initTrustSave(){
const saveBtn = document.getElementById("saveTrustBtn");
if (!saveBtn) {
console.error("Bouton de sauvegarde non trouvé!");
return;
}
saveBtn.onclick = () => {
console.log("=== DÉBUT SAUVEGARDE ===");
// Récupérer les informations GDPR
const gdprAccept = document.getElementById('gdprAccept').checked;
const gdprDecline = document.getElementById('gdprDecline').checked;
// Récupérer les informations conditionnelles
let evaluatorName = "";
let evaluatorRole = "";
let expertiseLevel = "";
let messageMastery = "";
if (gdprAccept) {
evaluatorName = document.getElementById("evaluatorName").value.trim();
evaluatorRole = document.getElementById("evaluatorRole").value;
expertiseLevel = document.getElementById("expertiseLevel").value;
messageMastery = document.getElementById("messageMastery").value;
}
const comment = document.getElementById("trustComment").value;
const msgDiv = document.getElementById("saveTrustMsg");
// Validation : consentement GDPR obligatoire
if (!gdprAccept && !gdprDecline) {
const message = "❌ Veuillez sélectionner une option de consentement RGPD.";
console.error(message);
if (msgDiv) {
msgDiv.textContent = message;
msgDiv.style.background = "#FFEBEE";
msgDiv.style.color = "#C62828";
}
return;
}
// Validation : si GDPR accepté, vérifier les champs obligatoires
if (gdprAccept) {
if (!evaluatorName) {
const message = "❌ Veuillez saisir votre nom et prénom.";
console.error(message);
if (msgDiv) {
msgDiv.textContent = message;
msgDiv.style.background = "#FFEBEE";
msgDiv.style.color = "#C62828";
}
const nameField = document.getElementById("evaluatorName");
nameField.style.borderColor = "#E53935";
nameField.style.boxShadow = "0 0 0 3px rgba(229, 57, 53, 0.1)";
nameField.focus();
setTimeout(() => {
nameField.style.borderColor = "#e0e0e0";
nameField.style.boxShadow = "none";
}, 3000);
return;
}
if (!evaluatorRole) {
const message = "❌ Veuillez sélectionner votre rôle.";
console.error(message);
if (msgDiv) {
msgDiv.textContent = message;
msgDiv.style.background = "#FFEBEE";
msgDiv.style.color = "#C62828";
}
return;
}
if (!expertiseLevel) {
const message = "❌ Veuillez sélectionner votre niveau d'expertise en réflexivité.";
console.error(message);
if (msgDiv) {
msgDiv.textContent = message;
msgDiv.style.background = "#FFEBEE";
msgDiv.style.color = "#C62828";
}
return;
}
if (!messageMastery) {
const message = "❌ Veuillez sélectionner votre niveau de maîtrise des messages réflexifs.";
console.error(message);
if (msgDiv) {
msgDiv.textContent = message;
msgDiv.style.background = "#FFEBEE";
msgDiv.style.color = "#C62828";
}
return;
}
}
// Vérifier si au moins une évaluation
let hasEvaluation = false;
for (const metric in trustMetrics) {
if (trustMetrics[metric] > 0) {
hasEvaluation = true;
break;
}
}
if (!hasEvaluation) {
const message = "⚠️ Veuillez évaluer au moins une métrique avant de sauvegarder.";
console.warn(message);
if (msgDiv) {
msgDiv.textContent = message;
msgDiv.style.background = "#FFF3E0";
msgDiv.style.color = "#EF6C00";
}
return;
}
// Calculer l'indice de confiance
const trustScore = updateTrustBar(); // Cette fonction retourne le pourcentage
console.log(`Indice calculé pour sauvegarde: ${trustScore}%`);
// Préparer les données
const data = {
scenario_id: scenarios[currentScenarioIndex].id,
scenario_title: scenarios[currentScenarioIndex].title,
trust_metrics: {...trustMetrics},
trust_score: trustScore,
gdpr_consent: gdprAccept ? "accept" : "decline",
evaluator_name: gdprAccept ? evaluatorName : null,
evaluator_role: gdprAccept ? evaluatorRole : null,
expertise_level: gdprAccept ? expertiseLevel : null,
message_mastery: gdprAccept ? messageMastery : null,
comment: comment || null,
timestamp: new Date().toISOString(),
trust_weights: {...trustWeights}
};
console.log("Données à envoyer:", JSON.stringify(data, null, 2));
// Envoyer au serveur
fetch("/save_trust", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data)
})
.then(res => {
if (!res.ok) throw new Error(`Erreur HTTP ${res.status}: ${res.statusText}`);
return res.json();
})
.then(responseData => {
console.log("✅ Réponse du serveur:", responseData);
if (msgDiv) {
const nameDisplay = gdprAccept ? evaluatorName : "Évaluateur anonyme";
msgDiv.innerHTML = `
<div style="display: flex; align-items: center; gap: 10px;">
<div style="font-size: 24px;">✅</div>
<div>
<strong>Évaluation sauvegardée avec succès !</strong><br>
<small>Merci ${nameDisplay} pour votre contribution.</small><br>
<small><em>Indice global: ${trustScore}%</em></small>
</div>
</div>
`;
msgDiv.style.background = "#E8F5E9";
msgDiv.style.color = "#2E7D32";
msgDiv.style.padding = "15px";
}
// Réinitialiser après succès (optionnel)
setTimeout(() => {
resetTrustEvaluation();
}, 3000);
})
.catch(error => {
console.error("❌ Erreur de sauvegarde:", error);
if (msgDiv) {
msgDiv.textContent = `❌ Erreur: ${error.message}`;
msgDiv.style.background = "#FFEBEE";
msgDiv.style.color = "#C62828";
}
});
};
console.log("✅ Bouton de sauvegarde initialisé");
}
// Fonction pour réinitialiser l'évaluation
function resetTrustEvaluation() {
console.log("Réinitialisation de l'évaluation...");
// Réinitialiser les métriques
for (const key in trustMetrics) {
trustMetrics[key] = 0;
}
// Réinitialiser les étoiles
document.querySelectorAll(".stars").forEach(div => {
Array.from(div.children).forEach(star => {
star.classList.add("inactive");
star.classList.remove("active");
});
});
// Réinitialiser la barre
updateTrustBar();
// Vider le commentaire
document.getElementById("trustComment").value = "";
console.log("Évaluation réinitialisée");
}
// ================= FONCTIONS UTILITAIRES =================
// Mettre à jour les statistiques
function updateStats() {
if (!scenarios || scenarios.length === 0) return;
const currentScenario = scenarios[currentScenarioIndex];
document.getElementById("currentScenarioNum").textContent = currentScenario.id;
// Niveau de risque
const risk = currentScenario.risk || 50;
let riskLevel = "Moyen";
let riskColor = "#FB8C00";
if (risk < 40) {
riskLevel = "Faible";
riskColor = "#43A047";
} else if (risk > 70) {
riskLevel = "Élevé";
riskColor = "#E53935";
}
document.getElementById("riskLevel").textContent = riskLevel;
document.getElementById("riskLevel").style.color = riskColor;
// Activité moyenne à partir des données radar
if (currentScenario.radar && currentScenario.radar.length > 0) {
const avgRadar = currentScenario.radar.reduce((sum, item) => sum + item.value, 0) / currentScenario.radar.length;
document.getElementById("avgActivity").textContent = Math.round(avgRadar) + "%";
}
}
// Fonction pour remplir un exemple d'évaluateur
function fillSampleEvaluator() {
// Sélectionner l'option GDPR "accept"
document.getElementById('gdprAccept').checked = true;
document.getElementById('gdprDecline').checked = false;
// Mettre à jour les champs
initGDPR();
// Remplir les champs
document.getElementById("evaluatorName").value = "Martin Sophie";
document.getElementById("evaluatorRole").value = "enseignant";
document.getElementById("expertiseLevel").value = "avance";
document.getElementById("messageMastery").value = "intermediaire_msg";
showNotification("Exemple", "Champs d'évaluateur remplis avec un exemple", "info");
}
// Fonction pour effacer les champs évaluateur
function clearEvaluatorFields() {
// Sélectionner l'option GDPR "decline" par défaut
document.getElementById('gdprDecline').checked = true;
document.getElementById('gdprAccept').checked = false;
// Mettre à jour les champs
initGDPR();
showNotification("Champs effacés", "Les informations d'évaluateur ont été effacées", "info");
}
// Fonction pour afficher une notification
function showNotification(title, message, type = "info") {
console.log(`[${type}] ${title}: ${message}`);
const msgDiv = document.getElementById("saveTrustMsg");
if (msgDiv) {
msgDiv.textContent = `${title}: ${message}`;
msgDiv.style.background = type === "success" ? "#E8F5E9" :
type === "error" ? "#FFEBEE" : "#E3F2FD";
msgDiv.style.color = type === "success" ? "#2E7D32" :
type === "error" ? "#C62828" : "#1565C0";
}
}
// Fonction de test
window.testTrustMetrics = function() {
console.log("=== TEST MÉTRIQUES DE CONFIANCE ===");
// Tester quelques combinaisons
const tests = [
{ nom: "Parfait", valeurs: { AR: 5, AE: 5, ESR: 5, SDM: 5, SM: 5 } },
{ nom: "Moyen", valeurs: { AR: 3, AE: 3, ESR: 3, SDM: 3, SM: 3 } },
{ nom: "Faible", valeurs: { AR: 1, AE: 2, ESR: 1, SDM: 2, SM: 1 } },
{ nom: "Mixte", valeurs: { AR: 5, AE: 4, ESR: 3, SDM: 2, SM: 1 } }
];
tests.forEach(test => {
console.log(`\nTest: ${test.nom}`);
// Appliquer les valeurs
for (const metric in test.valeurs) {
if (trustMetrics.hasOwnProperty(metric)) {
trustMetrics[metric] = test.valeurs[metric];
}
}
// Calculer
const score = updateTrustBar();
console.log(`Résultat: ${score}%`);
// Réinitialiser pour le prochain test
for (const metric in trustMetrics) {
trustMetrics[metric] = 0;
}
});
console.log("=== FIN TESTS ===");
};
// Exposer les fonctions globales
window.changePersistenceAnalysis = changePersistenceAnalysis;
window.drawEnrichedHeatmap = drawEnrichedHeatmap;
window.updateStats = updateStats;
window.showNotification = showNotification;
window.fillSampleEvaluator = fillSampleEvaluator;
window.clearEvaluatorFields = clearEvaluatorFields;
window.updateRadarMetricsGrid = updateRadarMetricsGrid;
</script>
</body>
</html>