diff --git a/js/v2/bib-assignments.js b/js/v2/bib-assignments.js index f39b979..fe3e17d 100644 --- a/js/v2/bib-assignments.js +++ b/js/v2/bib-assignments.js @@ -1072,19 +1072,84 @@ return row.querySelectorAll('.batch-select-range:checked').length; } + /** MSIN-4436 — Développe un bloc repliable #module-bib. */ + function bibExpandBlock(block) { + if (!block || !block.classList.contains('is-collapsed')) { + return; + } + + block.classList.remove('is-collapsed'); + let btn = block.querySelector('.epr-block-toggle'); + if (btn) { + btn.setAttribute('aria-expanded', 'true'); + let label = btn.getAttribute('data-label-collapse') || ''; + if (label) { + btn.setAttribute('aria-label', label); + btn.setAttribute('title', label); + } + let icon = btn.querySelector('.fa'); + if (icon) { + icon.classList.add('fa-chevron-up'); + icon.classList.remove('fa-chevron-down'); + } + } + } + + let bibFocusApplied = false; + + /** MSIN-4436 — Retour depuis fiche inscription : rouvrir le panneau ciblé. */ + function bibApplyFocusFromUrlOnce() { + if (bibFocusApplied) { + return; + } + + let focus = new URLSearchParams(window.location.search).get('bib_focus'); + if (!focus) { + return; + } + + bibFocusApplied = true; + + let panelId = null; + if (focus === 'anomalies') { + panelId = 'bib-anomalies-panel'; + } else if (focus === 'qty_summary') { + panelId = 'bib-qty-summary-panel'; + } else if (focus === 'event_config') { + panelId = 'bib-event-config-panel'; + } + + if (!panelId) { + return; + } + + let panel = document.getElementById(panelId); + if (!panel) { + return; + } + + bibExpandBlock(panel); + + if (focus === 'qty_summary' && panel.getAttribute('data-bib-qty-summary-deferred') === '1') { + refreshBibQtySummaryPanel(); + } + + panel.scrollIntoView({ behavior: 'smooth', block: 'start' }); + } + /** MSIN-4379 — Rafraîchit le dashboard anomalies (chien de garde). */ function refreshBibAnomaliesPanel() { let mount = document.getElementById('bib-anomalies-mount'); if (!mount || !moduleBib) { - return; + return Promise.resolve(); } let eveId = moduleBib.dataset.eveId || ''; if (!eveId) { - return; + return Promise.resolve(); } - bibFetch('/ajax_bib_range.php', { + return bibFetch('/ajax_bib_range.php', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' @@ -2868,7 +2933,11 @@ // MSIN-4436 — Anomalies : coquille légère au chargement, détail en AJAX. if (document.querySelector('#bib-anomalies-panel[data-bib-anomalies-deferred="1"]')) { - refreshBibAnomaliesPanel(); + refreshBibAnomaliesPanel().then(function () { + bibApplyFocusFromUrlOnce(); + }); + } else { + bibApplyFocusFromUrlOnce(); } } })(); diff --git a/php/inc_fx_inscriptions_gestion.php b/php/inc_fx_inscriptions_gestion.php index 8dd6a97..7369e9e 100644 --- a/php/inc_fx_inscriptions_gestion.php +++ b/php/inc_fx_inscriptions_gestion.php @@ -7,6 +7,9 @@ * - Promoteur : /compte/inc_tableau_inscriptions_gestion → retour inc_tableau_promoteur * - Acces v2 seul : /mobile → retour /mobile ou Mon compte * + * MSIN-4436 — Parametre inscr_return (URL interne encodee) : retour contextuel depuis la fiche + * (ex. panneau anomalies dossards) au lieu de la liste des inscriptions. + * * Droits : v2 uniquement (fxInscrGestionCanAccess). i18n : toujours info_prg compte.php. */ @@ -148,6 +151,85 @@ function fxInscrGestionBaseUrl($strLangue, $blnMobileEntry = null) { return $vDomaine . ($strLangue === 'fr' ? '/compte/inc_tableau_inscriptions_gestion' : '/account/inc_tableau_inscriptions_gestion'); } +/** + * MSIN-4436 — Encode une URL interne relative (/compte/... ou /account/...) pour inscr_return. + */ +function fxInscrGestionEncodeReturnUrl($strRelativePath) { + $strRelativePath = trim((string)$strRelativePath); + if ($strRelativePath === '' || $strRelativePath[0] !== '/') { + return ''; + } + + return rtrim(strtr(base64_encode($strRelativePath), '+/', '-_'), '='); +} + +/** + * MSIN-4436 — Décode inscr_return ; retourne le chemin relatif validé ou ''. + */ +function fxInscrGestionDecodeReturnPath($strToken) { + $strToken = trim((string)$strToken); + if ($strToken === '') { + return ''; + } + + $strB64 = strtr($strToken, '-_', '+/'); + $intPad = strlen($strB64) % 4; + if ($intPad > 0) { + $strB64 .= str_repeat('=', 4 - $intPad); + } + + $strPath = base64_decode($strB64, true); + if ($strPath === false || $strPath === '' || $strPath[0] !== '/') { + return ''; + } + if (preg_match('#^https?://#i', $strPath)) { + return ''; + } + if (!preg_match('#^/(compte|account)/#', $strPath)) { + return ''; + } + + return $strPath; +} + +/** MSIN-4436 — URL absolue de retour depuis inscr_return, ou '' si invalide. */ +function fxInscrGestionDecodeReturnUrl($strToken) { + global $vDomaine; + + $strPath = fxInscrGestionDecodeReturnPath($strToken); + if ($strPath === '') { + return ''; + } + + return $vDomaine . $strPath; +} + +/** MSIN-4436 — Bouton retour fiche : URL externe (inscr_return) ou liste filtrée. */ +function fxInscrGestionResolveBackUrl($strBaseUrl, $intEveId, $arrReq) { + if (!empty($arrReq['inscr_return'])) { + $strReturn = fxInscrGestionDecodeReturnUrl($arrReq['inscr_return']); + if ($strReturn !== '') { + return $strReturn; + } + } + + return fxInscrGestionBuildUrl($strBaseUrl, $intEveId, $arrReq, array('pec_id' => null, 'pg' => $arrReq['pg'])); +} + +/** MSIN-4436 — Libellé du bouton retour selon le contexte d'origine. */ +function fxInscrGestionResolveBackLabel($arrReq, $strLangue = 'fr') { + if (!empty($arrReq['inscr_return'])) { + $strPath = fxInscrGestionDecodeReturnPath($arrReq['inscr_return']); + if ($strPath !== '' && strpos($strPath, 'inc_tableau_gestion_epreuves') !== false) { + return fxInscrGestionT('inscr_gestion_back_bib'); + } + + return fxInscrGestionT('inscr_gestion_back_prev'); + } + + return fxInscrGestionT('inscr_gestion_back_list'); +} + function fxInscrGestionEsc($str) { return htmlspecialchars((string)$str, ENT_QUOTES, 'UTF-8'); } @@ -284,6 +366,8 @@ function fxInscrGestionFallbackTexte($strClef, $strLangue) { 'inscr_gestion_statut_annule' => $blnFr ? 'Annulée' : 'Cancelled', 'inscr_gestion_statut_transfert' => $blnFr ? 'Transférée' : 'Transferred', 'inscr_gestion_statut_modif' => $blnFr ? 'Modifiée (changement d\'épreuve)' : 'Modified (race change)', + 'inscr_gestion_back_bib' => $blnFr ? 'Retour aux dossards' : 'Back to bibs', + 'inscr_gestion_back_prev' => $blnFr ? 'Retour' : 'Back', ); } @@ -471,6 +555,8 @@ function fxInscrGestionParseRequest() { 'sel_rech_epreuve' => 0, 'pg' => isset($_GET['pg']) ? max(1, intval($_GET['pg'])) : 1, 'pec_id' => isset($_GET['pec_id']) ? intval($_GET['pec_id']) : 0, + // MSIN-4436 — Page d'origine (ex. gestion dossards) pour le bouton retour de la fiche. + 'inscr_return' => isset($_GET['inscr_return']) ? trim((string)$_GET['inscr_return']) : '', // q = 1 lorsque l'utilisateur a lance une recherche/filtre. La liste reste vide tant que q = 0. 'q' => 0, ); @@ -519,6 +605,7 @@ function fxInscrGestionEmptyListRequest() { 'sel_rech_epreuve' => 0, 'pg' => 1, 'pec_id' => 0, + 'inscr_return' => '', 'q' => 0, ); } @@ -990,6 +1077,9 @@ function fxInscrGestionQueryParams($intEveId, $arrReq, $arrExtra = array()) { if (!empty($arrReq['pg']) && (int)$arrReq['pg'] > 1) { $tab['pg'] = (int)$arrReq['pg']; } + if (!empty($arrReq['inscr_return'])) { + $tab['inscr_return'] = $arrReq['inscr_return']; + } foreach ($arrExtra as $strKey => $mixVal) { if ($mixVal === null || $mixVal === '' || ($strKey === 'pec_id' && (int)$mixVal === 0)) { @@ -1473,7 +1563,7 @@ function fxInscrGestionRenderFiche($intEveId, $strLangue, $strBaseUrl, $arrReq, $blnHasGestion = ($blnCanEdit || $blnCanCancel || $blnCanRestore || $blnCanRefund); $blnInspect = function_exists('fxAdminPermInspectModeActive') && fxAdminPermInspectModeActive(); - $strListUrl = fxInscrGestionBuildUrl($strBaseUrl, $intEveId, $arrReq, array('pec_id' => null, 'pg' => $arrReq['pg'])); + $strBackUrl = fxInscrGestionResolveBackUrl($strBaseUrl, $intEveId, $arrReq); $strEveCode = fxGetEvenementsUrl($arrRow['eve_id']); $strBib = fxInscrGestionBibDisplay($arrRow); $intParId = $arrRow['par_id']; @@ -1499,7 +1589,7 @@ function fxInscrGestionRenderFiche($intEveId, $strLangue, $strBaseUrl, $arrReq, } echo '>'; - fxInscrGestionRenderNavBack($strListUrl, fxInscrGestionT('inscr_gestion_back_list')); + fxInscrGestionRenderNavBack($strBackUrl, fxInscrGestionResolveBackLabel($arrReq, $strLangue)); echo '
'; echo '

' . fxInscrGestionEsc(fxUnescape($arrRow['par_nom']) . ', ' . fxUnescape($arrRow['par_prenom'])) . '

'; echo '
'; @@ -1701,8 +1791,8 @@ function fxInscrGestionRun($intEveId, $strLangue) { $arrRow = fxInscrGestionFetchRowByPec($arrReq['pec_id'], $intEveId); if ($arrRow === null) { echo '
' . fxInscrGestionEsc(fxInscrGestionT('txt_recherche_no_result')) . '
'; - echo ''; - echo fxInscrGestionEsc(fxInscrGestionT('inscr_gestion_back_list')) . ''; + echo ''; + echo fxInscrGestionEsc(fxInscrGestionResolveBackLabel($arrReq, $strLangue)) . ''; return; } fxInscrGestionRenderFiche($intEveId, $strLangue, $strBaseUrl, $arrReq, $arrRow); diff --git a/php/inc_fx_promoteur.php b/php/inc_fx_promoteur.php index 1db7dcc..713cc07 100644 --- a/php/inc_fx_promoteur.php +++ b/php/inc_fx_promoteur.php @@ -5795,6 +5795,25 @@ function renderBibQtySummaryPanel($int_eve_id, $strLangue = 'fr', $tabSummary = return ob_get_clean(); } +/** + * MSIN-4436 — Chemin relatif de retour vers la page gestion dossards (avec focus optionnel). + */ +function fxBibGestionEpreuvesReturnPath($int_eve_id, $strLangue = 'fr', $strFocus = '') { + $strBase = ($strLangue === 'fr') + ? '/compte/inc_tableau_gestion_epreuves' + : '/account/inc_tableau_gestion_epreuves'; + + $strPath = $strBase + . '?promoteur_eve_id=' . rawurlencode(base64_encode((string)(int)$int_eve_id)) + . '&lng=' . rawurlencode($strLangue); + + if ($strFocus !== '') { + $strPath .= '&bib_focus=' . rawurlencode($strFocus); + } + + return $strPath; +} + /** * MSIN-4436 — URL fiche gestion inscriptions v2 pour une anomalie dossard. * Réutilise fxInscrGestionBuildUrl (même lien que la liste des inscriptions). @@ -5816,13 +5835,18 @@ function fxBibAnomalyInscrGestionUrl($int_eve_id, $int_pec_id, $strLangue = 'fr' return ''; } + $strReturn = fxInscrGestionEncodeReturnUrl( + fxBibGestionEpreuvesReturnPath($int_eve_id, $strLangue, 'anomalies') + ); + return fxInscrGestionBuildUrl( fxInscrGestionBaseUrl($strLangue, false), $int_eve_id, fxInscrGestionEmptyListRequest(), [ - 'pec_id' => $int_pec_id, - 'lng' => $strLangue, + 'pec_id' => $int_pec_id, + 'lng' => $strLangue, + 'inscr_return' => $strReturn, ] ); } diff --git a/sql/MSIN-4436-inscr-gestion-return.sql b/sql/MSIN-4436-inscr-gestion-return.sql new file mode 100644 index 0000000..d95c49c --- /dev/null +++ b/sql/MSIN-4436-inscr-gestion-return.sql @@ -0,0 +1,18 @@ +-- MSIN-4436 — Retour contextuel fiche gestion inscriptions (ex. depuis anomalies dossards) +-- Idempotent + +INSERT INTO info (info_clef, info_langue, info_texte, info_aide, info_prg, info_description, info_trie, info_actif, info_option1, info_option2, info_option3, info_creation) +SELECT 'inscr_gestion_back_bib', 'fr', 'Retour aux dossards', '', 'compte.php', '', 0, 1, '', '', '', NOW() +FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM info WHERE info_clef = 'inscr_gestion_back_bib' AND info_langue = 'fr' AND info_prg = 'compte.php'); + +INSERT INTO info (info_clef, info_langue, info_texte, info_aide, info_prg, info_description, info_trie, info_actif, info_option1, info_option2, info_option3, info_creation) +SELECT 'inscr_gestion_back_bib', 'en', 'Back to bibs', '', 'compte.php', '', 0, 1, '', '', '', NOW() +FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM info WHERE info_clef = 'inscr_gestion_back_bib' AND info_langue = 'en' AND info_prg = 'compte.php'); + +INSERT INTO info (info_clef, info_langue, info_texte, info_aide, info_prg, info_description, info_trie, info_actif, info_option1, info_option2, info_option3, info_creation) +SELECT 'inscr_gestion_back_prev', 'fr', 'Retour', '', 'compte.php', '', 0, 1, '', '', '', NOW() +FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM info WHERE info_clef = 'inscr_gestion_back_prev' AND info_langue = 'fr' AND info_prg = 'compte.php'); + +INSERT INTO info (info_clef, info_langue, info_texte, info_aide, info_prg, info_description, info_trie, info_actif, info_option1, info_option2, info_option3, info_creation) +SELECT 'inscr_gestion_back_prev', 'en', 'Back', '', 'compte.php', '', 0, 1, '', '', '', NOW() +FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM info WHERE info_clef = 'inscr_gestion_back_prev' AND info_langue = 'en' AND info_prg = 'compte.php');