mantenimento-app 2.2.3 → 2.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/app.js CHANGED
@@ -385,6 +385,7 @@ const defaultExpenseItems = [
385
385
  sepCostLossMonthly: "Perdita economica mensile",
386
386
  sepCostLossAnnually: "Perdita economica annua",
387
387
  sepCostLossSpouse: "Impatto stimato su {spouse}",
388
+ sepCostHousingUtilityAdj: "Adeguamento affitto/utenze (stima minima)",
388
389
  sepCostInlineHint: "Duplicazione mensile stimata: {amount}",
389
390
  sepCostWarning: "Inserisci le spese mensili in convivenza nel campo sopra per attivare questa analisi.",
390
391
  sepCostCurrentTotal: "Totale spese attuali dopo separazione: {amount}",
@@ -726,6 +727,7 @@ const defaultExpenseItems = [
726
727
  sepCostLossMonthly: "Monthly economic loss",
727
728
  sepCostLossAnnually: "Annual economic loss",
728
729
  sepCostLossSpouse: "Estimated impact on {spouse}",
730
+ sepCostHousingUtilityAdj: "Rent/utilities adjustment (minimum estimate)",
729
731
  sepCostInlineHint: "Estimated monthly duplication: {amount}",
730
732
  sepCostWarning: "Enter the cohabiting monthly expenses above to activate this analysis.",
731
733
  sepCostCurrentTotal: "Current total expenses after separation: {amount}",
@@ -3436,8 +3438,33 @@ const defaultExpenseItems = [
3436
3438
 
3437
3439
  // Separation cost analysis (only active when speseConvivenza > 0)
3438
3440
  const speseConvivenza = Math.max(0, Number(payload.speseConvivenza || 0));
3439
- const costoSeparazioneMensile = speseConvivenza > 0 ? speseTot - speseConvivenza : null;
3440
- const nettoInsiemeCombinato = speseConvivenza > 0 ? (r1 + r2 - speseConvivenza) : null;
3441
+ const isHousingUtilityLabel = (rawLabel) => {
3442
+ const label = String(rawLabel || "").toLowerCase();
3443
+ return label.includes("affitto")
3444
+ || label.includes("utenze")
3445
+ || label.includes("condominio")
3446
+ || label.includes("casa")
3447
+ || label.includes("mutuo");
3448
+ };
3449
+ const sumHousingUtilityBySpouse = (items, spouseKey) => {
3450
+ return (items || []).reduce((acc, amount, idx) => {
3451
+ if (!isHousingUtilityLabel(expenseItems[idx] && expenseItems[idx].label)) return acc;
3452
+ return acc + Number(amount || 0);
3453
+ }, 0);
3454
+ };
3455
+ const housingUtility1 = sumHousingUtilityBySpouse(payload.c1Spese, 1) + quotaMutuoSpese1;
3456
+ const housingUtility2 = sumHousingUtilityBySpouse(payload.c2Spese, 2) + quotaMutuoSpese2;
3457
+ const housingUtilityNonColl = collocatario === 1 ? housingUtility2 : housingUtility1;
3458
+
3459
+ const baseDuplicazione = speseConvivenza > 0 ? (speseTot - speseConvivenza) : null;
3460
+ const separationAdjustmentHousingUtilities = baseDuplicazione !== null && baseDuplicazione <= 0.005
3461
+ ? Math.max(0, housingUtilityNonColl)
3462
+ : 0;
3463
+ const speseConvivenzaEffettive = speseConvivenza > 0
3464
+ ? Math.max(0, speseConvivenza - separationAdjustmentHousingUtilities)
3465
+ : null;
3466
+ const costoSeparazioneMensile = speseConvivenzaEffettive !== null ? (speseTot - speseConvivenzaEffettive) : null;
3467
+ const nettoInsiemeCombinato = speseConvivenzaEffettive !== null ? (r1 + r2 - speseConvivenzaEffettive) : null;
3441
3468
  const nettoSeparatoTotale = post1 + post2;
3442
3469
  const perditaMensile = nettoInsiemeCombinato !== null ? nettoInsiemeCombinato - nettoSeparatoTotale : null;
3443
3470
  const perditaAnnua = perditaMensile !== null ? perditaMensile * 12 : null;
@@ -3464,7 +3491,8 @@ const defaultExpenseItems = [
3464
3491
  compensativeBenefits,
3465
3492
  assegnoDa1a2, assegnoDa2a1,
3466
3493
  post1, post2,
3467
- speseConvivenza, costoSeparazioneMensile,
3494
+ speseConvivenza, speseConvivenzaEffettive, costoSeparazioneMensile,
3495
+ separationAdjustmentHousingUtilities,
3468
3496
  nettoInsiemeCombinato, nettoSeparatoTotale,
3469
3497
  perditaMensile, perditaAnnua,
3470
3498
  perditaSpouse1, perditaSpouse2
@@ -4655,6 +4683,9 @@ const defaultExpenseItems = [
4655
4683
  </div>
4656
4684
  <div class="sep-cost-divider"></div>
4657
4685
  <div class="sep-cost-section">
4686
+ ${m.separationAdjustmentHousingUtilities > 0
4687
+ ? rowHtml(tr("sepCostHousingUtilityAdj"), m.separationAdjustmentHousingUtilities, false)
4688
+ : ""}
4658
4689
  ${rowHtml(tr("sepCostDuplication"), m.costoSeparazioneMensile, false)}
4659
4690
  ${rowHtml(tr("sepCostLossMonthly"), m.perditaMensile, true)}
4660
4691
  ${rowHtml(tr("sepCostLossAnnually"), m.perditaAnnua, true)}
@@ -4789,7 +4820,7 @@ const defaultExpenseItems = [
4789
4820
  const el = document.getElementById(`${spouseKey}d_${idx}`);
4790
4821
  const raw = String(el && el.value ? el.value : "").trim();
4791
4822
  if (!raw) return "";
4792
- return `${escapeHtml(raw)} <span class="expense-detail-meta">(${raw.length}/${EXPENSE_DETAIL_MAX_CHARS})</span>`;
4823
+ return `${escapeHtml(raw)}`;
4793
4824
  };
4794
4825
  const speseRowsBase = expenseItems.map((item, i) => {
4795
4826
  const c1 = num(`c1_${i}`);
@@ -4925,6 +4956,48 @@ const defaultExpenseItems = [
4925
4956
  const scenarioSectionClass = scenarioLab.length >= 3
4926
4957
  ? "section scenario-section compact-3"
4927
4958
  : "section scenario-section";
4959
+ const scenarioSavedTitle = currentLang === "en" ? "Saved scenarios overview" : "Quadro scenari salvati";
4960
+ const scenarioActiveLabel = currentLang === "en" ? "selected" : "selezionato";
4961
+ const scenarioSupportLabel = currentLang === "en" ? "Support" : "Assegno";
4962
+ const scenarioNeedsLabel = currentLang === "en" ? "Children needs" : "Fabbisogno figli";
4963
+ const scenarioPostLabel = currentLang === "en" ? "Post-support" : "Post-assegno";
4964
+ const scenarioCardsHtml = scenarioLab.length
4965
+ ? `<div class="scenario-mini-grid">${scenarioLab.map((scenario, idx) => {
4966
+ const sm = scenario.model || computeModelLocal(scenario.payload || {});
4967
+ const s1 = escapeHtml(String((scenario.payload && scenario.payload._nome1) || c1Name || tr("spouse1Default")));
4968
+ const s2 = escapeHtml(String((scenario.payload && scenario.payload._nome2) || c2Name || tr("spouse2Default")));
4969
+ const modeTxt = escapeHtml(getModeName(sm.mode, sm.simplePerc));
4970
+ const support = eur(Math.max(sm.assegnoDa1a2 || 0, sm.assegnoDa2a1 || 0));
4971
+ const selectedChip = idx === selectedScenarioIdx ? `<span class="scenario-mini-selected">${escapeHtml(scenarioActiveLabel)}</span>` : "";
4972
+ return `<div class="scenario-mini-card">
4973
+ <div class="scenario-mini-head"><span class="scenario-chip">Sc ${escapeHtml(scenario.label || SCENARIO_LABELS[idx] || String(idx + 1))}</span>${selectedChip}</div>
4974
+ <div class="scenario-mini-meta">${s1} / ${s2}</div>
4975
+ <div class="scenario-mini-mode">${modeTxt}</div>
4976
+ <div class="scenario-mini-row"><span>${escapeHtml(scenarioSupportLabel)}</span><strong>${support}</strong></div>
4977
+ <div class="scenario-mini-row"><span>${escapeHtml(scenarioNeedsLabel)}</span><strong>${eur(sm.fabbisognoFigli || 0)}</strong></div>
4978
+ <div class="scenario-mini-row"><span>${escapeHtml(scenarioPostLabel)} ${s1}</span><strong>${eur(sm.post1 || 0)}</strong></div>
4979
+ <div class="scenario-mini-row"><span>${escapeHtml(scenarioPostLabel)} ${s2}</span><strong>${eur(sm.post2 || 0)}</strong></div>
4980
+ </div>`;
4981
+ }).join("")}</div>`
4982
+ : "";
4983
+ const separationSectionHtml = m.speseConvivenza > 0
4984
+ ? `
4985
+ <div class="section">
4986
+ <div class="section-title">${tr("sepCostPanelTitle")}</div>
4987
+ <table>
4988
+ <tbody>
4989
+ <tr><td>${tr("sepCostNetTogether")}</td><td class="num"><strong>${eur(m.nettoInsiemeCombinato || 0)}</strong></td></tr>
4990
+ <tr><td>${tr("sepCostNetSeparated")}</td><td class="num"><strong>${eur(m.nettoSeparatoTotale || 0)}</strong></td></tr>
4991
+ ${m.separationAdjustmentHousingUtilities > 0 ? `<tr><td>${tr("sepCostHousingUtilityAdj")}</td><td class="num">${eur(m.separationAdjustmentHousingUtilities)}</td></tr>` : ""}
4992
+ <tr><td>${tr("sepCostDuplication")}</td><td class="num"><strong>${eur(m.costoSeparazioneMensile || 0)}</strong></td></tr>
4993
+ <tr><td>${tr("sepCostLossMonthly")}</td><td class="num"><strong>${eur(m.perditaMensile || 0)}</strong></td></tr>
4994
+ <tr><td>${tr("sepCostLossAnnually")}</td><td class="num"><strong>${eur(m.perditaAnnua || 0)}</strong></td></tr>
4995
+ <tr><td>${msg("sepCostLossSpouse", { spouse: c1NameEsc })}</td><td class="num">${eur(m.perditaSpouse1 || 0)}</td></tr>
4996
+ <tr><td>${msg("sepCostLossSpouse", { spouse: c2NameEsc })}</td><td class="num">${eur(m.perditaSpouse2 || 0)}</td></tr>
4997
+ </tbody>
4998
+ </table>
4999
+ </div>`
5000
+ : "";
4928
5001
 
4929
5002
  const html = `<!DOCTYPE html>
4930
5003
  <html lang="${pdfLang}">
@@ -5051,6 +5124,14 @@ const defaultExpenseItems = [
5051
5124
 
5052
5125
  /* ── SCENARIO LAB ── */
5053
5126
  .scenario-compare-wrap { overflow-x: auto; }
5127
+ .scenario-mini-grid { display: grid; grid-template-columns: repeat(2, minmax(0,1fr)); gap: 8px; margin-bottom: 10px; }
5128
+ .scenario-mini-card { border: 1px solid #c6ddd8; border-radius: 8px; background: #f7fcfb; padding: 7px 8px; }
5129
+ .scenario-mini-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 3px; }
5130
+ .scenario-mini-selected { font-size: 6.7pt; color: #0f6a61; font-weight: 700; border: 1px solid #a9d4c0; border-radius: 999px; padding: 0 6px; background: #eaf7f1; }
5131
+ .scenario-mini-meta { font-size: 7.1pt; color: #4c6964; margin-bottom: 2px; }
5132
+ .scenario-mini-mode { font-size: 7.2pt; font-weight: 700; color: #183d39; margin-bottom: 5px; }
5133
+ .scenario-mini-row { display: flex; justify-content: space-between; gap: 8px; font-size: 7.4pt; padding: 1px 0; }
5134
+ .scenario-mini-row strong { font-variant-numeric: tabular-nums; }
5054
5135
  .scenario-compare-table { width: 100%; border-collapse: collapse; font-size: 8.5pt; }
5055
5136
  .scenario-compare-table th, .scenario-compare-table td { border: 1px solid #c6ddd8; padding: 5px 8px; }
5056
5137
  .scenario-compare-table thead th { background: linear-gradient(90deg,#f0f7f5,#e6f1ee); color: #183d39; font-weight: 700; }
@@ -5354,6 +5435,8 @@ const defaultExpenseItems = [
5354
5435
  </div>
5355
5436
  </div>
5356
5437
 
5438
+ ${separationSectionHtml}
5439
+
5357
5440
  <div class="section">
5358
5441
  <div class="section-title">${tr("spiegTitle")}</div>
5359
5442
  <div class="pdf-explain-grid">
@@ -5388,6 +5471,8 @@ const defaultExpenseItems = [
5388
5471
  ${scenarioLab.length ? `
5389
5472
  <div class="${scenarioSectionClass}">
5390
5473
  <div class="section-title">${tr("pdfScenarioSection")}</div>
5474
+ <div style="font-size:8pt;font-weight:700;color:#1a4e49;margin-bottom:6px;">${escapeHtml(scenarioSavedTitle)}</div>
5475
+ ${scenarioCardsHtml}
5391
5476
  ${scenarioPdfTable}
5392
5477
  </div>
5393
5478
  ` : ""}
@@ -127,8 +127,19 @@ function calculateModel(input) {
127
127
 
128
128
  // Separation cost analysis (only active when speseConvivenza > 0)
129
129
  const speseConvivenza = Math.max(0, toNumber(input.speseConvivenza));
130
- const costoSeparazioneMensile = speseConvivenza > 0 ? speseTot - speseConvivenza : null;
131
- const nettoInsiemeCombinato = speseConvivenza > 0 ? (r1 + r2 - speseConvivenza) : null;
130
+ const housingIdx = new Set([0, 1, 2, 7]); // Affitto, casa/valore locativo, utenze, condominio
131
+ const housingUtility1 = c1Spese.reduce((acc, n, idx) => acc + (housingIdx.has(idx) ? toNumber(n) : 0), 0) + quotaMutuoSpese1;
132
+ const housingUtility2 = c2Spese.reduce((acc, n, idx) => acc + (housingIdx.has(idx) ? toNumber(n) : 0), 0) + quotaMutuoSpese2;
133
+ const housingUtilityNonColl = collocatario === 1 ? housingUtility2 : housingUtility1;
134
+ const baseDuplicazione = speseConvivenza > 0 ? (speseTot - speseConvivenza) : null;
135
+ const separationAdjustmentHousingUtilities = baseDuplicazione !== null && baseDuplicazione <= 0.005
136
+ ? Math.max(0, housingUtilityNonColl)
137
+ : 0;
138
+ const speseConvivenzaEffettive = speseConvivenza > 0
139
+ ? Math.max(0, speseConvivenza - separationAdjustmentHousingUtilities)
140
+ : null;
141
+ const costoSeparazioneMensile = speseConvivenzaEffettive !== null ? (speseTot - speseConvivenzaEffettive) : null;
142
+ const nettoInsiemeCombinato = speseConvivenzaEffettive !== null ? (r1 + r2 - speseConvivenzaEffettive) : null;
132
143
  const nettoSeparatoTotale = post1 + post2;
133
144
  const perditaMensile = nettoInsiemeCombinato !== null ? nettoInsiemeCombinato - nettoSeparatoTotale : null;
134
145
  const perditaAnnua = perditaMensile !== null ? perditaMensile * 12 : null;
@@ -156,7 +167,8 @@ function calculateModel(input) {
156
167
  compensativeBenefits,
157
168
  assegnoDa1a2, assegnoDa2a1,
158
169
  post1, post2,
159
- speseConvivenza, costoSeparazioneMensile,
170
+ speseConvivenza, speseConvivenzaEffettive, costoSeparazioneMensile,
171
+ separationAdjustmentHousingUtilities,
160
172
  nettoInsiemeCombinato, nettoSeparatoTotale,
161
173
  perditaMensile, perditaAnnua,
162
174
  perditaSpouse1, perditaSpouse2
@@ -385,6 +385,7 @@ const defaultExpenseItems = [
385
385
  sepCostLossMonthly: "Perdita economica mensile",
386
386
  sepCostLossAnnually: "Perdita economica annua",
387
387
  sepCostLossSpouse: "Impatto stimato su {spouse}",
388
+ sepCostHousingUtilityAdj: "Adeguamento affitto/utenze (stima minima)",
388
389
  sepCostInlineHint: "Duplicazione mensile stimata: {amount}",
389
390
  sepCostWarning: "Inserisci le spese mensili in convivenza nel campo sopra per attivare questa analisi.",
390
391
  sepCostCurrentTotal: "Totale spese attuali dopo separazione: {amount}",
@@ -726,6 +727,7 @@ const defaultExpenseItems = [
726
727
  sepCostLossMonthly: "Monthly economic loss",
727
728
  sepCostLossAnnually: "Annual economic loss",
728
729
  sepCostLossSpouse: "Estimated impact on {spouse}",
730
+ sepCostHousingUtilityAdj: "Rent/utilities adjustment (minimum estimate)",
729
731
  sepCostInlineHint: "Estimated monthly duplication: {amount}",
730
732
  sepCostWarning: "Enter the cohabiting monthly expenses above to activate this analysis.",
731
733
  sepCostCurrentTotal: "Current total expenses after separation: {amount}",
@@ -3436,8 +3438,33 @@ const defaultExpenseItems = [
3436
3438
 
3437
3439
  // Separation cost analysis (only active when speseConvivenza > 0)
3438
3440
  const speseConvivenza = Math.max(0, Number(payload.speseConvivenza || 0));
3439
- const costoSeparazioneMensile = speseConvivenza > 0 ? speseTot - speseConvivenza : null;
3440
- const nettoInsiemeCombinato = speseConvivenza > 0 ? (r1 + r2 - speseConvivenza) : null;
3441
+ const isHousingUtilityLabel = (rawLabel) => {
3442
+ const label = String(rawLabel || "").toLowerCase();
3443
+ return label.includes("affitto")
3444
+ || label.includes("utenze")
3445
+ || label.includes("condominio")
3446
+ || label.includes("casa")
3447
+ || label.includes("mutuo");
3448
+ };
3449
+ const sumHousingUtilityBySpouse = (items, spouseKey) => {
3450
+ return (items || []).reduce((acc, amount, idx) => {
3451
+ if (!isHousingUtilityLabel(expenseItems[idx] && expenseItems[idx].label)) return acc;
3452
+ return acc + Number(amount || 0);
3453
+ }, 0);
3454
+ };
3455
+ const housingUtility1 = sumHousingUtilityBySpouse(payload.c1Spese, 1) + quotaMutuoSpese1;
3456
+ const housingUtility2 = sumHousingUtilityBySpouse(payload.c2Spese, 2) + quotaMutuoSpese2;
3457
+ const housingUtilityNonColl = collocatario === 1 ? housingUtility2 : housingUtility1;
3458
+
3459
+ const baseDuplicazione = speseConvivenza > 0 ? (speseTot - speseConvivenza) : null;
3460
+ const separationAdjustmentHousingUtilities = baseDuplicazione !== null && baseDuplicazione <= 0.005
3461
+ ? Math.max(0, housingUtilityNonColl)
3462
+ : 0;
3463
+ const speseConvivenzaEffettive = speseConvivenza > 0
3464
+ ? Math.max(0, speseConvivenza - separationAdjustmentHousingUtilities)
3465
+ : null;
3466
+ const costoSeparazioneMensile = speseConvivenzaEffettive !== null ? (speseTot - speseConvivenzaEffettive) : null;
3467
+ const nettoInsiemeCombinato = speseConvivenzaEffettive !== null ? (r1 + r2 - speseConvivenzaEffettive) : null;
3441
3468
  const nettoSeparatoTotale = post1 + post2;
3442
3469
  const perditaMensile = nettoInsiemeCombinato !== null ? nettoInsiemeCombinato - nettoSeparatoTotale : null;
3443
3470
  const perditaAnnua = perditaMensile !== null ? perditaMensile * 12 : null;
@@ -3464,7 +3491,8 @@ const defaultExpenseItems = [
3464
3491
  compensativeBenefits,
3465
3492
  assegnoDa1a2, assegnoDa2a1,
3466
3493
  post1, post2,
3467
- speseConvivenza, costoSeparazioneMensile,
3494
+ speseConvivenza, speseConvivenzaEffettive, costoSeparazioneMensile,
3495
+ separationAdjustmentHousingUtilities,
3468
3496
  nettoInsiemeCombinato, nettoSeparatoTotale,
3469
3497
  perditaMensile, perditaAnnua,
3470
3498
  perditaSpouse1, perditaSpouse2
@@ -4655,6 +4683,9 @@ const defaultExpenseItems = [
4655
4683
  </div>
4656
4684
  <div class="sep-cost-divider"></div>
4657
4685
  <div class="sep-cost-section">
4686
+ ${m.separationAdjustmentHousingUtilities > 0
4687
+ ? rowHtml(tr("sepCostHousingUtilityAdj"), m.separationAdjustmentHousingUtilities, false)
4688
+ : ""}
4658
4689
  ${rowHtml(tr("sepCostDuplication"), m.costoSeparazioneMensile, false)}
4659
4690
  ${rowHtml(tr("sepCostLossMonthly"), m.perditaMensile, true)}
4660
4691
  ${rowHtml(tr("sepCostLossAnnually"), m.perditaAnnua, true)}
@@ -4789,7 +4820,7 @@ const defaultExpenseItems = [
4789
4820
  const el = document.getElementById(`${spouseKey}d_${idx}`);
4790
4821
  const raw = String(el && el.value ? el.value : "").trim();
4791
4822
  if (!raw) return "";
4792
- return `${escapeHtml(raw)} <span class="expense-detail-meta">(${raw.length}/${EXPENSE_DETAIL_MAX_CHARS})</span>`;
4823
+ return `${escapeHtml(raw)}`;
4793
4824
  };
4794
4825
  const speseRowsBase = expenseItems.map((item, i) => {
4795
4826
  const c1 = num(`c1_${i}`);
@@ -4925,6 +4956,48 @@ const defaultExpenseItems = [
4925
4956
  const scenarioSectionClass = scenarioLab.length >= 3
4926
4957
  ? "section scenario-section compact-3"
4927
4958
  : "section scenario-section";
4959
+ const scenarioSavedTitle = currentLang === "en" ? "Saved scenarios overview" : "Quadro scenari salvati";
4960
+ const scenarioActiveLabel = currentLang === "en" ? "selected" : "selezionato";
4961
+ const scenarioSupportLabel = currentLang === "en" ? "Support" : "Assegno";
4962
+ const scenarioNeedsLabel = currentLang === "en" ? "Children needs" : "Fabbisogno figli";
4963
+ const scenarioPostLabel = currentLang === "en" ? "Post-support" : "Post-assegno";
4964
+ const scenarioCardsHtml = scenarioLab.length
4965
+ ? `<div class="scenario-mini-grid">${scenarioLab.map((scenario, idx) => {
4966
+ const sm = scenario.model || computeModelLocal(scenario.payload || {});
4967
+ const s1 = escapeHtml(String((scenario.payload && scenario.payload._nome1) || c1Name || tr("spouse1Default")));
4968
+ const s2 = escapeHtml(String((scenario.payload && scenario.payload._nome2) || c2Name || tr("spouse2Default")));
4969
+ const modeTxt = escapeHtml(getModeName(sm.mode, sm.simplePerc));
4970
+ const support = eur(Math.max(sm.assegnoDa1a2 || 0, sm.assegnoDa2a1 || 0));
4971
+ const selectedChip = idx === selectedScenarioIdx ? `<span class="scenario-mini-selected">${escapeHtml(scenarioActiveLabel)}</span>` : "";
4972
+ return `<div class="scenario-mini-card">
4973
+ <div class="scenario-mini-head"><span class="scenario-chip">Sc ${escapeHtml(scenario.label || SCENARIO_LABELS[idx] || String(idx + 1))}</span>${selectedChip}</div>
4974
+ <div class="scenario-mini-meta">${s1} / ${s2}</div>
4975
+ <div class="scenario-mini-mode">${modeTxt}</div>
4976
+ <div class="scenario-mini-row"><span>${escapeHtml(scenarioSupportLabel)}</span><strong>${support}</strong></div>
4977
+ <div class="scenario-mini-row"><span>${escapeHtml(scenarioNeedsLabel)}</span><strong>${eur(sm.fabbisognoFigli || 0)}</strong></div>
4978
+ <div class="scenario-mini-row"><span>${escapeHtml(scenarioPostLabel)} ${s1}</span><strong>${eur(sm.post1 || 0)}</strong></div>
4979
+ <div class="scenario-mini-row"><span>${escapeHtml(scenarioPostLabel)} ${s2}</span><strong>${eur(sm.post2 || 0)}</strong></div>
4980
+ </div>`;
4981
+ }).join("")}</div>`
4982
+ : "";
4983
+ const separationSectionHtml = m.speseConvivenza > 0
4984
+ ? `
4985
+ <div class="section">
4986
+ <div class="section-title">${tr("sepCostPanelTitle")}</div>
4987
+ <table>
4988
+ <tbody>
4989
+ <tr><td>${tr("sepCostNetTogether")}</td><td class="num"><strong>${eur(m.nettoInsiemeCombinato || 0)}</strong></td></tr>
4990
+ <tr><td>${tr("sepCostNetSeparated")}</td><td class="num"><strong>${eur(m.nettoSeparatoTotale || 0)}</strong></td></tr>
4991
+ ${m.separationAdjustmentHousingUtilities > 0 ? `<tr><td>${tr("sepCostHousingUtilityAdj")}</td><td class="num">${eur(m.separationAdjustmentHousingUtilities)}</td></tr>` : ""}
4992
+ <tr><td>${tr("sepCostDuplication")}</td><td class="num"><strong>${eur(m.costoSeparazioneMensile || 0)}</strong></td></tr>
4993
+ <tr><td>${tr("sepCostLossMonthly")}</td><td class="num"><strong>${eur(m.perditaMensile || 0)}</strong></td></tr>
4994
+ <tr><td>${tr("sepCostLossAnnually")}</td><td class="num"><strong>${eur(m.perditaAnnua || 0)}</strong></td></tr>
4995
+ <tr><td>${msg("sepCostLossSpouse", { spouse: c1NameEsc })}</td><td class="num">${eur(m.perditaSpouse1 || 0)}</td></tr>
4996
+ <tr><td>${msg("sepCostLossSpouse", { spouse: c2NameEsc })}</td><td class="num">${eur(m.perditaSpouse2 || 0)}</td></tr>
4997
+ </tbody>
4998
+ </table>
4999
+ </div>`
5000
+ : "";
4928
5001
 
4929
5002
  const html = `<!DOCTYPE html>
4930
5003
  <html lang="${pdfLang}">
@@ -5051,6 +5124,14 @@ const defaultExpenseItems = [
5051
5124
 
5052
5125
  /* ── SCENARIO LAB ── */
5053
5126
  .scenario-compare-wrap { overflow-x: auto; }
5127
+ .scenario-mini-grid { display: grid; grid-template-columns: repeat(2, minmax(0,1fr)); gap: 8px; margin-bottom: 10px; }
5128
+ .scenario-mini-card { border: 1px solid #c6ddd8; border-radius: 8px; background: #f7fcfb; padding: 7px 8px; }
5129
+ .scenario-mini-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 3px; }
5130
+ .scenario-mini-selected { font-size: 6.7pt; color: #0f6a61; font-weight: 700; border: 1px solid #a9d4c0; border-radius: 999px; padding: 0 6px; background: #eaf7f1; }
5131
+ .scenario-mini-meta { font-size: 7.1pt; color: #4c6964; margin-bottom: 2px; }
5132
+ .scenario-mini-mode { font-size: 7.2pt; font-weight: 700; color: #183d39; margin-bottom: 5px; }
5133
+ .scenario-mini-row { display: flex; justify-content: space-between; gap: 8px; font-size: 7.4pt; padding: 1px 0; }
5134
+ .scenario-mini-row strong { font-variant-numeric: tabular-nums; }
5054
5135
  .scenario-compare-table { width: 100%; border-collapse: collapse; font-size: 8.5pt; }
5055
5136
  .scenario-compare-table th, .scenario-compare-table td { border: 1px solid #c6ddd8; padding: 5px 8px; }
5056
5137
  .scenario-compare-table thead th { background: linear-gradient(90deg,#f0f7f5,#e6f1ee); color: #183d39; font-weight: 700; }
@@ -5354,6 +5435,8 @@ const defaultExpenseItems = [
5354
5435
  </div>
5355
5436
  </div>
5356
5437
 
5438
+ ${separationSectionHtml}
5439
+
5357
5440
  <div class="section">
5358
5441
  <div class="section-title">${tr("spiegTitle")}</div>
5359
5442
  <div class="pdf-explain-grid">
@@ -5388,6 +5471,8 @@ const defaultExpenseItems = [
5388
5471
  ${scenarioLab.length ? `
5389
5472
  <div class="${scenarioSectionClass}">
5390
5473
  <div class="section-title">${tr("pdfScenarioSection")}</div>
5474
+ <div style="font-size:8pt;font-weight:700;color:#1a4e49;margin-bottom:6px;">${escapeHtml(scenarioSavedTitle)}</div>
5475
+ ${scenarioCardsHtml}
5391
5476
  ${scenarioPdfTable}
5392
5477
  </div>
5393
5478
  ` : ""}
@@ -599,7 +599,7 @@
599
599
  <script src="supabase.min.js"></script>
600
600
  <script src="fabric.min.js"></script>
601
601
  <script src="html2pdf.bundle.min.js"></script>
602
- <script src="app.js?v=2.2.3"></script>
602
+ <script src="app.js?v=2.2.5"></script>
603
603
  </body>
604
604
  </html>
605
605
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mantenimento-app",
3
- "version": "2.2.3",
3
+ "version": "2.2.5",
4
4
  "description": "Frontend + backend architecture for the mantenimento calculator",
5
5
  "type": "commonjs",
6
6
  "main": "backend/calculate-model.js",