diff --git a/css/style.css b/css/style.css
index e02fb78..a45ed23 100644
--- a/css/style.css
+++ b/css/style.css
@@ -4357,52 +4357,94 @@ a.ms1-trad-link.btn-aide-trad{
color:#b91c1c;
}
-/* MSIN-4515 — meta Face/Endos/Info sur chaque ligne sélectionnée (reuse kit). */
+/* MSIN-4515 — meta Face/Endos/Info : même visuel que le choix validé (toujours colorées). */
.ms1-ui-chooser-item-sides{
display:inline-flex;
flex:0 0 auto;
flex-wrap:wrap;
- gap:4px;
+ gap:6px;
align-items:center;
}
+.ms1-ui-chooser-side-pick{
+ margin-top:10px;
+ padding:12px;
+ background:#f5f9ff;
+ border:1px solid #c5d9f5;
+ border-radius:6px;
+}
+.ms1-ui-chooser-side-pick-title{
+ margin:0 0 8px;
+ font-size:13px;
+ font-weight:600;
+ color:#1a3a6b;
+}
+.ms1-ui-chooser-side-pick-btns{
+ display:flex;
+ flex-wrap:wrap;
+ gap:8px;
+}
+.ms1-ui-chooser-side-pick .ms1-ui-chooser-item-side{
+ opacity:1;
+ outline:none;
+ min-height:32px;
+ padding:6px 14px;
+ font-size:12px;
+}
+.ms1-ui-chooser-chip.is-pending{
+ outline:2px solid var(--ms1-ui-accent, #2f5fd0);
+ outline-offset:1px;
+}
.ms1-ui-chooser-item-side{
display:inline-flex;
align-items:center;
+ justify-content:center;
margin:0;
- padding:4px 10px;
+ min-height:28px;
+ padding:6px 12px;
border:1px solid #ced4da;
border-radius:4px;
- font-size:11px;
+ font-size:12px;
font-weight:600;
line-height:1.2;
cursor:pointer;
- background:#f8f9fa;
- color:#495057;
user-select:none;
+ opacity:.42;
+ box-shadow:none;
+ position:relative;
+ z-index:2;
}
button.ms1-ui-chooser-item-side:hover{
- filter:brightness(.97);
+ opacity:.8;
}
-.ms1-ui-chooser-item-side--face.is-selected{
+.ms1-ui-chooser-item-side--face{
background:#5B9BD5;
border-color:#3d7eae;
color:#fff;
}
-.ms1-ui-chooser-item-side--endos.is-selected{
+.ms1-ui-chooser-item-side--endos{
background:#C00000;
border-color:#8a0000;
color:#fff;
}
-.ms1-ui-chooser-item-side--info.is-selected{
+.ms1-ui-chooser-item-side--info{
background:#fff;
border-color:#6c757d;
color:#212529;
box-shadow:inset 0 0 0 1px #adb5bd;
}
+.ms1-ui-chooser-selected-item .ms1-ui-chooser-item-side.is-selected{
+ opacity:1;
+ outline:2px solid #1a3a6b;
+ outline-offset:1px;
+}
+.ms1-ui-chooser-selected-item{
+ flex-wrap:wrap;
+}
.ms1-ui-chooser[data-side-locked="1"] .ms1-ui-chooser-item-side,
.ms1-ui-chooser[data-locked="1"] .ms1-ui-chooser-item-side{
cursor:default;
- opacity:.9;
+ opacity:1;
+ outline:none;
}
.ms1-ui-chooser-ghost{
diff --git a/js/v2/bib-assignments.js b/js/v2/bib-assignments.js
index 76784db..e915834 100644
--- a/js/v2/bib-assignments.js
+++ b/js/v2/bib-assignments.js
@@ -595,7 +595,9 @@
// MSIN-4546 — Charger Production seulement quand on ouvre cet espace.
if (workspaceId === 'production') {
- bibEnsureProductionPanelLoaded();
+ bibEnsureProductionPanelLoaded().then(function () {
+ initBibProductionFieldsChooser(moduleBib);
+ });
}
// MSIN-4475 — Modes d'assignation : afficher les choix tout de suite (sans clic tuile).
@@ -618,13 +620,15 @@
if (!panel) {
return Promise.resolve();
}
- // Déjà chargé (mount remplacé par le HTML production).
+ // Déjà chargé (HTML production en place) → juste s’assurer que le chooser est vivant.
if (panel.getAttribute('data-production-loaded') === '1' || !mount) {
- return Promise.resolve();
+ initBibProductionFieldsChooser(panel);
+ return Promise.resolve(panel);
}
if (panel.getAttribute('data-production-deferred') !== '1'
&& mount.getAttribute('data-production-deferred') !== '1') {
- return Promise.resolve();
+ initBibProductionFieldsChooser(panel);
+ return Promise.resolve(panel);
}
if (bibProductionLoadPromise) {
return bibProductionLoadPromise;
@@ -642,7 +646,14 @@
if (!data || !data.success || !data.html) {
throw new Error((data && data.message) || bibJs('jsErrGeneric') || 'Erreur');
}
- mount.outerHTML = data.html;
+ // Garder #bib-production-mount (innerHTML) pour ne pas perdre la cible d’erreur.
+ var liveMount = document.getElementById('bib-production-mount');
+ if (liveMount) {
+ liveMount.innerHTML = data.html;
+ liveMount.removeAttribute('data-production-deferred');
+ } else {
+ panel.innerHTML = data.html;
+ }
panel.removeAttribute('data-production-deferred');
panel.setAttribute('data-production-loaded', '1');
initModuleBibTippy(panel);
@@ -651,9 +662,12 @@
})
.catch(function (err) {
var msg = (err && err.message) ? err.message : (bibJs('jsServerError') || 'Erreur serveur');
- mount.innerHTML = '
'
- + bibTippyEscapeHtml(msg)
- + '
';
+ var errHost = document.getElementById('bib-production-mount') || panel;
+ if (errHost) {
+ errHost.innerHTML = ''
+ + bibTippyEscapeHtml(msg)
+ + '
';
+ }
})
.finally(function () {
bibProductionLoadPromise = null;
@@ -2032,24 +2046,33 @@
}, 0);
}
- /** MSIN-4515 — mockup A + Face/Endos/Info via MS1UiChooser. */
+ /** MSIN-4515 — mockup A + Face/Endos/Info via MS1UiChooser (réessaie si script pas prêt). */
function initBibProductionFieldsChooser(root) {
- root = root || moduleBib;
- var panel = root.querySelector
- ? root.querySelector('.bib-production-fields')
- : null;
- if (!panel) {
- panel = document.querySelector('#module-bib .bib-production-fields');
+ var scope = (root && root.querySelector) ? root : moduleBib;
+ var fieldsPanel = scope.querySelector('.bib-production-fields')
+ || moduleBib.querySelector('.bib-production-fields');
+ if (!fieldsPanel) {
+ return false;
}
- if (!panel || panel.getAttribute('data-bib-prod-fields-init') === '1') {
- return;
+ var chooser = fieldsPanel.querySelector('.ms1-ui-chooser[data-ms1-ui-screen="bib-prod-fields"]')
+ || fieldsPanel.querySelector('.ms1-ui-chooser');
+ if (!chooser) {
+ return false;
}
- var chooser = panel.querySelector('.ms1-ui-chooser[data-ms1-ui-screen="bib-prod-fields"]')
- || panel.querySelector('.ms1-ui-chooser');
- if (!chooser || !window.MS1UiChooser || typeof window.MS1UiChooser.init !== 'function') {
- return;
+ // Déjà branché et vivant.
+ if (chooser._ms1UiChooser) {
+ return true;
+ }
+ if (!window.MS1UiChooser || typeof window.MS1UiChooser.init !== 'function') {
+ if (!fieldsPanel.getAttribute('data-bib-prod-chooser-retry')) {
+ fieldsPanel.setAttribute('data-bib-prod-chooser-retry', '1');
+ window.setTimeout(function () {
+ fieldsPanel.removeAttribute('data-bib-prod-chooser-retry');
+ initBibProductionFieldsChooser(scope);
+ }, 120);
+ }
+ return false;
}
- panel.setAttribute('data-bib-prod-fields-init', '1');
var sideOptions = [];
try {
@@ -2058,28 +2081,39 @@
sideOptions = [];
}
- window.MS1UiChooser.init(chooser, {
- // Pas de budget Excel (colonnes illimitées).
- getBudget: function () { return null; },
- labelRemove: panel.getAttribute('data-label-remove') || '',
- sideOptions: sideOptions,
- defaultSide: chooser.getAttribute('data-default-side') || 'face',
- sideLocked: panel.getAttribute('data-locked') === '1'
- || chooser.getAttribute('data-side-locked') === '1',
- onChange: function (_root, reason) {
- if (panel.getAttribute('data-locked') === '1') {
- return;
+ var blnLocked = fieldsPanel.getAttribute('data-locked') === '1'
+ || chooser.getAttribute('data-locked') === '1'
+ || chooser.getAttribute('data-side-locked') === '1';
+
+ try {
+ chooser.removeAttribute('data-ms1-ui-chooser-init');
+ chooser._ms1UiChooser = null;
+ window.MS1UiChooser.init(chooser, {
+ getBudget: function () { return null; },
+ labelRemove: fieldsPanel.getAttribute('data-label-remove') || '',
+ sideOptions: sideOptions,
+ defaultSide: chooser.getAttribute('data-default-side') || 'face',
+ sideLocked: blnLocked,
+ onChange: function (_root, reason) {
+ if (fieldsPanel.getAttribute('data-locked') === '1') {
+ return;
+ }
+ if (reason === 'apply') {
+ bibProdFieldsSaveNow(bibProdFieldsLabel(
+ 'data-label-apply-done',
+ 'Choix de la 1re épreuve appliqués aux autres.'
+ ));
+ return;
+ }
+ bibProdFieldsSaveSoon();
}
- if (reason === 'apply') {
- bibProdFieldsSaveNow(bibProdFieldsLabel(
- 'data-label-apply-done',
- 'Choix de la 1re épreuve appliqués aux autres.'
- ));
- return;
- }
- bibProdFieldsSaveSoon();
- }
- });
+ });
+ } catch (errInit) {
+ chooser.removeAttribute('data-ms1-ui-chooser-init');
+ chooser._ms1UiChooser = null;
+ return false;
+ }
+ return !!chooser._ms1UiChooser;
}
moduleBib.addEventListener('submit', function (e) {
diff --git a/js/v2/ms1-ui-chooser.js b/js/v2/ms1-ui-chooser.js
index ba5d3c7..d21ecca 100644
--- a/js/v2/ms1-ui-chooser.js
+++ b/js/v2/ms1-ui-chooser.js
@@ -102,8 +102,9 @@
}
function isChooserLocked(root) {
- return !!(root && (root.getAttribute('data-locked') === '1'
- || root.getAttribute('data-side-locked') === '1'));
+ // Verrouillage interaction = data-locked seulement (impression Excel).
+ // data-side-locked sert au rendu pastilles lecture seule, pas à bloquer tout le kit.
+ return !!(root && root.getAttribute('data-locked') === '1');
}
function notifyChange(root, opts, reason) {
@@ -357,7 +358,42 @@
}, 4500);
}
- function addFromChip(section, chip, opts, root) {
+ function clearSidePick(section) {
+ if (!section) {
+ return;
+ }
+ section.removeAttribute('data-pending-key');
+ section.removeAttribute('data-pending-label');
+ section.removeAttribute('data-pending-width');
+ section.querySelectorAll('.ms1-ui-chooser-chip.is-pending').forEach(function (chip) {
+ chip.classList.remove('is-pending');
+ });
+ var pick = section.querySelector('.ms1-ui-chooser-side-pick');
+ if (pick) {
+ pick.hidden = true;
+ }
+ }
+
+ function openSidePick(section, chip) {
+ if (!section || !chip) {
+ return;
+ }
+ clearSidePick(section);
+ var key = chip.getAttribute('data-item-key') || '';
+ if (!key) {
+ return;
+ }
+ section.setAttribute('data-pending-key', key);
+ section.setAttribute('data-pending-label', chip.getAttribute('data-item-label') || key);
+ section.setAttribute('data-pending-width', chip.getAttribute('data-item-width') || '0');
+ chip.classList.add('is-pending');
+ var pick = section.querySelector('.ms1-ui-chooser-side-pick');
+ if (pick) {
+ pick.hidden = false;
+ }
+ }
+
+ function addFromChip(section, chip, opts, root, sideOverride) {
var order = orderList(section);
if (!order || !chip) {
return { ok: false, reason: 'missing' };
@@ -368,6 +404,7 @@
}
if (order.querySelector('.ms1-ui-chooser-selected-item[data-item-key="' + cssEscape(key) + '"]')) {
setChipUsed(section, key, true);
+ clearSidePick(section);
return { ok: true };
}
var width = parseInt(chip.getAttribute('data-item-width') || '0', 10) || 0;
@@ -378,6 +415,7 @@
}
}
var label = chip.getAttribute('data-item-label') || key;
+ var buildOpts = itemBuildOpts(root, opts, sideOverride);
order.appendChild(buildSelectedItem(
order,
key,
@@ -385,13 +423,30 @@
width,
opts.labelRemove || root.getAttribute('data-label-remove') || '×',
false,
- itemBuildOpts(root, opts)
+ buildOpts
));
setChipUsed(section, key, true);
syncEmpty(section);
+ clearSidePick(section);
return { ok: true };
}
+ function addPendingWithSide(section, side, opts, root) {
+ if (!section || !side) {
+ return { ok: false, reason: 'missing' };
+ }
+ var key = section.getAttribute('data-pending-key') || '';
+ if (!key) {
+ return { ok: false, reason: 'missing' };
+ }
+ var chip = section.querySelector('.ms1-ui-chooser-chip[data-item-key="' + cssEscape(key) + '"]');
+ if (!chip) {
+ clearSidePick(section);
+ return { ok: false, reason: 'missing' };
+ }
+ return addFromChip(section, chip, opts, root, side);
+ }
+
function removeKey(section, key) {
var order = orderList(section);
if (!order || !key) {
@@ -512,18 +567,25 @@
if (order._ms1Sortable) {
return order._ms1Sortable;
}
- order._ms1Sortable = global.Sortable.create(order, {
- animation: 150,
- handle: '.ms1-ui-chooser-drag-handle',
- draggable: '.ms1-ui-chooser-selected-item',
- ghostClass: 'ms1-ui-chooser-ghost',
- chosenClass: 'ms1-ui-chooser-chosen',
- dragClass: 'ms1-ui-chooser-drag',
- onEnd: function () {
- updateFitUi(root, opts);
- notifyChange(root, opts, 'sort');
- }
- });
+ try {
+ order._ms1Sortable = global.Sortable.create(order, {
+ animation: 150,
+ handle: '.ms1-ui-chooser-drag-handle',
+ filter: '.ms1-ui-chooser-item-side, .ms1-ui-chooser-selected-remove, .ms1-ui-chooser-item-sides, .ms1-ui-chooser-side-pick',
+ preventOnFilter: false,
+ draggable: '.ms1-ui-chooser-selected-item',
+ ghostClass: 'ms1-ui-chooser-ghost',
+ chosenClass: 'ms1-ui-chooser-chosen',
+ dragClass: 'ms1-ui-chooser-drag',
+ onEnd: function () {
+ updateFitUi(root, opts);
+ notifyChange(root, opts, 'sort');
+ }
+ });
+ } catch (err) {
+ order._ms1Sortable = null;
+ return null;
+ }
return order._ms1Sortable;
}
@@ -560,20 +622,30 @@
if (!root) {
return null;
}
- if (root.getAttribute('data-ms1-ui-chooser-init') === '1') {
- return root._ms1UiChooser || null;
+ // Déjà vivant → ne pas rebrancher.
+ if (root._ms1UiChooser) {
+ return root._ms1UiChooser;
}
- root.setAttribute('data-ms1-ui-chooser-init', '1');
+ // Flag orphelin (planté avant les listeners) → on réessaie.
+ root.removeAttribute('data-ms1-ui-chooser-init');
var opts = options || {};
var radioSelector = opts.radioSelector || '.ms1-ui-chooser-radio';
root.querySelectorAll('.ms1-ui-chooser-section').forEach(function (sec) {
- syncEmpty(sec);
- initSortable(sec, opts, root);
+ try {
+ syncEmpty(sec);
+ initSortable(sec, opts, root);
+ } catch (errInit) {
+ // Ne pas bloquer les clics chip / épreuve si Sortable échoue.
+ }
});
- syncSegActive(root);
- updateFitUi(root, opts);
+ try {
+ syncSegActive(root);
+ updateFitUi(root, opts);
+ } catch (errFit) {
+ // ignore
+ }
root.addEventListener('change', function (ev) {
var el = ev.target;
@@ -624,14 +696,32 @@
: null;
if (sideBtn) {
ev.preventDefault();
- var itemSide = sideBtn.closest('.ms1-ui-chooser-selected-item');
- if (!itemSide) {
- return;
- }
+ ev.stopPropagation();
var newSide = sideBtn.getAttribute('data-side') || '';
if (!newSide) {
return;
}
+
+ // MSIN-4515 — Choix Face/Endos/Info après clic chip (panneau disponible).
+ var pickWrap = sideBtn.closest('.ms1-ui-chooser-side-pick');
+ if (pickWrap) {
+ var secPick = sideBtn.closest('.ms1-ui-chooser-section');
+ var pickResult = addPendingWithSide(secPick, newSide, opts, root);
+ if (!pickResult.ok) {
+ if (pickResult.reason === 'full') {
+ flashFitFull(root, opts, secPick);
+ }
+ return;
+ }
+ updateFitUi(root, opts);
+ notifyChange(root, opts, 'add');
+ return;
+ }
+
+ var itemSide = sideBtn.closest('.ms1-ui-chooser-selected-item');
+ if (!itemSide) {
+ return;
+ }
itemSide.setAttribute('data-item-side', newSide);
itemSide.querySelectorAll('button.ms1-ui-chooser-item-side[data-side]').forEach(function (btn) {
var on = btn.getAttribute('data-side') === newSide;
@@ -648,6 +738,12 @@
if (chip && !chip.classList.contains('is-used') && !chip.hidden) {
ev.preventDefault();
var secChip = chip.closest('.ms1-ui-chooser-section');
+ var sideOpts = parseSideOptions(root, opts);
+ // Mode production : chip → choisir Face/Endos/Info (visuel validé), puis ajouter.
+ if (sideOpts.length && secChip && secChip.querySelector('.ms1-ui-chooser-side-pick')) {
+ openSidePick(secChip, chip);
+ return;
+ }
var result = addFromChip(secChip, chip, opts, root);
if (!result.ok) {
if (result.reason === 'full') {
@@ -734,6 +830,7 @@
}
};
root._ms1UiChooser = api;
+ root.setAttribute('data-ms1-ui-chooser-init', '1');
return api;
}
diff --git a/php/inc_fx_ms1_ui.php b/php/inc_fx_ms1_ui.php
index c3b85d5..d55b3e7 100644
--- a/php/inc_fx_ms1_ui.php
+++ b/php/inc_fx_ms1_ui.php
@@ -250,6 +250,7 @@ if (!function_exists('fxMs1UiEsc')) {
$tabSideOptions = is_array($tabCfg['side_options'] ?? null) ? $tabCfg['side_options'] : [];
$blnSideLocked = !empty($tabCfg['side_locked']);
$blnHideRemove = !empty($tabCfg['hide_remove']);
+ $strSidePickLabel = (string)($tabCfg['side_pick_label'] ?? '');
$tabSelectedKeys = [];
foreach ($tabSelected as $opt) {
@@ -332,6 +333,28 @@ if (!function_exists('fxMs1UiEsc')) {
}
?>
+
+
+
diff --git a/php/inc_fx_promoteur.php b/php/inc_fx_promoteur.php
index f5bd6ea..062a4fd 100644
--- a/php/inc_fx_promoteur.php
+++ b/php/inc_fx_promoteur.php
@@ -5708,6 +5708,7 @@ function renderBibProductionPreview(array $tabEpreuves, $strLangue = 'fr', array
'side_options' => $tabSideOptions,
'side_locked' => $blnProductionLocked,
'hide_remove' => $blnProductionLocked,
+ 'side_pick_label' => fxBibTexte('bib_v5_production_fields_pick', 0),
],
]);
}
diff --git a/php/inc_settings.php b/php/inc_settings.php
index 59a42a1..3cebb0d 100644
--- a/php/inc_settings.php
+++ b/php/inc_settings.php
@@ -7,7 +7,7 @@
* Constantes *
*
**************/
-define('_VERSION_CODE', '4.72.942'); // MSIN-4515 — production champs = UI kit chooser + Face/Endos/Info
+define('_VERSION_CODE', '4.72.945'); // MSIN-4515 — init chooser production fiable (plus d’écran fumée)
define('_DATE_CODE', '2026-08-03');
//MSIN-4290
define('QR_SECRET_KEY', 'ms1_qr_2026_cle_secrete_longue_et_fixe');