Onboarding guidé systématique + tableau de bord simplifié
Build and deploy / test-python (push) Failing after 1m48s
Build and deploy / test-js (push) Successful in 53s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped

Comportement SYSTÉMATIQUE à chaque création de jeu (admin compris), pas
une formalité réservée à l'inscription : /onboarding (routes/onboarding/)
devient le point d'entrée unique — 4 cartes retournables (survol =
explication au dos), défilement horizontal animé vers le nom du jeu. Un
admin y repasse à volonté (pas de project_slug dédié, jamais bloqué/
redirigé vers un projet précédent) ; un compte "user" n'en a plus qu'un
créé d'office (routes/auth/register_2fa.py), guidé ici à la place.

- db/games/game_type_catalog.py : catalogue des 4 types (Quiz/
  Embranchement-escape game/RPG/Créer mon jeu de A à Z), _meta['onboarding_type']
  décide du "kind" du premier écran créé et si le tableau de bord complet
  reste accessible.
- routes/games/game_dashboard.py, templates/game_dashboard_simple.html :
  un type restreint (quiz/embranchement/rpg) voit désormais SON tableau
  de bord (même route que "custom"), rendu en version simplifiée — juste
  ses écrans en cartes avec un aperçu RÉEL du contenu (scène mise à
  l'échelle par container query CSS, adaptée à la largeur réelle de la
  carte). "+ Ajouter un écran" n'y propose pas de choix de type : imposé
  par le projet (routes/screens/screens_new.py), verrouillé aussi côté
  serveur.
- core/auth_guard.py : plus de blocage de game_dashboard par type — la
  restriction se fait au rendu, pas à l'accès à la route.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
