This commit introduces a new diagnostic feature in the ChronoTrack API, allowing users to run diagnostics on events and retrieve detailed reports. The functionality includes a new diagnostic panel in the admin interface, which displays API diagnostic results, including OAuth status and endpoint responses. Additionally, the backend is updated to handle diagnostic requests, ensuring comprehensive error handling and reporting. This enhancement aims to improve troubleshooting and support for event management within the ChronoTrack system.
532 lines
25 KiB
PHP
532 lines
25 KiB
PHP
<?php
|
|
/**
|
|
* MSIN API ChronoTrack — rendu admin superadm (Phase 1 : lien + mapping).
|
|
*/
|
|
|
|
function fxChronotrackApiAdminEsc($str) {
|
|
return htmlspecialchars((string)$str, ENT_QUOTES, 'UTF-8');
|
|
}
|
|
|
|
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) {
|
|
echo '<p class="text-muted mb-0">Lecture seule.</p>';
|
|
} else {
|
|
echo '<div class="form-row align-items-end mb-3">';
|
|
echo '<div class="col-md-5 mb-2"><label for="ct_api_search">Recherche</label>';
|
|
echo '<input type="text" class="form-control form-control-sm" id="ct_api_search" placeholder="Nom ou ID ChronoTrack"></div>';
|
|
echo '<div class="col-md-4 mb-2"><div class="form-check mt-4">';
|
|
echo '<input type="checkbox" class="form-check-input" id="ct_api_show_past" value="1">';
|
|
echo '<label class="form-check-label" for="ct_api_show_past">Afficher les événements passés</label></div></div>';
|
|
echo '<div class="col-md-3 mb-2"><button type="button" class="btn btn-primary btn-sm btn-block" id="ct_api_btn_load">Charger la liste</button></div>';
|
|
echo '</div>';
|
|
echo '<div id="ct_api_events_wrap" class="border rounded p-2" style="max-height:320px;overflow:auto;">';
|
|
echo '<p class="text-muted small mb-0">Cliquez sur « Charger la liste » pour voir les événements à venir (tri par date).</p>';
|
|
echo '</div>';
|
|
echo '<button type="button" class="btn btn-success btn-sm mt-3" id="ct_api_btn_save_link" disabled>Enregistrer le lien</button>';
|
|
}
|
|
echo '</div></div>';
|
|
}
|
|
|
|
function fxChronotrackApiAdminRenderEventPage($intEveId, $strLangue = 'fr', $blnInlineOnEventForm = false) {
|
|
global $vDomaine;
|
|
|
|
$arrEvent = fxChronotrackApiMs1EventRow($intEveId, $strLangue);
|
|
if ($arrEvent === null) {
|
|
echo '<div class="alert alert-danger">Événement MS1 introuvable.</div>';
|
|
return;
|
|
}
|
|
|
|
$arrConfig = fxChronotrackApiConfigGet($intEveId);
|
|
$blnConfigured = fxChronotrackApiSettingsConfigured();
|
|
$strStatus = $arrConfig['sync_status'] ?? '';
|
|
$blnClosed = ($strStatus === MSIN_API_CHRONOTRACK_SYNC_FERME);
|
|
|
|
$strWrapperId = '';
|
|
echo '<div class="ct-api-admin mt-2 mb-3"' . $strWrapperId . '>';
|
|
if ($blnInlineOnEventForm) {
|
|
echo '<h2 class="h5">MSIN API ChronoTrack</h2>';
|
|
} else {
|
|
echo '<h1 class="h3">MSIN API ChronoTrack</h1>';
|
|
}
|
|
echo '<p class="text-muted">Configuration du lien pour l\'événement MS1 <strong>' . fxChronotrackApiAdminEsc($arrEvent['eve_nom']) . '</strong> (eve_id '
|
|
. intval($arrEvent['eve_id']) . ').</p>';
|
|
|
|
if (!$blnConfigured) {
|
|
echo '<div class="alert alert-warning">' . fxChronotrackApiAdminEsc(fxChronotrackApiSettingsErrorMessage()) . '</div>';
|
|
}
|
|
|
|
if ($arrConfig !== null) {
|
|
echo '<div class="alert alert-info">';
|
|
echo 'Statut : <strong>' . fxChronotrackApiAdminEsc(fxChronotrackApiConfigStatusLabel($strStatus)) . '</strong>';
|
|
echo ' — ChronoTrack : <strong>' . fxChronotrackApiAdminEsc($arrConfig['ct_event_name']) . '</strong>';
|
|
echo ' (ID ' . intval($arrConfig['ct_event_id']) . ')';
|
|
echo '</div>';
|
|
}
|
|
|
|
if ($blnClosed) {
|
|
echo '<div class="alert alert-secondary">Cet événement est <strong>fermé</strong>. Aucune modification ni sync automatique.</div>';
|
|
}
|
|
|
|
$blnEventLinked = ($arrConfig !== null && intval($arrConfig['ct_event_id'] ?? 0) > 0);
|
|
if (!$blnEventLinked) {
|
|
fxChronotrackApiAdminRenderEventLinkPicker($blnClosed);
|
|
}
|
|
|
|
if ($arrConfig !== null && !$blnClosed) {
|
|
fxChronotrackApiAdminRenderStatusActions($intEveId, $strStatus, $blnEventLinked);
|
|
}
|
|
|
|
if ($arrConfig !== null) {
|
|
fxChronotrackApiAdminRenderRaceMapping($intEveId, $arrConfig, $strLangue, $blnClosed, $blnEventLinked);
|
|
}
|
|
|
|
if ($blnEventLinked && $blnConfigured) {
|
|
fxChronotrackApiAdminRenderDiagnosticPanel(intval($arrConfig['ct_event_id']));
|
|
}
|
|
|
|
if (!$blnInlineOnEventForm) {
|
|
echo '<p class="mt-3"><a class="btn btn-outline-secondary btn-sm" href="index.php?t='
|
|
. urlencode(base64_encode(4)) . '&a=mod&id=' . intval($intEveId) . '">← Retour fiche événement</a></p>';
|
|
}
|
|
echo '</div>';
|
|
|
|
fxChronotrackApiAdminRenderPageScript($intEveId, $arrConfig, $blnClosed, $vDomaine);
|
|
}
|
|
|
|
function fxChronotrackApiAdminRenderStatusActions($intEveId, $strStatus, $blnEventLinked = true) {
|
|
$strStep = $blnEventLinked ? '1' : '2';
|
|
echo '<div class="card mb-4"><div class="card-header">' . $strStep . '. Cycle de vie sync</div><div class="card-body">';
|
|
echo '<p class="small text-muted">Activer avant la course ; <strong>Terminer</strong> après la course pour bloquer tout envoi vers ChronoTrack.</p>';
|
|
echo '<div class="btn-group" role="group">';
|
|
if ($strStatus === MSIN_API_CHRONOTRACK_SYNC_LIE) {
|
|
echo '<button type="button" class="btn btn-warning btn-sm ct-api-set-status" data-status="' . MSIN_API_CHRONOTRACK_SYNC_ACTIF . '">Activer la sync</button>';
|
|
}
|
|
if ($strStatus === MSIN_API_CHRONOTRACK_SYNC_ACTIF) {
|
|
echo '<span class="badge badge-success align-self-center mr-2">Sync active</span>';
|
|
echo '<button type="button" class="btn btn-danger btn-sm ct-api-set-status" data-status="' . MSIN_API_CHRONOTRACK_SYNC_FERME . '">Terminer / Déconnecter</button>';
|
|
}
|
|
echo '</div></div></div>';
|
|
}
|
|
|
|
function fxChronotrackApiAdminRenderRaceMapping($intEveId, $arrConfig, $strLangue, $blnClosed, $blnEventLinked = true) {
|
|
$arrEpreuves = fxChronotrackApiMs1EpreuvesForEvent($intEveId, $strLangue);
|
|
$arrMap = fxChronotrackApiRaceMapGetForEvent($intEveId);
|
|
|
|
$blnHasSavedMapping = false;
|
|
if ($arrEpreuves !== null) {
|
|
for ($i = 1; $i <= count($arrEpreuves); $i++) {
|
|
$intEprId = intval($arrEpreuves[$i]['epr_id']);
|
|
if (intval($arrMap[$intEprId]['ct_race_id'] ?? 0) > 0) {
|
|
$blnHasSavedMapping = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
$strStep = $blnEventLinked ? '2' : '3';
|
|
echo '<div class="card mb-4"><div class="card-header">' . $strStep . '. Mapping épreuves MS1 → races ChronoTrack</div><div class="card-body">';
|
|
if ($arrEpreuves === null || count($arrEpreuves) === 0) {
|
|
echo '<p class="text-muted mb-0">Aucune épreuve MS1 pour cet événement.</p></div></div>';
|
|
return;
|
|
}
|
|
|
|
if ($blnHasSavedMapping) {
|
|
echo '<div id="ct_api_race_summary_wrap">';
|
|
echo '<table class="table table-sm table-bordered mb-2"><thead><tr><th>Épreuve MS1</th><th>Race ChronoTrack</th></tr></thead><tbody>';
|
|
for ($i = 1; $i <= count($arrEpreuves); $i++) {
|
|
$intEprId = intval($arrEpreuves[$i]['epr_id']);
|
|
$strLabel = trim($arrEpreuves[$i]['epr_type'] . ' — ' . $arrEpreuves[$i]['epr_nom']);
|
|
$intCtRaceId = intval($arrMap[$intEprId]['ct_race_id'] ?? 0);
|
|
$strCtRaceName = trim((string)($arrMap[$intEprId]['ct_race_name'] ?? ''));
|
|
echo '<tr><td>' . fxChronotrackApiAdminEsc($strLabel) . ' <span class="text-muted">(epr_id '
|
|
. $intEprId . ')</span></td><td>';
|
|
if ($intCtRaceId > 0) {
|
|
echo fxChronotrackApiAdminEsc($strCtRaceName !== '' ? $strCtRaceName : ('Race ID ' . $intCtRaceId));
|
|
echo ' <span class="text-muted">(ID ' . $intCtRaceId . ')</span>';
|
|
} else {
|
|
echo '<span class="text-muted">— Non mappé —</span>';
|
|
}
|
|
echo '</td></tr>';
|
|
}
|
|
echo '</tbody></table>';
|
|
if (!$blnClosed) {
|
|
echo '<button type="button" class="btn btn-outline-secondary btn-sm" id="ct_api_btn_edit_map">'
|
|
. '<i class="fa fa-pencil" aria-hidden="true"></i> Modifier le mapping</button>';
|
|
}
|
|
echo '</div>';
|
|
}
|
|
|
|
$strEditStyle = $blnHasSavedMapping ? ' style="display:none;"' : '';
|
|
echo '<div id="ct_api_race_edit_wrap"' . $strEditStyle . '>';
|
|
echo '<p class="small text-muted">ChronoTrack event ID : ' . intval($arrConfig['ct_event_id'])
|
|
. ' — liste des <strong>choix d\'inscription</strong> (Registration Choices), comme dans CT Live.</p>';
|
|
echo '<div id="ct_api_races_status" class="small mb-2"></div>';
|
|
if (!$blnClosed) {
|
|
echo '<button type="button" class="btn btn-outline-secondary btn-sm mb-2" id="ct_api_btn_reload_races">'
|
|
. '<i class="fa fa-refresh" aria-hidden="true"></i> Charger les courses ChronoTrack</button>';
|
|
}
|
|
echo '<table class="table table-sm table-bordered"><thead><tr><th>Épreuve MS1</th><th>Race ChronoTrack</th></tr></thead><tbody>';
|
|
|
|
for ($i = 1; $i <= count($arrEpreuves); $i++) {
|
|
$intEprId = intval($arrEpreuves[$i]['epr_id']);
|
|
$strLabel = trim($arrEpreuves[$i]['epr_type'] . ' — ' . $arrEpreuves[$i]['epr_nom']);
|
|
|
|
echo '<tr><td>' . fxChronotrackApiAdminEsc($strLabel) . ' <span class="text-muted">(epr_id '
|
|
. $intEprId . ')</span></td><td>';
|
|
echo '<select class="form-control form-control-sm ct-api-race-select" data-epr-id="' . $intEprId . '"'
|
|
. ($blnClosed ? ' disabled' : '') . '>';
|
|
echo '<option value="">— Non mappé —</option>';
|
|
echo '</select></td></tr>';
|
|
}
|
|
|
|
echo '</tbody></table>';
|
|
if (!$blnClosed) {
|
|
echo '<button type="button" class="btn btn-primary btn-sm" id="ct_api_btn_save_map">Enregistrer le mapping</button>';
|
|
if ($blnHasSavedMapping) {
|
|
echo ' <button type="button" class="btn btn-link btn-sm" id="ct_api_btn_cancel_edit_map">Annuler</button>';
|
|
}
|
|
}
|
|
echo '</div></div></div>';
|
|
}
|
|
|
|
function fxChronotrackApiAdminRenderDiagnosticPanel($intCtEventId) {
|
|
echo '<div class="card mb-4 border-secondary"><div class="card-header py-2">';
|
|
echo '<a class="d-block text-dark collapsed" data-toggle="collapse" href="#ct_api_diagnostic_collapse" role="button" aria-expanded="false">';
|
|
echo 'Diagnostic API ChronoTrack <span class="text-muted small">(support — event ID ' . intval($intCtEventId) . ')</span>';
|
|
echo '</a></div>';
|
|
echo '<div class="collapse" id="ct_api_diagnostic_collapse"><div class="card-body">';
|
|
echo '<p class="small text-muted mb-2">Utilise les credentials MS1 déjà configurés (OAuth en cache). ';
|
|
echo 'Aucun Postman requis — copie le résultat et envoie-le au support / développement.</p>';
|
|
echo '<button type="button" class="btn btn-outline-dark btn-sm mb-2" id="ct_api_btn_diagnostic">';
|
|
echo '<i class="fa fa-stethoscope" aria-hidden="true"></i> Lancer le diagnostic API</button> ';
|
|
echo '<button type="button" class="btn btn-outline-secondary btn-sm mb-2 d-none" id="ct_api_btn_diagnostic_copy">';
|
|
echo 'Copier le rapport</button>';
|
|
echo '<div id="ct_api_diagnostic_out" class="small text-muted">Cliquez sur « Lancer le diagnostic API ».</div>';
|
|
echo '</div></div></div>';
|
|
}
|
|
|
|
function fxChronotrackApiAdminRenderPageScript($intEveId, $arrConfig, $blnClosed, $vDomaine) {
|
|
$intCtEventId = ($arrConfig !== null) ? intval($arrConfig['ct_event_id']) : 0;
|
|
$arrMapCurrent = array();
|
|
$blnHasSavedMapping = false;
|
|
if ($arrConfig !== null) {
|
|
$arrMapRows = fxChronotrackApiRaceMapGetForEvent($intEveId);
|
|
foreach ($arrMapRows as $intEprId => $arrRow) {
|
|
$intCtRaceId = intval($arrRow['ct_race_id']);
|
|
$arrMapCurrent[intval($intEprId)] = $intCtRaceId;
|
|
if ($intCtRaceId > 0) {
|
|
$blnHasSavedMapping = true;
|
|
}
|
|
}
|
|
}
|
|
?>
|
|
<script>
|
|
(function ($) {
|
|
var intEveId = <?php echo intval($intEveId); ?>;
|
|
var intLinkedCtEventId = <?php echo $intCtEventId; ?>;
|
|
var tabMapCurrent = <?php echo json_encode($arrMapCurrent); ?>;
|
|
var strAjaxUrl = <?php echo json_encode($vDomaine . '/superadm/ajax_chronotrack_api.php'); ?>;
|
|
var strSelectedCtEventId = intLinkedCtEventId > 0 ? String(intLinkedCtEventId) : '';
|
|
var blnHasSavedMapping = <?php echo $blnHasSavedMapping ? 'true' : 'false'; ?>;
|
|
var blnClosed = <?php echo $blnClosed ? 'true' : 'false'; ?>;
|
|
|
|
function showMsg(strType, strText) {
|
|
window.alert(strText);
|
|
}
|
|
|
|
function renderEvents(arrEvents) {
|
|
var $wrap = $('#ct_api_events_wrap');
|
|
$wrap.empty();
|
|
if (!arrEvents || !arrEvents.length) {
|
|
$wrap.append('<p class="text-muted small mb-0">Aucun événement trouvé (essayez « événements passés » ou autre recherche).</p>');
|
|
return;
|
|
}
|
|
var $list = $('<div class="list-group list-group-flush"></div>');
|
|
$.each(arrEvents, function (_, ev) {
|
|
var strId = String(ev.ct_event_id || '');
|
|
var $item = $('<label class="list-group-item list-group-item-action py-2 mb-0"></label>');
|
|
var $radio = $('<input type="radio" name="ct_api_event_pick" class="mr-2">').val(strId);
|
|
if (strSelectedCtEventId === strId) {
|
|
$radio.prop('checked', true);
|
|
}
|
|
$item.append($radio);
|
|
$item.append(document.createTextNode((ev.start_label ? ev.start_label + ' — ' : '') + (ev.name || '') + ' (ID ' + strId + ')'));
|
|
if (ev.is_past) {
|
|
$item.append(' <span class="badge badge-secondary ml-1">passé</span>');
|
|
}
|
|
$list.append($item);
|
|
});
|
|
$wrap.append($list);
|
|
$('#ct_api_btn_save_link').prop('disabled', false);
|
|
}
|
|
|
|
$('#ct_api_btn_load').on('click', function () {
|
|
$.post(strAjaxUrl, {
|
|
a: 'list_events',
|
|
eve_id: intEveId,
|
|
search: $('#ct_api_search').val(),
|
|
show_past: $('#ct_api_show_past').is(':checked') ? 1 : 0
|
|
}, function (res) {
|
|
if (!res || res.state !== 'ok') {
|
|
showMsg('error', (res && res.message) ? res.message : 'Erreur chargement');
|
|
return;
|
|
}
|
|
renderEvents(res.events || []);
|
|
}, 'json').fail(function () {
|
|
showMsg('error', 'Erreur réseau');
|
|
});
|
|
});
|
|
|
|
$(document).on('change', 'input[name="ct_api_event_pick"]', function () {
|
|
strSelectedCtEventId = $(this).val();
|
|
});
|
|
|
|
$('#ct_api_btn_save_link').on('click', function () {
|
|
if (!strSelectedCtEventId) {
|
|
showMsg('error', 'Sélectionnez un événement ChronoTrack');
|
|
return;
|
|
}
|
|
$.post(strAjaxUrl, {
|
|
a: 'save_link',
|
|
eve_id: intEveId,
|
|
ct_event_id: strSelectedCtEventId
|
|
}, function (res) {
|
|
if (!res || res.state !== 'ok') {
|
|
showMsg('error', (res && res.message) ? res.message : 'Erreur enregistrement');
|
|
return;
|
|
}
|
|
window.location.reload();
|
|
}, 'json');
|
|
});
|
|
|
|
$('.ct-api-set-status').on('click', function () {
|
|
var strStatus = $(this).data('status');
|
|
var strConfirm = (strStatus === 'ferme')
|
|
? 'Terminer la course ? Aucun changement ne sera plus envoyé à ChronoTrack.'
|
|
: 'Activer la synchronisation automatique pour cet événement ?';
|
|
if (!window.confirm(strConfirm)) {
|
|
return;
|
|
}
|
|
$.post(strAjaxUrl, { a: 'set_status', eve_id: intEveId, sync_status: strStatus }, function (res) {
|
|
if (!res || res.state !== 'ok') {
|
|
showMsg('error', (res && res.message) ? res.message : 'Erreur statut');
|
|
return;
|
|
}
|
|
window.location.reload();
|
|
}, 'json');
|
|
});
|
|
|
|
function loadRacesIntoSelects() {
|
|
if (intLinkedCtEventId <= 0) {
|
|
return;
|
|
}
|
|
var $status = $('#ct_api_races_status');
|
|
$status.removeClass('text-danger text-warning text-success').text('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');
|
|
return;
|
|
}
|
|
var arrRaces = res.races || [];
|
|
var strKind = res.kind || 'reg_choice';
|
|
if (!arrRaces.length) {
|
|
$status.addClass('text-warning').text((res.message) ? res.message : 'Aucune course ChronoTrack pour cet event.');
|
|
} else if (strKind === 'reg_choice') {
|
|
$status.addClass('text-success').text(arrRaces.length + ' choix d\'inscription ChronoTrack chargé(s).');
|
|
} else {
|
|
$status.addClass('text-success').text(arrRaces.length + ' course(s) ChronoTrack chargée(s).');
|
|
}
|
|
$('.ct-api-race-select').each(function () {
|
|
var $sel = $(this);
|
|
var intEprId = parseInt($sel.data('epr-id'), 10);
|
|
var intCurrent = tabMapCurrent[intEprId] || 0;
|
|
$sel.find('option:not(:first)').remove();
|
|
$.each(arrRaces, function (_, race) {
|
|
var strText = race.label || race.name || 'Course';
|
|
if (race.kind !== 'reg_choice' && race.start_label) {
|
|
strText += ' · ' + race.start_label;
|
|
}
|
|
strText += ' (ID ' + race.ct_race_id + ')';
|
|
var $opt = $('<option></option>').val(race.ct_race_id).text(strText);
|
|
$sel.append($opt);
|
|
});
|
|
if (intCurrent > 0) {
|
|
$sel.val(String(intCurrent));
|
|
}
|
|
});
|
|
}, 'json').fail(function () {
|
|
$status.addClass('text-danger').text('Erreur réseau lors du chargement des races.');
|
|
});
|
|
}
|
|
|
|
function showRaceEditMode(blnLoadRaces) {
|
|
$('#ct_api_race_summary_wrap').hide();
|
|
$('#ct_api_race_edit_wrap').show();
|
|
if (blnLoadRaces) {
|
|
loadRacesIntoSelects();
|
|
}
|
|
}
|
|
|
|
function showRaceSummaryMode() {
|
|
$('#ct_api_race_edit_wrap').hide();
|
|
$('#ct_api_races_status').empty();
|
|
$('#ct_api_race_summary_wrap').show();
|
|
}
|
|
|
|
$('#ct_api_btn_edit_map').on('click', function () {
|
|
showRaceEditMode(true);
|
|
});
|
|
|
|
$('#ct_api_btn_cancel_edit_map').on('click', function () {
|
|
showRaceSummaryMode();
|
|
});
|
|
|
|
$('#ct_api_btn_reload_races').on('click', function () {
|
|
loadRacesIntoSelects();
|
|
});
|
|
|
|
$('#chronotrackApiCollapse').on('shown.bs.collapse', function () {
|
|
if (!blnHasSavedMapping && intLinkedCtEventId > 0 && !blnClosed) {
|
|
loadRacesIntoSelects();
|
|
}
|
|
});
|
|
|
|
<?php if ($arrConfig !== null && !$blnHasSavedMapping) { ?>
|
|
if (intLinkedCtEventId > 0 && !blnClosed && !$('#chronotrackApiCollapse').length) {
|
|
loadRacesIntoSelects();
|
|
}
|
|
<?php } ?>
|
|
|
|
$('#ct_api_btn_save_map').on('click', function () {
|
|
var tabMap = {};
|
|
$('.ct-api-race-select').each(function () {
|
|
var intEprId = parseInt($(this).data('epr-id'), 10);
|
|
var strRaceId = $(this).val();
|
|
tabMap[intEprId] = { ct_race_id: strRaceId ? parseInt(strRaceId, 10) : 0, ct_race_name: $(this).find('option:selected').text() };
|
|
});
|
|
$.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');
|
|
return;
|
|
}
|
|
window.location.reload();
|
|
}, 'json');
|
|
});
|
|
|
|
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');
|
|
if (!res || res.state !== 'ok') {
|
|
$out.html('<div class="text-danger">' + escHtml((res && res.message) ? res.message : 'Erreur diagnostic') + '</div>');
|
|
$('#ct_api_btn_diagnostic_copy').addClass('d-none');
|
|
return;
|
|
}
|
|
|
|
var html = '<p class="mb-2"><strong>OAuth :</strong> ' + escHtml(res.oauth || '?')
|
|
+ ' — <strong>Event CT :</strong> ' + escHtml(String(res.ct_event_id || '')) + '</p>';
|
|
html += '<div class="table-responsive"><table class="table table-sm table-bordered mb-2">';
|
|
html += '<thead><tr><th>Endpoint</th><th>HTTP</th><th>Résultat</th><th>Entités détectées</th></tr></thead><tbody>';
|
|
|
|
$.each(res.probes || [], function (_, probe) {
|
|
html += '<tr>';
|
|
html += '<td><code>' + escHtml(probe.path || '') + '</code></td>';
|
|
html += '<td>' + escHtml(String(probe.http_code || '')) + '</td>';
|
|
html += '<td>' + escHtml(probe.state === 'ok' ? 'OK' : (probe.message || 'erreur')) + '</td>';
|
|
html += '<td>';
|
|
if (probe.summary) {
|
|
if (probe.summary.reg_choice && probe.summary.reg_choice.count > 0) {
|
|
html += 'reg_choice: ' + probe.summary.reg_choice.count + '<br>';
|
|
}
|
|
if (probe.summary.race && probe.summary.race.count > 0) {
|
|
html += 'race: ' + probe.summary.race.count + '<br>';
|
|
}
|
|
if (probe.summary.reg_choice && probe.summary.reg_choice.samples && probe.summary.reg_choice.samples.length) {
|
|
html += '<span class="text-muted">ex. ';
|
|
$.each(probe.summary.reg_choice.samples, function (i, s) {
|
|
if (i > 0) {
|
|
html += ' · ';
|
|
}
|
|
html += escHtml((s.name || s.id || '?'));
|
|
});
|
|
html += '</span>';
|
|
}
|
|
}
|
|
html += '</td></tr>';
|
|
});
|
|
html += '</tbody></table></div>';
|
|
|
|
$.each(res.probes || [], function (idx, probe) {
|
|
html += '<details class="mb-2"><summary class="small"><strong>JSON brut — '
|
|
+ escHtml(probe.path || ('probe ' + idx)) + '</strong></summary>';
|
|
html += '<pre class="small border rounded p-2 mb-0" style="max-height:280px;overflow:auto;background:#f8f9fa;">'
|
|
+ escHtml(probe.raw_json || '') + '</pre></details>';
|
|
});
|
|
|
|
$out.html(html);
|
|
$('#ct_api_btn_diagnostic_copy').removeClass('d-none');
|
|
}
|
|
|
|
$('#ct_api_btn_diagnostic').on('click', function () {
|
|
var $out = $('#ct_api_diagnostic_out');
|
|
$out.text('Diagnostic en cours… (OAuth + 3 endpoints CT)');
|
|
$('#ct_api_btn_diagnostic_copy').addClass('d-none');
|
|
$.post(strAjaxUrl, {
|
|
a: 'diagnostic',
|
|
eve_id: intEveId,
|
|
ct_event_id: intLinkedCtEventId
|
|
}, function (res) {
|
|
renderDiagnosticReport(res);
|
|
}, 'json').fail(function () {
|
|
$out.html('<div class="text-danger">Erreur réseau lors du diagnostic.</div>');
|
|
});
|
|
});
|
|
|
|
$('#ct_api_btn_diagnostic_copy').on('click', function () {
|
|
if (!objDiagnosticReport) {
|
|
return;
|
|
}
|
|
var strText = JSON.stringify(objDiagnosticReport, null, 2);
|
|
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
navigator.clipboard.writeText(strText).then(function () {
|
|
window.alert('Rapport copié dans le presse-papiers.');
|
|
});
|
|
return;
|
|
}
|
|
window.prompt('Copier le rapport (Ctrl+C) :', strText);
|
|
});
|
|
|
|
})(jQuery);
|
|
</script>
|
|
<?php
|
|
}
|
|
|
|
function fxChronotrackApiAdminRenderDashboard($strLangue = 'fr') {
|
|
$arrRows = fxChronotrackApiListActiveConfigs();
|
|
echo '<h1 class="h3">MSIN API ChronoTrack — Courses connectées</h1>';
|
|
echo '<p class="text-muted">Événements liés ou sync active. Le dashboard détaillé (diff, push) viendra en Phase 2.</p>';
|
|
|
|
if ($arrRows === null || count($arrRows) === 0) {
|
|
echo '<div class="alert alert-light">Aucune course connectée.</div>';
|
|
return;
|
|
}
|
|
|
|
echo '<table class="table table-sm table-striped"><thead><tr><th>Événement MS1</th><th>ChronoTrack</th><th>Statut</th><th></th></tr></thead><tbody>';
|
|
for ($i = 1; $i <= count($arrRows); $i++) {
|
|
$arr = $arrRows[$i];
|
|
echo '<tr>';
|
|
echo '<td>' . fxChronotrackApiAdminEsc($arr['eve_nom']) . ' <span class="text-muted">(' . intval($arr['eve_id']) . ')</span></td>';
|
|
echo '<td>' . fxChronotrackApiAdminEsc($arr['ct_event_name']) . ' <span class="text-muted">(' . intval($arr['ct_event_id']) . ')</span></td>';
|
|
echo '<td>' . fxChronotrackApiAdminEsc(fxChronotrackApiConfigStatusLabel($arr['sync_status'])) . '</td>';
|
|
echo '<td><a class="btn btn-outline-primary btn-sm" href="chronotrack_api.php?eve_id=' . intval($arr['eve_id']) . '">Configurer</a></td>';
|
|
echo '</tr>';
|
|
}
|
|
echo '</tbody></table>';
|
|
}
|