' . '' . '' . $strMsg . '' . ''; } function fxChronotrackApiAdminRenderLoaderAssets($vDomaine) { echo ''; echo ''; } function fxChronotrackApiAdminRenderEventLinkPicker($blnClosed) { echo ''; } /** * MSIN-4328 — résumé compact + CTA hub (fiche événement superadm uniquement). */ function fxChronotrackApiAdminRenderEventFormLinkedSummary($intEveId, $arrConfig, $blnClosed) { $strHubUrl = 'chronotrack_api.php?eve_id=' . intval($intEveId) . '&focus=1'; $strStatus = $arrConfig['sync_status'] ?? ''; echo '
'; echo '
'; echo '

Événement lié à ChronoTrack.'; echo ' La configuration, l’envoi et le diagnostic s’ouvrent en fenêtre opération (plein écran).

'; echo '

'; echo 'Statut : ' . fxChronotrackApiAdminEsc(fxChronotrackApiConfigStatusLabel($strStatus)) . ''; echo ' — CT : ' . fxChronotrackApiAdminEsc($arrConfig['ct_event_name'] ?? '') . ''; echo ' (ID ' . intval($arrConfig['ct_event_id'] ?? 0) . ')'; if ($blnClosed) { echo ' — fermé'; } echo '

'; echo '' . ' Ouvrir le tableau de bord (nouvelle fenêtre)'; echo '
'; } /** * MSIN-4328 — formulaire sync auto (zone Configuration). */ function fxChronotrackApiAdminRenderAutoSyncForm($blnClosed, $arrConfig) { $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 '
Mise à jour automatique
'; 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. 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 '
'; } /** * MSIN-4328 — bandeau compact sync auto (zone Opération). */ function fxChronotrackApiAdminRenderAutoSyncBanner($blnClosed, $arrConfig) { $blnAutoOn = (intval($arrConfig['auto_sync_enabled'] ?? 0) === 1); $strUntilRaw = trim((string)($arrConfig['auto_sync_until'] ?? '')); $strLastAuto = trim((string)($arrConfig['last_auto_run_at'] ?? '')); $intInterval = intval($arrConfig['auto_sync_interval_min'] ?? 15); echo '
'; if ($blnAutoOn && !$blnClosed) { echo 'Sync auto ON'; echo 'toutes les ' . intval($intInterval) . ' min'; if ($strUntilRaw !== '' && $strUntilRaw !== '0000-00-00 00:00:00') { echo ' · fin ' . fxChronotrackApiAdminEsc($strUntilRaw); } echo ''; } elseif ($blnClosed) { echo 'Sync auto off'; echo 'Événement fermé'; } else { echo 'Sync auto OFF'; echo 'Pas de push automatique'; } if ($strLastAuto !== '' && $strLastAuto !== '0000-00-00 00:00:00') { echo ' Dernier auto : ' . fxChronotrackApiAdminEsc($strLastAuto) . ''; } echo ' ' . 'Régler'; echo '
'; } function fxChronotrackApiAdminHasSavedRaceMapping($intEveId) { $arrMapRows = fxChronotrackApiRaceMapGetForEvent($intEveId); foreach ($arrMapRows as $arrMapRow) { if (intval($arrMapRow['ct_race_id'] ?? 0) > 0) { return true; } } return false; } function fxChronotrackApiAdminRenderEventPage($intEveId, $strLangue = 'fr', $blnInlineOnEventForm = false, $blnInsideV2Panel = false, $blnFocus = 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); $blnEventLinked = ($arrConfig !== null && intval($arrConfig['ct_event_id'] ?? 0) > 0); echo '
'; if (!$blnInsideV2Panel) { if ($blnInlineOnEventForm) { echo '

MSIN API ChronoTrack

'; } else { echo '

MSIN API ChronoTrack

'; } } // --- Fiche événement : lien seulement + CTA hub (MSIN-4328) --- if ($blnInlineOnEventForm) { echo '

Branchement MS1 ↔ ChronoTrack pour ' . fxChronotrackApiAdminEsc($arrEvent['eve_nom']) . ' (eve_id ' . intval($arrEvent['eve_id']) . '). Opération et config : Courses connectées.

