Dev #7
@@ -1,7 +1,9 @@
|
||||
from ..connection import connect
|
||||
from .ensure_field_bounds_schema import ensure_field_bounds_schema
|
||||
|
||||
|
||||
def list_definitions(slug):
|
||||
ensure_field_bounds_schema(slug)
|
||||
conn = connect(slug)
|
||||
rows = conn.execute("SELECT * FROM _definitions ORDER BY id").fetchall()
|
||||
conn.close()
|
||||
|
||||
@@ -13,6 +13,11 @@ def create_global_var(slug):
|
||||
name = (request.form.get("name") or "").strip()
|
||||
var_type = request.form.get("type") or "texte"
|
||||
default_value = request.form.get("default_value") or ""
|
||||
# État par joueur (Phase 1) : coché par défaut (une case DÉCOCHÉE
|
||||
# explicitement rend la variable partagée par tous les joueurs, voir
|
||||
# db/global_vars/create_global_variable.py) — absent du formulaire
|
||||
# (jeux/pages qui n'ont pas encore ce champ) => reste per_player.
|
||||
per_player = request.form.get("per_player", "1") == "1"
|
||||
if name:
|
||||
db.create_global_variable(slug, name, var_type, default_value)
|
||||
db.create_global_variable(slug, name, var_type, default_value, per_player=per_player)
|
||||
return redirect(url_for("game_dashboard", slug=slug, tab="variables"))
|
||||
|
||||
@@ -23,8 +23,13 @@ def object_new(slug):
|
||||
# message d'erreur, juste un retour au tableau de bord) dès qu'on
|
||||
# tapait le nom sans avoir encore cliqué "+ Ajouter un champ".
|
||||
fields = _parse_field_rows(request.form)
|
||||
# État par joueur (Phase 1) : coché par défaut (une case DÉCOCHÉE
|
||||
# explicitement rend l'objet partagé par tous les joueurs — ex. un
|
||||
# catalogue/une liste de questions — voir db/definitions/
|
||||
# create_definition.py).
|
||||
per_player = request.form.get("per_player", "1") == "1"
|
||||
if name:
|
||||
definition_id = db.create_definition(slug, name, fields)
|
||||
definition_id = db.create_definition(slug, name, fields, per_player=per_player)
|
||||
return redirect(url_for("object_view", slug=slug, definition_id=definition_id))
|
||||
|
||||
return redirect(url_for("game_dashboard", slug=slug))
|
||||
|
||||
@@ -76,12 +76,13 @@
|
||||
{% if definitions %}
|
||||
<div class="fieldsTableWrap">
|
||||
<table class="table is-fullwidth is-hoverable fieldsTable">
|
||||
<thead><tr><th>Nom</th><th>Table SQL</th><th></th></tr></thead>
|
||||
<thead><tr><th>Nom</th><th>Table SQL</th><th>Par joueur</th><th></th></tr></thead>
|
||||
<tbody>
|
||||
{% for d in definitions %}
|
||||
<tr>
|
||||
<td><button type="button" class="linkLikeButton" onclick="openObjectEditPanel({{ d.id }})">{{ d.name }}</button></td>
|
||||
<td class="has-text-grey">{{ d.table_name }}</td>
|
||||
<td><span class="hint">{{ 'Oui' if d.per_player else 'Non — partagé' }}</span></td>
|
||||
<td class="fieldsTableActions">
|
||||
<button type="button" class="button is-small" onclick="openObjectEditPanel({{ d.id }})" title="Modifier cet objet">✏️</button>
|
||||
</td>
|
||||
@@ -158,6 +159,15 @@
|
||||
</select>
|
||||
<input type="text" id="newVarValue" name="default_value" placeholder="Valeur de départ">
|
||||
<button type="button" id="newVarConfigureBtn" class="button is-small" style="display:none;" onclick="openVarValuePanel('newVarValue', document.getElementById('newVarType').value)">🔧 Configurer la valeur</button>
|
||||
<!-- État par joueur (Phase 1) : réglé une seule fois, à la création
|
||||
(voir db/global_vars/create_global_variable.py) — coché par
|
||||
défaut (chaque joueur a sa propre valeur), décoché pour une
|
||||
variable explicitement PARTAGÉE par tous les joueurs (ex. un
|
||||
compteur de visiteurs global). -->
|
||||
<label class="checkbox" style="display:flex; align-items:center; gap:6px; font-size:12px; white-space:nowrap;" title="Décoché : une seule valeur, partagée par tous les joueurs.">
|
||||
<input type="checkbox" name="per_player" value="1" checked style="width:auto;">
|
||||
Par joueur
|
||||
</label>
|
||||
<button type="submit" class="primary">Créer</button>
|
||||
</form>
|
||||
{% if variables %}
|
||||
@@ -166,7 +176,7 @@
|
||||
{% endfor %}
|
||||
<div class="fieldsTableWrap">
|
||||
<table class="table is-fullwidth is-hoverable fieldsTable">
|
||||
<thead><tr><th>Nom</th><th>Type</th><th>Valeur</th><th></th></tr></thead>
|
||||
<thead><tr><th>Nom</th><th>Type</th><th>Valeur</th><th>Par joueur</th><th></th></tr></thead>
|
||||
<tbody>
|
||||
{% for v in variables %}
|
||||
<tr>
|
||||
@@ -188,6 +198,7 @@
|
||||
<input type="text" form="varEditForm{{ v.id }}" name="value" value="{{ v.value or '' }}" aria-label="Valeur de « {{ v.name }} »">
|
||||
{% endif %}
|
||||
</td>
|
||||
<td><span class="hint">{{ 'Oui' if v.per_player else 'Non — partagée' }}</span></td>
|
||||
<td class="fieldsTableActions">
|
||||
<button type="submit" form="varEditForm{{ v.id }}" class="button is-small">Enregistrer</button>
|
||||
<form method="post" action="{{ url_for('global_var_delete', slug=game.slug, var_id=v.id) }}"
|
||||
@@ -295,6 +306,16 @@
|
||||
<input type="text" id="object_name" name="object_name" required autocomplete="off">
|
||||
</div>
|
||||
|
||||
<!-- État par joueur (Phase 1) : réglé une seule fois, à la création
|
||||
(voir db/definitions/create_definition.py) — coché par défaut
|
||||
(chaque joueur a ses propres lignes, ex. un inventaire),
|
||||
décoché pour un objet explicitement PARTAGÉ par tous les
|
||||
joueurs (ex. un catalogue, une liste de questions). -->
|
||||
<label class="checkbox" style="display:flex; align-items:center; gap:6px; margin-top:10px;" title="Décoché : les lignes de cet objet sont partagées par tous les joueurs.">
|
||||
<input type="checkbox" name="per_player" value="1" checked style="width:auto;">
|
||||
Chaque joueur a ses propres lignes ("par joueur")
|
||||
</label>
|
||||
|
||||
<div class="dashPanelHeader" style="margin-top:14px;">
|
||||
<h2>Champs</h2>
|
||||
<button type="button" class="button is-small" id="btnAddField">+ Ajouter un champ</button>
|
||||
|
||||
@@ -97,6 +97,29 @@ def test_get_row_does_not_leak_across_players(game):
|
||||
assert db.get_row(game, definition, row_id, player_id="alice") is not None
|
||||
|
||||
|
||||
def test_create_global_var_route_respects_per_player_checkbox(client, game):
|
||||
client.post(f"/game/{game}/variables/new", data={
|
||||
"name": "score", "type": "nombre_entier", "default_value": "0",
|
||||
})
|
||||
assert db.list_global_variables(game)[0]["per_player"] == 1
|
||||
|
||||
client.post(f"/game/{game}/variables/new", data={
|
||||
"name": "compteur_visites", "type": "nombre_entier", "default_value": "0", "per_player": "0",
|
||||
})
|
||||
variables = {v["name"]: v for v in db.list_global_variables(game)}
|
||||
assert variables["compteur_visites"]["per_player"] == 0
|
||||
|
||||
|
||||
def test_object_new_route_respects_per_player_checkbox(client, game):
|
||||
client.post(f"/game/{game}/objects/new", data={"object_name": "Inventaire"})
|
||||
definitions = {d["name"]: d for d in db.list_definitions(game)}
|
||||
assert definitions["Inventaire"]["per_player"] == 1
|
||||
|
||||
client.post(f"/game/{game}/objects/new", data={"object_name": "Catalogue", "per_player": "0"})
|
||||
definitions = {d["name"]: d for d in db.list_definitions(game)}
|
||||
assert definitions["Catalogue"]["per_player"] == 0
|
||||
|
||||
|
||||
def test_creator_preview_default_matches_shared_definition_behavior(game):
|
||||
"""L'aperçu créateur (aucun player_id passé nulle part, donc
|
||||
PLAYER_SHARED partout) doit continuer de fonctionner exactement comme
|
||||
|
||||
Reference in New Issue
Block a user