Add comment system with models, forms, and UI integration for lessons
This commit is contained in:
parent
c22622ebc1
commit
95111240bc
26 changed files with 1001 additions and 77 deletions
|
|
@ -109,6 +109,103 @@
|
|||
}
|
||||
|
||||
/* Light theme values — applied via colors_light.css inclusion */
|
||||
|
||||
/* ==========================
|
||||
Cours — Sommaire (TOC)
|
||||
========================== */
|
||||
.courseToc {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border-subtle);
|
||||
border-radius: var(--r-3);
|
||||
box-shadow: var(--shadow-1);
|
||||
/* Make the TOC span the full width of its section */
|
||||
width: 100%;
|
||||
margin: var(--space-5) 0;
|
||||
padding: var(--space-5);
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.courseToc .tocModules,
|
||||
.courseToc .tocLessons {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.courseToc .tocModule + .tocModule {
|
||||
margin-top: var(--space-5);
|
||||
padding-top: var(--space-5);
|
||||
border-top: 2px dashed var(--border-subtle);
|
||||
}
|
||||
|
||||
.courseToc .tocModuleHeader {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-3);
|
||||
color: var(--text);
|
||||
font-weight: 600;
|
||||
letter-spacing: .2px;
|
||||
margin-bottom: var(--space-3);
|
||||
}
|
||||
|
||||
.courseToc .tocModuleIndex {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 999px;
|
||||
background: var(--primary);
|
||||
color: var(--primary-contrast);
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
box-shadow: 0 0 0 3px rgba(78,158,214,0.15);
|
||||
}
|
||||
|
||||
.courseToc .tocModuleTitle {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.courseToc .tocLessons {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.courseToc .tocLesson {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.courseToc .tocLink {
|
||||
display: block;
|
||||
padding: 10px 12px;
|
||||
border-radius: var(--r-2);
|
||||
text-decoration: none;
|
||||
color: var(--link);
|
||||
background: rgba(255,255,255,0.02);
|
||||
border: 1px solid transparent;
|
||||
transition: all var(--transition-1);
|
||||
}
|
||||
|
||||
.courseToc .tocLink:hover {
|
||||
color: var(--link-hover);
|
||||
background: rgba(78,158,214,0.08);
|
||||
border-color: var(--border-subtle);
|
||||
}
|
||||
|
||||
.courseToc .tocLesson.current .tocLink {
|
||||
color: var(--success);
|
||||
background: rgba(47,168,108,0.12);
|
||||
border-color: rgba(47,168,108,0.35);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.courseToc .tocCurrentTag {
|
||||
color: var(--text-muted);
|
||||
font-weight: 600;
|
||||
margin-left: 8px;
|
||||
font-size: 12px;
|
||||
}
|
||||
[data-theme='light'] {
|
||||
/* Palette: plus nuancé, moins "blanc" */
|
||||
--bg: #eef3f7; /* fond légèrement teinté bleu-gris */
|
||||
|
|
@ -719,7 +816,227 @@ pre {
|
|||
|
||||
/* Reduced motion */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
* { transition: none !important; animation: none !important; }
|
||||
* { transition: none !important; animation: none !important; }
|
||||
}
|
||||
|
||||
/* ---------------------------------------------
|
||||
Container Global
|
||||
--------------------------------------------- */
|
||||
.lessonComments {
|
||||
margin-top: var(--space-6);
|
||||
background: var(--surface); /* Assure-toi que c'est une couleur un peu plus claire que le fond de page */
|
||||
border: 1px solid var(--border-subtle);
|
||||
border-radius: var(--r-3);
|
||||
/* On enlève le padding global pour coller les éléments aux bords si besoin,
|
||||
ou on le garde pour un effet "carte" */
|
||||
padding: var(--space-5);
|
||||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.lessonComments h3 {
|
||||
font-size: 1.1rem;
|
||||
font-weight: 700;
|
||||
color: var(--text);
|
||||
margin-bottom: var(--space-4);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------
|
||||
Liste des commentaires
|
||||
--------------------------------------------- */
|
||||
.commentsList {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0 0 var(--space-6) 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-4); /* Espace entre les commentaires */
|
||||
}
|
||||
|
||||
.commentItem {
|
||||
background: var(--neutral-100); /* Un fond très léger pour détacher le commentaire */
|
||||
border: 1px solid var(--border-subtle);
|
||||
border-radius: var(--r-2);
|
||||
padding: var(--space-4);
|
||||
transition: border-color 0.2s ease;
|
||||
}
|
||||
|
||||
.commentItem:hover {
|
||||
border-color: var(--border-strong);
|
||||
}
|
||||
|
||||
/* En-tête du commentaire (Auteur + Date) */
|
||||
.commentHeader {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: var(--space-3);
|
||||
padding-bottom: var(--space-3);
|
||||
border-bottom: 1px solid var(--border-subtle);
|
||||
}
|
||||
|
||||
.commentAuthor {
|
||||
font-weight: 700;
|
||||
color: var(--primary); /* Couleur primaire pour l'auteur */
|
||||
font-size: 0.95rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
/* Petit point décoratif avant le nom */
|
||||
.commentAuthor::before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background-color: var(--primary);
|
||||
border-radius: 50%;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.commentDate {
|
||||
font-size: 0.8rem;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
/* Contenu du texte */
|
||||
.commentContent {
|
||||
color: var(--text);
|
||||
font-size: 0.95rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
/* Markdown Styles dans les commentaires */
|
||||
.commentContent p { margin-bottom: 0.8em; }
|
||||
.commentContent p:last-child { margin-bottom: 0; }
|
||||
|
||||
.commentContent code {
|
||||
font-family: 'Fira Code', monospace;
|
||||
background: rgba(124, 58, 237, 0.1); /* Petite teinte violette légère */
|
||||
color: var(--primary);
|
||||
padding: 2px 5px;
|
||||
border-radius: 4px;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.commentContent pre {
|
||||
background: #1e1e2e; /* Fond sombre style IDE */
|
||||
color: #cdd6f4;
|
||||
padding: 15px;
|
||||
border-radius: 8px;
|
||||
overflow-x: auto;
|
||||
margin: 10px 0;
|
||||
border: 1px solid rgba(255,255,255,0.05);
|
||||
}
|
||||
|
||||
.commentContent a {
|
||||
color: var(--primary);
|
||||
text-decoration: none;
|
||||
border-bottom: 1px dotted var(--primary);
|
||||
}
|
||||
.commentContent a:hover { border-bottom-style: solid; }
|
||||
|
||||
/* ---------------------------------------------
|
||||
Formulaire "Éditeur Riche"
|
||||
--------------------------------------------- */
|
||||
.commentFormBlock {
|
||||
margin-top: var(--space-4);
|
||||
}
|
||||
|
||||
.commentForm {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border-strong); /* Bordure plus visible */
|
||||
border-radius: var(--r-2);
|
||||
overflow: hidden; /* Pour que les enfants ne dépassent pas des coins */
|
||||
transition: box-shadow 0.2s;
|
||||
}
|
||||
|
||||
.commentForm:focus-within {
|
||||
box-shadow: 0 0 0 2px var(--primary-focus, rgba(124, 58, 237, 0.3));
|
||||
border-color: var(--primary);
|
||||
}
|
||||
|
||||
/* La barre d'outils collée au textarea */
|
||||
.commentToolbar {
|
||||
background: var(--neutral-200); /* Fond gris clair/sombre pour la barre */
|
||||
padding: 8px 12px;
|
||||
border-bottom: 1px solid var(--border-subtle);
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.commentToolbar .btnTool {
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: var(--text-muted);
|
||||
cursor: pointer;
|
||||
padding: 6px;
|
||||
border-radius: 4px;
|
||||
transition: all 0.2s;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.commentToolbar .btnTool:hover {
|
||||
background: rgba(0,0,0,0.05); /* Ou blanc semi-transparent en dark mode */
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
/* Le champ texte */
|
||||
.commentField {
|
||||
padding: 0; /* On enlève le padding du container */
|
||||
}
|
||||
|
||||
.commentField textarea {
|
||||
width: 100%; /* Prend toute la largeur */
|
||||
border: none; /* Pas de bordure, c'est le container .commentForm qui gère */
|
||||
background: transparent;
|
||||
color: var(--text);
|
||||
padding: 15px;
|
||||
min-height: 120px;
|
||||
resize: vertical;
|
||||
font-family: inherit;
|
||||
outline: none; /* Le focus est géré par le parent */
|
||||
display: block; /* Évite les espaces fantômes */
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
/* Footer du formulaire (Aide + Bouton) */
|
||||
.formActions {
|
||||
padding: 10px 15px;
|
||||
background: var(--neutral-100); /* Pied de formulaire légèrement différent */
|
||||
border-top: 1px solid var(--border-subtle);
|
||||
display: flex;
|
||||
justify-content: space-between; /* Aide à gauche, Bouton à droite */
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.commentHelp {
|
||||
margin: 0;
|
||||
font-size: 0.75rem;
|
||||
color: var(--text-muted);
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.lessonComments .btn {
|
||||
background: var(--primary);
|
||||
color: #fff;
|
||||
border: none;
|
||||
padding: 8px 20px;
|
||||
border-radius: 6px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.lessonComments .btn:hover {
|
||||
background: var(--bg, #6d28d9);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
|
|
@ -1345,7 +1662,7 @@ footer { background-color: var(--card); color: var(--text-muted); }
|
|||
form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 50%;
|
||||
width: 90%;
|
||||
margin: 20px auto;
|
||||
}
|
||||
|
||||
|
|
@ -1360,7 +1677,7 @@ form {
|
|||
border-radius: 10px; /* Bordure arrondie */
|
||||
box-shadow: var(--shadow-1); /* Ombre légère */
|
||||
margin: 20px auto;
|
||||
width: 50%;
|
||||
width: min(680px, 92%);
|
||||
}
|
||||
|
||||
.login-form {
|
||||
|
|
@ -1425,6 +1742,82 @@ input[type="text"], input[type="email"], input[type="password"], textarea {
|
|||
color: var(--fg);
|
||||
}
|
||||
|
||||
/*
|
||||
Système de formulaires générique — pour un rendu cohérent avec le site
|
||||
À appliquer avec class="form" sur <form> (fonctionne aussi avec {{ form.as_p }})
|
||||
*/
|
||||
|
||||
.form {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.form p { /* Django {{ form.as_p }} */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
margin: 0 0 14px 0;
|
||||
}
|
||||
|
||||
.form label {
|
||||
font-weight: 600;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.form input[type="text"],
|
||||
.form input[type="email"],
|
||||
.form input[type="password"],
|
||||
.form input[type="url"],
|
||||
.form input[type="number"],
|
||||
.form input[type="file"],
|
||||
.form input[type="search"],
|
||||
.form input[type="tel"],
|
||||
.form select,
|
||||
.form textarea {
|
||||
width: 100%;
|
||||
padding: 10px 12px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--r-2);
|
||||
background-color: var(--surface);
|
||||
color: var(--text);
|
||||
box-shadow: inset var(--shadow-1);
|
||||
transition: border-color var(--transition-1), box-shadow var(--transition-1), background-color var(--transition-1);
|
||||
}
|
||||
|
||||
.form input:focus,
|
||||
.form select:focus,
|
||||
.form textarea:focus {
|
||||
outline: none;
|
||||
box-shadow: var(--focus-ring);
|
||||
border-color: var(--primary);
|
||||
}
|
||||
|
||||
.form .helptext,
|
||||
.form small.helptext {
|
||||
color: var(--text-muted);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
/* Erreurs Django: ul.errorlist > li */
|
||||
.form ul.errorlist {
|
||||
list-style: none;
|
||||
margin: 0 0 6px 0;
|
||||
padding: 8px 10px;
|
||||
border: 1px solid color-mix(in oklab, var(--danger) 60%, var(--border));
|
||||
background: color-mix(in oklab, var(--danger) 12%, var(--surface));
|
||||
color: var(--danger);
|
||||
border-radius: var(--r-2);
|
||||
}
|
||||
|
||||
.form ul.errorlist li { margin: 0; }
|
||||
|
||||
.form .actions,
|
||||
.form .form-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
justify-content: flex-end;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
/*
|
||||
Système de boutons — harmonisé avec le thème
|
||||
Utilise les tokens de couleurs et de rayons définis en haut de fichier.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue