Ajoute l'onglet "Données" au panneau objet, retire object_view.html
Suite de la demande : le panneau "Modifier un objet" (crayon ✏️, onglet Objets du tableau de bord) a maintenant 2 sous-onglets — "Champs" (déjà en place) et "Données", qui reprend data_list.html + data_form.html (retirés) : ajouter une entrée (une ligne compacte avec le bon type de champ par colonne — texte/nombre/case à cocher/date/relation, comme l'ancien formulaire), modifier/supprimer une entrée existante (tableau dense, cellules éditables en ligne, même principe que "Champs existants"). Sous-onglets scopés au panneau de LEUR objet (switchObjectPanelTab(), classes .objectPanelTabs/.objectPanelTabPanel distinctes de .builderTabs/ .builderTabPanel) — plusieurs objets ont chacun leurs propres sous-onglets indépendants sur la même page, sans jamais interférer avec les onglets du tableau de bord lui-même. routes/games/game_dashboard.py fournit maintenant, par objet : ses lignes (rows_by_definition), les libellés lisibles de ses champs relation (relation_labels_by_definition, pour l'affichage) et leurs options (relation_options_by_definition, pour les <select>), ainsi que referenced_by_definition (avertissement permanent si un autre objet a une relation vers celui-ci — repris de l'ancien object_view.py, affiché maintenant en continu plutôt qu'après une tentative de suppression échouée). data_form.py (partagé par data_new/data_edit), data_delete.py et object_delete.py redirigent maintenant vers le tableau de bord (?edit=<id>&subtab=data, +?blocked_row=<id> si la suppression d'une entrée est bloquée par une relation) au lieu de object_view/object_edit. Piège évité : de nombreux tests déduisent l'id d'un objet fraîchement créé du DERNIER SEGMENT du chemin dans le header Location d'une redirection (.../objects/<id>) — rediriger object_new directement vers le tableau de bord (chemin sans id) cassait donc 33 tests d'un coup. Fix : object_view.py reste en place, mais seulement comme redirecteur (plus de page rendue) — object_new redirige toujours vers lui (chemin qui se termine par l'id, donc les tests continuent de fonctionner), qui redirige à son tour vers le tableau de bord. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
b8dfdcb205
commit
0e390a679b
@@ -22,6 +22,30 @@ def game_dashboard(slug):
|
||||
# simplement montré/masqué en JS, plutôt qu'un aller-retour serveur à
|
||||
# l'ouverture (voir openObjectEditPanel()).
|
||||
definitions_full = {d["id"]: db.get_definition(slug, d["id"]) for d in definitions}
|
||||
|
||||
# Pour l'onglet "Données" de ce même panneau (remplace data_list.html/
|
||||
# object_view.py, retirés) : les lignes déjà enregistrées de chaque
|
||||
# objet, les libellés lisibles de ses champs relation (pour l'affichage
|
||||
# du tableau) et les options possibles de ces mêmes champs relation
|
||||
# (pour le formulaire "Ajouter une entrée").
|
||||
rows_by_definition = {}
|
||||
relation_labels_by_definition = {}
|
||||
relation_options_by_definition = {}
|
||||
referenced_by_definition = {}
|
||||
for d in definitions:
|
||||
full = definitions_full[d["id"]]
|
||||
rows_by_definition[d["id"]] = db.list_rows(slug, full)
|
||||
relation_labels = {}
|
||||
relation_options = {}
|
||||
for f in full["fields"]:
|
||||
if f["type"] == "relation":
|
||||
options = db.relation_options(slug, f["relation_definition_id"])
|
||||
relation_labels[f["id"]] = {opt["id"]: opt["label"] for opt in options}
|
||||
relation_options[f["id"]] = options
|
||||
relation_labels_by_definition[d["id"]] = relation_labels
|
||||
relation_options_by_definition[d["id"]] = relation_options
|
||||
referenced_by_definition[d["id"]] = db.definitions_referencing(slug, d["id"])
|
||||
|
||||
screen_list = screens.list_screens(slug)
|
||||
element_types = screens.list_element_types(slug)
|
||||
element_type_usage = {t["id"]: screens.element_type_usage_count(slug, t["id"]) for t in element_types}
|
||||
@@ -29,6 +53,10 @@ def game_dashboard(slug):
|
||||
return render_template(
|
||||
"game_dashboard.html", game=game, definitions=definitions,
|
||||
definitions_full=definitions_full,
|
||||
rows_by_definition=rows_by_definition,
|
||||
relation_labels_by_definition=relation_labels_by_definition,
|
||||
relation_options_by_definition=relation_options_by_definition,
|
||||
referenced_by_definition=referenced_by_definition,
|
||||
screen_list=screen_list, element_types=element_types,
|
||||
element_type_usage=element_type_usage, variables=variables,
|
||||
variable_types=db.GLOBAL_VARIABLE_TYPES, field_types=db.FIELD_TYPES,
|
||||
|
||||
@@ -13,8 +13,9 @@ def data_delete(slug, definition_id, row_id):
|
||||
referencing = db.rows_referencing(slug, definition_id, row_id)
|
||||
if referencing:
|
||||
# Suppression bloquée : d'autres lignes pointent vers celle-ci via
|
||||
# une relation. On revient à la liste, qui explique pourquoi via un
|
||||
# paramètre de requête simple plutôt qu'une session/flash message.
|
||||
return redirect(url_for("object_view", slug=slug, definition_id=definition_id, blocked_row=row_id))
|
||||
# une relation. On revient au panneau de cet objet (onglet
|
||||
# Données), qui explique pourquoi via un paramètre de requête
|
||||
# simple plutôt qu'une session/flash message.
|
||||
return redirect(url_for("game_dashboard", slug=slug, tab="objects", edit=definition_id, subtab="data", blocked_row=row_id))
|
||||
db.delete_row(slug, definition, row_id)
|
||||
return redirect(url_for("object_view", slug=slug, definition_id=definition_id))
|
||||
return redirect(url_for("game_dashboard", slug=slug, tab="objects", edit=definition_id, subtab="data"))
|
||||
|
||||
@@ -1,32 +1,24 @@
|
||||
from flask import render_template, request, redirect, url_for, abort
|
||||
from flask import request, redirect, url_for, abort
|
||||
|
||||
import db
|
||||
|
||||
|
||||
def _data_form(slug, definition_id, row):
|
||||
game = db.game_meta(slug)
|
||||
"""Ajouter/modifier une entrée se fait désormais depuis l'onglet
|
||||
"Données" du panneau déplaçable de son objet (voir game_dashboard.html,
|
||||
ouvert par le crayon ✏️ de l'onglet Objets) plutôt qu'une page dédiée
|
||||
(data_form.html, retirée) — cette fonction ne fait donc plus que
|
||||
traiter la soumission de ce formulaire. Un GET direct redirige vers le
|
||||
tableau de bord, en rouvrant le panneau de cet objet sur son onglet
|
||||
Données."""
|
||||
definition = db.get_definition(slug, definition_id)
|
||||
if not definition:
|
||||
abort(404)
|
||||
|
||||
# Pour chaque champ de type relation, on prépare les options du <select>
|
||||
# à partir des lignes déjà enregistrées de l'objet lié.
|
||||
relation_options = {}
|
||||
for f in definition["fields"]:
|
||||
if f["type"] == "relation":
|
||||
relation_options[f["id"]] = db.relation_options(slug, f["relation_definition_id"])
|
||||
|
||||
if request.method == "POST":
|
||||
if row is None:
|
||||
db.insert_row(slug, definition, request.form)
|
||||
else:
|
||||
db.update_row(slug, definition, row["id"], request.form)
|
||||
return redirect(url_for("object_view", slug=slug, definition_id=definition_id))
|
||||
|
||||
return render_template(
|
||||
"data_form.html",
|
||||
game=game,
|
||||
definition=definition,
|
||||
relation_options=relation_options,
|
||||
row=row,
|
||||
)
|
||||
return redirect(url_for("game_dashboard", slug=slug, tab="objects", edit=definition_id, subtab="data"))
|
||||
|
||||
@@ -10,9 +10,10 @@ def object_delete(slug, definition_id):
|
||||
referenced_by = db.definitions_referencing(slug, definition_id)
|
||||
if referenced_by:
|
||||
# Suppression bloquée : d'autres objets ont une relation vers
|
||||
# celui-ci. On revient à l'écran de l'objet, qui affiche déjà cet
|
||||
# avertissement (referenced_by), plutôt que de casser ces relations
|
||||
# silencieusement.
|
||||
return redirect(url_for("object_view", slug=slug, definition_id=definition_id))
|
||||
# celui-ci. On revient à son panneau, qui affiche déjà cet
|
||||
# avertissement (referenced_by_definition, voir game_dashboard.py)
|
||||
# en permanence, pas seulement après une tentative — plutôt que de
|
||||
# casser ces relations silencieusement.
|
||||
return redirect(url_for("game_dashboard", slug=slug, tab="objects", edit=definition_id))
|
||||
db.delete_definition(slug, definition_id)
|
||||
return redirect(url_for("game_dashboard", slug=slug))
|
||||
return redirect(url_for("game_dashboard", slug=slug, tab="objects"))
|
||||
|
||||
@@ -1,33 +1,18 @@
|
||||
from flask import render_template, request, abort
|
||||
|
||||
import db
|
||||
from flask import redirect, url_for
|
||||
|
||||
from core.flask_app import app
|
||||
|
||||
|
||||
@app.route("/game/<slug>/objects/<int:definition_id>")
|
||||
def object_view(slug, definition_id):
|
||||
game = db.game_meta(slug)
|
||||
definition = db.get_definition(slug, definition_id)
|
||||
if not definition:
|
||||
abort(404)
|
||||
rows = db.list_rows(slug, definition)
|
||||
|
||||
# Pour l'affichage : traduit chaque id de relation en un libellé lisible
|
||||
# (ex. "Salle serveur (#1)") au lieu d'un identifiant numérique brut.
|
||||
relation_labels = {}
|
||||
for f in definition["fields"]:
|
||||
if f["type"] == "relation":
|
||||
options = db.relation_options(slug, f["relation_definition_id"])
|
||||
relation_labels[f["id"]] = {opt["id"]: opt["label"] for opt in options}
|
||||
|
||||
referenced_by = db.definitions_referencing(slug, definition_id)
|
||||
|
||||
blocked_row_id = request.args.get("blocked_row", type=int)
|
||||
blocked_row_refs = db.rows_referencing(slug, definition_id, blocked_row_id) if blocked_row_id else None
|
||||
|
||||
return render_template(
|
||||
"data_list.html", game=game, definition=definition, rows=rows,
|
||||
relation_labels=relation_labels, referenced_by=referenced_by,
|
||||
blocked_row_id=blocked_row_id, blocked_row_refs=blocked_row_refs,
|
||||
)
|
||||
"""Ancienne page de visualisation des données d'un objet (data_list.html) —
|
||||
retirée : les données vivent désormais dans l'onglet "Données" du
|
||||
panneau déplaçable de cet objet (voir game_dashboard.html). Cette
|
||||
route reste néanmoins en place, comme pur redirecteur, pour deux
|
||||
raisons : un lien externe/ancien vers cette URL continue de mener
|
||||
quelque part de sensé, et surtout beaucoup de tests (et certains
|
||||
formulaires) déduisent l'id d'un objet fraîchement créé du DERNIER
|
||||
segment du chemin renvoyé par le header Location d'une redirection
|
||||
(ex. .../objects/<id>) — object_new redirige donc ICI (chemin qui se
|
||||
termine par l'id), qui redirige ensuite vers le tableau de bord."""
|
||||
return redirect(url_for("game_dashboard", slug=slug, tab="objects", edit=definition_id))
|
||||
|
||||
Reference in New Issue
Block a user