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.
312 lines
14 KiB
PHP
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)) . '&a=mod&id=' . intval($intEveId) . '">← 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>';
|
|
}
|