diff --git a/ecole/public/css/global.css b/ecole/public/css/global.css index 6b16f59c..3a502e3a 100644 --- a/ecole/public/css/global.css +++ b/ecole/public/css/global.css @@ -422,6 +422,79 @@ a.grille-garanties:hover{ color : white } +#rcProjetBlockingSummary, +#rcTarifBlockingSummary { + margin: 1rem 0 1.5rem 0; +} + +.rc-blocking-summary { + display: none; + border-left: 6px solid #c62828; + background-color: #ffebee; + color: #b71c1c; + padding: 12px 16px; + border-radius: 6px; +} + +.rc-blocking-title { + font-weight: 700; + margin-bottom: 8px; +} + +.rc-blocking-list { + margin: 0; + padding-left: 20px; +} + +.rc-blocking-list li { + margin: 3px 0; +} + +.rc-field-label { + display: block; + color: #1a237e; + font-weight: 700; + margin-bottom: 6px; + text-align: left; +} + +.rc-three-col-grid { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 18px; +} + +.rc-three-col-grid > [class*="col"] { + width: 100% !important; + margin-left: 0 !important; + padding: 0 !important; + display: flex; +} + +.rc-three-col-grid .card { + width: 100%; + display: flex; + flex-direction: column; +} + +.rc-three-col-grid .card-content { + flex: 1; +} + +.rc-equal-card-row > [class*="col"] { + display: flex; +} + +.rc-equal-card-row .card { + width: 100%; + display: flex; + flex-direction: column; +} + +.rc-equal-card-row .card-content { + flex: 1; +} + #modalTarifCom span.material-icons { font-size: 4rem; } @@ -571,4 +644,4 @@ a.grille-garanties:hover{ color: #90CAF9; text-decoration-thickness: 2px; text-shadow: 0 0 10px rgba(102, 178, 255, 0.6); -} \ No newline at end of file +} diff --git a/ecole/public/js/tarif-form-rc.js b/ecole/public/js/tarif-form-rc.js index 4281d2ba..8be4e5b9 100644 --- a/ecole/public/js/tarif-form-rc.js +++ b/ecole/public/js/tarif-form-rc.js @@ -75,6 +75,8 @@ window.initSubmenuForm = initSubmenuForm;// Module IIFE pour éviter la pollutio //Variables modulos et liste let modRCCA, modRCActRCC, modRCActRCE, modRCActCompl, modRCMar, modRCZone, modRCEngagCompl, modRCGarAdd, modRCSinistre, modRCFranchise, modRCPrimeMini + let rcTarifGuard = null; + let rcTarifHasErrors = false; // ═══════════════════════════════════════════════════════════════ // FONCTIONS HELPERS @@ -93,6 +95,202 @@ window.initSubmenuForm = initSubmenuForm;// Module IIFE pour éviter la pollutio return tranches[tranches.length - 1]; } + function updateTarifChoiceButtonsState() { + const buttons = document.querySelectorAll('.franchise-card button[name]'); + buttons.forEach((button) => { + const shouldDisable = rcTarifHasErrors; + button.disabled = shouldDisable; + + if (!shouldDisable) { + button.removeAttribute('title'); + return; + } + button.title = 'Corrigez les erreurs de saisie avant de sélectionner un tarif.'; + }); + } + + function setupRCSafeValidation() { + if (!window.RCValidationUtils || typeof window.RCValidationUtils.createGuard !== 'function') { + return; + } + + rcTarifGuard = window.RCValidationUtils.createGuard({ + summaryId: 'rcTarifBlockingSummary', + summaryTitle: 'Impossible de choisir un tarif ou de continuer car :', + blockTargets: ['#generateDeclinaison', '#generateProject', '#comm-OK'], + onChange: function (messages) { + rcTarifHasErrors = messages.length > 0; + updateTarifChoiceButtonsState(); + } + }); + + rcTarifGuard.registerField('#chiffreAffaire', { + profile: 'numeric', + label: "Chiffre d'affaires", + min: 0, + positive: true, + requiredWhen: function () { + return document.querySelector('input[name="cotisation"]:checked')?.value === 'revisable'; + } + }); + + rcTarifGuard.registerField('#nbrVehicule', { + profile: 'integer', + label: 'Nombre de véhicule(s)', + min: 1, + max: 2, + requiredWhen: function () { + return document.querySelector('input[name="cotisation"]:checked')?.value === 'forfaitaire'; + } + }); + + rcTarifGuard.registerField('#sinistre', { + profile: 'numeric', + label: 'Montant de sinistres annuel', + min: 0 + }); + + rcTarifGuard.observe('.input-pourcent', { + profile: 'numeric', + label: "Pourcentage d'activité", + min: 0, + max: 100, + requiredWhen: function (field) { + return field && field.offsetParent !== null; + }, + activeWhen: function (field) { + return field && field.offsetParent !== null; + } + }); + + const capitalFieldNames = [ + 'selectActVoiturier/Loueur', + 'selectActCommissionnaire de Transport', + 'selectActDéménageur', + 'selectActLogistique', + 'selectActAutocariste', + 'selectActAutres activites' + ]; + + capitalFieldNames.forEach(function (fieldName) { + const field = document.getElementsByName(fieldName)[0]; + if (!field) return; + rcTarifGuard.registerField(field, { + profile: 'numeric', + label: 'Capital à assurer', + min: 0, + positive: true, + requiredWhen: function () { + return field.offsetParent !== null; + }, + activeWhen: function () { + return field.offsetParent !== null; + } + }); + }); + + rcTarifGuard.registerField('#inputDomImmat', { + profile: 'numeric', + label: 'Capital dommages immatériels', + min: 0, + positive: true, + requiredWhen: function () { + return Boolean(document.getElementById('checkDomImmat')?.checked); + }, + activeWhen: function () { + return Boolean(document.getElementById('checkDomImmat')?.checked); + } + }); + + rcTarifGuard.registerField('#inputContConf', { + profile: 'numeric', + label: 'Capital contenants confiés', + min: 0, + positive: true, + requiredWhen: function () { + return Boolean(document.getElementById('checkContConf')?.checked); + }, + activeWhen: function () { + return Boolean(document.getElementById('checkContConf')?.checked); + } + }); + + rcTarifGuard.registerField('#inputDiffInv', { + profile: 'numeric', + label: "Capital différence inventaire", + min: 0, + positive: true, + requiredWhen: function () { + return Boolean(document.getElementById('checkDiffInv')?.checked); + }, + activeWhen: function () { + return Boolean(document.getElementById('checkDiffInv')?.checked); + } + }); + + rcTarifGuard.registerField('#selTPPCcapital', { + profile: 'numeric', + label: 'Capital TPPC', + min: 0, + positive: true, + requiredWhen: function () { + return Boolean(document.getElementById('checkTPPC')?.checked); + }, + activeWhen: function () { + return Boolean(document.getElementById('checkTPPC')?.checked); + } + }); + + rcTarifGuard.registerField('#selTPPCveh', { + profile: 'integer', + label: 'Nombre de véhicules TPPC', + min: 1, + max: 10, + requiredWhen: function () { + return Boolean(document.getElementById('checkTPPC')?.checked); + }, + activeWhen: function () { + return Boolean(document.getElementById('checkTPPC')?.checked); + } + }); + + rcTarifGuard.registerField('#tarifCom', { + profile: 'numeric', + label: 'Tarif commercial', + min: 0, + positive: true, + requiredWhen: function () { + return Boolean(document.getElementById('modalTarifCom')?.classList.contains('open')); + }, + activeWhen: function () { + return Boolean(document.getElementById('modalTarifCom')?.classList.contains('open')); + } + }); + + rcTarifGuard.registerField('#commentaire', { + profile: 'text', + label: 'Commentaire explicatif', + requiredWhen: function () { + return document.getElementById('tarifCom-error')?.style.display !== 'none'; + }, + activeWhen: function () { + return document.getElementById('col-commentaire')?.style.display !== 'none'; + } + }); + + rcTarifGuard.observe('#tabAdvaloTerrestre input[type="text"], #tabAdvaloMultimodal input[type="text"], #tabAdvaloAerien input[type="text"]', { + profile: 'number_or_consulter', + label: 'Valeur ad valorem', + min: 0, + positive: true, + decimals: 3, + required: false + }); + + rcTarifGuard.refresh(); + updateTarifChoiceButtonsState(); + } + // Fonction pour afficher un avertissement visuel de dépassement % function showPercentageWarning(excess) { let warningDiv = document.getElementById('percentageWarning'); @@ -219,8 +417,14 @@ window.initSubmenuForm = initSubmenuForm;// Module IIFE pour éviter la pollutio setupEventListeners(); setupTarifetteButtons(); populateFormData(); + setupRCSafeValidation(); updatePercentageIndicator(100); // Initialiser à 100% calcGlobal(); + + setTimeout(() => { + if (rcTarifGuard) rcTarifGuard.refresh(); + updateTarifChoiceButtonsState(); + }, 300); }) } @@ -2349,6 +2553,8 @@ window.initSubmenuForm = initSubmenuForm;// Module IIFE pour éviter la pollutio if (totalPourcentCheck > 100.01) { // Total > 100% : BLOQUER tous les calculs hideAllPrimes(); + if (rcTarifGuard) rcTarifGuard.refresh(); + updateTarifChoiceButtonsState(); return; } @@ -2370,6 +2576,9 @@ window.initSubmenuForm = initSubmenuForm;// Module IIFE pour éviter la pollutio calcTarifettes(result.primePJ, result.CA, result.activites, capitalTPPC, nbVehicules, coefTPPC, result.primeRCCbase, result.primeRCEbase); } + + if (rcTarifGuard) rcTarifGuard.refresh(); + updateTarifChoiceButtonsState(); } function calcRevisable() { @@ -3716,6 +3925,11 @@ window.initSubmenuForm = initSubmenuForm;// Module IIFE pour éviter la pollutio return; } + if (rcTarifGuard && rcTarifGuard.refresh().length > 0) { + M.toast({html: 'Corrigez les erreurs du formulaire avant de valider ce tarif.', classes: 'red'}); + return; + } + // Fermer le modal window.modalTarifCom.close(); diff --git a/ecole/views/tarifformrc.ejs b/ecole/views/tarifformrc.ejs index 83748a1e..202bb1ea 100644 --- a/ecole/views/tarifformrc.ejs +++ b/ecole/views/tarifformrc.ejs @@ -39,15 +39,15 @@