' . '' . '' . $strMsg . '' . ''; } function fxChronotrackApiAdminRenderLoaderAssets($vDomaine) { echo ''; echo ''; } function fxChronotrackApiAdminRenderEventLinkPicker($blnClosed) { echo ''; } function fxChronotrackApiAdminRenderEventPage($intEveId, $strLangue = 'fr', $blnInlineOnEventForm = false, $blnInsideV2Panel = false) { global $vDomaine; $arrEvent = fxChronotrackApiMs1EventRow($intEveId, $strLangue); if ($arrEvent === null) { echo '
Événement MS1 introuvable.
'; return; } $arrConfig = fxChronotrackApiConfigGet($intEveId); $blnConfigured = fxChronotrackApiSettingsConfigured(); $strStatus = $arrConfig['sync_status'] ?? ''; $blnClosed = ($strStatus === MSIN_API_CHRONOTRACK_SYNC_FERME); $strWrapperId = ''; echo '
'; if (!$blnInsideV2Panel) { if ($blnInlineOnEventForm) { echo '

MSIN API ChronoTrack

'; } else { echo '

MSIN API ChronoTrack

'; } } echo '

Configuration du lien pour l\'événement MS1 ' . fxChronotrackApiAdminEsc($arrEvent['eve_nom']) . ' (eve_id ' . intval($arrEvent['eve_id']) . ').

'; if ($blnConfigured) { $strApiBase = fxChronotrackApiGetBaseUrl(); $blnIsTestApi = (strpos($strApiBase, 'api-test') !== false); echo '

Endpoint API : ' . fxChronotrackApiAdminEsc($strApiBase) . ''; if ($blnIsTestApi) { echo ' QA'; } else { echo ' prod'; } echo '

'; } if (!$blnConfigured) { echo '
' . fxChronotrackApiAdminEsc(fxChronotrackApiSettingsErrorMessage()) . '
'; } if ($arrConfig !== null) { echo '
'; echo 'Statut : ' . fxChronotrackApiAdminEsc(fxChronotrackApiConfigStatusLabel($strStatus)) . ''; echo ' — ChronoTrack : ' . fxChronotrackApiAdminEsc($arrConfig['ct_event_name']) . ''; echo ' (ID ' . intval($arrConfig['ct_event_id']) . ')'; echo '
'; } if ($blnClosed) { echo '
Statut fermé : plus de sync auto ni modification du lien/mapping. '; echo 'Le push manuel reste disponible ci-dessous.
'; } $blnEventLinked = ($arrConfig !== null && intval($arrConfig['ct_event_id'] ?? 0) > 0); if (!$blnEventLinked) { fxChronotrackApiAdminRenderEventLinkPicker($blnClosed); } // Sync d’abord (usage courant) ; config repliable ensuite. MSIN-4328 if ($arrConfig !== null && $blnEventLinked && $blnConfigured) { fxChronotrackApiAdminRenderPushPanel($intEveId, $blnClosed, $arrConfig); } if ($arrConfig !== null && $blnEventLinked) { $blnHasSavedMapping = false; $arrMapRows = fxChronotrackApiRaceMapGetForEvent($intEveId); foreach ($arrMapRows as $arrMapRow) { if (intval($arrMapRow['ct_race_id'] ?? 0) > 0) { $blnHasSavedMapping = true; break; } } $strConfigExpanded = $blnHasSavedMapping ? '' : ' show'; echo '
'; echo ''; echo '
'; fxChronotrackApiAdminRenderStatusActions($intEveId, $strStatus, $blnEventLinked); fxChronotrackApiAdminRenderRaceMapping($intEveId, $arrConfig, $strLangue, $blnClosed, $blnEventLinked); echo '
'; } elseif ($arrConfig !== null) { fxChronotrackApiAdminRenderStatusActions($intEveId, $strStatus, $blnEventLinked); fxChronotrackApiAdminRenderRaceMapping($intEveId, $arrConfig, $strLangue, $blnClosed, $blnEventLinked); } if ($blnEventLinked && $blnConfigured) { fxChronotrackApiAdminRenderDiagnosticPanel(intval($arrConfig['ct_event_id'])); } if (!$blnInlineOnEventForm) { echo '

← Retour fiche événement

'; } echo '
'; fxChronotrackApiAdminRenderPageScript($intEveId, $arrConfig, $blnClosed, $vDomaine, $blnInlineOnEventForm); } function fxChronotrackApiAdminRenderStatusActions($intEveId, $strStatus, $blnEventLinked = true) { echo '
Cycle de vie sync
'; echo '

Activer avant la course ; Terminer après pour bloquer la sync auto ' . 'et les changements de config. (La sync auto différentielle viendra ensuite.)

'; echo '
'; if ($strStatus === MSIN_API_CHRONOTRACK_SYNC_LIE) { echo ''; } if ($strStatus === MSIN_API_CHRONOTRACK_SYNC_ACTIF) { echo 'Sync active'; echo ''; } if ($strStatus === MSIN_API_CHRONOTRACK_SYNC_FERME) { echo 'Fermé'; echo ''; } echo '
'; } 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; } } } echo '
Mapping épreuves MS1 → races ChronoTrack
'; if ($arrEpreuves === null || count($arrEpreuves) === 0) { echo '

Aucune épreuve MS1 pour cet événement.

