diff --git a/ajax_bib_range.php b/ajax_bib_range.php index 5997e3f..97f1408 100644 --- a/ajax_bib_range.php +++ b/ajax_bib_range.php @@ -1132,6 +1132,29 @@ if ($action == 'anomalies') { exit; } +// MSIN-4424 — Sommaire des quantités (toutes épreuves) +if ($action == 'qty_summary') { + + $int_eve_id = (int)($_POST['eve_id'] ?? $_GET['eve_id'] ?? 0); + + if ($int_eve_id <= 0) { + echo json_encode([ + 'success' => false, + 'message' => fxBibMsg('bib_v4_ajax_epr_invalid') + ]); + exit; + } + + $tabSummary = fxBibCollectEventQtySummary($int_eve_id, $strLangue); + + echo json_encode([ + 'success' => true, + 'totals' => $tabSummary['totals'] ?? [], + 'html' => renderBibQtySummaryPanel($int_eve_id, $strLangue, $tabSummary), + ]); + exit; +} + // ===================== // ACTION INVALIDE // ===================== diff --git a/css/style.css b/css/style.css index 3046aad..2601850 100644 --- a/css/style.css +++ b/css/style.css @@ -1064,34 +1064,8 @@ ul.ms1-menu-compte li a:hover, ul.ms1-menu-compte li a:active { line-height:1.45; } -/* MSIN-4424 — Bandeau réglages dossards (haut du module). */ -.bib-event-settings{ - border:1px solid #c8d6e5; - border-radius:6px; - background:#f8fafc; - padding:10px 14px; - margin-bottom:4px; -} -.bib-event-settings__label{ - display:flex; - align-items:flex-start; - gap:8px; - margin:0; - font-weight:600; - font-size:14px; - line-height:1.4; - cursor:pointer; -} - -.bib-event-settings__label input[type="checkbox"]{ - margin-top:3px; - flex-shrink:0; -} - -.bib-event-settings__text{ - flex:1 1 auto; -} +/* MSIN-4424 — ancien bandeau remplacé par panneau repliable .bib-event-config-panel */ .bib-anomaly-block--inter-epr-dupes .bib-anomaly-block-title{ color:#8a3b12; @@ -2537,6 +2511,114 @@ a.ms1-trad-link.btn-aide-trad{ margin:0; } +/* MSIN-4424 — Panneau configuration globale (contenu dans .epr-content). */ +.bib-event-config-content{ + background:#fff; +} + +.bib-event-config-content .bib-event-settings__label{ + display:flex; + align-items:flex-start; + gap:8px; + margin:0; + font-weight:600; + font-size:14px; + line-height:1.4; + cursor:pointer; +} + +.bib-event-config-content .bib-event-settings__label input[type="checkbox"]{ + margin-top:3px; + flex-shrink:0; +} + +.bib-event-config-content .bib-event-settings__text{ + flex:1 1 auto; +} + +/* MSIN-4424 — Sommaire des quantités (même gabarit que anomalies). */ +.bib-qty-summary-panel--alert{ + border-color:#f0ad4e; +} + +.bib-qty-summary-badges{ + display:inline-flex; + flex-wrap:wrap; + align-items:center; + gap:6px; +} + +.bib-qty-summary-badges--pending{ + font-size:12px; + opacity:.85; +} + +.bib-qty-summary-badge{ + font-size:12px; + font-weight:600; +} + +.bib-qty-summary-badge--warn{ + background:rgba(220,53,69,.9); + border-color:rgba(220,53,69,.9); +} + +.bib-qty-summary-content{ + background:#fff; +} + +.bib-qty-summary-section-title{ + margin:0 0 10px; + font-size:14px; + font-weight:700; + color:#212529; +} + +.bib-qty-summary-totals{ + margin-bottom:16px; +} + +.bib-qty-summary-dl{ + display:grid; + grid-template-columns:repeat(auto-fill, minmax(200px, 1fr)); + gap:8px 16px; + margin:0; +} + +.bib-qty-summary-dl__row{ + display:flex; + justify-content:space-between; + gap:10px; + padding:8px 10px; + background:#f8f9fa; + border:1px solid #e9ecef; + border-radius:4px; + font-size:13px; +} + +.bib-qty-summary-dl__row dt{ + margin:0; + font-weight:600; + color:#495057; +} + +.bib-qty-summary-dl__row dd{ + margin:0; + font-weight:700; + color:#212529; +} + +.bib-qty-summary-table th, +.bib-qty-summary-table td{ + font-size:13px; + vertical-align:middle; +} + +.bib-qty-summary-by-epr{ + border-top:1px solid #e9ecef; + padding-top:14px; +} + .bib-anomalies-panel{ margin-bottom:14px; border:1px solid #ced4da; diff --git a/js/v2/bib-assignments.js b/js/v2/bib-assignments.js index 9b2912f..1411f6e 100644 --- a/js/v2/bib-assignments.js +++ b/js/v2/bib-assignments.js @@ -920,6 +920,12 @@ icon.classList.toggle('fa-chevron-up', !collapsed); icon.classList.toggle('fa-chevron-down', collapsed); } + + // MSIN-4424 — Sommaire quantités : chargement à la première ouverture. + if (!collapsed && block.id === 'bib-qty-summary-panel' + && block.getAttribute('data-bib-qty-summary-deferred') === '1') { + refreshBibQtySummaryPanel(); + } }); // ============================================================ @@ -1065,6 +1071,64 @@ }); } + /** MSIN-4424 — Rafraîchit le sommaire des quantités (si déjà chargé ou ouverture). */ + function refreshBibQtySummaryPanel() { + let mount = document.getElementById('bib-qty-summary-mount'); + if (!mount || !moduleBib) { + return; + } + + let panel = document.getElementById('bib-qty-summary-panel'); + let blnDeferred = panel && panel.getAttribute('data-bib-qty-summary-deferred') === '1'; + let blnLoaded = panel && panel.getAttribute('data-bib-qty-summary-loaded') === '1'; + if (!blnDeferred && !blnLoaded) { + return; + } + + let eveId = moduleBib.dataset.eveId || ''; + if (!eveId) { + return; + } + + let blnWasExpanded = panel && !panel.classList.contains('is-collapsed'); + + bibFetch('/ajax_bib_range.php', { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded' + }, + body: 'action=qty_summary&eve_id=' + encodeURIComponent(eveId) + bibAjaxLangSuffix() + }, { loader: false }) + .then(function (res) { return res.json(); }) + .then(function (data) { + if (!data.success || !data.html) { + return; + } + mount.innerHTML = data.html; + initModuleBibTippy(mount); + if (blnWasExpanded) { + let newPanel = document.getElementById('bib-qty-summary-panel'); + if (newPanel) { + newPanel.classList.remove('is-collapsed'); + let btn = newPanel.querySelector('.epr-block-toggle'); + if (btn) { + btn.setAttribute('aria-expanded', 'true'); + let icon = btn.querySelector('.fa'); + if (icon) { + icon.classList.remove('fa-chevron-down'); + icon.classList.add('fa-chevron-up'); + } + } + } + } + }); + } + + function bibQtySummaryShouldRefresh() { + let panel = document.getElementById('bib-qty-summary-panel'); + return panel && panel.getAttribute('data-bib-qty-summary-loaded') === '1'; + } + function setBibContainerHtml(row, html, opts) { opts = opts || {}; let container = getBibContainer(row); @@ -1084,6 +1148,9 @@ if (!opts.skipAnomalies) { refreshBibAnomaliesPanel(); } + if (!opts.skipQtySummary && bibQtySummaryShouldRefresh()) { + refreshBibQtySummaryPanel(); + } return container; } @@ -2746,6 +2813,10 @@ mount.innerHTML = data.anomalies_html; initModuleBibTippy(mount); } + + if (bibQtySummaryShouldRefresh()) { + refreshBibQtySummaryPanel(); + } }) .catch(function () { chkAllowInterEpr.checked = !chkAllowInterEpr.checked; diff --git a/php/inc_fx_promoteur.php b/php/inc_fx_promoteur.php index 730b808..b67199a 100644 --- a/php/inc_fx_promoteur.php +++ b/php/inc_fx_promoteur.php @@ -2997,6 +2997,18 @@ function fxBibStaticFallback($clef) { 'bib_v4_js_global_no_seq' => ['fr' => 'Cochez au moins une séquence pour les épreuves sélectionnées.', 'en' => 'Check at least one sequence for the selected races.'], 'bib_v4_global_batch_plan_invalid' => ['fr' => 'Sélection invalide : choisissez au moins une épreuve solo et des séquences.', 'en' => 'Invalid selection: pick at least one solo race and sequences.'], 'bib_v4_global_batch_need_two' => ['fr' => "L'assignation globale nécessite au moins deux épreuves solo avec des séquences disponibles.", 'en' => 'Global assignment requires at least two solo races with available sequences.'], + 'bib_v4_event_config_title' => ['fr' => "Configuration globale pour l'assignation de dossard", 'en' => 'Global bib assignment settings'], + 'bib_v4_qty_summary_title' => ['fr' => 'Sommaire des quantités', 'en' => 'Quantity summary'], + 'bib_v4_qty_summary_a_assigner' => ['fr' => 'À assigner', 'en' => 'To assign'], + 'bib_v4_qty_summary_bib_restant' => ['fr' => 'Dossards restants', 'en' => 'Bibs remaining'], + 'bib_v4_qty_summary_orphelins' => ['fr' => 'Personnes orphelines', 'en' => 'Orphan participants'], + 'bib_v4_qty_summary_total' => ['fr' => 'Inscrits', 'en' => 'Registered'], + 'bib_v4_qty_summary_avec_bib' => ['fr' => 'Avec dossard', 'en' => 'With bib'], + 'bib_v4_qty_summary_sans_bib' => ['fr' => 'Sans dossard', 'en' => 'Without bib'], + 'bib_v4_qty_summary_places_dispo' => ['fr' => 'Places disponibles', 'en' => 'Places available'], + 'bib_v4_qty_summary_global' => ['fr' => 'Total événement', 'en' => 'Event total'], + 'bib_v4_qty_summary_by_epr' => ['fr' => 'Détail par épreuve', 'en' => 'Breakdown by race'], + 'bib_v4_qty_summary_col_epr' => ['fr' => 'Épreuve', 'en' => 'Race'], 'bib_v4_ajax_global_sim_summary' => ['fr' => '%d épreuve(s) · %d dispo / %d à assigner → %d assignables (%d manque)', 'en' => '%d race(s) · %d avail. / %d to assign → %d assignable (%d short)'], ]; @@ -4226,7 +4238,11 @@ function fxShowBibTool4($str_code, $int_eve_id, $strLangue){
> - + + +
+ +
@@ -5112,22 +5128,285 @@ function fxBibAnomalyTotalCount(array $tabAnomalies) { return $intTotal; } -/** MSIN-4424 — Bandeau réglages dossards (haut du module, au-dessus des épreuves). */ -function renderBibEventSettingsBanner($int_eve_id, $strLangue = 'fr') { +/** MSIN-4424 — Corps du panneau configuration globale (réglages événement). */ +function renderBibEventConfigPanelContent($int_eve_id) { $int_eve_id = (int)$int_eve_id; $blnAllow = fxBibEventAllowsInterEprDuplicates($int_eve_id); ob_start(); ?> -
- + + + + , epreuves: array>} + */ +function fxBibCollectEventQtySummary($int_eve_id, $strLangue = 'fr') { + global $objDatabase; + + $int_eve_id = (int)$int_eve_id; + $tabTotals = [ + 'a_assigner' => 0, + 'bib_restant' => 0, + 'orphelins' => 0, + 'total' => 0, + 'avec_bib' => 0, + 'sans_bib' => 0, + 'places_dispo' => 0, + ]; + $tabEpreuvesRows = []; + + if ($int_eve_id <= 0) { + return ['totals' => $tabTotals, 'epreuves' => $tabEpreuvesRows]; + } + + $sqlEpreuves = " + SELECT * + FROM inscriptions_epreuves + WHERE eve_id = $int_eve_id + AND epr_actif = 1 + ORDER BY epr_id + "; + $tabEpreuves = $objDatabase->fxGetResults($sqlEpreuves); + if (!is_array($tabEpreuves)) { + $tabEpreuves = []; + } + + $tabOrphans = fxBibAnomalyOrphanBibs($int_eve_id, $strLangue); + $tabOrphansByEpr = []; + foreach ($tabOrphans as $item) { + $intEprId = (int)($item['epr_id'] ?? 0); + if ($intEprId > 0) { + $tabOrphansByEpr[$intEprId] = ($tabOrphansByEpr[$intEprId] ?? 0) + 1; + } + } + + foreach ($tabEpreuves as $epr) { + $epr_id = (int)$epr['epr_id']; + $infoBib = fxInfosBibEpreuve($epr_id); + $arrQte = fxGetQuantites($epr_id); + + $intBibRestant = 0; + $tabRanges = fxInfosBibRange($epr_id, 0, false); + if (is_array($tabRanges)) { + foreach ($tabRanges as $range) { + if (is_array($range)) { + $intBibRestant += fxBibRangeDispoFromRow($range); + } + } + } + + $tabRow = [ + 'epr_id' => $epr_id, + 'epr_label' => fxBibAnomalyEprLabel($epr, $strLangue), + 'total' => (int)($infoBib['total'] ?? 0), + 'avec_bib' => (int)($infoBib['avec_bib'] ?? 0), + 'sans_bib' => (int)($infoBib['sans_bib'] ?? 0), + 'a_assigner' => (int)($infoBib['a_assigner'] ?? 0), + 'bib_restant' => $intBibRestant, + 'orphelins' => (int)($tabOrphansByEpr[$epr_id] ?? 0), + 'places_dispo' => (int)($arrQte['qte_restante'] ?? 0), + ]; + $tabEpreuvesRows[] = $tabRow; + + $tabTotals['a_assigner'] += $tabRow['a_assigner']; + $tabTotals['bib_restant'] += $tabRow['bib_restant']; + $tabTotals['total'] += $tabRow['total']; + $tabTotals['avec_bib'] += $tabRow['avec_bib']; + $tabTotals['sans_bib'] += $tabRow['sans_bib']; + $tabTotals['places_dispo'] += $tabRow['places_dispo']; + } + + $tabTotals['orphelins'] = count($tabOrphans); + + return ['totals' => $tabTotals, 'epreuves' => $tabEpreuvesRows]; +} + +/** MSIN-4424 — Coquille sommaire quantités (détail chargé à la première ouverture). */ +function renderBibQtySummaryPanelShell($int_eve_id, $strLangue = 'fr') { + global $vDomaine; + + $int_eve_id = (int)$int_eve_id; + $strWait = fxBibTexte('bib_v4_js_loader_wait', 0); + if ($strWait === '' || $strWait === 'bib_v4_js_loader_wait') { + $strWait = ($strLangue === 'en') ? 'Loading...' : 'Chargement...'; + } + $strRunnerSrc = $vDomaine . '/images/ms1-runner-loader.gif?v=' . _VERSION_CODE; + + ob_start(); + ?> + + 0; + $blnHasOrphans = (int)($tabTotals['orphelins'] ?? 0) > 0; + + ob_start(); + ?> +