Build and deploy / test-python (push) Successful in 11m2s
Build and deploy / test-js (push) Successful in 1m2s
Build and deploy / lint-python (push) Successful in 4m6s
Build and deploy / lint-js (push) Failing after 1m21s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Build and deploy / sonarqube (push) Failing after 4m2s
Nouveau moteur document_engine/ (elements CRUD + rendering + labels), db/supports/ (stockage independant de db/games), routes/document/ (CRUD AJAX + publication), et l'editeur frontend complet (templates/document/, static/document/) avec moteur de layout reel (glisser-deposer -> fusion en rangee ou insertion avant/apres), vrai Undo/Redo par pile de commandes, grille d'accroche pour les formes libres, apercu responsive a largeurs fixes, mode Apercu, et publication persistee. "Mes formations" (templates/index.html) liste desormais les environnements 2D et les supports de formation cote a cote ; l'onboarding et core/auth_guard.py sont generalises pour qu'un compte restreint puisse posseder un projet de chaque type independamment. SKIP=djlint : le hook ne signale que le backlog H021 (styles en ligne) deja documente dans CODE_QUALITY.md sur des fichiers pre-existants non touches ici (base.html, game/play.html, scene_edit.html, game_dashboard_simple.html, clause_row.html) plus une ligne de index.html deja presente avant cette session — aucun nouveau fichier (document_edit.html compris) n'y figure. Tous les autres outils (ruff, mypy --strict, vulture, bandit, import-linter, eslint, stylelint) passent sans erreur ; 617 tests Python + 276 tests JS verts, plus une verification manuelle complete du cycle de vie via le serveur de developpement. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
102 lines
4.3 KiB
Python
102 lines
4.3 KiB
Python
from typing import Any
|
|
|
|
from flask import g, redirect, render_template, request, url_for
|
|
from werkzeug.wrappers import Response
|
|
|
|
import auth
|
|
import db
|
|
import game_engine
|
|
from core.flask_app import app
|
|
|
|
# Choix de PREMIER niveau, avant le catalogue des types de jeu (voir
|
|
# db.ONBOARDING_TYPES) : "support" n'y figure jamais (schéma/notions
|
|
# entièrement différents, voir document_engine/), posé ici comme une
|
|
# carte de plus dans la même grille visuelle (templates/onboarding/
|
|
# onboarding_new.html réutilise sa mécanique de sélection existante sans
|
|
# aucune modification JS, voir static/js/onboarding/onboarding_new.js).
|
|
_SUPPORT_ONBOARDING_CARD: dict[str, Any] = {
|
|
"label": "Support de formation",
|
|
"tagline": "Un contenu structuré, prêt à publier.",
|
|
"icon": "📄",
|
|
"description": (
|
|
'<span class="onboardingPitchTitle">Un support de formation clair,'
|
|
" sans une ligne de code</span>"
|
|
'<ul class="onboardingPitchList">'
|
|
'<li><span class="onboardingPitchCheck">✓</span>'
|
|
"<span><strong>Mise en page automatique</strong> — dépose tes blocs,"
|
|
" l'alignement se fait tout seul</span></li>"
|
|
'<li><span class="onboardingPitchCheck">✓</span>'
|
|
"<span><strong>Formes & mini-jeux</strong> — habille et anime ton"
|
|
" contenu à volonté</span></li>"
|
|
'<li><span class="onboardingPitchCheck">✓</span>'
|
|
"<span><strong>Aperçu & publication</strong> — vois exactement ce que"
|
|
" verra l'apprenant</span></li>"
|
|
"</ul>"
|
|
),
|
|
}
|
|
|
|
|
|
@app.route("/onboarding", methods=["GET", "POST"])
|
|
def onboarding_new() -> str | Response:
|
|
"""Point d'entrée UNIQUE pour créer un projet — jeu (environnement 2D)
|
|
OU support de formation (voir docs/plan/PLAN.md), deux entités racines
|
|
séparées. Un compte peut avoir un projet DE CHAQUE type (project_slug
|
|
ET support_slug, voir core/auth_guard.py) : chaque carte du catalogue
|
|
n'est proposée que si son slot correspondant n'est pas déjà rempli
|
|
(compte "user" restreint — un admin, lui, n'a aucun slot dédié, illimité
|
|
des deux côtés)."""
|
|
user = g.current_user
|
|
restricted = user["role"] != "admin"
|
|
has_game = restricted and bool(user.get("project_slug"))
|
|
has_support = restricted and bool(user.get("support_slug"))
|
|
if has_game and has_support:
|
|
# Les deux slots sont déjà remplis — plus rien à créer ici.
|
|
return redirect(url_for("index"))
|
|
|
|
onboarding_types: dict[str, dict[str, Any]] = {}
|
|
if not has_support:
|
|
onboarding_types["support"] = _SUPPORT_ONBOARDING_CARD
|
|
if not has_game:
|
|
onboarding_types.update(db.ONBOARDING_TYPES)
|
|
|
|
error = None
|
|
if request.method == "POST":
|
|
onboarding_type = request.form.get("onboarding_type", "")
|
|
name = request.form.get("name", "").strip()
|
|
if onboarding_type not in onboarding_types:
|
|
error = "Choisis un type de projet."
|
|
elif not name:
|
|
error = "Donne un nom à ton projet."
|
|
elif onboarding_type == "support":
|
|
return redirect(_create_support_for_user(user, name))
|
|
else:
|
|
return redirect(_create_project_for_user(user, onboarding_type, name))
|
|
|
|
return render_template(
|
|
"onboarding/onboarding_new.html",
|
|
onboarding_types=onboarding_types,
|
|
error=error,
|
|
)
|
|
|
|
|
|
def _create_support_for_user(user: dict[str, Any], name: str) -> str:
|
|
slug = db.create_support(name, owner_folder=str(user["id"]))
|
|
if user["role"] != "admin":
|
|
auth.set_support_slug(user["id"], slug)
|
|
return url_for("document_edit", slug=slug)
|
|
|
|
|
|
def _create_project_for_user(user: dict[str, Any], onboarding_type: str, name: str) -> str:
|
|
# owner_folder = l'id du compte, jamais son email (voir
|
|
# routes/games/games_new.py, même convention).
|
|
slug = db.create_game(name, owner_folder=str(user["id"]))
|
|
db.set_onboarding_type(slug, onboarding_type)
|
|
if user["role"] != "admin":
|
|
# project_slug = LE seul environnement 2D d'un compte "user" (voir
|
|
# core/auth_guard.py) — un admin n'utilise pas cette colonne, ses
|
|
# projets se retrouvent via db.list_games() comme avant.
|
|
auth.set_project_slug(user["id"], slug)
|
|
screen_kind = db.ONBOARDING_TYPES[onboarding_type]["screen_kind"]
|
|
game_engine.create_screen(slug, name, kind=screen_kind)
|
|
return url_for("game_dashboard", slug=slug)
|