MSIN-4379

This commit is contained in:
2026-06-02 15:04:03 -04:00
parent 54d5363f09
commit dabb797060
4 changed files with 507 additions and 8 deletions

View File

@ -605,6 +605,45 @@ if ($action == 'save_team_mode') {
exit;
}
// MSIN-4379 — Étape 2
// Endpoint AJAX : sauvegarde ou désactivation de la config auto (epr_bib_auto).
// Paramètres : epr_id, active (0|1), ranges (JSON des epr_bib_id).
// Prochain tour : étape 67 (assignation réelle à l'achat, sans passer par cet endpoint).
if ($action == 'save_auto_config') {
$epr_id = intval($_POST['epr_id'] ?? 0);
$active = intval($_POST['active'] ?? 1) === 1;
$ranges = json_decode($_POST['ranges'] ?? '[]', true);
if ($epr_id <= 0) {
echo json_encode([
'success' => false,
'message' => 'epr_id invalide'
]);
exit;
}
$result = fxSaveBibAutoConfig($epr_id, is_array($ranges) ? $ranges : [], $active);
if (!$result['success']) {
echo json_encode([
'success' => false,
'message' => $result['message'] ?? 'Erreur'
]);
exit;
}
$tabBibStats = fxInfosBibRange($epr_id);
echo json_encode([
'success' => true,
'auto_active' => !empty($result['auto_active']),
'html' => renderBibRanges($tabBibStats, $epr_id, 'assign')
]);
exit;
}
// =====================
// ACTION INVALIDE
// =====================

View File

@ -1101,6 +1101,30 @@ ul.ms1-menu-compte li a:hover, ul.ms1-menu-compte li a:active {
display:none;
}
/* MSIN-4379 — Étape 2 : styles du mode « auto » (masque batch, verrouille édition des plages).
Prochain tour : étape 67 (logique PHP à l'achat, pas de CSS supplémentaire prévu). */
.epr-row.auto-mode .btn-assign-batch,
.epr-row.auto-mode .btn-reset,
.epr-row.auto-mode .batch-config-container {
display: none;
}
.epr-row.auto-mode .btn-auto {
display: none;
}
.epr-row.auto-mode .btn-save-range,
.epr-row.auto-mode .btn-delete-range,
.epr-row.auto-mode .btn-add-range {
visibility: hidden;
}
.epr-row.auto-mode .bib-start,
.epr-row.auto-mode .bib-end {
pointer-events: none;
background: #eee;
}
.epr-row.batch-mode .btn-go-batch {
display: none;
}

View File

