Dev #2

Merged
w-vandal merged 24 commits from dev into main 2026-08-27 13:00:44 +00:00
Showing only changes of commit 03db681022 - Show all commits
+151 -69
View File
@@ -296,30 +296,9 @@
<div class="objectPanelTabPanel active" data-optab-panel="fields">
<div class="dashPanelHeader">
<h2>Ajouter un champ</h2>
<h2>Champs existants</h2>
<button type="button" class="button is-small primary" onclick="openAddFieldPanel({{ d.id }})">+ Ajouter</button>
</div>
<form method="post" action="{{ url_for('object_field_add', slug=game.slug, definition_id=d.id) }}" class="dashCreateBar objectAddFieldBar">
<input type="text" name="field_name" placeholder="Nom du champ" required autocomplete="off">
<select name="field_type" class="fieldTypeSelect">
{% for key, val in field_types.items() %}
<option value="{{ key }}">{{ val.label }}</option>
{% endfor %}
</select>
<select name="field_relation" class="relationTarget" style="display:none;">
{% for od in existing_definitions %}
<option value="{{ od.id }}">{{ od.name }}</option>
{% endfor %}
</select>
<input type="number" step="any" name="field_min" placeholder="Mini" class="boundsTarget" style="display:none;">
<input type="number" step="any" name="field_max" placeholder="Maxi" class="boundsTarget" style="display:none;">
<select name="field_required">
<option value="0">Non obligatoire</option>
<option value="1">Obligatoire</option>
</select>
<button type="submit" class="primary">+ Ajouter</button>
</form>
<h2>Champs existants</h2>
{% if definition.fields %}
{% for f in definition.fields %}
<form method="post" action="{{ url_for('object_field_edit', slug=game.slug, definition_id=d.id, field_id=f.id) }}" id="fieldEditForm{{ f.id }}"></form>
@@ -394,37 +373,12 @@
</div>
{% endif %}
<div class="dashPanelHeader">
<h2>Ajouter une entrée</h2>
<h2>Données enregistrées</h2>
{% if definition.fields %}
<button type="button" class="button is-small primary" onclick="openAddEntryPanel({{ d.id }})">+ Ajouter</button>
{% endif %}
</div>
{% if definition.fields %}
<form method="post" action="{{ url_for('data_new', slug=game.slug, definition_id=d.id) }}" class="dashCreateBar">
{% for f in definition.fields %}
{% set col = f.name|colname %}
{% if f.type == 'texte' %}
<input type="text" name="{{ col }}" placeholder="{{ f.name }}{{ ' *' if f.required else '' }}" {{ 'required' if f.required else '' }}>
{% elif f.type == 'texte_long' %}
<textarea name="{{ col }}" placeholder="{{ f.name }}{{ ' *' if f.required else '' }}" rows="1" {{ 'required' if f.required else '' }}></textarea>
{% elif f.type == 'nombre_entier' %}
<input type="number" step="1" name="{{ col }}" placeholder="{{ f.name }}{{ ' *' if f.required else '' }}" {{ 'required' if f.required else '' }}>
{% elif f.type == 'nombre_decimal' %}
<input type="number" step="any" name="{{ col }}" placeholder="{{ f.name }}{{ ' *' if f.required else '' }}" {{ 'required' if f.required else '' }}>
{% elif f.type == 'booleen' %}
<label class="checkbox" style="white-space:nowrap; align-self:center;"><input type="checkbox" name="{{ col }}"> {{ f.name }}</label>
{% elif f.type == 'date' %}
<input type="date" name="{{ col }}" title="{{ f.name }}" {{ 'required' if f.required else '' }}>
{% elif f.type == 'relation' %}
<select name="{{ col }}_id" title="{{ f.name }}" {{ 'required' if f.required else '' }}>
<option value="">{{ f.name }} : —</option>
{% for opt in relation_options_by_definition[d.id].get(f.id, []) %}
<option value="{{ opt.id }}">{{ opt.label }}</option>
{% endfor %}
</select>
{% endif %}
{% endfor %}
<button type="submit" class="primary">+ Ajouter</button>
</form>
<h2>Données enregistrées</h2>
{% set rows = rows_by_definition[d.id] %}
{% if rows %}
{% for r in rows %}
@@ -493,6 +447,107 @@
</div>
<div class="floatPanelResizeHandle"></div>
</div>
<!-- Panneaux "Ajouter" (champ / entrée) de CET objet — déplaçables et
redimensionnables comme les autres, ouverts par le bouton "+ Ajouter"
du panneau ci-dessus. Formulaire vertical (un champ par ligne,
étiquette au-dessus) plutôt que la barre en ligne d'origine : au-delà
d'une poignée de champs, une seule ligne devenait illisible (voir
capture d'écran fournie par l'utilisateur — 10 champs alignés sur une
largeur d'écran normale). -->
<div class="floatPanel floatPanel--center hidden addFieldPanel" data-definition-id="{{ d.id }}" id="addFieldPanel-{{ d.id }}">
<div class="floatPanelHeader">
<span class="floatPanelTitle">⋮⋮ Ajouter un champ — {{ definition.name }}</span>
<button type="button" class="button is-small" onclick="closeAddFieldPanel({{ d.id }})">✕</button>
</div>
<div class="floatPanelBody">
<form method="post" action="{{ url_for('object_field_add', slug=game.slug, definition_id=d.id) }}">
<div class="formField">
<label for="new_field_name-{{ d.id }}">Nom du champ</label>
<input type="text" id="new_field_name-{{ d.id }}" name="field_name" placeholder="ex. nom, pv, description..." required autocomplete="off">
</div>
<div class="formField">
<label for="new_field_type-{{ d.id }}">Type</label>
<select id="new_field_type-{{ d.id }}" name="field_type" class="fieldTypeSelect">
{% for key, val in field_types.items() %}
<option value="{{ key }}">{{ val.label }}</option>
{% endfor %}
</select>
</div>
<div class="formField relationTarget" style="display:none;">
<label for="new_field_relation-{{ d.id }}">Objet lié</label>
<select id="new_field_relation-{{ d.id }}" name="field_relation">
{% for od in existing_definitions %}
<option value="{{ od.id }}">{{ od.name }}</option>
{% endfor %}
</select>
</div>
<div class="fieldRow boundsTarget" style="display:none;">
<div class="formField">
<label for="new_field_min-{{ d.id }}">Valeur mini</label>
<input type="number" step="any" id="new_field_min-{{ d.id }}" name="field_min" placeholder="ex. 0">
</div>
<div class="formField">
<label for="new_field_max-{{ d.id }}">Valeur maxi</label>
<input type="number" step="any" id="new_field_max-{{ d.id }}" name="field_max" placeholder="ex. 100">
</div>
</div>
<div class="formField">
<label for="new_field_required-{{ d.id }}">Obligatoire ?</label>
<select id="new_field_required-{{ d.id }}" name="field_required">
<option value="0">Non</option>
<option value="1">Oui</option>
</select>
</div>
<div class="actionsRow"><button type="submit" class="primary">+ Ajouter le champ</button></div>
</form>
</div>
<div class="floatPanelResizeHandle"></div>
</div>
{% if definition.fields %}
<div class="floatPanel floatPanel--center hidden addEntryPanel" data-definition-id="{{ d.id }}" id="addEntryPanel-{{ d.id }}">
<div class="floatPanelHeader">
<span class="floatPanelTitle">⋮⋮ Ajouter une entrée — {{ definition.name }}</span>
<button type="button" class="button is-small" onclick="closeAddEntryPanel({{ d.id }})">✕</button>
</div>
<div class="floatPanelBody">
<form method="post" action="{{ url_for('data_new', slug=game.slug, definition_id=d.id) }}">
{% for f in definition.fields %}
{% set col = f.name|colname %}
<div class="formField">
<label for="new_entry_{{ col }}-{{ d.id }}">{{ f.name }}{% if f.required %} *{% endif %}</label>
{% if f.type == 'texte' %}
<input type="text" id="new_entry_{{ col }}-{{ d.id }}" name="{{ col }}" {{ 'required' if f.required else '' }}>
{% elif f.type == 'texte_long' %}
<textarea id="new_entry_{{ col }}-{{ d.id }}" name="{{ col }}" rows="4" {{ 'required' if f.required else '' }}></textarea>
{% elif f.type == 'nombre_entier' %}
<input type="number" step="1" id="new_entry_{{ col }}-{{ d.id }}" name="{{ col }}" {{ 'required' if f.required else '' }}>
{% elif f.type == 'nombre_decimal' %}
<input type="number" step="any" id="new_entry_{{ col }}-{{ d.id }}" name="{{ col }}" {{ 'required' if f.required else '' }}>
{% elif f.type == 'booleen' %}
<label class="checkbox"><input type="checkbox" id="new_entry_{{ col }}-{{ d.id }}" name="{{ col }}"> Oui</label>
{% elif f.type == 'date' %}
<input type="date" id="new_entry_{{ col }}-{{ d.id }}" name="{{ col }}" {{ 'required' if f.required else '' }}>
{% elif f.type == 'relation' %}
<select id="new_entry_{{ col }}-{{ d.id }}" name="{{ col }}_id" {{ 'required' if f.required else '' }}>
<option value="">—</option>
{% for opt in relation_options_by_definition[d.id].get(f.id, []) %}
<option value="{{ opt.id }}">{{ opt.label }}</option>
{% endfor %}
</select>
{% if not relation_options_by_definition[d.id].get(f.id) %}
<p class="help">Aucune entrée n'existe encore pour l'objet lié.</p>
{% endif %}
{% endif %}
</div>
{% endfor %}
<div class="actionsRow"><button type="submit" class="primary">+ Ajouter l'entrée</button></div>
</form>
</div>
<div class="floatPanelResizeHandle"></div>
</div>
{% endif %}
{% endfor %}
<template id="fieldRowTemplate">
@@ -630,26 +685,9 @@ function makeFloatPanelResizable(panel, handle) {
// ---------- Panneaux déplaçables "Modifier un objet" (un par objet) ----------
(function () {
var NUMERIC_FIELD_TYPES = ['nombre_entier', 'nombre_decimal'];
document.querySelectorAll('.objectEditPanel').forEach(function (panel) {
makeFloatPanelDraggable(panel, panel.querySelector('.floatPanelHeader'));
makeFloatPanelResizable(panel, panel.querySelector('.floatPanelResizeHandle'));
// Formulaire "Ajouter un champ" : une ligne fixe (pas de liste
// extensible comme dans le panneau "Nouvel objet"), donc pas besoin
// d'espace réservé "—" pour garder des colonnes alignées — un simple
// masquage suffit ici.
var addBar = panel.querySelector('.objectAddFieldBar');
var typeSelect = addBar.querySelector('.fieldTypeSelect');
var relationTarget = addBar.querySelector('.relationTarget');
var boundsTargets = addBar.querySelectorAll('.boundsTarget');
function syncAddFieldVisibility() {
relationTarget.style.display = typeSelect.value === 'relation' ? '' : 'none';
var isNumeric = NUMERIC_FIELD_TYPES.indexOf(typeSelect.value) !== -1;
boundsTargets.forEach(function (el) { el.style.display = isNumeric ? '' : 'none'; });
}
typeSelect.addEventListener('change', syncAddFieldVisibility);
syncAddFieldVisibility();
});
window.openObjectEditPanel = function (definitionId, subtab) {
document.querySelectorAll('.objectEditPanel').forEach(function (panel) {
@@ -677,6 +715,50 @@ function makeFloatPanelResizable(panel, handle) {
if (requestedEdit) openObjectEditPanel(requestedEdit, requestedSubtab);
})();
// ---------- Panneaux déplaçables "Ajouter un champ"/"Ajouter une entrée" ----------
// Un panneau par objet et par action (voir game_dashboard.html, ouverts
// par le bouton "+ Ajouter" du panneau "Modifier un objet") — formulaire
// vertical, un champ par ligne : au-delà d'une poignée de champs, une
// seule ligne (l'ancienne barre en ligne) devenait illisible.
(function () {
var NUMERIC_FIELD_TYPES = ['nombre_entier', 'nombre_decimal'];
document.querySelectorAll('.addFieldPanel').forEach(function (panel) {
makeFloatPanelDraggable(panel, panel.querySelector('.floatPanelHeader'));
makeFloatPanelResizable(panel, panel.querySelector('.floatPanelResizeHandle'));
var typeSelect = panel.querySelector('.fieldTypeSelect');
var relationTarget = panel.querySelector('.relationTarget');
var boundsTarget = panel.querySelector('.boundsTarget');
function syncAddFieldVisibility() {
relationTarget.style.display = typeSelect.value === 'relation' ? '' : 'none';
boundsTarget.style.display = NUMERIC_FIELD_TYPES.indexOf(typeSelect.value) !== -1 ? '' : 'none';
}
typeSelect.addEventListener('change', syncAddFieldVisibility);
syncAddFieldVisibility();
});
document.querySelectorAll('.addEntryPanel').forEach(function (panel) {
makeFloatPanelDraggable(panel, panel.querySelector('.floatPanelHeader'));
makeFloatPanelResizable(panel, panel.querySelector('.floatPanelResizeHandle'));
});
window.openAddFieldPanel = function (definitionId) {
var panel = document.querySelector('.addFieldPanel[data-definition-id="' + definitionId + '"]');
if (panel) panel.classList.remove('hidden');
};
window.closeAddFieldPanel = function (definitionId) {
var panel = document.querySelector('.addFieldPanel[data-definition-id="' + definitionId + '"]');
if (panel) panel.classList.add('hidden');
};
window.openAddEntryPanel = function (definitionId) {
var panel = document.querySelector('.addEntryPanel[data-definition-id="' + definitionId + '"]');
if (panel) panel.classList.remove('hidden');
};
window.closeAddEntryPanel = function (definitionId) {
var panel = document.querySelector('.addEntryPanel[data-definition-id="' + definitionId + '"]');
if (panel) panel.classList.add('hidden');
};
})();
// Sous-onglets Champs/Données d'UN panneau précis (voir plus haut) — scopé
// au panneau ancêtre du bouton cliqué, jamais global (plusieurs objets
// peuvent chacun avoir leurs sous-onglets sur leur propre panneau, en même