Enhance MS1 loader functionality and integrate loading indicators in ChronoTrack API
This commit updates the MS1 loader to include a message parameter for inline HTML usage, ensuring consistent messaging during AJAX operations. Additionally, new functions are introduced to render loading indicators and assets, improving user feedback during data synchronization processes. The integration of these features aims to enhance the overall user experience and clarity within the ChronoTrack API admin interface.
This commit is contained in:
@ -7,6 +7,20 @@ function fxChronotrackApiAdminEsc($str) {
|
||||
return htmlspecialchars((string)$str, ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
|
||||
function fxChronotrackApiAdminRenderWaitHtml($strMessage, $vDomaine) {
|
||||
$strSrc = fxChronotrackApiAdminEsc($vDomaine . '/images/ms1-runner-loader.gif?v=' . _VERSION_CODE);
|
||||
$strMsg = fxChronotrackApiAdminEsc($strMessage);
|
||||
return '<div class="ms1-loader ms1-loader--inline" role="status" aria-live="polite" aria-busy="true">'
|
||||
. '<img class="ms1-loader__runner" src="' . $strSrc . '" alt="" role="presentation" decoding="async">'
|
||||
. '<span class="ms1-loader__label">' . $strMsg . '</span>'
|
||||
. '</div>';
|
||||
}
|
||||
|
||||
function fxChronotrackApiAdminRenderLoaderAssets($vDomaine) {
|
||||
echo '<link rel="stylesheet" href="' . fxChronotrackApiAdminEsc($vDomaine) . '/css/ms1-loader.css?v=' . _VERSION_CODE . '">';
|
||||
echo '<script src="' . fxChronotrackApiAdminEsc($vDomaine) . '/js/ms1-loader.js?v=' . _VERSION_CODE . '"></script>';
|
||||
}
|
||||
|
||||
function fxChronotrackApiAdminRenderEventLinkPicker($blnClosed) {
|
||||
echo '<div class="card mb-4" id="ct_api_event_link_card"><div class="card-header">1. Lier un événement ChronoTrack</div><div class="card-body">';
|
||||
if ($blnClosed) {
|
||||
@ -200,6 +214,7 @@ function fxChronotrackApiAdminRenderRaceMapping($intEveId, $arrConfig, $strLangu
|
||||
}
|
||||
|
||||
function fxChronotrackApiAdminRenderPushPanel($intEveId, $blnClosed = false) {
|
||||
global $vDomaine;
|
||||
echo '<div class="card mb-4 border-primary" id="ct_api_push_card">';
|
||||
echo '<div class="card-header">3. Synchronisation manuelle (participants)</div>';
|
||||
echo '<div class="card-body">';
|
||||
@ -207,8 +222,9 @@ function fxChronotrackApiAdminRenderPushPanel($intEveId, $blnClosed = false) {
|
||||
echo '<div class="alert alert-warning py-2 small mb-2">Événement fermé — push manuel autorisé pour tests ou rattrapage.</div>';
|
||||
}
|
||||
echo '<p class="small text-muted mb-2">Lit <code>resultats_participants</code> (comme le rapport chrono). ';
|
||||
echo 'External ID = <code>par_id_original</code>. POST vers ChronoTrack — aucune modification MS1.</p>';
|
||||
echo '<div id="ct_api_push_summary" class="mb-2"><span class="text-muted">Chargement…</span></div>';
|
||||
echo 'External ID = <code>par_id_original</code>. Dossard obligatoire ; sexe vide → NOT SPECIFIED côté CT ; ';
|
||||
echo 'doublon de dossard MS1 = aucun des inscrits concernés n\'est transféré.</p>';
|
||||
echo '<div id="ct_api_push_summary" class="mb-2">' . fxChronotrackApiAdminRenderWaitHtml('Chargement…', $vDomaine) . '</div>';
|
||||
echo '<button type="button" class="btn btn-primary btn-sm" id="ct_api_btn_push" disabled>';
|
||||
echo '<i class="fa fa-cloud-upload" aria-hidden="true"></i> Pousser vers ChronoTrack</button> ';
|
||||
echo '<button type="button" class="btn btn-outline-secondary btn-sm" id="ct_api_btn_push_refresh">';
|
||||
@ -251,6 +267,7 @@ function fxChronotrackApiAdminRenderPageScript($intEveId, $arrConfig, $blnClosed
|
||||
}
|
||||
}
|
||||
?>
|
||||
<?php fxChronotrackApiAdminRenderLoaderAssets($vDomaine); ?>
|
||||
<script>
|
||||
(function ($) {
|
||||
var intEveId = <?php echo intval($intEveId); ?>;
|
||||
@ -261,6 +278,23 @@ function fxChronotrackApiAdminRenderPageScript($intEveId, $arrConfig, $blnClosed
|
||||
var blnHasSavedMapping = <?php echo $blnHasSavedMapping ? 'true' : 'false'; ?>;
|
||||
var blnClosed = <?php echo $blnClosed ? 'true' : 'false'; ?>;
|
||||
|
||||
function escHtml(str) {
|
||||
return String(str).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
||||
}
|
||||
|
||||
if (window.Ms1Loader) {
|
||||
Ms1Loader.init({
|
||||
src: <?php echo json_encode($vDomaine . '/images/ms1-runner-loader.gif?v=' . _VERSION_CODE); ?>
|
||||
});
|
||||
}
|
||||
|
||||
function waitHtml(strMessage) {
|
||||
if (window.Ms1Loader) {
|
||||
return Ms1Loader.inlineHtml(strMessage);
|
||||
}
|
||||
return '<span class="text-muted">' + escHtml(strMessage || 'Chargement…') + '</span>';
|
||||
}
|
||||
|
||||
function showMsg(strType, strText) {
|
||||
window.alert(strText);
|
||||
}
|
||||
@ -292,6 +326,8 @@ function fxChronotrackApiAdminRenderPageScript($intEveId, $arrConfig, $blnClosed
|
||||
}
|
||||
|
||||
$('#ct_api_btn_load').on('click', function () {
|
||||
var $wrap = $('#ct_api_events_wrap');
|
||||
$wrap.html(waitHtml('Chargement de la liste ChronoTrack…'));
|
||||
$.post(strAjaxUrl, {
|
||||
a: 'list_events',
|
||||
eve_id: intEveId,
|
||||
@ -317,6 +353,8 @@ function fxChronotrackApiAdminRenderPageScript($intEveId, $arrConfig, $blnClosed
|
||||
showMsg('error', 'Sélectionnez un événement ChronoTrack');
|
||||
return;
|
||||
}
|
||||
var $btn = $(this);
|
||||
$btn.prop('disabled', true);
|
||||
$.post(strAjaxUrl, {
|
||||
a: 'save_link',
|
||||
eve_id: intEveId,
|
||||
@ -324,10 +362,14 @@ function fxChronotrackApiAdminRenderPageScript($intEveId, $arrConfig, $blnClosed
|
||||
}, function (res) {
|
||||
if (!res || res.state !== 'ok') {
|
||||
showMsg('error', (res && res.message) ? res.message : 'Erreur enregistrement');
|
||||
$btn.prop('disabled', false);
|
||||
return;
|
||||
}
|
||||
window.location.reload();
|
||||
}, 'json');
|
||||
}, 'json').fail(function () {
|
||||
showMsg('error', 'Erreur réseau');
|
||||
$btn.prop('disabled', false);
|
||||
});
|
||||
});
|
||||
|
||||
$('.ct-api-set-status').on('click', function () {
|
||||
@ -357,7 +399,7 @@ function fxChronotrackApiAdminRenderPageScript($intEveId, $arrConfig, $blnClosed
|
||||
return;
|
||||
}
|
||||
var $status = $('#ct_api_races_status');
|
||||
$status.removeClass('text-danger text-warning text-success').text('Chargement des courses ChronoTrack…');
|
||||
$status.removeClass('text-danger text-warning text-success').html(waitHtml('Chargement des courses ChronoTrack…'));
|
||||
$.post(strAjaxUrl, { a: 'list_races', eve_id: intEveId, ct_event_id: intLinkedCtEventId }, function (res) {
|
||||
if (!res || res.state !== 'ok') {
|
||||
$status.addClass('text-danger').text((res && res.message) ? res.message : 'Erreur chargement des courses');
|
||||
@ -435,21 +477,23 @@ function fxChronotrackApiAdminRenderPageScript($intEveId, $arrConfig, $blnClosed
|
||||
var strRaceId = $(this).val();
|
||||
tabMap[intEprId] = { ct_race_id: strRaceId ? parseInt(strRaceId, 10) : 0, ct_race_name: $(this).find('option:selected').text() };
|
||||
});
|
||||
var $btn = $(this);
|
||||
$btn.prop('disabled', true);
|
||||
$.post(strAjaxUrl, { a: 'save_race_map', eve_id: intEveId, map: tabMap }, function (res) {
|
||||
if (!res || res.state !== 'ok') {
|
||||
showMsg('error', (res && res.message) ? res.message : 'Erreur mapping');
|
||||
$btn.prop('disabled', false);
|
||||
return;
|
||||
}
|
||||
window.location.reload();
|
||||
}, 'json');
|
||||
}, 'json').fail(function () {
|
||||
showMsg('error', 'Erreur réseau');
|
||||
$btn.prop('disabled', false);
|
||||
});
|
||||
});
|
||||
|
||||
var objDiagnosticReport = null;
|
||||
|
||||
function escHtml(str) {
|
||||
return String(str).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
||||
}
|
||||
|
||||
function renderDiagnosticReport(res) {
|
||||
objDiagnosticReport = res;
|
||||
var $out = $('#ct_api_diagnostic_out');
|
||||
@ -539,7 +583,7 @@ function fxChronotrackApiAdminRenderPageScript($intEveId, $arrConfig, $blnClosed
|
||||
|
||||
$('#ct_api_btn_diagnostic').on('click', function () {
|
||||
var $out = $('#ct_api_diagnostic_out');
|
||||
$out.text('Diagnostic en cours… (OAuth + 4 endpoints CT, dont entry)');
|
||||
$out.html(waitHtml('Diagnostic en cours… (OAuth + 4 endpoints CT, dont entry)'));
|
||||
$('#ct_api_btn_diagnostic_copy').addClass('d-none');
|
||||
$.post(strAjaxUrl, {
|
||||
a: 'diagnostic',
|
||||
@ -566,6 +610,30 @@ function fxChronotrackApiAdminRenderPageScript($intEveId, $arrConfig, $blnClosed
|
||||
window.prompt('Copier le rapport (Ctrl+C) :', strText);
|
||||
});
|
||||
|
||||
function renderAnomaliesList(res, strLimit) {
|
||||
var tabItems = (res && res.anomalies) ? res.anomalies : [];
|
||||
if (!tabItems.length) {
|
||||
return '';
|
||||
}
|
||||
var intMax = strLimit || 12;
|
||||
var html = '<details class="mt-2 mb-0"><summary class="text-danger small"><strong>Anomalies MS1 ('
|
||||
+ tabItems.length + ')</strong> — corriger avant transfert</summary>';
|
||||
html += '<ul class="text-danger small mb-0 pl-3 mt-1">';
|
||||
$.each(tabItems.slice(0, intMax), function (_, a) {
|
||||
var strLine = escHtml(a.name || ('external_id=' + a.external_id));
|
||||
if (a.no_bib) {
|
||||
strLine += ' · dossard #' + escHtml(a.no_bib);
|
||||
}
|
||||
strLine += ' — ' + escHtml(a.message || a.code || 'anomalie');
|
||||
html += '<li>' + strLine + '</li>';
|
||||
});
|
||||
if (tabItems.length > intMax) {
|
||||
html += '<li class="text-muted">… et ' + (tabItems.length - intMax) + ' autre(s)</li>';
|
||||
}
|
||||
html += '</ul></details>';
|
||||
return html;
|
||||
}
|
||||
|
||||
function renderPushPreview(res) {
|
||||
var $sum = $('#ct_api_push_summary');
|
||||
var $btn = $('#ct_api_btn_push');
|
||||
@ -579,7 +647,7 @@ function fxChronotrackApiAdminRenderPageScript($intEveId, $arrConfig, $blnClosed
|
||||
}
|
||||
var html = '<ul class="mb-0 pl-3">';
|
||||
html += '<li><strong>' + res.ms1_total + '</strong> participant(s) MS1 actifs</li>';
|
||||
html += '<li><strong>' + res.eligible + '</strong> éligible(s) (épreuve mappée + external ID)</li>';
|
||||
html += '<li><strong>' + res.transferable + '</strong> transférable(s) (dossard + sexe + épreuve mappée)</li>';
|
||||
html += '<li><strong>' + res.ct_total + '</strong> déjà dans ChronoTrack</li>';
|
||||
html += '<li><strong class="text-primary">' + res.to_create + '</strong> nouveau(x) à créer';
|
||||
if (res.already_in_ct > 0) {
|
||||
@ -592,9 +660,24 @@ function fxChronotrackApiAdminRenderPageScript($intEveId, $arrConfig, $blnClosed
|
||||
if (res.skipped_no_external > 0) {
|
||||
html += '<li class="text-warning">' + res.skipped_no_external + ' ignoré(s) — par_id_original manquant</li>';
|
||||
}
|
||||
if (res.anomaly_no_bib > 0) {
|
||||
html += '<li class="text-danger">' + res.anomaly_no_bib + ' anomalie(s) — dossard manquant</li>';
|
||||
}
|
||||
if (res.anomaly_no_sex > 0) {
|
||||
html += '<li class="text-danger">' + res.anomaly_no_sex + ' anomalie(s) — sexe MS1 non reconnu (ex. n, a)</li>';
|
||||
}
|
||||
if (res.anomaly_duplicate_bib > 0) {
|
||||
html += '<li class="text-danger">' + res.anomaly_duplicate_bib + ' anomalie(s) — dossard en double MS1';
|
||||
if (res.duplicate_bib_numbers && res.duplicate_bib_numbers.length) {
|
||||
var arrBibLabels = $.map(res.duplicate_bib_numbers, function (n) { return '#' + n; });
|
||||
html += ' (' + escHtml(arrBibLabels.join(', ')) + ')';
|
||||
}
|
||||
html += '</li>';
|
||||
}
|
||||
html += '</ul>';
|
||||
html += renderAnomaliesList(res, 15);
|
||||
$sum.html(html);
|
||||
$btn.prop('disabled', res.eligible <= 0);
|
||||
$btn.prop('disabled', res.transferable <= 0);
|
||||
}
|
||||
|
||||
function loadPushPreview() {
|
||||
@ -602,7 +685,7 @@ function fxChronotrackApiAdminRenderPageScript($intEveId, $arrConfig, $blnClosed
|
||||
if (!$sum.length) {
|
||||
return;
|
||||
}
|
||||
$sum.html('<span class="text-muted">Calcul du diff MS1 ↔ ChronoTrack…</span>');
|
||||
$sum.html(waitHtml('Calcul du diff MS1 ↔ ChronoTrack…'));
|
||||
$.post(strAjaxUrl, { a: 'sync_preview', eve_id: intEveId }, function (res) {
|
||||
renderPushPreview(res);
|
||||
}, 'json').fail(function () {
|
||||
@ -622,7 +705,7 @@ function fxChronotrackApiAdminRenderPageScript($intEveId, $arrConfig, $blnClosed
|
||||
var $btn = $(this);
|
||||
var $res = $('#ct_api_push_result');
|
||||
$btn.prop('disabled', true);
|
||||
$res.html('<span class="text-muted">Sonde POST (1 participant)…</span>');
|
||||
$res.html(waitHtml('Sonde POST (1 participant)…'));
|
||||
$.post(strAjaxUrl, { a: 'sync_probe_push', eve_id: intEveId }, function (res) {
|
||||
var strClass = (res && res.state === 'ok') ? 'text-success' : 'text-danger';
|
||||
var strHtml = '<div class="' + strClass + '"><strong>Sonde</strong> — ' + escHtml((res && res.message) ? res.message : 'Erreur') + '</div>';
|
||||
@ -649,7 +732,7 @@ function fxChronotrackApiAdminRenderPageScript($intEveId, $arrConfig, $blnClosed
|
||||
var $btn = $(this);
|
||||
var $res = $('#ct_api_push_result');
|
||||
$btn.prop('disabled', true);
|
||||
$res.html('<span class="text-muted">Envoi en cours…</span>');
|
||||
$res.html(waitHtml('Envoi en cours… (cela peut prendre quelques minutes)'));
|
||||
$.post(strAjaxUrl, { a: 'sync_push', eve_id: intEveId }, function (res) {
|
||||
var strClass = 'text-danger';
|
||||
if (res && (res.state === 'ok' || res.state === 'partial')) {
|
||||
@ -663,6 +746,7 @@ function fxChronotrackApiAdminRenderPageScript($intEveId, $arrConfig, $blnClosed
|
||||
});
|
||||
strHtml += '</ul>';
|
||||
}
|
||||
strHtml += renderAnomaliesList(res, 12);
|
||||
$res.html(strHtml);
|
||||
loadPushPreview();
|
||||
}, 'json').fail(function () {
|
||||
|
||||
Reference in New Issue
Block a user