From f2519dc987cc36754a309c311243862d5aaf85c3 Mon Sep 17 00:00:00 2001 From: stephan Date: Fri, 3 Jul 2026 20:35:43 -0400 Subject: [PATCH] Enhance ChronoTrack API push functionality and improve user feedback This commit updates the ChronoTrack API push process by refining the user interface messaging for manual pushes, clarifying the status of participants, and improving the confirmation prompts. Key changes include the introduction of new variables for tracking eligible participants and orphans, as well as enhanced HTML output for better clarity on the current state of entries. These improvements aim to streamline the push process and enhance the overall user experience in managing participant data. --- php/chronotrack_api/fx_chronotrack_admin.php | 70 ++++++++++++-------- php/chronotrack_api/fx_chronotrack_sync.php | 30 ++++++++- 2 files changed, 72 insertions(+), 28 deletions(-) diff --git a/php/chronotrack_api/fx_chronotrack_admin.php b/php/chronotrack_api/fx_chronotrack_admin.php index 221ad54..dcfbebd 100644 --- a/php/chronotrack_api/fx_chronotrack_admin.php +++ b/php/chronotrack_api/fx_chronotrack_admin.php @@ -223,9 +223,9 @@ function fxChronotrackApiAdminRenderPushPanel($intEveId, $blnClosed = false) { if ($blnClosed) { echo '
Événement fermé — push manuel autorisé pour tests ou rattrapage.
'; } - echo '

Lit resultats_participants (comme le rapport chrono). '; - echo 'External ID = par_id_original. Dossard obligatoire ; sexe vide ou « n » (non défini) → NOT SPECIFIED côté CT ; '; - echo 'doublon de dossard MS1 = aucun des inscrits concernés n\'est transféré.

'; + echo '

Push manuel — pas de sync automatique. Lit resultats_participants '; + echo '(comme le rapport chrono). External ID = par_id_original. '; + echo 'Après une correction MS1 : Actualiser puis Pousser.

'; echo '
' . 'Cliquez sur Actualiser pour calculer le diff MS1 ↔ ChronoTrack.
'; echo '
'; @@ -754,46 +754,60 @@ function fxChronotrackApiAdminRenderPageScript($intEveId, $arrConfig, $blnClosed } var intBlocked = res.blocked_count || 0; - var intTransfer = res.transferable || 0; + var intEligible = res.transferable || 0; var intMs1 = res.ms1_total || 0; - var intAlreadyCt = res.already_in_ct || 0; + var intWillUpdate = res.will_update || res.already_in_ct || 0; var intWillCreate = res.will_create || res.to_create || 0; var intCtTotal = res.ct_total || 0; + var intOrphans = res.ct_orphans || 0; - var html = '

Avant le prochain push — lecture MS1 + ChronoTrack Live :

'; - html += '
'; - html += '
' + intMs1 + 'inscrits MS1
'; - html += '
' + intTransfer + 'seront envoyés
'; - html += '
'; - html += '' + intBlocked + 'exclus
'; - html += '
' + intAlreadyCt + 'déjà sur CT Live
'; - html += '
' + intCtTotal + 'entries CT (total)
'; - html += '
'; + var html = '

État actuel (MS1 + ChronoTrack Live) :

'; - if (intTransfer > 0) { - html += '

Parmi les ' + intTransfer + ' envoyés : '; - html += '' + intWillCreate + ' création(s) · '; - html += '' + intAlreadyCt + ' mise(s) à jour.

'; + html += '
'; + html += '

MS1' + intMs1 + ' inscrits : '; + html += '' + intEligible + ' éligibles'; + if (intBlocked > 0) { + html += ' · ' + intBlocked + ' exclus'; } + html += ' (équipe, dossard, sexe, etc.)

'; + html += '

ChronoTrack' + intCtTotal + ' entries'; + if (intEligible > 0 || intOrphans > 0) { + html += ' ('; + if (intWillUpdate > 0 || intWillCreate > 0) { + html += intWillUpdate + ' déjà liées à un éligible MS1'; + if (intWillCreate > 0) { + html += ', ' + intWillCreate + ' éligible(s) pas encore sur CT'; + } + } + if (intOrphans > 0) { + html += (intWillUpdate > 0 || intWillCreate > 0 ? ', ' : '') + intOrphans + ' sans correspondant MS1'; + } + html += ')'; + } + html += '

'; - html += '

Un deuxième push renvoie les mêmes transférables : ChronoTrack fait une mise à jour (upsert par external_id), pas de doublon.

'; + html += '

Au prochain push — renvoie les ' + intEligible + + ' éligibles avec les données MS1 actuelles '; + html += '(' + intWillCreate + ' création · ' + intWillUpdate + ' mise à jour). '; + html += 'Ce n\'est pas une sync auto : corrigez dans MS1 → ActualiserPousser.

'; - if (intCtTotal > 0 && (res.ct_orphans || 0) > 0) { - html += '

' + res.ct_orphans + ' entry(s) CT sans correspondant MS1 transférable.

'; + if (intOrphans > 0) { + html += '

' + intOrphans + + ' entry(s) CT sans correspondant MS1 éligible (tests, anciennes données).

'; } if (intBlocked > 0) { html += '

'; - html += 'Les ' + intBlocked + ' exclus ne seront pas envoyés tant que non corrigés dans MS1. '; + html += 'Les ' + intBlocked + ' exclus ne partent pas tant que non corrigés dans MS1. '; if (res.blocked_team > 0) { - html += '' + res.blocked_team + ' sont des inscriptions équipe (section dédiée). '; + html += '' + res.blocked_team + ' sont des inscriptions équipe. '; } - html += 'Détail dans la zone ci-dessous.

'; + html += 'Détail ci-dessous.

'; } $sum.html(html); renderBlockedPanel(res); - $btn.prop('disabled', intTransfer <= 0); + $btn.prop('disabled', intEligible <= 0); } function renderPushResult(res, strTitle) { @@ -904,7 +918,11 @@ function fxChronotrackApiAdminRenderPageScript($intEveId, $arrConfig, $blnClosed }); $('#ct_api_btn_push').on('click', function () { - if (!window.confirm('Envoyer les participants éligibles vers ChronoTrack ?\n\nMS1 ne sera pas modifié.')) { + var intEligible = (objPushPreview && objPushPreview.transferable) ? objPushPreview.transferable : 0; + var strConfirm = 'Push manuel — renvoyer les ' + intEligible + ' participants éligibles vers ChronoTrack ?\n\n'; + strConfirm += 'Données MS1 actuelles (upsert par external_id). Pas de sync automatique.\n'; + strConfirm += 'MS1 ne sera pas modifié.'; + if (!window.confirm(strConfirm)) { return; } var $btn = $(this); diff --git a/php/chronotrack_api/fx_chronotrack_sync.php b/php/chronotrack_api/fx_chronotrack_sync.php index bd51c44..b755c99 100644 --- a/php/chronotrack_api/fx_chronotrack_sync.php +++ b/php/chronotrack_api/fx_chronotrack_sync.php @@ -485,9 +485,25 @@ function fxChronotrackApiSyncBuildEntryPayload(array $arrRow, array $arrRaceMap, $arrEntry['country_code'] = $strCountry; } + $arrEntry['entry_status'] = fxChronotrackApiSyncMapEntryStatus( + $arrRow['par_statut_course'] ?? 'CONF', + intval($arrRow['is_cancelled'] ?? 0) === 1 + ); + return $arrEntry; } +function fxChronotrackApiSyncEnrichPayloadWithCtEntry(array $arrPayload, $arrCtEntity) { + if (!is_array($arrCtEntity)) { + return $arrPayload; + } + $strEntryId = fxChronotrackApiEntityId($arrCtEntity); + if ($strEntryId !== '') { + $arrPayload['entry_id'] = $strEntryId; + } + return $arrPayload; +} + function fxChronotrackApiSyncPostEntries($intCtEventId, array $tabEntries) { $intCtEventId = intval($intCtEventId); if ($intCtEventId <= 0 || count($tabEntries) === 0) { @@ -770,10 +786,20 @@ function fxChronotrackApiSyncPushEvent($intEveId) { ); } + $tabCtEntries = array(); + $arrCtFetch = fxChronotrackApiSyncFetchCtEntriesIndexed($intCtEventId); + if ($arrCtFetch['state'] === 'ok') { + $tabCtEntries = $arrCtFetch['entries']; + } + $tabBatch = array(); $tabMeta = array(); foreach ($arrClass['transferable'] as $arrItem) { - $tabBatch[] = $arrItem['payload']; + $arrPayload = fxChronotrackApiSyncEnrichPayloadWithCtEntry( + $arrItem['payload'], + $tabCtEntries[$arrItem['payload']['external_id']] ?? null + ); + $tabBatch[] = $arrPayload; $tabMeta[] = array( 'par_id' => intval($arrItem['row']['par_id'] ?? 0), 'external_id' => $arrItem['payload']['external_id'], @@ -860,7 +886,7 @@ function fxChronotrackApiSyncPushEvent($intEveId) { $intBlockedCount = $arrClass['blocked_count']; $strMessage = ($intErr === 0) - ? ($intOk . ' participant(s) envoyé(s) vers ChronoTrack.') + ? ($intOk . ' éligible(s) (re)envoyé(s) vers ChronoTrack (données MS1 actuelles, upsert par external_id).') : ($intOk . ' OK, ' . $intErr . ' erreur(s) API.'); return array(