'; return; } if ($blnHasSavedMapping) { echo '
'; echo ''; 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 ''; } echo '
Épreuve MS1Race ChronoTrack
' . fxChronotrackApiAdminEsc($strLabel) . ' (epr_id ' . $intEprId . ')'; if ($intCtRaceId > 0) { echo fxChronotrackApiAdminEsc($strCtRaceName !== '' ? $strCtRaceName : ('Race ID ' . $intCtRaceId)); echo ' (ID ' . $intCtRaceId . ')'; } else { echo '— Non mappé —'; } echo '
'; if (!$blnClosed) { echo ''; } echo '
'; } $strEditStyle = $blnHasSavedMapping ? ' style="display:none;"' : ''; echo '
'; echo '

ChronoTrack event ID : ' . intval($arrConfig['ct_event_id']) . ' — courses CT Live (race_name via GET event/{id}/race).

'; echo '
'; if (!$blnClosed) { echo ''; } echo ''; for ($i = 1; $i <= count($arrEpreuves); $i++) { $intEprId = intval($arrEpreuves[$i]['epr_id']); $strLabel = trim($arrEpreuves[$i]['epr_type'] . ' — ' . $arrEpreuves[$i]['epr_nom']); echo ''; } echo '
Épreuve MS1Race ChronoTrack
' . fxChronotrackApiAdminEsc($strLabel) . ' (epr_id ' . $intEprId . ')'; echo '
'; if (!$blnClosed) { echo ''; if ($blnHasSavedMapping) { echo ' '; } } echo '
'; } function fxChronotrackApiAdminRenderPushPanel($intEveId, $blnClosed = false, $arrConfig = null) { echo '
'; echo '
Synchronisation ChronoTrack
'; echo '
'; if ($blnClosed) { echo '
Événement fermé — envoi manuel encore possible.
'; } // MSIN-4328 — Phase A : config sync auto (exécutée plus tard par cron) $blnAutoOn = (intval($arrConfig['auto_sync_enabled'] ?? 0) === 1); $intAutoInterval = intval($arrConfig['auto_sync_interval_min'] ?? 15); if (!in_array($intAutoInterval, array(1, 5, 15, 30, 60), true)) { $intAutoInterval = 15; } $strUntilRaw = trim((string)($arrConfig['auto_sync_until'] ?? '')); $strUntilLocal = ''; if ($strUntilRaw !== '' && $strUntilRaw !== '0000-00-00 00:00:00') { $intUntilTs = strtotime($strUntilRaw); if ($intUntilTs !== false) { $strUntilLocal = date('Y-m-d\TH:i', $intUntilTs); } } echo '
'; echo '
'; echo ''; echo ''; echo '
'; echo '
'; echo '
'; echo ''; echo '
'; echo '
'; echo ''; echo ''; echo '
'; if (!$blnClosed) { echo '
'; echo ''; echo '
'; } echo '
'; echo '

'; if ($blnAutoOn && $strUntilRaw !== '') { echo 'Config enregistrée — cron auto_chronotrack_sync.php (comme liste d’attente). Arrêt : ' . fxChronotrackApiAdminEsc($strUntilRaw) . '.'; } else { echo 'Cochez, choisissez fréquence + date/heure de fin, puis Enregistrer.'; } echo '

'; $strLastAuto = trim((string)($arrConfig['last_auto_run_at'] ?? '')); echo '

'; echo 'Dernier passage auto : '; if ($strLastAuto !== '' && $strLastAuto !== '0000-00-00 00:00:00') { echo fxChronotrackApiAdminEsc($strLastAuto); } else { echo 'jamais (le cron n’a pas encore poussé cet event)'; } echo '

'; echo '
'; echo '
'; echo '
'; // Zone employé : statut clair + actions echo '
'; echo '
Cliquez Actualiser pour vérifier s’il y a des changements à envoyer.
'; echo '
'; echo '
'; echo ' '; echo ' '; echo ' '; echo ' '; echo ''; echo '
'; echo '

Envoi manuel : nouveaux / modifiés depuis le dernier push. ' . 'Cet écran se rafraîchit doucement chaque minute (statut MS1 seulement).

'; echo '
'; echo '
'; // Détails / debug : sous les boutons, repliés par défaut echo '
'; echo ''; echo '
'; echo '
'; echo '
'; echo ' '; echo ''; echo '

Info technique : external ID = par_id_original. ' . 'Le comptage Live sert aussi à lier les entry_id pour le bouton fiche.

'; echo '
'; echo '
'; } function fxChronotrackApiAdminRenderDiagnosticPanel($intCtEventId) { echo '
'; echo '
'; echo '
'; echo '

Outils de support (credentials MS1 / OAuth en cache). ' . 'Pas nécessaire pour une course en production.

'; echo ' '; echo ' '; echo ' '; echo ''; echo '
Cliquez sur « Lancer le diagnostic API » ou « Sonde ».
'; echo '
'; } function fxChronotrackApiAdminRenderPageScript($intEveId, $arrConfig, $blnClosed, $vDomaine, $blnInlineOnEventForm = false) { $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; } } } ?> MSIN API ChronoTrack — Courses connectées'; } echo '

Événements liés ou sync active. Le dashboard détaillé (diff, push) viendra en Phase 2.

'; if ($arrRows === null || count($arrRows) === 0) { echo '
Aucune course connectée.
'; return; } echo ''; for ($i = 1; $i <= count($arrRows); $i++) { $arr = $arrRows[$i]; echo ''; echo ''; echo ''; echo ''; echo ''; echo ''; } echo '
Événement MS1ChronoTrackStatut
' . fxChronotrackApiAdminEsc($arr['eve_nom']) . ' (' . intval($arr['eve_id']) . ')' . fxChronotrackApiAdminEsc($arr['ct_event_name']) . ' (' . intval($arr['ct_event_id']) . ')' . fxChronotrackApiAdminEsc(fxChronotrackApiConfigStatusLabel($arr['sync_status'])) . 'Configurer
'; }