Files
ms1inscription-v5/php/inc_fx_inscriptions_gestion.php
stephan 8f428a5c18 Refactor event management system to transition from mobile to gestion
This commit updates the event management system by replacing references to mobile inscriptions with gestion inscriptions. It includes changes to AJAX endpoints, helper functions, and UI elements to reflect this transition. The updates enhance the overall structure and maintainability of the codebase while ensuring that user access and permissions are properly managed for the new gestion interface.
2026-06-27 12:10:19 -04:00

1412 lines
60 KiB
PHP

<?php
/**
* Gestion des inscriptions — recherche, filtres, cartes, fiche.
*
* Deux entrees distinctes (meme module, navigation differente) :
* - Promoteur : /compte/inc_tableau_inscriptions_gestion → retour inc_tableau_promoteur
* - Acces v2 seul : /mobile → retour /mobile ou Mon compte
*
* Droits : v2 uniquement (fxInscrGestionCanAccess). i18n : toujours info_prg compte.php.
*/
function fxInscrGestionIsEntry() {
return defined('MS1_INSCR_GESTION_ENTRY') && MS1_INSCR_GESTION_ENTRY;
}
/** Utilisateur avec menu promoteur legacy (section Info promoteur). */
function fxInscrGestionUserHasPromoteurMenu($intComId) {
global $objDatabase;
$row = $objDatabase->fxGetRow(
'SELECT com_eve_promoteur, com_rapports FROM inscriptions_comptes WHERE com_id = ' . intval($intComId)
);
if ($row === null) {
return false;
}
return trim((string)$row['com_eve_promoteur']) !== '' || trim((string)$row['com_rapports']) !== '';
}
/** Permissions v2 requises pour la fiche de gestion des inscriptions (web). */
function fxInscrGestionPermKeysView() {
return array('registrations.view', 'inscriptions_gestion.view');
}
function fxInscrGestionPermKeysScan() {
return array('registrations.scan');
}
/** Permission action (niveau fonction dans la fiche de gestion). */
function fxInscrGestionCanDo($intComId, $intEveId, $strActionPerm) {
fxInscrGestionLoadEveAcces();
if (!fxInscrGestionCanAccess($intComId, $intEveId)) {
return false;
}
if (fxEveAccesHasPermission(intval($intComId), intval($intEveId), $strActionPerm)) {
return true;
}
/* Kit inscriptions complet (desktop) inclut les actions de gestion. */
if (fxEveAccesHasPermission(intval($intComId), intval($intEveId), 'inscriptions.edit')) {
return true;
}
/* dossards.manage autorise aussi la modification du no dossard sur la fiche de gestion. */
if ($strActionPerm === 'inscriptions_gestion.bib_edit'
&& fxEveAccesHasPermission(intval($intComId), intval($intEveId), 'dossards.manage')) {
return true;
}
return false;
}
function fxInscrGestionLoadEveAcces() {
require_once __DIR__ . '/inc_fx_eve_acces.php';
}
/**
* Evenements accessibles via acces v2 uniquement (pas com_eve_promoteur, pas super admin).
*/
function fxInscrGestionGetPromoteurEveIds($intComId) {
fxInscrGestionLoadEveAcces();
return fxEveAccesGetEventIdsWithInscrGestionAccess(intval($intComId));
}
function fxInscrGestionCanAccess($intComId, $intEveId) {
fxInscrGestionLoadEveAcces();
return fxEveAccesHasInscrGestionWebAccess(intval($intComId), intval($intEveId));
}
function fxInscrGestionCanScan($intComId, $intEveId) {
fxInscrGestionLoadEveAcces();
return fxEveAccesHasAnyPermission(intval($intComId), intval($intEveId), fxInscrGestionPermKeysScan());
}
function fxInscrGestionBaseUrl($strLangue, $blnMobileEntry = null) {
global $vDomaine;
if ($blnMobileEntry === null) {
$blnMobileEntry = fxInscrGestionIsEntry();
}
if ($blnMobileEntry) {
return $vDomaine . '/mobile';
}
return $vDomaine . ($strLangue === 'fr' ? '/compte/inc_tableau_inscriptions_gestion' : '/account/inc_tableau_inscriptions_gestion');
}
function fxInscrGestionEsc($str) {
return htmlspecialchars((string)$str, ENT_QUOTES, 'UTF-8');
}
/** Page info / i18n des libelles de gestion des inscriptions (toujours compte.php). */
function fxInscrGestionTextesPage() {
return 'compte.php';
}
function fxInscrGestionLoadTextes($strLangue) {
static $tabCache = array();
static $strCacheLang = '';
if ($strCacheLang === $strLangue && !empty($tabCache)) {
return $tabCache;
}
$tabCache = obtenirTextepage(fxInscrGestionTextesPage(), $strLangue, 2);
if (!is_array($tabCache)) {
$tabCache = array();
}
$strCacheLang = $strLangue;
return $tabCache;
}
/**
* Recherche un libelle info par clef (toutes pages), priorite compte.php > global > mobile.php.
*/
function fxInscrGestionLookupTexteDb($strClef, $strLangue) {
global $objDatabase;
static $tabMemo = array();
$strMemoKey = $strClef . "\0" . $strLangue;
if (array_key_exists($strMemoKey, $tabMemo)) {
return $tabMemo[$strMemoKey];
}
$sql = "SELECT info_texte FROM info
WHERE info_clef = '" . $objDatabase->fxEscape($strClef) . "'
AND info_langue = '" . $objDatabase->fxEscape($strLangue) . "'
AND info_actif = 1
ORDER BY FIELD(info_prg, 'compte.php', 'global', 'mobile.php', 'mobile')
LIMIT 1";
$strTexte = $objDatabase->fxGetVar($sql);
if ($strTexte === null || trim((string)$strTexte) === '' || trim((string)$strTexte) === $strClef) {
$tabMemo[$strMemoKey] = null;
return null;
}
$tabMemo[$strMemoKey] = (string)$strTexte;
return $tabMemo[$strMemoKey];
}
function fxInscrGestionResolveTexte($strClef, $strLangue) {
global $vtexte_page;
$tab = fxInscrGestionLoadTextes($strLangue);
if (isset($tab[$strClef]['info_texte'])) {
$str = trim((string)$tab[$strClef]['info_texte']);
if ($str !== '' && $str !== $strClef) {
return $str;
}
}
if (is_array($vtexte_page) && isset($vtexte_page[$strClef]['info_texte'])) {
$str = trim((string)$vtexte_page[$strClef]['info_texte']);
if ($str !== '' && $str !== $strClef) {
return $str;
}
}
$strDb = fxInscrGestionLookupTexteDb($strClef, $strLangue);
if ($strDb !== null) {
return $strDb;
}
if (function_exists('fxAdminCanUseTextEditTools') && fxAdminCanUseTextEditTools()) {
global $vPage;
$memPage = $vPage;
$vPage = fxInscrGestionTextesPage();
$str = afficheTexte($strClef, 0);
$vPage = $memPage;
if (strpos($str, '*') === 0 && substr($str, -1) === '*') {
return fxInscrGestionFallbackTexte($strClef, $strLangue);
}
if (trim($str) === '' || trim($str) === $strClef) {
return fxInscrGestionFallbackTexte($strClef, $strLangue);
}
return $str;
}
return fxInscrGestionFallbackTexte($strClef, $strLangue);
}
/** Libelles integres (secours si table info non a jour). */
function fxInscrGestionFallbackTexte($strClef, $strLangue) {
static $tabMemo = array();
if (!isset($tabMemo[$strLangue])) {
$blnFr = ($strLangue !== 'en');
$tabMemo[$strLangue] = array(
'inscr_gestion_qr_title' => $blnFr ? 'Code QR — Lecteur' : 'QR code reader',
'inscr_gestion_qr_hint' => $blnFr ? 'Pointez la caméra vers le code QR du participant.' : 'Point the camera at the participant\'s QR code.',
'inscr_gestion_qr_invalid' => $blnFr ? 'Code QR invalide ou introuvable.' : 'Invalid or unknown QR code.',
'inscr_gestion_qr_not_found' => $blnFr ? 'Participant introuvable pour cet événement.' : 'Participant not found for this event.',
'inscr_gestion_qr_wrong_event' => $blnFr ? 'Ce code QR appartient à un autre événement : %s' : 'This QR code belongs to another event: %s',
'inscr_gestion_qr_camera_error' => $blnFr ? 'Impossible d\'accéder à la caméra.' : 'Unable to access the camera.',
'inscr_gestion_bib_scan_btn' => $blnFr ? 'Scanner le numéro' : 'Scan number',
'inscr_gestion_bib_scan_hint' => $blnFr ? 'Ouvrez la caméra, cadrez le numéro, puis touchez l\'image pour lire.' : 'Open the camera, frame the number, then tap the image to read.',
'inscr_gestion_bib_scan_ready' => $blnFr ? 'Cadrez le numéro, puis touchez l\'image' : 'Frame the number, then tap the image',
'inscr_gestion_bib_scan_tap' => $blnFr ? 'Toucher pour lire' : 'Tap to read',
'inscr_gestion_bib_scan_scanning' => $blnFr ? 'Lecture en cours...' : 'Reading...',
'inscr_gestion_bib_scan_loading' => $blnFr ? 'Initialisation (1re fois)...' : 'Initializing (first time)...',
'inscr_gestion_bib_scan_found' => $blnFr ? 'Numéro %s détecté — validez avec OK.' : 'Number %s detected — press OK.',
'inscr_gestion_bib_scan_invalid' => $blnFr ? 'Numéro illisible. Cadrez un seul numéro.' : 'Unreadable number. Frame a single number.',
// Libelles de la barre de boutons (fiche de gestion) — editables via la table info (page compte.php).
'inscr_gestion_show_invoice' => $blnFr ? 'Afficher la facture' : 'Show invoice',
'inscr_gestion_renvoi' => $blnFr ? 'RENVOI' : 'RESEND',
'inscr_gestion_modifier_participant' => $blnFr ? 'Modifier l\'inscription' : 'Edit registration',
// Invite affichee quand la page arrive vide (aucune recherche lancee).
'inscr_gestion_search_prompt' => $blnFr
? 'Utilisez la recherche ci-dessus pour afficher les inscriptions. Laissez les champs vides et lancez la recherche pour toutes les afficher.'
: 'Use the search above to display registrations. Leave the fields empty and search to show them all.',
);
}
if (isset($tabMemo[$strLangue][$strClef])) {
return $tabMemo[$strLangue][$strClef];
}
return $strClef;
}
/** Charge $vtexte_page pour le module gestion (entry /mobile ou compte). */
function fxInscrGestionInitTextes($strLangue) {
global $vtexte_page, $vPage;
$vPage = fxInscrGestionTextesPage();
$vtexte_page = fxInscrGestionLoadTextes($strLangue);
}
/** Libelle i18n sans echo parasite — independant de la page PHP courante. */
function fxInscrGestionT($strClef) {
global $strLangue;
if (!isset($strLangue) || $strLangue === '') {
$strLangue = 'fr';
}
$strTexte = fxInscrGestionResolveTexte($strClef, $strLangue);
if ($strTexte === $strClef || trim((string)$strTexte) === '') {
$strFallback = fxInscrGestionFallbackTexte($strClef, $strLangue);
if ($strFallback !== $strClef && trim((string)$strFallback) !== '') {
$strTexte = $strFallback;
}
}
if (function_exists('fxAdminTradMarker')) {
$strMarker = fxAdminTradMarker($strClef, 0);
if ($strMarker !== '' && strpos($strTexte, $strMarker) === false) {
$strTexte .= $strMarker;
}
}
return $strTexte;
}
function fxInscrGestionLoginActionUrl($strLangue) {
global $vDomaine;
if ($strLangue === 'en') {
return $vDomaine . '/mobile/en';
}
return $vDomaine . '/mobile';
}
function fxInscrGestionRenderLoginPage($strLangue) {
$strActionUrl = fxInscrGestionLoginActionUrl($strLangue);
$strLblUser = ($strLangue === 'fr') ? 'Nom d\'utilisateur' : 'Username';
$strLblPass = ($strLangue === 'fr') ? 'Mot de passe' : 'Password';
$strBtn = ($strLangue === 'fr') ? 'Connexion' : 'Sign in';
$strIntro = ($strLangue === 'fr')
? 'Connectez-vous pour accéder aux inscriptions mobile.'
: 'Sign in to access mobile registrations.';
echo '<div class="inscr-gestion-page inscr-gestion-login-wrap">';
echo '<div id="module-inscr-gestion" class="inscr-gestion-shell inscr-gestion-shell--login">';
fxInscrGestionRenderPageHead(
($strLangue === 'fr') ? 'Inscriptions mobile' : 'Mobile registrations'
);
echo '<p class="inscr-gestion-login-intro text-muted">' . fxInscrGestionEsc($strIntro) . '</p>';
echo '<form action="' . fxInscrGestionEsc($strActionUrl) . '" id="frm_inscr_gestion_login" method="post" class="inscr-gestion-login-form">';
echo '<input type="hidden" name="form_action" value="' . fxInscrGestionEsc(urlencode(base64_encode('login'))) . '">';
echo '<label class="inscr-gestion-label" for="txt_login_mobile">' . fxInscrGestionEsc($strLblUser) . '</label>';
echo '<input class="form-control form-control-sm rounded-0" type="text" id="txt_login_mobile" name="txt_login" required autofocus>';
echo '<label class="inscr-gestion-label" for="txt_password_mobile">' . fxInscrGestionEsc($strLblPass) . '</label>';
echo '<input class="form-control form-control-sm rounded-0" type="password" id="txt_password_mobile" name="txt_password" required>';
echo '<button class="btn btn-primary btn-sm rounded-0 btn-block mt-3" type="submit" id="btn_login_mobile" name="btn_login">' . fxInscrGestionEsc($strBtn) . '</button>';
echo '</form></div></div>';
}
/** Libellé menu promoteur V2 (même texte que l'ancien + suffixe V2). */
function fxInscrGestionPromoteurMenuLabel() {
return afficheTexte('tableau_promoteur_menueinscription', 0) . ' V2';
}
function fxInscrGestionListBackLabel($strLangue) {
if (fxInscrGestionIsEntry()) {
return ($strLangue === 'fr') ? 'Événements' : 'Events';
}
return fxInscrGestionT('promoteur_back');
}
function fxInscrGestionShouldShowListBack($intComId) {
if (!fxInscrGestionIsEntry()) {
return true;
}
return count(fxInscrGestionGetPromoteurEveIds($intComId)) > 1;
}
function fxInscrGestionRenderNavBack($strUrl, $strLabel) {
echo '<a class="btn btn-primary rounded-pill inscr-gestion-nav-back" href="' . fxInscrGestionEsc($strUrl) . '">';
echo '<i class="fa fa-chevron-left mr-2" aria-hidden="true"></i>' . fxInscrGestionEsc($strLabel);
echo '</a>';
}
function fxInscrGestionRenderPageHead($strTitle, $strSub = '') {
echo '<div class="inscr-gestion-page-head">';
echo '<h1 class="inscr-gestion-page-title">' . fxInscrGestionEsc($strTitle) . '</h1>';
if (trim((string)$strSub) !== '') {
echo '<p class="inscr-gestion-page-sub text-muted mb-0">' . fxInscrGestionEsc($strSub) . '</p>';
}
echo '</div>';
}
function fxInscrGestionRenderSectionTitle($strLabel, $strIconClass) {
echo '<h2 class="inscr-gestion-fiche-section__title">';
echo '<i class="fa ' . fxInscrGestionEsc($strIconClass) . ' inscr-gestion-fiche-section__icon" aria-hidden="true"></i>';
echo '<span>' . fxInscrGestionEsc($strLabel) . '</span>';
echo '</h2>';
}
function fxInscrGestionRenderFicheSectionStart($strModifier, $strLabel, $strIconClass, $blnOpen = true) {
echo '<details class="inscr-gestion-fiche-section inscr-gestion-fiche-section--' . fxInscrGestionEsc($strModifier) . '"';
if ($blnOpen) {
echo ' open';
}
echo '>';
echo '<summary class="inscr-gestion-fiche-section__title">';
echo '<i class="fa ' . fxInscrGestionEsc($strIconClass) . ' inscr-gestion-fiche-section__icon" aria-hidden="true"></i>';
echo '<span>' . fxInscrGestionEsc($strLabel) . '</span>';
echo '</summary>';
echo '<div class="inscr-gestion-fiche-section__body">';
}
function fxInscrGestionRenderFicheSectionEnd() {
echo '</div></details>';
}
function fxInscrGestionRenderPanelSummary($strLabel, $strIconClass) {
echo '<i class="fa ' . fxInscrGestionEsc($strIconClass) . ' inscr-gestion-panel__icon" aria-hidden="true"></i>';
echo '<span>' . fxInscrGestionEsc($strLabel) . '</span>';
}
function fxInscrGestionParseEveId($strRaw) {
if ($strRaw === null || $strRaw === '') {
return 0;
}
$strDecoded = base64_decode(urldecode($strRaw), true);
if ($strDecoded === false || $strDecoded === '') {
return 0;
}
return (int)$strDecoded;
}
function fxInscrGestionParseRequest() {
$arr = array(
'rech_prenom' => isset($_GET['rech_prenom']) ? trim($_GET['rech_prenom']) : '',
'rech_nom' => isset($_GET['rech_nom']) ? trim($_GET['rech_nom']) : '',
'rech_no_commande' => isset($_GET['rech_no_commande']) ? trim($_GET['rech_no_commande']) : '',
'rech_nom_equipe' => isset($_GET['rech_nom_equipe']) ? trim($_GET['rech_nom_equipe']) : '',
'chk_bib' => !empty($_GET['chk_bib']) ? 1 : 0,
'chk_cancelled' => !empty($_GET['chk_cancelled']) ? 1 : 0,
'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,
// q = 1 lorsque l'utilisateur a lance une recherche/filtre. La liste reste vide tant que q = 0.
'q' => 0,
);
if (!empty($_GET['sel_rech_epreuve'])) {
$decoded = base64_decode(urldecode($_GET['sel_rech_epreuve']), true);
if ($decoded !== false && $decoded !== '') {
$arr['sel_rech_epreuve'] = intval($decoded);
} else {
$arr['sel_rech_epreuve'] = intval($_GET['sel_rech_epreuve']);
}
}
// Recherche "active" si : bouton Rechercher/Filtres soumis, marqueur q transporte par les liens,
// ou au moins un critere present (utile pour le retour depuis une fiche / la pagination).
$blnSubmit = !empty($_GET['btn_recherche']) || !empty($_GET['apply_filters']) || !empty($_GET['q']);
$blnCriteria = ($arr['rech_prenom'] !== '' || $arr['rech_nom'] !== '' || $arr['rech_no_commande'] !== ''
|| $arr['rech_nom_equipe'] !== '' || $arr['sel_rech_epreuve'] > 0 || $arr['chk_bib'] || $arr['chk_cancelled']);
$arr['q'] = ($blnSubmit || $blnCriteria) ? 1 : 0;
return $arr;
}
function fxInscrGestionFilterActiveCount($arrReq) {
$int = 0;
if ($arrReq['sel_rech_epreuve'] > 0) {
$int++;
}
if ($arrReq['chk_bib']) {
$int++;
}
if ($arrReq['chk_cancelled']) {
$int++;
}
return $int;
}
function fxInscrGestionEmptyListRequest() {
return array(
'rech_prenom' => '',
'rech_nom' => '',
'rech_no_commande' => '',
'rech_nom_equipe' => '',
'chk_bib' => 0,
'chk_cancelled' => 0,
'sel_rech_epreuve' => 0,
'pg' => 1,
'pec_id' => 0,
'q' => 0,
);
}
function fxInscrGestionSearchResetUrl($strBaseUrl, $intEveId) {
return fxInscrGestionBuildUrl($strBaseUrl, $intEveId, fxInscrGestionEmptyListRequest(), array('pg' => 1));
}
function fxInscrGestionBuildWhere($intEveId, $arrReq) {
global $objDatabase;
$strWhere = ' e.eve_id = ' . intval($intEveId);
if ($arrReq['rech_nom'] !== '') {
$strWhere .= ' AND p.par_nom LIKE "%' . $objDatabase->fxEscape($arrReq['rech_nom']) . '%"';
}
if ($arrReq['rech_prenom'] !== '') {
$strWhere .= ' AND p.par_prenom LIKE "%' . $objDatabase->fxEscape($arrReq['rech_prenom']) . '%"';
}
if ($arrReq['rech_no_commande'] !== '') {
$strEscCmd = $objDatabase->fxEscape($arrReq['rech_no_commande']);
$strWhere .= ' AND (e.no_commande LIKE "%' . $strEscCmd . '%" OR p.no_commande LIKE "%' . $strEscCmd . '%")';
}
if ($arrReq['rech_nom_equipe'] !== '') {
$strEscBibEquipe = $objDatabase->fxEscape($arrReq['rech_nom_equipe']);
$strWhere .= ' AND ('
. ' TRIM(p.no_bib) LIKE "%' . $strEscBibEquipe . '%"'
. ' OR TRIM(e.no_equipe) LIKE "%' . $strEscBibEquipe . '%"'
. ' OR e.pec_nom_equipe LIKE "%' . $strEscBibEquipe . '%"'
. ' OR p.par_nom_equipe LIKE "%' . $strEscBibEquipe . '%"'
. ')';
}
if ($arrReq['sel_rech_epreuve'] > 0) {
$strWhere .= ' AND p.epr_id = ' . intval($arrReq['sel_rech_epreuve']) . ' AND p.epr_id = e.epr_id';
}
if ($arrReq['chk_bib']) {
$strWhere .= " AND TRIM(p.no_bib) = ''";
}
if ($arrReq['chk_cancelled']) {
$strWhere .= ' AND e.is_cancelled = 1';
} else {
$strWhere .= ' AND e.is_cancelled = 0 AND e.pec_actif = 1';
}
return $strWhere;
}
function fxInscrGestionFetchRows($intEveId, $arrReq) {
global $objDatabase;
$strWhere = fxInscrGestionBuildWhere($intEveId, $arrReq);
$sql = 'SELECT p.no_bib, p.no_bib_remis, p.no_bib_remis_date, p.no_bib_remis_par, p.par_statut_course, p.par_id, p.par_equipe,'
. ' p.par_prenom, p.par_nom, e.pec_id_original, e.epr_id, e.eve_id, e.pec_id, e.is_cancelled, p.rol_id,'
. ' e.no_commande, e.no_panier, e.no_equipe, e.pec_equipe'
. ' FROM resultats_epreuves_commandees e, resultats_participants p'
. ' WHERE ' . $strWhere . ' AND p.pec_id = e.pec_id_original'
. ' GROUP BY e.pec_id_original'
. ' ORDER BY p.par_nom, p.par_prenom';
return $objDatabase->fxGetResults($sql);
}
function fxInscrGestionFetchRowByPec($intPecId, $intEveId) {
global $objDatabase;
$sql = 'SELECT p.no_bib, p.no_bib_remis, p.no_bib_remis_date, p.no_bib_remis_par, p.par_statut_course, p.par_id, p.par_equipe,'
. ' p.par_prenom, p.par_nom, e.pec_id_original, e.epr_id, e.eve_id, e.pec_id, e.is_cancelled, p.rol_id,'
. ' e.no_commande, e.no_panier, e.no_equipe, e.pec_equipe'
. ' FROM resultats_epreuves_commandees e, resultats_participants p'
. ' WHERE e.eve_id = ' . intval($intEveId)
. ' AND e.pec_id_original = ' . intval($intPecId)
. ' AND p.pec_id = e.pec_id_original'
. ' LIMIT 1';
return $objDatabase->fxGetRow($sql);
}
function fxInscrGestionEpreuveLabel($intEprId, $strLangue) {
$tab = fxGetEpreuve($intEprId);
if ($tab === null) {
return '';
}
$str = '';
if (trim($tab['epr_categorie_' . $strLangue]) !== '') {
$str .= $tab['epr_categorie_' . $strLangue] . ' — ';
}
$str .= $tab['epr_type_' . $strLangue];
if (trim($tab['epr_nom_' . $strLangue]) !== '') {
$str .= ' — ' . $tab['epr_nom_' . $strLangue];
}
return fxUnescape($str);
}
function fxInscrGestionBibDisplay($arrRow) {
if (intval($arrRow['pec_equipe']) === 1) {
return fxShowBibNumber($arrRow['no_equipe'], $arrRow['epr_id'], 'equipe');
}
return fxShowBibNumber($arrRow['no_bib'], $arrRow['epr_id'], 'participant');
}
function fxInscrGestionRenderKvListOpen() {
echo '<div class="inscr-gestion-kv-list">';
}
function fxInscrGestionRenderKvListClose() {
echo '</div>';
}
function fxInscrGestionRenderKvRow($strLabel, $strValue, $blnHtml = false, $strExtraClass = '') {
if (!$blnHtml && trim((string)$strValue) === '') {
return;
}
$strClass = 'inscr-gestion-kv-row';
if ($strExtraClass !== '') {
$strClass .= ' ' . $strExtraClass;
}
echo '<div class="' . fxInscrGestionEsc($strClass) . '">';
echo '<div class="inscr-gestion-kv-row__label">' . fxInscrGestionEsc($strLabel) . '</div>';
echo '<div class="inscr-gestion-kv-row__value">';
if ($blnHtml) {
echo $strValue;
} else {
echo fxInscrGestionEsc($strValue);
}
echo '</div></div>';
}
function fxInscrGestionFetchFicheProfile($arrRow, $strLangue) {
global $objDatabase;
$intParId = (int)$arrRow['par_id'];
$intPecId = (int)$arrRow['pec_id_original'];
$intEveId = (int)$arrRow['eve_id'];
$strLangCol = ($strLangue === 'en') ? 'en' : 'fr';
$tabProfile = $objDatabase->fxGetRow(
'SELECT p.par_courriel, p.par_naissance, p.par_sexe, p.par_id_original'
. ' FROM resultats_participants p'
. ' WHERE p.par_id = ' . $intParId
. ' LIMIT 1'
);
if ($tabProfile === null) {
return array(
'bln_capitaine' => ((int)$arrRow['rol_id'] === 1),
'email' => '',
'birth_gender' => '',
'questions' => array(),
);
}
$strEmail = trim((string)$tabProfile['par_courriel']);
$strBirthGender = '';
$strNaissance = trim((string)$tabProfile['par_naissance']);
if ($strNaissance !== '' && $strNaissance !== '0000-00-00') {
$strBirthGender = fxShowDate($strNaissance, $strLangue)
. ' - '
. fxGetGender($tabProfile['par_sexe'], $strLangue);
}
$intParIdOriginal = (int)$tabProfile['par_id_original'];
$sqlQuestions = 'SELECT rq.que_choix_' . $strLangCol . ' AS que_choix,'
. ' iq.que_cart_label_' . $strLangCol . ' AS que_label,'
. ' rq.que_question_' . $strLangCol . ' AS que_question,'
. ' iq.que_tri, iq.que_id'
. ' FROM resultats_questions rq'
. ' INNER JOIN inscriptions_questions iq ON iq.que_id = rq.que_id'
. ' WHERE iq.que_cart_show = 1'
. ' AND iq.eve_id = ' . $intEveId
. ' AND rq.pec_id = ' . $intPecId
. ' AND (rq.par_id = ' . $intParId . ' OR rq.par_id = ' . $intParIdOriginal . ')'
. ' AND rq.que_actif = 1'
. " AND TRIM(COALESCE(rq.que_choix_" . $strLangCol . ", '')) <> ''"
. ' ORDER BY iq.que_tri, iq.que_id';
$tabQuestionsRaw = $objDatabase->fxGetResults($sqlQuestions);
$tabQuestions = array();
if ($tabQuestionsRaw !== null) {
for ($i = 1; $i <= count($tabQuestionsRaw); $i++) {
$tabQ = $tabQuestionsRaw[$i];
$strLabel = trim(fxUnescape($tabQ['que_label']));
if ($strLabel === '') {
$strLabel = trim(fxUnescape($tabQ['que_question']));
}
$strValue = trim(fxUnescape($tabQ['que_choix']));
if ($strLabel === '' || $strValue === '') {
continue;
}
$tabQuestions[] = array(
'label' => $strLabel,
'value' => $strValue,
);
}
}
return array(
'bln_capitaine' => ((int)$arrRow['rol_id'] === 1),
'email' => $strEmail,
'birth_gender' => $strBirthGender,
'questions' => $tabQuestions,
);
}
function fxInscrGestionRenderKvRowFlag($strText, $strExtraClass = '') {
$strClass = 'inscr-gestion-kv-row inscr-gestion-kv-row--flag';
if ($strExtraClass !== '') {
$strClass .= ' ' . $strExtraClass;
}
echo '<div class="' . fxInscrGestionEsc($strClass) . '">';
echo '<span class="inscr-gestion-kv-flag">' . fxInscrGestionEsc($strText) . '</span>';
echo '</div>';
}
function fxInscrGestionRenderFicheProfileKvRows($arrRow, $strLangue) {
$tabProfile = fxInscrGestionFetchFicheProfile($arrRow, $strLangue);
$blnFirstProfile = true;
$strProfileSep = 'inscr-gestion-kv-row--profile';
if ($tabProfile['bln_capitaine']) {
$strCaptainLabel = trim(fxInscrGestionT('participant-titre-capitaine'));
if ($strCaptainLabel === '') {
$strCaptainLabel = ($strLangue === 'fr') ? 'Capitaine' : 'Captain';
}
fxInscrGestionRenderKvRowFlag(
$strCaptainLabel,
$blnFirstProfile ? $strProfileSep : ''
);
$blnFirstProfile = false;
}
if ($tabProfile['email'] !== '') {
fxInscrGestionRenderKvRow(
fxInscrGestionT('inscr_gestion_email'),
$tabProfile['email'],
false,
$blnFirstProfile ? 'inscr-gestion-kv-row--profile' : ''
);
$blnFirstProfile = false;
}
if ($tabProfile['birth_gender'] !== '') {
fxInscrGestionRenderKvRow(
fxInscrGestionT('inscr_gestion_birthdate'),
$tabProfile['birth_gender'],
false,
$blnFirstProfile ? 'inscr-gestion-kv-row--profile' : ''
);
$blnFirstProfile = false;
}
foreach ($tabProfile['questions'] as $tabQuestion) {
fxInscrGestionRenderKvRow(
$tabQuestion['label'],
$tabQuestion['value'],
false,
$blnFirstProfile ? 'inscr-gestion-kv-row--profile' : ''
);
$blnFirstProfile = false;
}
}
/** Code statut course par défaut (Confirmé). */
function fxInscrGestionParStatutCourseDefault() {
return 'CONF';
}
function fxInscrGestionResolveParStatutCourse($strCurrent) {
$strCurrent = trim((string)$strCurrent);
return $strCurrent !== '' ? $strCurrent : fxInscrGestionParStatutCourseDefault();
}
/** Options statut course depuis info (info_description = opt). */
function fxInscrGestionGetStatutCourseOptions($strLangue) {
global $objDatabase;
static $tabCache = array();
if (isset($tabCache[$strLangue])) {
return $tabCache[$strLangue];
}
$sql = "SELECT info_texte, info_option2 FROM info"
. " WHERE info_actif = 1 AND info_clef = 'inscr_statut_course'"
. " AND info_langue = '" . $objDatabase->fxEscape($strLangue) . "'"
. " AND info_description = 'opt'"
. " ORDER BY info_trie";
$tabRows = $objDatabase->fxGetResults($sql);
$tabCache[$strLangue] = ($tabRows !== null) ? $tabRows : array();
return $tabCache[$strLangue];
}
function fxInscrGestionRenderStatutField($intParId, $strCurrent, $intEveId, $strLangue) {
$tabOptions = fxInscrGestionGetStatutCourseOptions($strLangue);
if (count($tabOptions) === 0) {
echo '<div class="inscr-gestion-muted">' . fxInscrGestionEsc(fxInscrGestionT('inscr_gestion_statut_label')) . '</div>';
return;
}
$strCurrent = fxInscrGestionResolveParStatutCourse($strCurrent);
$strSelectId = 'inscr_statut_' . (int)$intParId;
echo '<select class="form-control rounded-0 inscr-gestion-statut-select" id="' . fxInscrGestionEsc($strSelectId) . '"'
. ' data-par_id="' . (int)$intParId . '"'
. ' data-eve_id="' . rawurlencode(base64_encode((string)$intEveId)) . '">';
foreach ($tabOptions as $arrOpt) {
$strCode = trim((string)$arrOpt['info_option2']);
$strLabel = trim((string)$arrOpt['info_texte']);
if ($strCode === '') {
continue;
}
echo '<option value="' . fxInscrGestionEsc($strCode) . '"'
. ($strCurrent === $strCode ? ' selected' : '') . '>'
. fxInscrGestionEsc($strLabel) . '</option>';
}
echo '</select>';
}
/** Paramètres GET pour liens (sans double-encodage). */
function fxInscrGestionQueryParams($intEveId, $arrReq, $arrExtra = array()) {
$tab = array(
'promoteur_eve_id' => base64_encode((string)$intEveId),
);
if ($arrReq['rech_prenom'] !== '') {
$tab['rech_prenom'] = $arrReq['rech_prenom'];
}
if ($arrReq['rech_nom'] !== '') {
$tab['rech_nom'] = $arrReq['rech_nom'];
}
if ($arrReq['rech_no_commande'] !== '') {
$tab['rech_no_commande'] = $arrReq['rech_no_commande'];
}
if ($arrReq['rech_nom_equipe'] !== '') {
$tab['rech_nom_equipe'] = $arrReq['rech_nom_equipe'];
}
if ($arrReq['chk_bib']) {
$tab['chk_bib'] = 1;
}
if ($arrReq['chk_cancelled']) {
$tab['chk_cancelled'] = 1;
}
if ($arrReq['sel_rech_epreuve'] > 0) {
$tab['sel_rech_epreuve'] = base64_encode((string)$arrReq['sel_rech_epreuve']);
}
if (!empty($_GET['lng'])) {
$tab['lng'] = $_GET['lng'];
}
if (!empty($arrReq['q'])) {
// Transporte l'etat "recherche lancee" pour que la pagination et le retour de fiche gardent la liste.
$tab['q'] = 1;
}
if (!empty($arrReq['pg']) && (int)$arrReq['pg'] > 1) {
$tab['pg'] = (int)$arrReq['pg'];
}
foreach ($arrExtra as $strKey => $mixVal) {
if ($mixVal === null || $mixVal === '' || ($strKey === 'pec_id' && (int)$mixVal === 0)) {
unset($tab[$strKey]);
} else {
$tab[$strKey] = $mixVal;
}
}
return $tab;
}
function fxInscrGestionBuildUrl($strBaseUrl, $intEveId, $arrReq, $arrExtra = array()) {
$tab = fxInscrGestionQueryParams($intEveId, $arrReq, $arrExtra);
$strQs = http_build_query($tab, '', '&');
return $strBaseUrl . ($strQs !== '' ? '?' . $strQs : '');
}
function fxInscrGestionPaginationUrl($strBaseUrl, $intEveId, $arrReq) {
$tab = fxInscrGestionQueryParams($intEveId, $arrReq, array('pec_id' => null));
unset($tab['pg'], $tab['pec_id']);
$strQs = http_build_query($tab, '', '&amp;');
return $strBaseUrl . ($strQs !== '' ? '?' . $strQs : '?promoteur_eve_id=' . rawurlencode(base64_encode((string)$intEveId)));
}
function fxInscrGestionRenderPagination($strBaseUrl, $intEveId, $arrReq, $intTotal, $intPerPage, $intPage) {
if ($intTotal <= $intPerPage) {
return '';
}
return '<div class="inscr-gestion-pager">'
. fxGetPagination(
fxInscrGestionPaginationUrl($strBaseUrl, $intEveId, $arrReq),
$intTotal,
$intPerPage,
$intPage
)
. '</div>';
}
function fxInscrGestionExtractQrToken($strRaw) {
$strRaw = trim((string)$strRaw);
if ($strRaw === '') {
return '';
}
if (preg_match('/[?&]t=([^&\s#]+)/', $strRaw, $tabMatch)) {
return urldecode($tabMatch[1]);
}
if (preg_match('/^https?:\/\//i', $strRaw)) {
$tabParts = parse_url($strRaw);
if (!empty($tabParts['query'])) {
parse_str($tabParts['query'], $tabQuery);
if (!empty($tabQuery['t'])) {
return trim((string)$tabQuery['t']);
}
}
}
if (preg_match('/^[a-zA-Z0-9_-]{6,64}$/', $strRaw)) {
return $strRaw;
}
return '';
}
function fxInscrGestionResolveQrScan($strRaw, $intEveId, $strLangue) {
global $objDatabase;
$strToken = fxInscrGestionExtractQrToken($strRaw);
if ($strToken === '') {
return array('state' => 'error', 'message' => fxInscrGestionT('inscr_gestion_qr_invalid'));
}
$tabToken = $objDatabase->fxGetRow(
'SELECT qr_public_id, pec_id_original, eve_id'
. ' FROM qr_tokens'
. " WHERE qr_public_id = '" . $objDatabase->fxEscape($strToken) . "'"
. ' LIMIT 1'
);
if ($tabToken === null) {
return array('state' => 'error', 'message' => fxInscrGestionT('inscr_gestion_qr_invalid'));
}
$intPecId = (int)$tabToken['pec_id_original'];
if ($intPecId <= 0) {
return array('state' => 'error', 'message' => fxInscrGestionT('inscr_gestion_qr_invalid'));
}
$tabPec = $objDatabase->fxGetRow(
'SELECT ec.pec_id_original'
. ' FROM resultats_epreuves_commandees ec'
. ' WHERE ec.pec_id_original = ' . $intPecId
. ' AND ec.eve_id = ' . intval($intEveId)
. ' AND ec.pec_actif = 1'
. ' LIMIT 1'
);
if ($tabPec !== null) {
$arrRow = fxInscrGestionFetchRowByPec($intPecId, $intEveId);
if ($arrRow === null) {
return array('state' => 'error', 'message' => fxInscrGestionT('inscr_gestion_qr_not_found'));
}
return array(
'state' => 'success',
'pec_id' => $intPecId,
);
}
$intQrEveId = (int)$tabToken['eve_id'];
if ($intQrEveId <= 0) {
$tabOther = $objDatabase->fxGetRow(
'SELECT ec.eve_id FROM resultats_epreuves_commandees ec'
. ' WHERE ec.pec_id_original = ' . $intPecId
. ' AND ec.pec_actif = 1'
. ' ORDER BY ec.pec_id_original DESC LIMIT 1'
);
if ($tabOther !== null) {
$intQrEveId = (int)$tabOther['eve_id'];
}
}
$strEveNom = '';
if ($intQrEveId > 0) {
$arrEv = fxGetEvenementsId($intQrEveId);
if (!empty($arrEv)) {
$strEveNom = fxUnescape($arrEv['eve_nom_' . $strLangue]);
}
}
if ($strEveNom === '') {
$strEveNom = '#' . $intQrEveId;
}
return array(
'state' => 'wrong_event',
'message' => str_replace('%s', $strEveNom, fxInscrGestionT('inscr_gestion_qr_wrong_event')),
'eve_nom' => $strEveNom,
);
}
function fxInscrGestionRenderEventPicker($tabEveIds, $strLangue, $strBaseUrl) {
global $objDatabase;
$strTitle = ($strLangue === 'fr') ? 'Choisir un événement' : 'Choose an event';
echo '<div id="module-inscr-gestion" class="inscr-gestion-shell">';
fxInscrGestionRenderPageHead($strTitle);
echo '<div class="inscr-gestion-event-list">';
foreach ($tabEveIds as $intEveId) {
$qry = 'SELECT e.* FROM inscriptions_evenements e WHERE e.eve_actif = 1 AND e.eve_id = ' . intval($intEveId);
$tabEv = $objDatabase->fxGetRow($qry);
if ($tabEv === null) {
continue;
}
$strUrl = fxInscrGestionBuildUrl($strBaseUrl, $intEveId, fxInscrGestionParseRequest(), array());
echo '<a class="inscr-gestion-event-item" href="' . fxInscrGestionEsc($strUrl) . '">';
echo '<span class="inscr-gestion-event-item__name">' . fxInscrGestionEsc(fxUnescape($tabEv['eve_nom_' . $strLangue])) . '</span>';
echo '<i class="fa fa-chevron-right" aria-hidden="true"></i>';
echo '</a>';
}
echo '</div></div>';
}
function fxInscrGestionRenderQrPanel($intEveId, $strLangue) {
global $vDomaine;
$intComId = intval($_SESSION['com_id'] ?? 0);
if (!fxInscrGestionCanScan($intComId, $intEveId)) {
return;
}
$strQrAjaxUrl = $vDomaine . '/ajax_inscr_gestion_qr.php';
echo '<details class="inscr-gestion-panel inscr-gestion-panel--qr" id="inscr-gestion-qr-panel"';
echo ' data-qr-resolve-url="' . fxInscrGestionEsc($strQrAjaxUrl) . '"';
echo ' data-qr-eve-id="' . fxInscrGestionEsc(base64_encode((string)$intEveId)) . '"';
echo ' data-qr-lang="' . fxInscrGestionEsc($strLangue) . '"';
echo ' data-qr-msg-invalid="' . fxInscrGestionEsc(fxInscrGestionT('inscr_gestion_qr_invalid')) . '"';
echo ' data-qr-msg-wrong="' . fxInscrGestionEsc(fxInscrGestionT('inscr_gestion_qr_wrong_event')) . '"';
echo ' data-qr-msg-camera="' . fxInscrGestionEsc(fxInscrGestionT('inscr_gestion_qr_camera_error')) . '">';
echo '<summary class="inscr-gestion-panel__head">';
fxInscrGestionRenderPanelSummary(fxInscrGestionT('inscr_gestion_qr_title'), 'fa-qrcode');
echo '</summary>';
echo '<div class="inscr-gestion-panel__body">';
echo '<p class="inscr-gestion-qr-hint text-muted small mb-2">' . fxInscrGestionEsc(fxInscrGestionT('inscr_gestion_qr_hint')) . '</p>';
echo '<div id="inscr-gestion-qr-feedback" class="alert d-none mb-2" role="alert"></div>';
echo '<div id="inscr-gestion-qr-reader-wrap" class="inscr-gestion-qr-reader-wrap">';
echo '<div id="inscr-gestion-qr-reader"></div>';
echo '</div></div></details>';
}
function fxInscrGestionRenderBibScanBlock($strBibInputId, $strLangue) {
echo '<div class="inscr-gestion-bib-scan-panel" hidden';
echo ' data-bib-input-id="' . fxInscrGestionEsc($strBibInputId) . '"';
echo ' data-msg-scanning="' . fxInscrGestionEsc(fxInscrGestionT('inscr_gestion_bib_scan_scanning')) . '"';
echo ' data-msg-loading="' . fxInscrGestionEsc(fxInscrGestionT('inscr_gestion_bib_scan_loading')) . '"';
echo ' data-msg-ready="' . fxInscrGestionEsc(fxInscrGestionT('inscr_gestion_bib_scan_ready')) . '"';
echo ' data-msg-found="' . fxInscrGestionEsc(fxInscrGestionT('inscr_gestion_bib_scan_found')) . '"';
echo ' data-msg-invalid="' . fxInscrGestionEsc(fxInscrGestionT('inscr_gestion_bib_scan_invalid')) . '"';
echo ' data-msg-camera="' . fxInscrGestionEsc(fxInscrGestionT('inscr_gestion_qr_camera_error')) . '">';
echo '<p class="inscr-gestion-bib-scan-hint text-muted small mb-1">' . fxInscrGestionEsc(fxInscrGestionT('inscr_gestion_bib_scan_hint')) . '</p>';
echo '<div class="inscr-gestion-bib-scan-status small mb-1" aria-live="polite"></div>';
echo '<div class="inscr-gestion-bib-scan-capture" role="button" tabindex="0">';
echo '<div class="inscr-gestion-bib-scan-video"></div>';
echo '<span class="inscr-gestion-bib-scan-capture-label">' . fxInscrGestionEsc(fxInscrGestionT('inscr_gestion_bib_scan_tap')) . '</span>';
echo '</div>';
echo '</div>';
}
function fxInscrGestionRenderList($intEveId, $strLangue, $strBaseUrl, $arrReq) {
global $vDomaine;
$arrEvenement = fxGetEvenementsId($intEveId);
$strEveNom = fxUnescape($arrEvenement['eve_nom_' . $strLangue]);
$intFilterCount = fxInscrGestionFilterActiveCount($arrReq);
// La page arrive vide : on n'affiche les resultats qu'apres une recherche (meme vide = tout afficher).
$blnSearchActive = !empty($arrReq['q']);
$strBackUrl = fxInscrGestionIsEntry()
? $strBaseUrl
: ($vDomaine . ($strLangue === 'fr' ? '/compte/inc_tableau_promoteur' : '/account/inc_tableau_promoteur')
. '?promoteur_eve_id=' . rawurlencode(base64_encode((string)$intEveId)));
echo '<div id="module-inscr-gestion" class="inscr-gestion-shell" data-inscr-gestion="1">';
if (fxInscrGestionShouldShowListBack((int)$_SESSION['com_id'])) {
fxInscrGestionRenderNavBack($strBackUrl, fxInscrGestionListBackLabel($strLangue));
}
fxInscrGestionRenderPageHead(fxInscrGestionT('inscr_gestion_title'), $strEveNom);
fxInscrGestionRenderQrPanel($intEveId, $strLangue);
// Recherche (repliable — ouverte par défaut tant qu'aucune recherche n'a été lancée, car la liste arrive vide)
echo '<details class="inscr-gestion-panel"' . ($blnSearchActive ? '' : ' open') . '>';
echo '<summary class="inscr-gestion-panel__head">';
fxInscrGestionRenderPanelSummary(fxInscrGestionT('txt_recherche'), 'fa-search');
echo '</summary>';
echo '<div class="inscr-gestion-panel__body">';
echo '<form action="' . fxInscrGestionEsc($strBaseUrl) . '" method="get" class="inscr-gestion-form">';
echo '<input type="hidden" name="promoteur_eve_id" value="' . fxInscrGestionEsc(base64_encode((string)$intEveId)) . '">';
$arrFields = array(
'rech_prenom' => 'rech_prenom',
'rech_nom' => 'rech_nom',
'rech_no_commande' => 'rech_no_commande',
'rech_nom_equipe' => 'inscr_gestion_rech_bib_equipe',
);
foreach ($arrFields as $strKey => $strLbl) {
echo '<label class="inscr-gestion-label" for="' . $strKey . '">' . fxInscrGestionEsc(fxInscrGestionT($strLbl)) . '</label>';
echo '<input class="form-control form-control-sm rounded-0" type="text" id="' . $strKey . '" name="' . $strKey . '" value="' . fxInscrGestionEsc($arrReq[$strKey]) . '">';
}
if ($arrReq['sel_rech_epreuve'] > 0) {
echo '<input type="hidden" name="sel_rech_epreuve" value="' . fxInscrGestionEsc(base64_encode((string)$arrReq['sel_rech_epreuve'])) . '">';
}
if ($arrReq['chk_bib']) {
echo '<input type="hidden" name="chk_bib" value="1">';
}
if ($arrReq['chk_cancelled']) {
echo '<input type="hidden" name="chk_cancelled" value="1">';
}
echo '<div class="inscr-gestion-form-actions mt-2">';
echo '<a class="btn btn-secondary btn-sm rounded-0" href="' . fxInscrGestionEsc(fxInscrGestionSearchResetUrl($strBaseUrl, $intEveId)) . '">'
. fxInscrGestionEsc(fxInscrGestionT('inscr_gestion_reset_search')) . '</a>';
echo '<button class="btn btn-primary btn-sm rounded-0" type="submit" name="btn_recherche" value="1">' . fxInscrGestionEsc(fxInscrGestionT('txt_recherche')) . '</button>';
echo '</div></form></div></details>';
// Filtres
global $objDatabase;
$tabEpreuves = $objDatabase->fxGetResults(
'SELECT * FROM inscriptions_epreuves e WHERE e.eve_id = ' . intval($intEveId) . ' AND epr_actif = 1 ORDER BY epr_id'
);
$strFilterLabel = fxInscrGestionT('inscr_gestion_filters');
if ($intFilterCount > 0) {
$strFilterLabel .= ' (' . $intFilterCount . ')';
}
echo '<details class="inscr-gestion-panel">';
echo '<summary class="inscr-gestion-panel__head">';
fxInscrGestionRenderPanelSummary($strFilterLabel, 'fa-filter');
echo '</summary>';
echo '<div class="inscr-gestion-panel__body">';
echo '<form action="' . fxInscrGestionEsc($strBaseUrl) . '" method="get" class="inscr-gestion-form">';
echo '<input type="hidden" name="promoteur_eve_id" value="' . fxInscrGestionEsc(base64_encode((string)$intEveId)) . '">';
foreach (array('rech_prenom', 'rech_nom', 'rech_no_commande', 'rech_nom_equipe') as $strKey) {
if ($arrReq[$strKey] !== '') {
echo '<input type="hidden" name="' . $strKey . '" value="' . fxInscrGestionEsc($arrReq[$strKey]) . '">';
}
}
echo '<label class="inscr-gestion-label" for="sel_rech_epreuve">' . fxInscrGestionEsc(fxInscrGestionT('sel_rech_epreuve')) . '</label>';
echo '<select class="form-control form-control-sm rounded-0" id="sel_rech_epreuve" name="sel_rech_epreuve">';
echo '<option value="">—</option>';
for ($i = 1; $i <= count($tabEpreuves); $i++) {
$strOpt = fxInscrGestionEpreuveLabel($tabEpreuves[$i]['epr_id'], $strLangue);
$strSel = ($arrReq['sel_rech_epreuve'] == $tabEpreuves[$i]['epr_id']) ? ' selected' : '';
$strVal = base64_encode((string)$tabEpreuves[$i]['epr_id']);
echo '<option value="' . fxInscrGestionEsc($strVal) . '"' . $strSel . '>' . fxInscrGestionEsc($strOpt) . '</option>';
}
echo '</select>';
echo '<label class="inscr-gestion-check mt-2"><input type="checkbox" name="chk_bib" value="1"' . ($arrReq['chk_bib'] ? ' checked' : '') . '> ' . fxInscrGestionEsc(fxInscrGestionT('chk_bib')) . '</label>';
echo '<label class="inscr-gestion-check"><input type="checkbox" name="chk_cancelled" value="1"' . ($arrReq['chk_cancelled'] ? ' checked' : '') . '> ' . fxInscrGestionEsc(fxInscrGestionT('chk_cancelled')) . '</label>';
echo '<div class="inscr-gestion-form-actions mt-2">';
echo '<a class="btn btn-secondary btn-sm rounded-0" href="' . fxInscrGestionEsc(fxInscrGestionSearchResetUrl($strBaseUrl, $intEveId)) . '">'
. fxInscrGestionEsc(fxInscrGestionT('inscr_gestion_reset_search')) . '</a>';
echo '<button class="btn btn-primary btn-sm rounded-0" type="submit" name="apply_filters" value="1">' . fxInscrGestionEsc(fxInscrGestionT('inscr_gestion_apply_filters')) . '</button>';
echo '</div></form></div></details>';
echo '<div id="inscr-gestion-list" class="inscr-gestion-list-anchor">';
// Tant qu'aucune recherche n'a été lancée, on n'affiche aucune inscription : seulement une invite.
if (!$blnSearchActive) {
echo '<div class="alert alert-light border inscr-gestion-empty">'
. fxInscrGestionEsc(fxInscrGestionT('inscr_gestion_search_prompt')) . '</div>';
echo '</div>';
return;
}
// Résultats (la recherche a été lancée — une recherche vide affiche toutes les inscriptions).
$tabRows = fxInscrGestionFetchRows($intEveId, $arrReq);
$intNbItemsParPage = 50;
$intNbTotal = ($tabRows !== null) ? count($tabRows) : 0;
$intPage = 1;
$intStart = 1;
$intEnd = $intNbItemsParPage;
if (!empty($_GET['pg'])) {
$intPage = max(1, intval($_GET['pg']));
}
if (intval($intPage) > 1) {
$intStart = ($intPage - 1) * $intNbItemsParPage + 1;
$intEnd = $intStart + $intNbItemsParPage - 1;
}
if ($intEnd > $intNbTotal) {
$intEnd = $intNbTotal;
}
echo '<div class="inscr-gestion-list-head">';
if ($strLangue === 'fr') {
echo '<span>' . (int)$intNbTotal . ' inscription' . ($intNbTotal > 1 ? 's' : '') . '</span>';
} else {
echo '<span>' . (int)$intNbTotal . ' registration' . ($intNbTotal !== 1 ? 's' : '') . '</span>';
}
if ($intNbTotal > $intNbItemsParPage) {
$intNbPages = (int)ceil($intNbTotal / $intNbItemsParPage);
echo '<span class="inscr-gestion-list-head__pg">' . ($strLangue === 'fr' ? 'Page ' : 'Page ') . $intPage . ' / ' . $intNbPages . '</span>';
}
echo '</div>';
if ($intNbTotal > 0) {
echo '<div class="inscr-gestion-list">';
for ($j = $intStart; $j <= $intEnd; $j++) {
$row = $tabRows[$j];
$strFicheUrl = fxInscrGestionBuildUrl($strBaseUrl, $intEveId, $arrReq, array(
'pec_id' => (int)$row['pec_id_original'],
'pg' => $intPage,
));
$strBib = fxInscrGestionBibDisplay($row);
$strName = fxUnescape($row['par_nom']) . ', ' . fxUnescape($row['par_prenom']);
$strRace = fxInscrGestionEpreuveLabel($row['epr_id'], $strLangue);
$strCardClass = 'inscr-gestion-list-item';
if ($row['is_cancelled'] == '1') {
$strCardClass .= ' inscr-gestion-list-item--cancelled';
}
echo '<a class="' . $strCardClass . '" href="' . fxInscrGestionEsc($strFicheUrl) . '">';
echo '<span class="inscr-gestion-list-item__name">' . fxInscrGestionEsc($strName) . '</span>';
echo '<span class="inscr-gestion-list-item__race">' . fxInscrGestionEsc($strRace) . '</span>';
echo '<span class="inscr-gestion-list-item__order">' . fxInscrGestionEsc($row['no_commande']) . '</span>';
echo '<span class="inscr-gestion-list-item__bib">';
echo trim($strBib) !== '' ? fxInscrGestionEsc($strBib) : '—';
echo '</span>';
echo '<span class="inscr-gestion-list-item__check">';
if ($row['no_bib_remis'] == 1) {
echo '<span class="inscr-gestion-badge inscr-gestion-badge--ok">' . fxInscrGestionEsc(fxInscrGestionT('promoteur_bib_enregistre')) . '</span>';
}
echo '</span>';
echo '<i class="fa fa-chevron-right inscr-gestion-list-item__arrow" aria-hidden="true"></i>';
echo '</a>';
}
echo '</div>';
echo fxInscrGestionRenderPagination($strBaseUrl, $intEveId, $arrReq, $intNbTotal, $intNbItemsParPage, $intPage);
} else {
echo '<div class="alert alert-light border inscr-gestion-empty">' . fxInscrGestionEsc(fxInscrGestionT('txt_recherche_no_result')) . '</div>';
}
echo '</div>';
}
function fxInscrGestionRenderFiche($intEveId, $strLangue, $strBaseUrl, $arrReq, $arrRow) {
global $vDomaine;
$intComId = intval($_SESSION['com_id'] ?? 0);
$intRowEveId = intval($arrRow['eve_id']);
$blnCanStatut = fxInscrGestionCanDo($intComId, $intRowEveId, 'inscriptions_gestion.statut_edit');
$blnCanBib = fxInscrGestionCanDo($intComId, $intRowEveId, 'inscriptions_gestion.bib_edit');
$blnCanRemis = fxInscrGestionCanDo($intComId, $intRowEveId, 'inscriptions_gestion.bib_remis');
$blnCanEdit = fxInscrGestionCanDo($intComId, $intRowEveId, 'inscriptions_gestion.edit');
$blnCanCancel = fxInscrGestionCanDo($intComId, $intRowEveId, 'inscriptions_gestion.cancel');
$blnCanRestore = fxInscrGestionCanDo($intComId, $intRowEveId, 'inscriptions_gestion.restore');
// Droit de remboursement PayPal (etape remboursement) : controle l'affichage du bouton + panneau.
$blnCanRefund = fxInscrGestionCanDo($intComId, $intRowEveId, 'inscriptions_gestion.refund');
$blnHasEditable = ($blnCanStatut || $blnCanBib || $blnCanRemis);
$blnHasGestion = ($blnCanEdit || $blnCanCancel || $blnCanRestore || $blnCanRefund);
$strListUrl = fxInscrGestionBuildUrl($strBaseUrl, $intEveId, $arrReq, array('pec_id' => null, 'pg' => $arrReq['pg']));
$strEveCode = fxGetEvenementsUrl($arrRow['eve_id']);
$strBib = fxInscrGestionBibDisplay($arrRow);
$intParId = $arrRow['par_id'];
$blnCancelled = ($arrRow['is_cancelled'] == '1');
$blnEquipe = (intval($arrRow['pec_equipe']) === 1);
$strBibAction = $blnEquipe ? 'no_equipe' : 'no_bib';
$strBibInputId = $blnEquipe ? 'txt_no_bib_e' : 'txt_no_bib_p';
if ($arrRow['no_bib_remis'] == 1) {
$strRemisClass = 'btn-success';
$strRemisIcon = '<i class="fa fa-check" aria-hidden="true"></i>';
$intRemisVal = 0;
} else {
$strRemisClass = 'btn-primary';
$strRemisIcon = '<i class="fa fa-sharp fa-regular fa-square fa-2xs" aria-hidden="true"></i>';
$intRemisVal = 1;
}
echo '<div id="module-inscr-gestion" class="inscr-gestion-shell inscr-gestion-fiche" data-inscr-gestion="1"';
$blnFromQr = !empty($_GET['from_qr']);
if ($blnFromQr) {
echo ' data-from-qr="1"';
}
echo '>';
fxInscrGestionRenderNavBack($strListUrl, fxInscrGestionT('inscr_gestion_back_list'));
echo '<div class="inscr-gestion-fiche-head">';
echo '<h1 class="inscr-gestion-page-title">' . fxInscrGestionEsc(fxUnescape($arrRow['par_nom']) . ', ' . fxUnescape($arrRow['par_prenom'])) . '</h1>';
echo '</div>';
$strDetailsType = $blnEquipe && fxIsParEquipe($arrRow['pec_id_original'], $arrRow['epr_id'])
? fxInscrGestionT('promoteur_bib_show_teamates')
: fxInscrGestionT('epreuve_individuelle');
fxInscrGestionRenderFicheSectionStart('details', fxInscrGestionT('inscr_gestion_details'), 'fa-info-circle', !$blnFromQr);
fxInscrGestionRenderKvListOpen();
fxInscrGestionRenderKvRow(fxInscrGestionT('inscr_gestion_epreuve'), fxInscrGestionEpreuveLabel($arrRow['epr_id'], $strLangue));
fxInscrGestionRenderKvRow(fxInscrGestionT('inscr_gestion_type'), $strDetailsType);
fxInscrGestionRenderKvRow(fxInscrGestionT('inscr_gestion_modified'), fxCheckMAJ($arrRow['pec_id_original'], $intParId, $strLangue), true);
fxInscrGestionRenderFicheProfileKvRows($arrRow, $strLangue);
fxInscrGestionRenderKvListClose();
fxInscrGestionRenderFicheSectionEnd();
$strStyleEdit = $strStyleCancel = '';
$strStyleRetablir = ' d-none';
if ($blnCancelled) {
$strStyleEdit = $strStyleCancel = ' d-none';
$strStyleRetablir = '';
}
// Libelle du bouton de renvoi (facture/invitation) : passe par la table info pour etre editable.
$strRenvoi = fxInscrGestionT('inscr_gestion_renvoi');
if ($blnHasGestion) {
fxInscrGestionRenderFicheSectionStart('gestion', fxInscrGestionT('inscr_gestion_section_management'), 'fa-cog', !$blnFromQr);
echo '<div class="inscr-gestion-gestion-actions">';
// Libelle devant le bouton facture (numero de commande) — invite a afficher la facture.
echo '<span class="inscr-gestion-facture-label">' . fxInscrGestionEsc(fxInscrGestionT('inscr_gestion_show_invoice')) . '</span>';
echo '<a class="btn btn-primary btn-sm rounded-pill" href="' . $vDomaine . '/facture.php?no_panier=' . rawurlencode($arrRow['no_panier']) . '&amp;lang=' . $strLangue . '" target="_blank">' . fxInscrGestionEsc($arrRow['no_commande']) . '</a>';
echo '<button class="btn btn-primary btn-sm rounded-0 link_confirmation" type="button"'
. ' data-no_panier="' . fxInscrGestionEsc($arrRow['no_panier']) . '"'
. ' data-no_commande="' . fxInscrGestionEsc($arrRow['no_commande']) . '"'
. ' data-lng="' . $strLangue . '">' . fxInscrGestionEsc($strRenvoi) . '</button>';
if ($blnCanEdit) {
echo '<button class="btn btn-primary btn-sm rounded-0 btn_edit_event_promo' . $strStyleEdit . '" type="button"'
. ' id="modifier_fiche" data-eve_code="' . fxInscrGestionEsc($strEveCode) . '"'
. ' data-epr_id="' . (int)$arrRow['epr_id'] . '" data-eve_id="' . (int)$arrRow['eve_id'] . '"'
. ' value="' . (int)$arrRow['pec_id_original'] . '">' . fxInscrGestionEsc(fxInscrGestionT('inscr_gestion_modifier_participant')) . '</button>';
}
if ($blnCanRefund) {
// Bouton bascule : ouvre/ferme le panneau de remboursement et declenche le chargement AJAX du formulaire.
echo '<button class="btn btn-warning btn-sm rounded-0 btn_refund_toggle" type="button"'
. ' data-inscr-field="remboursement"'
. ' data-no_panier="' . fxInscrGestionEsc($arrRow['no_panier']) . '"'
. ' data-refund-target="#inscr-gestion-refund-panel"'
. ' aria-expanded="false">' . fxInscrGestionEsc(fxInscrGestionT('inscr_gestion_remboursement')) . '</button>';
}
if ($blnCanCancel) {
echo '<button class="btn btn-secondary btn-sm rounded-0 btn_cancel_event_promo' . $strStyleCancel . '" type="button"'
. ' id="annuler_fiche" data-eve_code="' . fxInscrGestionEsc($strEveCode) . '"'
. ' data-epr_id="' . (int)$arrRow['epr_id'] . '" data-eve_id="' . (int)$arrRow['eve_id'] . '"'
. ' value="' . (int)$arrRow['pec_id_original'] . '">' . fxInscrGestionEsc(fxInscrGestionT('btn_annulerinscriptions')) . '</button>';
}
if ($blnCanRestore) {
echo '<button class="btn btn-secondary btn-sm rounded-0 btn_retablir_event_promo' . $strStyleRetablir . '" type="button"'
. ' id="retablir_fiche" data-eve_code="' . fxInscrGestionEsc($strEveCode) . '"'
. ' data-epr_id="' . (int)$arrRow['epr_id'] . '" data-eve_id="' . (int)$arrRow['eve_id'] . '"'
. ' value="' . (int)$arrRow['pec_id_original'] . '">' . fxInscrGestionEsc(fxInscrGestionT('btn_retablirinscriptions')) . '</button>';
}
echo '</div>';
if ($blnCanRefund) {
// Panneau de remboursement : vide au depart, rempli en AJAX a la 1ere ouverture (action=refund_form).
// data-loaded evite de recharger inutilement ; data-no_panier identifie la commande cote serveur.
echo '<div class="inscr-gestion-refund-panel" id="inscr-gestion-refund-panel" style="margin-top:10px;"'
. ' data-no_panier="' . fxInscrGestionEsc($arrRow['no_panier']) . '"'
. ' data-loaded="0" hidden></div>';
}
fxInscrGestionRenderFicheSectionEnd();
}
if ($blnHasEditable) {
fxInscrGestionRenderFicheSectionStart('editable', fxInscrGestionT('inscr_gestion_section_editable'), 'fa-pencil-square-o', true);
if ($blnCanBib) {
$strFullBibInputId = $strBibInputId . $intParId;
$blnCanBibScan = fxInscrGestionCanScan($intComId, $intEveId);
echo '<div class="inscr-gestion-field-block" data-inscr-field="bib">';
echo '<div class="inscr-gestion-field-block__label">' . fxInscrGestionEsc(fxInscrGestionT('promoteur_bib_number')) . '</div>';
if ($blnCanBibScan) {
fxInscrGestionRenderBibScanBlock($strFullBibInputId, $strLangue);
}
echo '<div class="inscr-gestion-bib-row">';
echo '<input class="form-control rounded-0" type="text" id="' . $strFullBibInputId . '" value="' . fxInscrGestionEsc($strBib) . '">';
if ($blnCanBibScan) {
echo '<button class="btn btn-secondary rounded-0 inscr-gestion-bib-scan-toggle" type="button" aria-expanded="false"'
. ' title="' . fxInscrGestionEsc(fxInscrGestionT('inscr_gestion_bib_scan_btn')) . '"'
. ' aria-label="' . fxInscrGestionEsc(fxInscrGestionT('inscr_gestion_bib_scan_btn')) . '">';
echo '<i class="fa fa-camera" aria-hidden="true"></i>';
echo '</button>';
}
echo '<button class="btn btn-primary rounded-0 btn_bib" type="button" data-action="' . $strBibAction . '"'
. ' data-eve_id="' . rawurlencode(base64_encode((string)$arrRow['eve_id'])) . '"'
. ' data-epr_id="' . (int)$arrRow['epr_id'] . '"'
. ' data-par_id="' . (int)$intParId . '"'
. ' data-pec_id="' . (int)$arrRow['pec_id_original'] . '">OK</button>';
echo '</div>';
echo '</div>';
}
if ($blnCanStatut) {
echo '<div class="inscr-gestion-field-block inscr-gestion-field-block--statut" data-inscr-field="statut">';
echo '<div class="inscr-gestion-field-block__label">' . fxInscrGestionEsc(fxInscrGestionT('inscr_gestion_statut_label')) . '</div>';
fxInscrGestionRenderStatutField($intParId, $arrRow['par_statut_course'] ?? '', $arrRow['eve_id'], $strLangue);
echo '</div>';
}
$strRemisDate = trim((string)$arrRow['no_bib_remis_date']);
if ($strRemisDate === '0000-00-00 00:00:00') {
$strRemisDate = '';
}
$strRemisPar = trim((string)$arrRow['no_bib_remis_par']);
if ($blnCanRemis) {
echo '<div class="inscr-gestion-field-block" data-inscr-field="remis">';
echo '<div class="inscr-gestion-remis-row">';
echo '<div class="inscr-gestion-remis-row__left">';
echo '<div class="inscr-gestion-field-block__label inscr-gestion-field-block__label--inline">' . fxInscrGestionEsc(fxInscrGestionT('promoteur_bib_enregistre')) . '</div>';
echo '<span id="' . (int)$intParId . '">';
echo '<a class="btn btn-sm rounded-0 btn_remis ' . $strRemisClass . '" href="#" data-action="remis"'
. ' data-table="' . rawurlencode(base64_encode('resultats_participants')) . '"'
. ' data-field="no_bib_remis" data-key="par_id" data-valeur="' . $intRemisVal . '"'
. ' data-id="' . (int)$intParId . '">' . $strRemisIcon . '</a>';
echo '</span>';
echo '</div>';
echo '<div class="inscr-gestion-remis-row__meta inscr-gestion-muted">';
echo '<span id="date_' . (int)$intParId . '">' . fxInscrGestionEsc($strRemisDate) . '</span>';
if ($strRemisDate !== '' && $strRemisPar !== '') {
echo '<span class="inscr-gestion-remis-row__sep"> — </span>';
}
echo '<span id="par_' . (int)$intParId . '">' . fxInscrGestionEsc($strRemisPar) . '</span>';
echo '</div></div></div>';
}
fxInscrGestionRenderFicheSectionEnd();
}
echo '</div>';
}
function fxInscrGestionRun($intEveId, $strLangue) {
$strBaseUrl = fxInscrGestionBaseUrl($strLangue);
$arrReq = fxInscrGestionParseRequest();
if ($arrReq['pec_id'] > 0) {
$arrRow = fxInscrGestionFetchRowByPec($arrReq['pec_id'], $intEveId);
if ($arrRow === null) {
echo '<div class="alert alert-danger">' . fxInscrGestionEsc(fxInscrGestionT('txt_recherche_no_result')) . '</div>';
echo '<a class="btn btn-primary rounded-0" href="' . fxInscrGestionEsc(fxInscrGestionBuildUrl($strBaseUrl, $intEveId, $arrReq, array('pec_id' => null))) . '">';
echo fxInscrGestionEsc(fxInscrGestionT('inscr_gestion_back_list')) . '</a>';
return;
}
fxInscrGestionRenderFiche($intEveId, $strLangue, $strBaseUrl, $arrReq, $arrRow);
return;
}
fxInscrGestionRenderList($intEveId, $strLangue, $strBaseUrl, $arrReq);
}