william
2026-09-02 06:22:45 +02:00
co-authored by Claude Sonnet 5
parent 0f23024882
commit 76a50fa87a
23 changed files with 876 additions and 52 deletions
+118
View File
@@ -226,6 +226,87 @@ input[type=checkbox]{ width:auto; margin-top:6px; }
.content{ max-width:760px; margin:0 auto; padding:28px 20px; }
.card{ border:1px solid var(--border); border-radius:10px; padding:16px; background:var(--panel); margin:12px 0; }
/* Onboarding guidé (voir routes/onboarding/onboarding_new.py) — comportement
SYSTÉMATIQUE à chaque création de jeu (admin compris) : c'est la première
impression du moteur, pensée pour "vendre du rêve" plutôt qu'un simple
formulaire administratif. Deux "pages" côte à côte (choix du type / nom
du jeu) dans #onboardingTrack, révélées par un défilement horizontal
animé — voir static/js/onboarding/onboarding_new.js pour la mécanique
(jamais de JS mélangé au HTML, voir CLAUDE.md). */
.onboardingPage{ max-width:1200px; }
.onboardingError{ text-align:center; color:var(--accent); font-weight:600; margin-bottom:12px; }
.onboardingTitle{ display:block; text-align:center; font-size:2em; font-weight:700; margin:0 0 24px; }
.onboardingViewport{ overflow:hidden; }
.onboardingTrack{
display:flex; width:200%;
transition:transform .65s cubic-bezier(.65,0,.35,1);
}
.onboardingTrack.onboardingTrack--step2{ transform:translateX(-50%); }
.onboardingStep{ flex:0 0 50%; width:50%; box-sizing:border-box; padding:12px 24px; }
/* ---------- Étape 1 : cartes de type, grandes, retournables ---------- */
.onboardingGrid{ display:flex; gap:18px; height:66vh; min-height:420px; max-height:640px; }
.onboardingCard{
flex:1; position:relative; cursor:pointer; padding:0; margin:0; border:none; background:none;
border-radius:20px; perspective:1400px;
opacity:0; animation:forgeOnboardCardIn .5s ease both;
transition:flex-grow .3s ease;
}
.onboardingCard:hover{ flex-grow:1.25; }
.onboardingCardInner{
position:relative; display:block; width:100%; height:100%;
transform-style:preserve-3d; transition:transform .6s cubic-bezier(.4,.2,.2,1);
border-radius:20px; box-shadow:0 10px 30px rgba(0,0,0,.28);
}
.onboardingCard:hover .onboardingCardInner, .onboardingCard.is-selected .onboardingCardInner{ transform:rotateY(180deg); }
.onboardingCardFace{
position:absolute; inset:0; backface-visibility:hidden; border-radius:20px;
display:flex; flex-direction:column; align-items:center; justify-content:center; gap:14px;
padding:20px; text-align:center;
}
.onboardingCardFront{ color:#fff; }
.onboardingCardIcon{ font-size:3.4em; line-height:1; filter:drop-shadow(0 4px 10px rgba(0,0,0,.35)); }
.onboardingCardLabel{ font-weight:800; font-size:1.3em; letter-spacing:.01em; }
.onboardingCardBack{
transform:rotateY(180deg); background:var(--panel); border:2px solid var(--accent);
}
.onboardingCardDesc{ color:var(--text); font-size:1em; line-height:1.5; }
/* Une couleur de dégradé par type — voir data-type dans le template,
généré depuis db.ONBOARDING_TYPES (mêmes clés que ci-dessous). */
.onboardingCard[data-type="quiz"] .onboardingCardFront{ background:linear-gradient(150deg, #2ea3ff, #1560d8); }
.onboardingCard[data-type="embranchement"] .onboardingCardFront{ background:linear-gradient(150deg, #b06bff, #6a2fd6); }
.onboardingCard[data-type="rpg"] .onboardingCardFront{ background:linear-gradient(150deg, #33d182, #159a5c); }
.onboardingCard[data-type="custom"] .onboardingCardFront{ background:var(--gradient); color:#1a1108; }
.onboardingCard[data-type="custom"] .onboardingCardIcon{ filter:drop-shadow(0 4px 10px rgba(0,0,0,.2)); }
/* ---------- Étape 2 : nom du jeu, grand champ centré ---------- */
.onboardingStep--name{ display:flex; flex-direction:column; align-items:center; justify-content:center; min-height:66vh; }
.onboardingBackBtn{
align-self:flex-start; background:none; border:none; color:var(--forge-text-muted, #8b95a7);
font-size:.95em; cursor:pointer; padding:6px 0; margin-bottom:auto;
}
.onboardingBackBtn:hover{ color:var(--accent); }
.onboardingNameBox{ width:100%; max-width:520px; text-align:center; }
.onboardingNameInput{
width:100%; text-align:center; font-size:1.5em; font-weight:600; padding:18px 16px;
border-radius:14px; border:2px solid var(--border); background:var(--panel); color:var(--text);
transition:border-color .18s ease, box-shadow .18s ease;
}
.onboardingNameInput:focus{ outline:none; border-color:var(--accent); box-shadow:0 0 0 3px rgba(255,95,46,.2); }
.onboardingSubmitBtn{
margin-top:20px; background:var(--gradient); border:none; font-weight:700; letter-spacing:.01em;
font-size:1.05em; padding:12px 28px; transition:opacity .18s ease, transform .12s ease;
}
.onboardingSubmitBtn:disabled{ opacity:.4; cursor:not-allowed; }
.onboardingSubmitBtn:not(:disabled):hover{ transform:translateY(-2px); }
@keyframes forgeOnboardCardIn{ from{ opacity:0; transform:translateY(14px); } to{ opacity:1; transform:translateY(0); } }
@media (prefers-reduced-motion: reduce){
.onboardingCard{ animation:none; opacity:1; }
.onboardingTrack, .onboardingCardInner{ transition:none; }
}
.inline-form .fieldRow{ display:flex; gap:8px; align-items:flex-end; }
.inline-form .fieldRow input{ flex:1; }
.fieldRow{ display:flex; gap:10px; }
@@ -773,6 +854,43 @@ code{ background:var(--panel2); border:1px solid var(--border); border-radius:4p
.dashCreateBar input, .dashCreateBar select{ width:auto; flex:1 1 160px; }
.dashDangerZone{ margin-top:28px; }
/* Dashboard simplifié (templates/game_dashboard_simple.html, voir
routes/games/game_dashboard.py) — un compte "restreint" (quiz/
embranchement/rpg) ne voit que ses écrans, en cartes avec un aperçu
RÉEL de leur contenu (élements % réutilisés tels quels pour un écran
document — voir elstyle/eltransform — ou objets de scène mis à
l'échelle pour un écran jeu_2d, voir --scenePreviewScale posé par
carte). */
.screenCardGrid{ display:grid; grid-template-columns:repeat(auto-fill, minmax(260px, 1fr)); gap:20px; margin-top:14px; }
.screenCard{
border:1px solid var(--border); border-radius:14px; overflow:hidden; background:var(--panel);
transition:transform .18s ease, box-shadow .18s ease;
}
.screenCard:hover{ transform:translateY(-3px); box-shadow:0 10px 24px rgba(0,0,0,.25); }
.screenCardPreviewLink{ display:block; }
.screenPreviewBox{
position:relative; width:100%; overflow:hidden; container-type:inline-size;
background:#0b0d12; border-bottom:1px solid var(--border);
}
/* Scène "jeu_2d" (positionnement en PIXELS fixes, voir render_scene_object.py)
mise à l'échelle EXACTEMENT à la largeur réelle de .screenPreviewBox —
"cqw" (1% de la largeur du conteneur, posé ci-dessus) rend ce calcul
toujours juste, quelle que soit la largeur réellement occupée par la
carte (grille responsive) — jamais une largeur fixe supposée. */
.screenPreviewScene{
position:absolute; top:0; left:0; transform-origin:top left;
transform:scale(calc(100cqw / var(--scene-w)));
}
.screenPreviewEmpty{
position:absolute; inset:0; display:flex; align-items:center; justify-content:center;
color:var(--forge-text-muted, #8b95a7); font-size:.85em;
}
.screenCardFooter{ display:flex; align-items:center; justify-content:space-between; gap:8px; padding:10px 12px; }
.screenCardName{ font-weight:600; color:var(--text); text-decoration:none; }
.screenCardName:hover{ color:var(--accent); }
.screenCardActions{ display:flex; gap:4px; }
.screenCardActions form{ display:inline; }
/* ---------- Filtre de colonnes (onglet Données, game_dashboard.html) ---------- */
/* Menu déroulant compact sous le bouton "🔧 Colonnes" — un objet avec
beaucoup de champs rend le tableau illisible, ce menu permet d'en