MSIN-2365 Implement global batch assignment enhancements with improved error handling and UI feedback
This commit introduces several updates to the global batch assignment functionality, including new CSS styles for unavailable states, enhanced JavaScript functions for batch processing, and improved error handling in AJAX requests. Language support is expanded with new localized messages for user guidance. The version code is incremented to reflect these changes, aiming to enhance user experience and maintainability in the bib management system.
This commit is contained in:
@ -1083,6 +1083,10 @@ ul.ms1-menu-compte li a:hover, ul.ms1-menu-compte li a:active {
|
||||
padding-top:12px;
|
||||
border-top:1px solid #d8e3ef;
|
||||
}
|
||||
.bib-global-batch--unavailable{
|
||||
background:#f8f9fa;
|
||||
border-style:dashed;
|
||||
}
|
||||
.bib-global-epr{
|
||||
margin-bottom:12px;
|
||||
padding:8px 10px;
|
||||
|
||||
@ -89,6 +89,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
/** MSIN-4436 — Force la fermeture du loader (batch global : évite overlay bloqué). */
|
||||
function bibBatchProgressForceHide() {
|
||||
bibLoaderDepth = 0;
|
||||
bibBatchProgressHide();
|
||||
}
|
||||
|
||||
function bibAfterBatchGoSuccess(row, data, isResetMode) {
|
||||
let existing = document.querySelector('.epr-row-view');
|
||||
if (existing) {
|
||||
@ -102,9 +108,10 @@
|
||||
bibAfterBatchGoRefreshOnly(row, isResetMode);
|
||||
}
|
||||
|
||||
function bibAfterBatchGoRefreshOnly(row, isResetMode) {
|
||||
function bibAfterBatchGoRefreshOnly(row, isResetMode, options) {
|
||||
options = options || {};
|
||||
if (!row) {
|
||||
return;
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
if (isResetMode) {
|
||||
@ -139,7 +146,10 @@
|
||||
|
||||
syncBibUiState(row);
|
||||
|
||||
bibFetch('/ajax_bib_range.php', {
|
||||
var useLoader = options.loader !== false;
|
||||
var skipAnomalies = options.skipAnomalies === true;
|
||||
|
||||
return bibFetch('/ajax_bib_range.php', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||
body:
|
||||
@ -147,15 +157,25 @@
|
||||
+ '&epr_id=' + encodeURIComponent(row.dataset.eprId)
|
||||
+ '&mode=assign'
|
||||
+ bibAjaxLangSuffix()
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(refresh => {
|
||||
}, { loader: useLoader })
|
||||
.then(function (res) {
|
||||
if (!res.ok) {
|
||||
throw new Error(bibJs('jsErrGeneric'));
|
||||
}
|
||||
return res.json();
|
||||
})
|
||||
.then(function (refresh) {
|
||||
|
||||
if (!refresh.success) return;
|
||||
if (!refresh.success) {
|
||||
return;
|
||||
}
|
||||
|
||||
let container = setBibContainerHtml(row, refresh.html);
|
||||
if (!container) return;
|
||||
bibFetch('/ajax_bib_range.php', {
|
||||
let containerRef = setBibContainerHtml(row, refresh.html, { skipAnomalies: skipAnomalies });
|
||||
if (!containerRef) {
|
||||
return;
|
||||
}
|
||||
|
||||
return bibFetch('/ajax_bib_range.php', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
@ -164,11 +184,13 @@
|
||||
'action=batch_analysis'
|
||||
+ '&epr_id=' + encodeURIComponent(row.dataset.eprId)
|
||||
+ bibAjaxLangSuffix()
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(stats => {
|
||||
}, { loader: false })
|
||||
.then(function (res) { return res.json(); })
|
||||
.then(function (stats) {
|
||||
|
||||
if (!stats.success) return;
|
||||
if (!stats.success) {
|
||||
return;
|
||||
}
|
||||
|
||||
bibSyncEprBibStats(row, stats.info);
|
||||
syncBibUiState(row);
|
||||
@ -192,30 +214,50 @@
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||
body: body
|
||||
}).then(function (res) { return res.json(); });
|
||||
})
|
||||
.then(function (res) {
|
||||
if (!res.ok) {
|
||||
throw new Error(bibJs('jsErrGeneric'));
|
||||
}
|
||||
return res.json();
|
||||
})
|
||||
.catch(function (err) {
|
||||
if (err && err.message) {
|
||||
throw err;
|
||||
}
|
||||
throw new Error(bibJs('jsErrGeneric'));
|
||||
});
|
||||
}
|
||||
|
||||
function bibRunBatchApplyChunks(row, selected, ba_id, options) {
|
||||
options = options || {};
|
||||
var showResult = options.showResult !== false;
|
||||
var manageLoader = options.manageLoader !== false;
|
||||
var deferUiRefresh = options.deferUiRefresh === true;
|
||||
var onProgress = options.onProgress;
|
||||
var totalInitial = options.totalInitial > 0
|
||||
? options.totalInitial
|
||||
: bibRowPendingBibs(row);
|
||||
var batchReset = true;
|
||||
var chunkGuard = 0;
|
||||
var maxChunks = Math.max(50, Math.ceil((totalInitial || 1) / BIB_BATCH_CHUNK_SIZE) + 5);
|
||||
|
||||
if (manageLoader) {
|
||||
bibBatchProgressShow(0, totalInitial > 0 ? totalInitial : 1);
|
||||
}
|
||||
|
||||
function runChunk() {
|
||||
chunkGuard++;
|
||||
if (chunkGuard > maxChunks) {
|
||||
return Promise.reject(new Error(bibJs('jsErrGeneric')));
|
||||
}
|
||||
|
||||
return bibFetchBatchApplyChunk(row, selected, ba_id, {
|
||||
batchReset: batchReset,
|
||||
totalInitial: totalInitial
|
||||
}).then(function (data) {
|
||||
if (!data.success) {
|
||||
throw new Error(data.message || bibJs('jsErrGeneric'));
|
||||
if (!data || !data.success) {
|
||||
throw new Error((data && data.message) || bibJs('jsErrGeneric'));
|
||||
}
|
||||
|
||||
if (data.total_initial) {
|
||||
@ -233,8 +275,8 @@
|
||||
if (data.complete) {
|
||||
if (showResult) {
|
||||
bibAfterBatchGoSuccess(row, data, false);
|
||||
} else {
|
||||
bibAfterBatchGoRefreshOnly(row, false);
|
||||
} else if (!deferUiRefresh) {
|
||||
bibAfterBatchGoRefreshOnly(row, false, { loader: false, skipAnomalies: true });
|
||||
}
|
||||
return data;
|
||||
}
|
||||
@ -260,6 +302,25 @@
|
||||
// MSIN-4436 — Batch global (épreuves solo).
|
||||
var bibGlobalBatchPanel = document.getElementById('bib-global-batch');
|
||||
|
||||
function bibSetGlobalBatchInfo(infoEl, text, state) {
|
||||
if (!infoEl) {
|
||||
return;
|
||||
}
|
||||
|
||||
infoEl.textContent = text || '';
|
||||
infoEl.classList.remove('text-muted', 'text-danger', 'text-warning', 'text-success');
|
||||
|
||||
if (state === 'error') {
|
||||
infoEl.classList.add('text-danger');
|
||||
} else if (state === 'warning') {
|
||||
infoEl.classList.add('text-warning');
|
||||
} else if (state === 'ok') {
|
||||
infoEl.classList.add('text-success');
|
||||
} else {
|
||||
infoEl.classList.add('text-muted');
|
||||
}
|
||||
}
|
||||
|
||||
function bibCollectGlobalBatchPlans() {
|
||||
if (!bibGlobalBatchPanel) {
|
||||
return [];
|
||||
@ -304,17 +365,25 @@
|
||||
}
|
||||
|
||||
if (plans.length === 0) {
|
||||
infoEl.textContent = bibJs('jsGlobalNoEpr') || bibJs('jsBatchNone');
|
||||
return Promise.resolve();
|
||||
bibSetGlobalBatchInfo(
|
||||
infoEl,
|
||||
bibJs('jsGlobalNoEpr') || bibJs('jsBatchNone'),
|
||||
'error'
|
||||
);
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
var hasRange = plans.some(function (p) { return p.ranges.length > 0; });
|
||||
if (!hasRange) {
|
||||
infoEl.textContent = bibJs('jsGlobalNoSeq') || bibJs('jsNoSeqSelected');
|
||||
return Promise.resolve();
|
||||
bibSetGlobalBatchInfo(
|
||||
infoEl,
|
||||
bibJs('jsGlobalNoSeq') || bibJs('jsNoSeqSelected'),
|
||||
'error'
|
||||
);
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
infoEl.textContent = bibJs('jsLoaderWait') || '…';
|
||||
bibSetGlobalBatchInfo(infoEl, bibJs('jsLoaderWait') || '…', 'normal');
|
||||
|
||||
return fetch('/ajax_bib_range.php', {
|
||||
method: 'POST',
|
||||
@ -329,13 +398,21 @@
|
||||
.then(function (res) { return res.json(); })
|
||||
.then(function (data) {
|
||||
if (!data.success) {
|
||||
infoEl.textContent = data.message || bibJs('jsErrGeneric');
|
||||
return;
|
||||
bibSetGlobalBatchInfo(
|
||||
infoEl,
|
||||
data.message || bibJs('jsErrGeneric'),
|
||||
'error'
|
||||
);
|
||||
return data;
|
||||
}
|
||||
infoEl.textContent = data.info || '';
|
||||
|
||||
var state = (data.stats && data.stats.nb_manque > 0) ? 'warning' : 'normal';
|
||||
bibSetGlobalBatchInfo(infoEl, data.info || '', state);
|
||||
return data;
|
||||
})
|
||||
.catch(function () {
|
||||
infoEl.textContent = bibJs('jsErrGeneric');
|
||||
bibSetGlobalBatchInfo(infoEl, bibJs('jsErrGeneric'), 'error');
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@ -369,16 +446,22 @@
|
||||
+ '&plans=' + encodeURIComponent(JSON.stringify(plans))
|
||||
+ bibAjaxLangSuffix()
|
||||
})
|
||||
.then(function (res) { return res.json(); })
|
||||
.then(function (res) {
|
||||
if (!res.ok) {
|
||||
throw new Error(bibJs('jsErrGeneric'));
|
||||
}
|
||||
return res.json();
|
||||
})
|
||||
.then(function (analysis) {
|
||||
if (!analysis.success) {
|
||||
throw new Error(analysis.message || bibJs('jsErrGeneric'));
|
||||
if (!analysis || !analysis.success) {
|
||||
throw new Error((analysis && analysis.message) || bibJs('jsErrGeneric'));
|
||||
}
|
||||
|
||||
var globalTotal = (analysis.stats && analysis.stats.nb_participants)
|
||||
? analysis.stats.nb_participants
|
||||
: 1;
|
||||
var completedOffset = 0;
|
||||
var runResults = [];
|
||||
|
||||
bibBatchProgressShow(0, globalTotal);
|
||||
|
||||
@ -388,7 +471,7 @@
|
||||
chain = chain.then(function () {
|
||||
var row = moduleBib.querySelector('.epr-row[data-epr-id="' + plan.epr_id + '"]');
|
||||
if (!row) {
|
||||
return null;
|
||||
throw new Error(bibJs('jsErrGeneric'));
|
||||
}
|
||||
|
||||
var pendingBefore = bibRowPendingBibs(row);
|
||||
@ -397,11 +480,17 @@
|
||||
return bibRunBatchApplyChunks(row, plan.ranges, ba_id, {
|
||||
showResult: false,
|
||||
manageLoader: false,
|
||||
deferUiRefresh: true,
|
||||
totalInitial: pendingBefore,
|
||||
onProgress: function (processed) {
|
||||
bibBatchProgressShow(offset + processed, globalTotal);
|
||||
}
|
||||
}).then(function () {
|
||||
}).then(function (data) {
|
||||
runResults.push({
|
||||
epr_id: plan.epr_id,
|
||||
assigned: data.assigned_total || 0,
|
||||
total: data.total_initial || pendingBefore
|
||||
});
|
||||
completedOffset = offset + pendingBefore;
|
||||
});
|
||||
});
|
||||
@ -409,17 +498,62 @@
|
||||
|
||||
return chain.then(function () {
|
||||
bibBatchProgressShow(globalTotal, globalTotal);
|
||||
if (typeof refreshBibAnomaliesPanel === 'function') {
|
||||
refreshBibAnomaliesPanel();
|
||||
}
|
||||
return updateGlobalBatchAnalysis();
|
||||
|
||||
var refreshChain = Promise.resolve();
|
||||
runResults.forEach(function (result) {
|
||||
refreshChain = refreshChain.then(function () {
|
||||
var row = moduleBib.querySelector('.epr-row[data-epr-id="' + result.epr_id + '"]');
|
||||
if (!row) {
|
||||
return null;
|
||||
}
|
||||
return bibAfterBatchGoRefreshOnly(row, false, {
|
||||
loader: false,
|
||||
skipAnomalies: true
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return refreshChain.then(function () {
|
||||
if (typeof refreshBibAnomaliesPanel === 'function') {
|
||||
refreshBibAnomaliesPanel();
|
||||
}
|
||||
|
||||
var infoEl = bibGlobalBatchPanel.querySelector('.bib-global-batch__info');
|
||||
var totalAssigned = 0;
|
||||
var totalExpected = 0;
|
||||
|
||||
runResults.forEach(function (result) {
|
||||
totalAssigned += result.assigned;
|
||||
totalExpected += result.total;
|
||||
});
|
||||
|
||||
var remaining = Math.max(0, totalExpected - totalAssigned);
|
||||
|
||||
return updateGlobalBatchAnalysis().then(function () {
|
||||
if (totalAssigned === 0 && totalExpected > 0) {
|
||||
var msgNone = bibJs('jsAssignNone') || bibJs('jsErrGeneric');
|
||||
bibSetGlobalBatchInfo(infoEl, msgNone, 'error');
|
||||
alert(msgNone);
|
||||
} else if (remaining > 0) {
|
||||
var msgPartial = bibJsFmt('jsAssignPartial', totalAssigned, remaining);
|
||||
bibSetGlobalBatchInfo(infoEl, msgPartial, 'warning');
|
||||
alert(msgPartial);
|
||||
} else if (totalAssigned > 0) {
|
||||
bibSetGlobalBatchInfo(
|
||||
infoEl,
|
||||
bibJsFmt('jsAssignFull', totalAssigned),
|
||||
'ok'
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
})
|
||||
.catch(function (err) {
|
||||
alert(err.message || bibJs('jsErrGeneric'));
|
||||
})
|
||||
.finally(function () {
|
||||
bibBatchProgressHide();
|
||||
bibBatchProgressForceHide();
|
||||
});
|
||||
}
|
||||
|
||||
@ -714,7 +848,8 @@
|
||||
});
|
||||
}
|
||||
|
||||
function setBibContainerHtml(row, html) {
|
||||
function setBibContainerHtml(row, html, opts) {
|
||||
opts = opts || {};
|
||||
let container = getBibContainer(row);
|
||||
if (!container) return null;
|
||||
// renderBibRanges renvoie un bloc .epr-table.bib-container complet.
|
||||
@ -729,7 +864,9 @@
|
||||
initBibRangeRows(container);
|
||||
}
|
||||
syncBibUiState(row);
|
||||
refreshBibAnomaliesPanel();
|
||||
if (!opts.skipAnomalies) {
|
||||
refreshBibAnomaliesPanel();
|
||||
}
|
||||
return container;
|
||||
}
|
||||
|
||||
|
||||
@ -2901,6 +2901,11 @@ function fxBibStaticFallback($clef) {
|
||||
'bib_v4_anomalies_order_fmt' => ['fr' => 'Commande %s', 'en' => 'Order %s'],
|
||||
'bib_v4_anomalies_assign_link' => ['fr' => 'Assigner un dossard', 'en' => 'Assign a bib'],
|
||||
'bib_v4_js_loader_wait' => ['fr' => 'Chargement...', 'en' => 'Loading...'],
|
||||
'bib_v4_js_global_no_epr' => ['fr' => 'Sélectionnez au moins une épreuve solo.', 'en' => 'Select at least one solo race.'],
|
||||
'bib_v4_js_global_no_seq' => ['fr' => 'Cochez au moins une séquence pour les épreuves sélectionnées.', 'en' => 'Check at least one sequence for the selected races.'],
|
||||
'bib_v4_global_batch_plan_invalid' => ['fr' => 'Sélection invalide : choisissez au moins une épreuve solo et des séquences.', 'en' => 'Invalid selection: pick at least one solo race and sequences.'],
|
||||
'bib_v4_global_batch_need_two' => ['fr' => 'L''assignation globale nécessite au moins deux épreuves solo avec des séquences disponibles.', 'en' => 'Global assignment requires at least two solo races with available sequences.'],
|
||||
'bib_v4_ajax_global_sim_summary' => ['fr' => '%d épreuve(s) · %d dispo / %d à assigner → %d assignables (%d manque)', 'en' => '%d race(s) · %d avail. / %d to assign → %d assignable (%d short)'],
|
||||
];
|
||||
|
||||
return $tab[$clef][$lng] ?? '';
|
||||
@ -3109,6 +3114,9 @@ function fxBibModuleJsDataAttrs() {
|
||||
'batch-progress' => 'bib_v4_js_batch_progress',
|
||||
'global-no-epr' => 'bib_v4_js_global_no_epr',
|
||||
'global-no-seq' => 'bib_v4_js_global_no_seq',
|
||||
'assign-full' => 'bib_v4_ajax_assign_full',
|
||||
'assign-partial' => 'bib_v4_ajax_assign_partial',
|
||||
'assign-none' => 'bib_v4_ajax_assign_none',
|
||||
];
|
||||
$html = '';
|
||||
foreach ($map as $attr => $clef) {
|
||||
@ -3908,7 +3916,19 @@ function renderBibGlobalBatchPanel($int_eve_id, $tabEpreuves, $tabBibAssignments
|
||||
}
|
||||
|
||||
if (count($tabSolo) < 2) {
|
||||
return '';
|
||||
if (empty($tabSolo)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
ob_start();
|
||||
?>
|
||||
<div id="bib-global-batch" class="bib-global-batch bib-global-batch--unavailable">
|
||||
<p class="bib-global-batch__unavailable text-muted small mb-0">
|
||||
<?php fxBibTexteTrad('bib_v4_global_batch_need_two', 1); ?>
|
||||
</p>
|
||||
</div>
|
||||
<?php
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
ob_start();
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
* Constantes *
|
||||
*
|
||||
**************/
|
||||
define('_VERSION_CODE', '4.72.735');
|
||||
define('_VERSION_CODE', '4.72.737');
|
||||
define('_DATE_CODE', '2026-07-08');
|
||||
//MSIN-4290
|
||||
define('QR_SECRET_KEY', 'ms1_qr_2026_cle_secrete_longue_et_fixe');
|
||||
|
||||
@ -88,3 +88,11 @@ FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM info WHERE info_clef = 'bib_v4_js_glob
|
||||
INSERT INTO info (info_clef, info_langue, info_texte, info_aide, info_prg, info_description, info_trie, info_actif, info_option1, info_option2, info_option3, info_creation)
|
||||
SELECT 'bib_v4_js_global_no_seq', 'en', 'Check at least one sequence for the selected races.', '', 'compte.php', '', 0, 1, '', '', '', NOW()
|
||||
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM info WHERE info_clef = 'bib_v4_js_global_no_seq' AND info_langue = 'en' AND info_prg = 'compte.php');
|
||||
|
||||
INSERT INTO info (info_clef, info_langue, info_texte, info_aide, info_prg, info_description, info_trie, info_actif, info_option1, info_option2, info_option3, info_creation)
|
||||
SELECT 'bib_v4_global_batch_need_two', 'fr', 'L''assignation globale nécessite au moins deux épreuves solo avec des séquences disponibles.', '', 'compte.php', '', 0, 1, '', '', '', NOW()
|
||||
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM info WHERE info_clef = 'bib_v4_global_batch_need_two' AND info_langue = 'fr' AND info_prg = 'compte.php');
|
||||
|
||||
INSERT INTO info (info_clef, info_langue, info_texte, info_aide, info_prg, info_description, info_trie, info_actif, info_option1, info_option2, info_option3, info_creation)
|
||||
SELECT 'bib_v4_global_batch_need_two', 'en', 'Global assignment requires at least two solo races with available sequences.', '', 'compte.php', '', 0, 1, '', '', '', NOW()
|
||||
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM info WHERE info_clef = 'bib_v4_global_batch_need_two' AND info_langue = 'en' AND info_prg = 'compte.php');
|
||||
|
||||
Reference in New Issue
Block a user