'; if (!$blnConfigured) { echo '
' . fxChronotrackApiAdminEsc(fxChronotrackApiSettingsErrorMessage()) . '
'; } if (!$blnEventLinked) { fxChronotrackApiAdminRenderEventLinkPicker($blnClosed); } else { fxChronotrackApiAdminRenderEventFormLinkedSummary($intEveId, $arrConfig, $blnClosed); } echo '
'; fxChronotrackApiAdminRenderPageScript($intEveId, $arrConfig, $blnClosed, $vDomaine, true); return; } // --- Hub Courses connectées : Opération / Configuration / Diagnostic --- echo '
'; echo '
'; echo '
' . fxChronotrackApiAdminEsc($arrEvent['eve_nom']) . '
'; echo '
eve_id ' . intval($arrEvent['eve_id']); if ($arrConfig !== null) { echo ' · CT ' . fxChronotrackApiAdminEsc($arrConfig['ct_event_name'] ?? '') . ' (#' . intval($arrConfig['ct_event_id'] ?? 0) . ')' . ' · ' . fxChronotrackApiAdminEsc(fxChronotrackApiConfigStatusLabel($strStatus)); } echo '
'; echo '
'; if ($blnFocus) { echo '' . ' Fermer / retour super admin'; } else { echo '← Liste '; echo '' . ' Ouvrir plein écran'; } echo '
'; 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 ($blnClosed) { echo '
Statut fermé : plus de sync auto ni modification du mapping. ' . 'Le push manuel reste disponible dans Opération.
'; } if (!$blnEventLinked) { fxChronotrackApiAdminRenderEventLinkPicker($blnClosed); } // Bloc A — Opération (toujours ouvert) if ($arrConfig !== null && $blnEventLinked && $blnConfigured) { fxChronotrackApiAdminRenderPushPanel($intEveId, $blnClosed, $arrConfig); } // Bloc B — Configuration (repliée si mapping prêt) if ($arrConfig !== null && $blnEventLinked) { $blnHasSavedMapping = fxChronotrackApiAdminHasSavedRaceMapping($intEveId); $strConfigExpanded = $blnHasSavedMapping ? '' : ' show'; echo '
'; echo '
'; echo ''; echo 'Configuration'; if ($blnHasSavedMapping) { echo ' Prêt'; } else { echo ' À compléter'; } echo ' (cycle de vie, mapping, sync auto)'; echo '
'; echo '
'; fxChronotrackApiAdminRenderStatusActions($intEveId, $strStatus, $blnEventLinked); fxChronotrackApiAdminRenderRaceMapping($intEveId, $arrConfig, $strLangue, $blnClosed, $blnEventLinked); if ($blnConfigured) { fxChronotrackApiAdminRenderAutoSyncForm($blnClosed, $arrConfig); } echo '
'; } elseif ($arrConfig !== null) { fxChronotrackApiAdminRenderStatusActions($intEveId, $strStatus, $blnEventLinked); fxChronotrackApiAdminRenderRaceMapping($intEveId, $arrConfig, $strLangue, $blnClosed, $blnEventLinked); } // Bloc C — Diagnostic (toujours fermé par défaut) if ($blnEventLinked && $blnConfigured) { fxChronotrackApiAdminRenderDiagnosticPanel(intval($arrConfig['ct_event_id'])); } if (!$blnFocus) { echo '

← Retour fiche événement

'; } echo ''; fxChronotrackApiAdminRenderPageScript($intEveId, $arrConfig, $blnClosed, $vDomaine, false); } 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) { // MSIN-4328 — tableau de bord Opération (layout stable, suivi course) echo '
'; echo '
Opération
'; echo '
'; if ($blnClosed) { echo '
Événement fermé — envoi manuel encore possible.
'; } if (is_array($arrConfig)) { fxChronotrackApiAdminRenderAutoSyncBanner($blnClosed, $arrConfig); } echo '
'; echo '

En attente

'; echo '

Cliquez Actualiser pour vérifier s’il y a des changements.

'; echo '

'; echo '
'; // KPI fixes (mis à jour en place — pas de reflow « Excel ») echo '
'; echo '
Total MS1
' . '
'; echo '
Éligibles
' . '
'; echo '
Exclus
' . '
'; echo '
À envoyer
' . '
'; echo '
'; echo '

ChronoTrack Live : non chargé ' . '(outil dans Diagnostic).

'; echo '
'; echo ' '; echo ' '; echo ' '; echo ''; echo '
'; echo '
'; echo ''; echo ''; echo '
'; echo '

Envoyer = nouveaux / modifiés. ' . 'Tout renvoyer = tous les éligibles (même barre %).

'; echo '
'; echo '
'; echo '
'; echo ''; echo '
'; echo '
'; echo '
'; echo '

external ID = par_id_original. ' . 'Comptage Live / sondes → Diagnostic.

'; echo '
'; echo '
'; } function fxChronotrackApiAdminRenderDiagnosticPanel($intCtEventId) { // MSIN-4328 — zone Diagnostic / debug (fermée par défaut) echo '
'; echo '
'; echo '
'; echo '

Outils de support et tests. Pas nécessaires pour l’opération courante.

'; echo '
'; echo ' '; echo ' '; echo ' '; echo ''; echo '
'; echo '
'; echo ' '; echo ' '; echo ' '; echo ''; echo '
'; echo '
Cliquez sur un outil ci-dessus.
'; 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 MS1 liés à ChronoTrack. « Ouvrir » lance le tableau de bord en nouvelle fenêtre (plein écran, sans menu).

'; if ($arrRows === null || count($arrRows) === 0) { echo '
Aucune course connectée. Liez un événement depuis sa fiche (panneau ChronoTrack).
'; 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'])) . 'Ouvrir
'; }