Files
ms1inscription-v5/php/chronotrack_api/fx_chronotrack_admin.php
stephan fee6f5c69d Refactor ChronoTrack API integration in event modification form
This commit updates the event modification form to conditionally display the ChronoTrack API section with a toggle button for better user interaction. The rendering function is adjusted to improve layout and heading levels, ensuring a consistent and user-friendly experience. Additionally, the inline rendering logic is refined to enhance the overall integration of ChronoTrack features.
2026-07-02 18:26:26 -04:00

312 lines
14 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 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>';
}
echo '<div class="card mb-4"><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>';
if ($arrConfig !== null && !$blnClosed) {
fxChronotrackApiAdminRenderStatusActions($intEveId, $strStatus);
}
if ($arrConfig !== null) {
fxChronotrackApiAdminRenderRaceMapping($intEveId, $arrConfig, $strLangue, $blnClosed);
}
if (!$blnInlineOnEventForm) {
echo '<p class="mt-3"><a class="btn btn-outline-secondary btn-sm" href="index.php?t='
. urlencode(base64_encode(4)) . '&amp;a=mod&amp;id=' . intval($intEveId) . '">&larr; Retour fiche événement</a></p>';
}
echo '</div>';
fxChronotrackApiAdminRenderPageScript($intEveId, $arrConfig, $blnClosed, $vDomaine);
}
function fxChronotrackApiAdminRenderStatusActions($intEveId, $strStatus) {
echo '<div class="card mb-4"><div class="card-header">2. 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) {
$arrEpreuves = fxChronotrackApiMs1EpreuvesForEvent($intEveId, $strLangue);
$arrMap = fxChronotrackApiRaceMapGetForEvent($intEveId);
echo '<div class="card mb-4"><div class="card-header">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;
}
echo '<p class="small text-muted">ChronoTrack event ID : ' . intval($arrConfig['ct_event_id']) . '</p>';
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']);
$intSel = intval($arrMap[$intEprId]['ct_race_id'] ?? 0);
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>';
}
echo '</div></div>';
}
function fxChronotrackApiAdminRenderPageScript($intEveId, $arrConfig, $blnClosed, $vDomaine) {
$intCtEventId = ($arrConfig !== null) ? intval($arrConfig['ct_event_id']) : 0;
$arrMapCurrent = array();
if ($arrConfig !== null) {
$arrMapRows = fxChronotrackApiRaceMapGetForEvent($intEveId);
foreach ($arrMapRows as $intEprId => $arrRow) {
$arrMapCurrent[intval($intEprId)] = intval($arrRow['ct_race_id']);
}
}
?>
<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) : '';
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;
}
$.post(strAjaxUrl, { a: 'list_races', eve_id: intEveId, ct_event_id: intLinkedCtEventId }, function (res) {
if (!res || res.state !== 'ok') {
return;
}
var arrRaces = res.races || [];
$('.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 $opt = $('<option></option>').val(race.ct_race_id).text((race.name || '') + ' (ID ' + race.ct_race_id + ')');
$sel.append($opt);
});
if (intCurrent > 0) {
$sel.val(String(intCurrent));
}
});
}, 'json');
}
<?php if ($arrConfig !== null) { ?>
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;
}
showMsg('ok', 'Mapping enregistré.');
}, 'json');
});
if (intLinkedCtEventId > 0 && !<?php echo $blnClosed ? 'true' : 'false'; ?>) {
$('#ct_api_btn_load').trigger('click');
}
})(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>';
}