@ -1948,6 +1948,189 @@ if ($strLangue == 'fr') {
if (moduleBib) {
// ============================================================
// MSIN-4379 — Étape 2 : assignation automatique (interface JS)
// Prochain tour : étape 6 (fxAssignAutoBibForParticipant), étape 7 (fxMajCommande).
// ============================================================
/** MSIN-4379 — Compte les séquences cochées et met à jour le texte d'aide. */
function updateAutoSelectionInfo(row) {
if (!row) return;
let count = row.querySelectorAll('.batch-select-range:checked').length;
let info = row.querySelector('.auto-info');
if (!info) return;
if (count === 0) {
info.textContent = 'Sélectionnez au moins une séquence.';
} else {
info.textContent = count + ' séquence(s) sélectionnée(s).';
}
}
/** MSIN-4379 — Après sauvegarde : badge, libellé du bouton, bouton Désactiver. */
function syncAutoUiState(row, isActive) {
if (!row) return;
let btnAuto = row.querySelector('.btn-auto');
let badge = row.querySelector('.bib-auto-badge');
let btnDesactiver = row.querySelector('.btn-desactiver-auto');
row.dataset.autoActive = isActive ? '1' : '0';
if (isActive) {
row.classList.add('auto-enabled');
} else {
row.classList.remove('auto-enabled');
}
if (btnAuto) {
btnAuto.textContent = isActive ? 'Configurer automatique' : 'Activer automatique';
btnAuto.classList.toggle('btn-success', isActive);
btnAuto.classList.toggle('btn-secondary', !isActive);
}
if (badge) {
badge.style.display = isActive ? '' : 'none';
}
if (btnDesactiver) {
btnDesactiver.style.display = isActive ? '' : 'none';
}
}
/** MSIN-4379 — Quitte le mode édition auto sans sauver ; recharge les plages (mode assign). */
function exitAutoMode(row) {
if (!row) return;
row.classList.remove('auto-mode');
let container = row.querySelector('.bib-container');
if (container) {
container.classList.remove('batch-mode');
}
row.querySelectorAll('.bib-start, .bib-end').forEach(input => {
input.removeAttribute('readonly');
});
let autoConfig = row.querySelector('.auto-config-container');
if (autoConfig) {
autoConfig.style.display = 'none';
}
let btnAuto = row.querySelector('.btn-auto');
if (btnAuto) {
btnAuto.style.display = '';
}
let btnBatch = row.querySelector('.btn-assign-batch');
if (btnBatch) {
btnBatch.style.display = '';
}
let btnReset = row.querySelector('.btn-reset');
if (btnReset) {
btnReset.style.display = '';
}
fetch('/ajax_bib_range.php', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body:
'action=refresh_ranges'
+ '&epr_id=' + encodeURIComponent(row.dataset.eprId)
+ '&mode=assign'
})
.then(res => res.json())
.then(data => {
if (!data.success) return;
let bibContainer = row.querySelector('.epr-block:nth-child(2) .bib-container');
if (bibContainer) {
bibContainer.innerHTML = data.html;
}
syncBibUiState(row);
});
}
/** MSIN-4379 — Ouvre le mode édition : checkboxes, bloc Accepter/Annuler, refresh mode=auto. */
function enterAutoMode(row) {
if (!row) return;
if (row.classList.contains('batch-mode') || row.classList.contains('reset-mode')) {
return;
}
row.classList.add('auto-mode');
let container = row.querySelector('.bib-container');
if (container) {
container.classList.add('batch-mode');
}
row.querySelectorAll('.bib-start, .bib-end').forEach(input => {
input.setAttribute('readonly', true);
});
let autoConfig = row.querySelector('.auto-config-container');
if (autoConfig) {
autoConfig.style.display = 'block';
}
let btnAuto = row.querySelector('.btn-auto');
if (btnAuto) {
btnAuto.style.display = 'none';
}
let btnBatch = row.querySelector('.btn-assign-batch');
if (btnBatch) {
btnBatch.style.display = 'none';
}
let btnReset = row.querySelector('.btn-reset');
if (btnReset) {
btnReset.style.display = 'none';
}
fetch('/ajax_bib_range.php', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body:
'action=refresh_ranges'
+ '&epr_id=' + encodeURIComponent(row.dataset.eprId)
+ '&mode=auto'
})
.then(res => res.json())
.then(data => {
if (!data.success) return;
let bibContainer = row.querySelector('.epr-block:nth-child(2) .bib-container');
if (!bibContainer) return;
bibContainer.innerHTML = data.html;
bibContainer.classList.add('batch-mode');
row.querySelectorAll('.bib-start, .bib-end').forEach(input => {
input.setAttribute('readonly', true);
});
updateAutoSelectionInfo(row);
});
}
function syncBibUiState(row) {
let avecEl = row.querySelector('.bib-avec');
@ -2113,6 +2296,12 @@ if ($strLangue == 'fr') {
let row = cb.closest('.epr-row');
if (!row) return;
// MSIN-4379 — En mode auto, pas de logique batch (refresh_ranges / GO).
if (row.classList.contains('auto-mode')) {
updateAutoSelectionInfo(row);
return;
}
if (row.dataset.loading === '1') return;
let existing = document.querySelector('.epr-row-view');
if (existing) existing.remove();
@ -2430,6 +2619,11 @@ if ($strLangue == 'fr') {
let row = btnBatch.closest('.epr-row');
if (!row) return;
// MSIN-4379 — Batch et auto sont mutuellement exclusifs.
if (row.classList.contains('auto-mode')) {
return;
}
let container = row.querySelector('.bib-container');
if (!container) return;
@ -2554,6 +2748,11 @@ if ($strLangue == 'fr') {
let row = btnReset.closest('.epr-row');
if (!row) return;
// MSIN-4379 — Reset et auto sont mutuellement exclusifs.
if (row.classList.contains('auto-mode')) {
return;
}
let container = row.querySelector('.bib-container');
if (!container) return;
@ -2855,6 +3054,125 @@ if ($strLangue == 'fr') {
return;
}
// =====================
// MSIN-4379 — Étape 2 : AUTO DOSSARD (clics Accepter / Annuler / Désactiver / Activer)
// Prochain tour : étape 67 (assignation PHP à l'inscription).
// =====================
let btnAccepterAuto = e.target.closest('.btn-accepter-auto');
if (btnAccepterAuto) {
e.preventDefault();
let row = btnAccepterAuto.closest('.epr-row');
if (!row) return;
let selected = [];
row.querySelectorAll('.batch-select-range:checked').forEach(cb => {
selected.push(cb.dataset.rangeId);
});
if (selected.length === 0) {
alert('Sélectionnez au moins une séquence');
return;
}
// MSIN-4379 — Persiste epr_bib_auto=1 sur les séquences choisies (save_auto_config).
fetch('/ajax_bib_range.php', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body:
'action=save_auto_config'
+ '&epr_id=' + encodeURIComponent(row.dataset.eprId)
+ '&active=1'
+ '&ranges=' + encodeURIComponent(JSON.stringify(selected))
})
.then(res => res.json())
.then(data => {
if (!data.success) {
alert(data.message || 'Erreur');
return;
}
syncAutoUiState(row, true);
exitAutoMode(row);
});
return;
}
// MSIN-4379 — Annule l'édition sans appeler save_auto_config.
let btnAnnulerAuto = e.target.closest('.btn-annuler-auto');
if (btnAnnulerAuto) {
e.preventDefault();
let row = btnAnnulerAuto.closest('.epr-row');
if (!row) return;
exitAutoMode(row);
return;
}
// MSIN-4379 — Désactive l'auto en base (active=0, toutes les séquences à 0).
let btnDesactiverAuto = e.target.closest('.btn-desactiver-auto');
if (btnDesactiverAuto) {
e.preventDefault();
let row = btnDesactiverAuto.closest('.epr-row');
if (!row) return;
if (!confirm('Désactiver l\'assignation automatique pour cette épreuve ?')) {
return;
}
fetch('/ajax_bib_range.php', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body:
'action=save_auto_config'
+ '&epr_id=' + encodeURIComponent(row.dataset.eprId)
+ '&active=0'
+ '&ranges=' + encodeURIComponent('[]')
})
.then(res => res.json())
.then(data => {
if (!data.success) {
alert(data.message || 'Erreur');
return;
}
syncAutoUiState(row, false);
exitAutoMode(row);
});
return;
}
// MSIN-4379 — Entrée dans le mode configuration auto.
let btnAuto = e.target.closest('.btn-auto');
if (btnAuto) {
e.preventDefault();
let row = btnAuto.closest('.epr-row');
if (!row) return;
if (row.classList.contains('batch-mode') || row.classList.contains('reset-mode')) {
return;
}
enterAutoMode(row);
return;
}
// =====================
// CREATE
// =====================

View File

@ -2636,14 +2636,16 @@ function fxShowBibTool4($str_code, $int_eve_id, $strLangue){
$tabBibStats=fxInfosBibRange(intval($tabEpreuves[$i]['epr_id']));
$infoBib = fxInfosBibEpreuve(intval($tabEpreuves[$i]['epr_id']));
// MSIN-4379 — Étape 2 : détecte si l'auto est actif (au moins une séquence epr_bib_auto=1).
// Prochain tour : étape 67 (assignation à l'achat).
$blnAutoActive = fxIsBibAutoActive((int)$tabEpreuves[$i]['epr_id']);
?>
<div class="epr-row <?php echo $strClass ?>"
data-epr-id="<?php echo (int)$tabEpreuves[$i]['epr_id']; ?>">
<?php /* MSIN-4379 — Étape 2 : attributs/data pour l'état auto au chargement de la page. */ ?>
<div class="epr-row <?php echo $strClass ?><?php echo $blnAutoActive ? ' auto-enabled' : ''; ?>"
data-epr-id="<?php echo (int)$tabEpreuves[$i]['epr_id']; ?>"
data-auto-active="<?php echo $blnAutoActive ? '1' : '0'; ?>">
<!-- GAUCHE -->
<div class="epr-block">
<div class="epr-header">Épreuve</div>
@ -2786,8 +2788,26 @@ if ($teamMode <= 0) {
style="<?php echo ((int)$infoBib['avec_bib'] > 0 ? '' : 'display:none;'); ?>">
Réinitialiser
</button>
<button type="button" class="btn btn-sm btn-secondary btn-auto">Activer automatique</button>
<?php /* MSIN-4379 — Étape 2 : boutons et bloc UI pour configurer l'assignation auto (sans assigner encore). */ ?>
<button type="button"
class="btn btn-sm btn-auto <?php echo $blnAutoActive ? 'btn-success' : 'btn-secondary'; ?>">
<?php echo $blnAutoActive ? 'Configurer automatique' : 'Activer automatique'; ?>
</button>
<span class="bib-auto-badge badge badge-success ml-1"
style="<?php echo $blnAutoActive ? '' : 'display:none;'; ?>">Auto actif</span>
<div class="auto-config-container" style="display:none;">
<div class="auto-info text-muted small mb-2">
Sélectionnez les séquences utilisées à l'inscription.
</div>
<button type="button" class="btn btn-sm btn-success btn-accepter-auto">Accepter</button>
<button type="button" class="btn btn-sm btn-secondary btn-annuler-auto">Annuler</button>
<button type="button"
class="btn btn-sm btn-danger btn-desactiver-auto"
style="<?php echo $blnAutoActive ? '' : 'display:none;'; ?>">
Désactiver automatique
</button>
</div>
<div class="batch-config-container" style="display:none;">
<?php
@ -2840,6 +2860,7 @@ function fxInfosBibRange($epr_id = 0, $epr_bib_id = 0){
b.epr_id,
b.epr_bib_start,
b.epr_bib_finish,
b.epr_bib_auto, /* MSIN-4379 — Étape 1 : flag par séquence (pool auto). */
COUNT(p.no_bib) AS nb_utilises,
MAX(p.no_bib) AS dernier_bib,
@ -2935,13 +2956,100 @@ function fxInfosBibRange($epr_id = 0, $epr_bib_id = 0){
b.epr_bib_id,
b.epr_id,
b.epr_bib_start,
b.epr_bib_finish
b.epr_bib_finish,
b.epr_bib_auto
ORDER BY b.epr_bib_start
";
return $objDatabase->fxGetResults($sqlBibStats);
}
/**
* MSIN-4379 — Étape 2
* Indique si l'assignation automatique est active pour une épreuve
* (au moins une ligne epr_bib_auto = 1 dans inscriptions_epreuves_bib).
* Prochain tour : étape 67 (lecture de ce flag à l'inscription).
*/
function fxIsBibAutoActive($epr_id) {
global $objDatabase;
$epr_id = intval($epr_id);
if ($epr_id <= 0) {
return false;
}
$sql = "
SELECT COUNT(*)
FROM inscriptions_epreuves_bib
WHERE epr_id = $epr_id
AND epr_bib_auto = 1
";
return (int)$objDatabase->fxGetVar($sql) > 0;
}
/**
* MSIN-4379 — Étape 2
* Enregistre la config auto : remet toutes les séquences à 0, puis marque
* epr_bib_auto = 1 sur les epr_bib_id choisis. Si $active = false, tout désactive.
* Prochain tour : étape 6 lira ces séquences pour fxAssignAutoBibForParticipant().
*/
function fxSaveBibAutoConfig($epr_id, $ranges, $active = true) {
global $objDatabase;
$epr_id = intval($epr_id);
$active = (bool)$active;
if ($epr_id <= 0) {
return ['success' => false, 'message' => 'epr_id invalide'];
}
$objDatabase->fxQuery("
UPDATE inscriptions_epreuves_bib
SET epr_bib_auto = 0
WHERE epr_id = $epr_id
");
if (!$active) {
return ['success' => true, 'auto_active' => false];
}
if (empty($ranges) || !is_array($ranges)) {
return ['success' => false, 'message' => 'Aucune séquence sélectionnée'];
}
$ids = array_values(array_filter(array_map('intval', $ranges)));
if (empty($ids)) {
return ['success' => false, 'message' => 'Aucune séquence sélectionnée'];
}
$strIds = implode(',', $ids);
$sqlCheck = "
SELECT COUNT(*)
FROM inscriptions_epreuves_bib
WHERE epr_id = $epr_id
AND epr_bib_id IN ($strIds)
";
$nbValid = (int)$objDatabase->fxGetVar($sqlCheck);
if ($nbValid !== count($ids)) {
return ['success' => false, 'message' => 'Séquences invalides pour cette épreuve'];
}
$objDatabase->fxQuery("
UPDATE inscriptions_epreuves_bib
SET epr_bib_auto = 1
WHERE epr_id = $epr_id
AND epr_bib_id IN ($strIds)
");
return ['success' => true, 'auto_active' => true];
}
function fxInfosBibEpreuve($epr_id){
global $objDatabase;
@ -3074,12 +3182,22 @@ if (
$showCheckbox = true;
}
// MSIN-4379 — Étape 2 : mode « auto » — checkbox sur chaque séquence enregistrée.
if (
$mode === 'auto'
&& (int)$range['epr_bib_id'] > 0
) {
$showCheckbox = true;
}
?>
<?php if ($showCheckbox) { ?>
<input type="checkbox"
class="batch-select-range"
data-range-id="<?php echo (int)$range['epr_bib_id']; ?>">
data-range-id="<?php echo (int)$range['epr_bib_id']; ?>"
<?php /* MSIN-4379 — Étape 2 : pré-cocher les séquences déjà en epr_bib_auto = 1. */ ?>
<?php if ($mode === 'auto' && (int)($range['epr_bib_auto'] ?? 0) === 1) { ?>checked<?php } ?>>
<?php } ?>
</div>