mantenimento-app 2.4.1 → 2.4.3
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 +49 -13
- package/frontend/public/app.js +49 -13
- package/frontend/public/index.html +1 -1
- package/frontend/public/styles.css +64 -0
- package/package.json +1 -1
package/app.js
CHANGED
|
@@ -281,6 +281,8 @@ const defaultExpenseItems = [
|
|
|
281
281
|
firstHomeBoxNote: "Dichiara se esiste un mutuo sulla prima casa dei coniugi ceduta a uno dei due: il modello considera il trasferimento implicito quando la casa e assegnata al collocatario.",
|
|
282
282
|
firstHomeMortgageEnabledLabel: "Mutuo su prima casa dei coniugi",
|
|
283
283
|
firstHomeMortgageEnabledHint: "Attiva per includere il mutuo della prima casa ceduta nei benefici compensativi.",
|
|
284
|
+
firstHomeLocativeValueLabel: "Casa (valore locativo) ({currency})",
|
|
285
|
+
firstHomeLocativeValueHint: "Valore locativo mensile della casa assegnata, usato per valorizzare il beneficio economico implicito.",
|
|
284
286
|
firstHomeMortgageAmountLabel: "Rata mutuo mensile ({currency})",
|
|
285
287
|
firstHomeMortgageAmountHint: "Importo mensile complessivo della rata del mutuo prima casa.",
|
|
286
288
|
firstHomeAssignedToLabel: "Casa assegnata a",
|
|
@@ -295,6 +297,7 @@ const defaultExpenseItems = [
|
|
|
295
297
|
calcBenefitFamilyAllowance: "Assegno familiare INPS percepito da {spouse}",
|
|
296
298
|
calcBenefitPrimaryHomeMortgage: "Quota mutuo prima casa ceduta al collocatario ({payer} -> {receiver})",
|
|
297
299
|
calcBenefitPrimaryHomeAssignment: "Assegnazione casa familiare - valore locativo ({receiver})",
|
|
300
|
+
calcBenefitExcludedFromTotal: "fuori totale",
|
|
298
301
|
pdfCompBenefitsSection: "Benefici compensativi gia allocati",
|
|
299
302
|
pdfCompBenefitsItem: "Beneficio",
|
|
300
303
|
pdfCompBenefitsAmount: "Valore {currency}/mese",
|
|
@@ -640,6 +643,8 @@ const defaultExpenseItems = [
|
|
|
640
643
|
firstHomeBoxNote: "Declare whether there is a mortgage on the spouses' primary home assigned to one spouse: the model counts the implicit transfer when the home is assigned to the custodial parent.",
|
|
641
644
|
firstHomeMortgageEnabledLabel: "Mortgage on spouses' primary home",
|
|
642
645
|
firstHomeMortgageEnabledHint: "Enable to include the assigned primary-home mortgage in compensative benefits.",
|
|
646
|
+
firstHomeLocativeValueLabel: "Home (rental value) ({currency})",
|
|
647
|
+
firstHomeLocativeValueHint: "Monthly rental value of the assigned home, used to value the implicit economic benefit.",
|
|
643
648
|
firstHomeMortgageAmountLabel: "Monthly mortgage payment ({currency})",
|
|
644
649
|
firstHomeMortgageAmountHint: "Total monthly amount of the primary-home mortgage payment.",
|
|
645
650
|
firstHomeAssignedToLabel: "Home assigned to",
|
|
@@ -654,6 +659,7 @@ const defaultExpenseItems = [
|
|
|
654
659
|
calcBenefitFamilyAllowance: "INPS family allowance received by {spouse}",
|
|
655
660
|
calcBenefitPrimaryHomeMortgage: "Primary-home mortgage share assigned to custodial parent ({payer} -> {receiver})",
|
|
656
661
|
calcBenefitPrimaryHomeAssignment: "Primary home assignment - rental value ({receiver})",
|
|
662
|
+
calcBenefitExcludedFromTotal: "excluded from total",
|
|
657
663
|
pdfCompBenefitsSection: "Compensative benefits already allocated",
|
|
658
664
|
pdfCompBenefitsItem: "Benefit",
|
|
659
665
|
pdfCompBenefitsAmount: "Value {currency}/month",
|
|
@@ -1213,6 +1219,8 @@ const defaultExpenseItems = [
|
|
|
1213
1219
|
const firstHomeBoxNote = document.getElementById("firstHomeBoxNote");
|
|
1214
1220
|
const lblPrimaCasaMutuoEnabled = document.getElementById("lblPrimaCasaMutuoEnabled");
|
|
1215
1221
|
const hintPrimaCasaMutuoEnabled = document.getElementById("hintPrimaCasaMutuoEnabled");
|
|
1222
|
+
const lblPrimaCasaValoreLocativo = document.getElementById("lblPrimaCasaValoreLocativo");
|
|
1223
|
+
const hintPrimaCasaValoreLocativo = document.getElementById("hintPrimaCasaValoreLocativo");
|
|
1216
1224
|
const lblPrimaCasaMutuoImporto = document.getElementById("lblPrimaCasaMutuoImporto");
|
|
1217
1225
|
const hintPrimaCasaMutuoImporto = document.getElementById("hintPrimaCasaMutuoImporto");
|
|
1218
1226
|
const lblPrimaCasaAssegnataA = document.getElementById("lblPrimaCasaAssegnataA");
|
|
@@ -1276,6 +1284,8 @@ const defaultExpenseItems = [
|
|
|
1276
1284
|
if (firstHomeBoxNote) firstHomeBoxNote.textContent = tr("firstHomeBoxNote");
|
|
1277
1285
|
if (lblPrimaCasaMutuoEnabled) lblPrimaCasaMutuoEnabled.textContent = tr("firstHomeMortgageEnabledLabel");
|
|
1278
1286
|
if (hintPrimaCasaMutuoEnabled) hintPrimaCasaMutuoEnabled.title = tr("firstHomeMortgageEnabledHint");
|
|
1287
|
+
if (lblPrimaCasaValoreLocativo) lblPrimaCasaValoreLocativo.textContent = msg("firstHomeLocativeValueLabel", { currency: currentCurrency });
|
|
1288
|
+
if (hintPrimaCasaValoreLocativo) hintPrimaCasaValoreLocativo.title = tr("firstHomeLocativeValueHint");
|
|
1279
1289
|
if (lblPrimaCasaMutuoImporto) lblPrimaCasaMutuoImporto.textContent = msg("firstHomeMortgageAmountLabel", { currency: currentCurrency });
|
|
1280
1290
|
if (hintPrimaCasaMutuoImporto) hintPrimaCasaMutuoImporto.title = tr("firstHomeMortgageAmountHint");
|
|
1281
1291
|
if (lblPrimaCasaAssegnataA) lblPrimaCasaAssegnataA.textContent = tr("firstHomeAssignedToLabel");
|
|
@@ -1496,6 +1506,14 @@ const defaultExpenseItems = [
|
|
|
1496
1506
|
return { label, help };
|
|
1497
1507
|
}
|
|
1498
1508
|
|
|
1509
|
+
function isLegacyLocativeExpenseLabel(label) {
|
|
1510
|
+
const normalized = String(label || "")
|
|
1511
|
+
.replace(/^[^A-Za-z\u00C0-\u024F]+/, "")
|
|
1512
|
+
.trim()
|
|
1513
|
+
.toLowerCase();
|
|
1514
|
+
return normalized === "casa (valore locativo)";
|
|
1515
|
+
}
|
|
1516
|
+
|
|
1499
1517
|
function populateSuggestedExpenseOptions() {
|
|
1500
1518
|
const select = document.getElementById("suggestedExpenseSelect");
|
|
1501
1519
|
if (!select) return;
|
|
@@ -3589,6 +3607,7 @@ const defaultExpenseItems = [
|
|
|
3589
3607
|
aFam1: num("assegnoFam1"),
|
|
3590
3608
|
aFam2: num("assegnoFam2"),
|
|
3591
3609
|
primaCasaMutuoEnabled: firstHome.enabled ? 1 : 0,
|
|
3610
|
+
primaCasaValoreLocativo: num("primaCasaValoreLocativo"),
|
|
3592
3611
|
primaCasaMutuoImporto: firstHome.amount,
|
|
3593
3612
|
primaCasaAssegnataA: firstHome.assignedTo,
|
|
3594
3613
|
primaCasaMutuoPerc1: firstHome.share1,
|
|
@@ -4638,14 +4657,14 @@ const defaultExpenseItems = [
|
|
|
4638
4657
|
const rawBenefs = Array.isArray(m.compensativeBenefits)
|
|
4639
4658
|
? m.compensativeBenefits.filter((r) => r && Number(r.amount || 0) > 0.005)
|
|
4640
4659
|
: [];
|
|
4641
|
-
const typeIcons = { family: "\uD83C\uDFDB", "primary-home-mortgage": "\uD83C\uDFE1" };
|
|
4660
|
+
const typeIcons = { family: "\uD83C\uDFDB", "primary-home-mortgage": "\uD83C\uDFE1", "primary-home-assignment": "\uD83C\uDFE0" };
|
|
4642
4661
|
const cardsHtml = benefitRows
|
|
4643
4662
|
.map((row, i) => {
|
|
4644
4663
|
const icon = (rawBenefs[i] && typeIcons[rawBenefs[i].type]) || "\u2726";
|
|
4645
4664
|
return `<li class="spieg-benefit-card"><span class="spieg-benefit-icon">${icon}</span><span class="spieg-benefit-label">${escapeHtml(row.label)}</span><strong class="spieg-benefit-amount">${eur(row.amount)}</strong></li>`;
|
|
4646
4665
|
})
|
|
4647
4666
|
.join("");
|
|
4648
|
-
const total = benefitRows
|
|
4667
|
+
const total = getCompensativeBenefitsTotal(benefitRows);
|
|
4649
4668
|
const totalLabel = currentLang === "en" ? "Total allocated benefits" : "Totale benefici allocati";
|
|
4650
4669
|
resultHtml = `
|
|
4651
4670
|
<div class="spieg-no-transfer-badge">⚖️ ${escapeHtml(tr("calcNoTransferSuggested"))}</div>
|
|
@@ -4730,21 +4749,32 @@ const defaultExpenseItems = [
|
|
|
4730
4749
|
const amount = Number(row.amount || 0);
|
|
4731
4750
|
if (row.type === "family") {
|
|
4732
4751
|
const spouse = Number(row.to) === 2 ? name2 : name1;
|
|
4733
|
-
return { label: msg("calcBenefitFamilyAllowance", { spouse }), amount };
|
|
4752
|
+
return { label: msg("calcBenefitFamilyAllowance", { spouse }), amount, includeInTotal: true };
|
|
4734
4753
|
}
|
|
4735
4754
|
if (row.type === "primary-home-mortgage") {
|
|
4736
4755
|
const payer = Number(row.from) === 2 ? name2 : name1;
|
|
4737
4756
|
const receiver = Number(row.to) === 2 ? name2 : name1;
|
|
4738
|
-
return { label: msg("calcBenefitPrimaryHomeMortgage", { payer, receiver }), amount };
|
|
4757
|
+
return { label: msg("calcBenefitPrimaryHomeMortgage", { payer, receiver }), amount, includeInTotal: true };
|
|
4739
4758
|
}
|
|
4740
4759
|
if (row.type === "primary-home-assignment") {
|
|
4741
4760
|
const receiver = Number(row.to) === 2 ? name2 : name1;
|
|
4742
|
-
return {
|
|
4761
|
+
return {
|
|
4762
|
+
label: `${msg("calcBenefitPrimaryHomeAssignment", { receiver })} (${tr("calcBenefitExcludedFromTotal")})`,
|
|
4763
|
+
amount,
|
|
4764
|
+
includeInTotal: false
|
|
4765
|
+
};
|
|
4743
4766
|
}
|
|
4744
|
-
return { label: tr("calcCompBenefitsLabel"), amount };
|
|
4767
|
+
return { label: tr("calcCompBenefitsLabel"), amount, includeInTotal: true };
|
|
4745
4768
|
});
|
|
4746
4769
|
}
|
|
4747
4770
|
|
|
4771
|
+
function getCompensativeBenefitsTotal(rows) {
|
|
4772
|
+
return (rows || []).reduce((sum, row) => {
|
|
4773
|
+
if (!row || row.includeInTotal === false) return sum;
|
|
4774
|
+
return sum + Number(row.amount || 0);
|
|
4775
|
+
}, 0);
|
|
4776
|
+
}
|
|
4777
|
+
|
|
4748
4778
|
function formatCompensativeBenefitsInline(m, name1 = c1n(), name2 = c2n()) {
|
|
4749
4779
|
return getCompensativeBenefitRows(m, name1, name2)
|
|
4750
4780
|
.map((row) => `${row.label}: ${eur(row.amount)}`)
|
|
@@ -4851,14 +4881,14 @@ const defaultExpenseItems = [
|
|
|
4851
4881
|
const rawBenefs = Array.isArray(m.compensativeBenefits)
|
|
4852
4882
|
? m.compensativeBenefits.filter((r) => r && Number(r.amount || 0) > 0.005)
|
|
4853
4883
|
: [];
|
|
4854
|
-
const typeIcons = { family: "\uD83C\uDFDB", "primary-home-mortgage": "\uD83C\uDFE1" };
|
|
4884
|
+
const typeIcons = { family: "\uD83C\uDFDB", "primary-home-mortgage": "\uD83C\uDFE1", "primary-home-assignment": "\uD83C\uDFE0" };
|
|
4855
4885
|
const cardsHtml = benefitRows
|
|
4856
4886
|
.map((row, i) => {
|
|
4857
4887
|
const icon = (rawBenefs[i] && typeIcons[rawBenefs[i].type]) || "\u2726";
|
|
4858
4888
|
return `<li class="spieg-benefit-card"><span class="spieg-benefit-icon">${icon}</span><span class="spieg-benefit-label">${escapeHtml(row.label)}</span><strong class="spieg-benefit-amount">${eur(row.amount)}</strong></li>`;
|
|
4859
4889
|
})
|
|
4860
4890
|
.join("");
|
|
4861
|
-
const total = benefitRows
|
|
4891
|
+
const total = getCompensativeBenefitsTotal(benefitRows);
|
|
4862
4892
|
const totalLabel = currentLang === "en" ? "Total allocated benefits" : "Totale benefici allocati";
|
|
4863
4893
|
benefitCardsHtml = `
|
|
4864
4894
|
<div class="result-benefits-box">
|
|
@@ -5122,12 +5152,12 @@ const defaultExpenseItems = [
|
|
|
5122
5152
|
const rawBenefs = Array.isArray(m.compensativeBenefits)
|
|
5123
5153
|
? m.compensativeBenefits.filter((row) => row && Number(row.amount || 0) > 0.005)
|
|
5124
5154
|
: [];
|
|
5125
|
-
const typeIcons = { family: "\uD83C\uDFDB", "primary-home-mortgage": "\uD83C\uDFE1" };
|
|
5155
|
+
const typeIcons = { family: "\uD83C\uDFDB", "primary-home-mortgage": "\uD83C\uDFE1", "primary-home-assignment": "\uD83C\uDFE0" };
|
|
5126
5156
|
const cardsHtml = compBenefits.map((row, idx) => {
|
|
5127
5157
|
const icon = (rawBenefs[idx] && typeIcons[rawBenefs[idx].type]) || "\u2726";
|
|
5128
5158
|
return `<li class="pdf-explain-benefit-card"><span class="pdf-explain-benefit-icon">${icon}</span><span class="pdf-explain-benefit-label">${escapeHtml(row.label)}</span><strong class="pdf-explain-benefit-amount">${eur(row.amount)}</strong></li>`;
|
|
5129
5159
|
}).join("");
|
|
5130
|
-
const benefitsTotal = compBenefits
|
|
5160
|
+
const benefitsTotal = getCompensativeBenefitsTotal(compBenefits);
|
|
5131
5161
|
explainResultHtml = `
|
|
5132
5162
|
<div class="pdf-explain-no-transfer-badge">⚖️ ${escapeHtml(tr("calcNoTransferSuggested"))}</div>
|
|
5133
5163
|
<div class="pdf-explain-benefits-section">
|
|
@@ -6007,6 +6037,7 @@ ${scenarioLab.length ? `
|
|
|
6007
6037
|
assegnoPagato2: num("assegnoPagato2"),
|
|
6008
6038
|
assegnoFam2: num("assegnoFam2"),
|
|
6009
6039
|
primaCasaMutuoEnabled: document.getElementById("primaCasaMutuoEnabled")?.checked ? 1 : 0,
|
|
6040
|
+
primaCasaValoreLocativo: num("primaCasaValoreLocativo"),
|
|
6010
6041
|
primaCasaMutuoImporto: num("primaCasaMutuoImporto"),
|
|
6011
6042
|
primaCasaAssegnataA: String(document.getElementById("primaCasaAssegnataA")?.value || ""),
|
|
6012
6043
|
primaCasaMutuoPerc1: num("primaCasaMutuoPerc1"),
|
|
@@ -6050,6 +6081,7 @@ ${scenarioLab.length ? `
|
|
|
6050
6081
|
|
|
6051
6082
|
function hydrateState(state) {
|
|
6052
6083
|
if (!state || !state.base || !state.spese) return;
|
|
6084
|
+
let stateSpeseRows = Array.isArray(state.spese) ? state.spese : [];
|
|
6053
6085
|
Object.entries(state.base).forEach(([k, v]) => {
|
|
6054
6086
|
const el = document.getElementById(k);
|
|
6055
6087
|
if (!el) return;
|
|
@@ -6065,11 +6097,15 @@ ${scenarioLab.length ? `
|
|
|
6065
6097
|
speseConvivenzaAutoMode = Math.max(0, Number(state.base && state.base.speseConvivenza || 0)) <= 0.005;
|
|
6066
6098
|
}
|
|
6067
6099
|
if (Array.isArray(state.expenseItems) && state.expenseItems.length) {
|
|
6068
|
-
|
|
6100
|
+
const filteredPairs = state.expenseItems
|
|
6101
|
+
.map((item, idx) => ({ item, row: stateSpeseRows[idx] }))
|
|
6102
|
+
.filter((pair) => !isLegacyLocativeExpenseLabel(pair && pair.item && pair.item.label));
|
|
6103
|
+
expenseItems = filteredPairs.map((pair, idx) => normalizeExpenseItem(pair.item, idx));
|
|
6104
|
+
stateSpeseRows = filteredPairs.map((pair) => pair.row).filter((row) => row !== undefined);
|
|
6069
6105
|
} else {
|
|
6070
6106
|
expenseItems = defaultExpenseItems.map((item) => ({ ...item }));
|
|
6071
6107
|
}
|
|
6072
|
-
while (expenseItems.length <
|
|
6108
|
+
while (expenseItems.length < stateSpeseRows.length) {
|
|
6073
6109
|
expenseItems.push(normalizeExpenseItem(null, expenseItems.length));
|
|
6074
6110
|
}
|
|
6075
6111
|
scenarioLab = normalizeScenarioLabState(state.scenarioLab);
|
|
@@ -6107,7 +6143,7 @@ ${scenarioLab.length ? `
|
|
|
6107
6143
|
updateSpouseLabels();
|
|
6108
6144
|
buildExpenseRows();
|
|
6109
6145
|
syncPermanenza("calendar");
|
|
6110
|
-
|
|
6146
|
+
stateSpeseRows.forEach((row, i) => {
|
|
6111
6147
|
const c1 = document.getElementById(`c1_${i}`);
|
|
6112
6148
|
const c2 = document.getElementById(`c2_${i}`);
|
|
6113
6149
|
const d1 = document.getElementById(`c1d_${i}`);
|
package/frontend/public/app.js
CHANGED
|
@@ -281,6 +281,8 @@ const defaultExpenseItems = [
|
|
|
281
281
|
firstHomeBoxNote: "Dichiara se esiste un mutuo sulla prima casa dei coniugi ceduta a uno dei due: il modello considera il trasferimento implicito quando la casa e assegnata al collocatario.",
|
|
282
282
|
firstHomeMortgageEnabledLabel: "Mutuo su prima casa dei coniugi",
|
|
283
283
|
firstHomeMortgageEnabledHint: "Attiva per includere il mutuo della prima casa ceduta nei benefici compensativi.",
|
|
284
|
+
firstHomeLocativeValueLabel: "Casa (valore locativo) ({currency})",
|
|
285
|
+
firstHomeLocativeValueHint: "Valore locativo mensile della casa assegnata, usato per valorizzare il beneficio economico implicito.",
|
|
284
286
|
firstHomeMortgageAmountLabel: "Rata mutuo mensile ({currency})",
|
|
285
287
|
firstHomeMortgageAmountHint: "Importo mensile complessivo della rata del mutuo prima casa.",
|
|
286
288
|
firstHomeAssignedToLabel: "Casa assegnata a",
|
|
@@ -295,6 +297,7 @@ const defaultExpenseItems = [
|
|
|
295
297
|
calcBenefitFamilyAllowance: "Assegno familiare INPS percepito da {spouse}",
|
|
296
298
|
calcBenefitPrimaryHomeMortgage: "Quota mutuo prima casa ceduta al collocatario ({payer} -> {receiver})",
|
|
297
299
|
calcBenefitPrimaryHomeAssignment: "Assegnazione casa familiare - valore locativo ({receiver})",
|
|
300
|
+
calcBenefitExcludedFromTotal: "fuori totale",
|
|
298
301
|
pdfCompBenefitsSection: "Benefici compensativi gia allocati",
|
|
299
302
|
pdfCompBenefitsItem: "Beneficio",
|
|
300
303
|
pdfCompBenefitsAmount: "Valore {currency}/mese",
|
|
@@ -640,6 +643,8 @@ const defaultExpenseItems = [
|
|
|
640
643
|
firstHomeBoxNote: "Declare whether there is a mortgage on the spouses' primary home assigned to one spouse: the model counts the implicit transfer when the home is assigned to the custodial parent.",
|
|
641
644
|
firstHomeMortgageEnabledLabel: "Mortgage on spouses' primary home",
|
|
642
645
|
firstHomeMortgageEnabledHint: "Enable to include the assigned primary-home mortgage in compensative benefits.",
|
|
646
|
+
firstHomeLocativeValueLabel: "Home (rental value) ({currency})",
|
|
647
|
+
firstHomeLocativeValueHint: "Monthly rental value of the assigned home, used to value the implicit economic benefit.",
|
|
643
648
|
firstHomeMortgageAmountLabel: "Monthly mortgage payment ({currency})",
|
|
644
649
|
firstHomeMortgageAmountHint: "Total monthly amount of the primary-home mortgage payment.",
|
|
645
650
|
firstHomeAssignedToLabel: "Home assigned to",
|
|
@@ -654,6 +659,7 @@ const defaultExpenseItems = [
|
|
|
654
659
|
calcBenefitFamilyAllowance: "INPS family allowance received by {spouse}",
|
|
655
660
|
calcBenefitPrimaryHomeMortgage: "Primary-home mortgage share assigned to custodial parent ({payer} -> {receiver})",
|
|
656
661
|
calcBenefitPrimaryHomeAssignment: "Primary home assignment - rental value ({receiver})",
|
|
662
|
+
calcBenefitExcludedFromTotal: "excluded from total",
|
|
657
663
|
pdfCompBenefitsSection: "Compensative benefits already allocated",
|
|
658
664
|
pdfCompBenefitsItem: "Benefit",
|
|
659
665
|
pdfCompBenefitsAmount: "Value {currency}/month",
|
|
@@ -1213,6 +1219,8 @@ const defaultExpenseItems = [
|
|
|
1213
1219
|
const firstHomeBoxNote = document.getElementById("firstHomeBoxNote");
|
|
1214
1220
|
const lblPrimaCasaMutuoEnabled = document.getElementById("lblPrimaCasaMutuoEnabled");
|
|
1215
1221
|
const hintPrimaCasaMutuoEnabled = document.getElementById("hintPrimaCasaMutuoEnabled");
|
|
1222
|
+
const lblPrimaCasaValoreLocativo = document.getElementById("lblPrimaCasaValoreLocativo");
|
|
1223
|
+
const hintPrimaCasaValoreLocativo = document.getElementById("hintPrimaCasaValoreLocativo");
|
|
1216
1224
|
const lblPrimaCasaMutuoImporto = document.getElementById("lblPrimaCasaMutuoImporto");
|
|
1217
1225
|
const hintPrimaCasaMutuoImporto = document.getElementById("hintPrimaCasaMutuoImporto");
|
|
1218
1226
|
const lblPrimaCasaAssegnataA = document.getElementById("lblPrimaCasaAssegnataA");
|
|
@@ -1276,6 +1284,8 @@ const defaultExpenseItems = [
|
|
|
1276
1284
|
if (firstHomeBoxNote) firstHomeBoxNote.textContent = tr("firstHomeBoxNote");
|
|
1277
1285
|
if (lblPrimaCasaMutuoEnabled) lblPrimaCasaMutuoEnabled.textContent = tr("firstHomeMortgageEnabledLabel");
|
|
1278
1286
|
if (hintPrimaCasaMutuoEnabled) hintPrimaCasaMutuoEnabled.title = tr("firstHomeMortgageEnabledHint");
|
|
1287
|
+
if (lblPrimaCasaValoreLocativo) lblPrimaCasaValoreLocativo.textContent = msg("firstHomeLocativeValueLabel", { currency: currentCurrency });
|
|
1288
|
+
if (hintPrimaCasaValoreLocativo) hintPrimaCasaValoreLocativo.title = tr("firstHomeLocativeValueHint");
|
|
1279
1289
|
if (lblPrimaCasaMutuoImporto) lblPrimaCasaMutuoImporto.textContent = msg("firstHomeMortgageAmountLabel", { currency: currentCurrency });
|
|
1280
1290
|
if (hintPrimaCasaMutuoImporto) hintPrimaCasaMutuoImporto.title = tr("firstHomeMortgageAmountHint");
|
|
1281
1291
|
if (lblPrimaCasaAssegnataA) lblPrimaCasaAssegnataA.textContent = tr("firstHomeAssignedToLabel");
|
|
@@ -1496,6 +1506,14 @@ const defaultExpenseItems = [
|
|
|
1496
1506
|
return { label, help };
|
|
1497
1507
|
}
|
|
1498
1508
|
|
|
1509
|
+
function isLegacyLocativeExpenseLabel(label) {
|
|
1510
|
+
const normalized = String(label || "")
|
|
1511
|
+
.replace(/^[^A-Za-z\u00C0-\u024F]+/, "")
|
|
1512
|
+
.trim()
|
|
1513
|
+
.toLowerCase();
|
|
1514
|
+
return normalized === "casa (valore locativo)";
|
|
1515
|
+
}
|
|
1516
|
+
|
|
1499
1517
|
function populateSuggestedExpenseOptions() {
|
|
1500
1518
|
const select = document.getElementById("suggestedExpenseSelect");
|
|
1501
1519
|
if (!select) return;
|
|
@@ -3589,6 +3607,7 @@ const defaultExpenseItems = [
|
|
|
3589
3607
|
aFam1: num("assegnoFam1"),
|
|
3590
3608
|
aFam2: num("assegnoFam2"),
|
|
3591
3609
|
primaCasaMutuoEnabled: firstHome.enabled ? 1 : 0,
|
|
3610
|
+
primaCasaValoreLocativo: num("primaCasaValoreLocativo"),
|
|
3592
3611
|
primaCasaMutuoImporto: firstHome.amount,
|
|
3593
3612
|
primaCasaAssegnataA: firstHome.assignedTo,
|
|
3594
3613
|
primaCasaMutuoPerc1: firstHome.share1,
|
|
@@ -4638,14 +4657,14 @@ const defaultExpenseItems = [
|
|
|
4638
4657
|
const rawBenefs = Array.isArray(m.compensativeBenefits)
|
|
4639
4658
|
? m.compensativeBenefits.filter((r) => r && Number(r.amount || 0) > 0.005)
|
|
4640
4659
|
: [];
|
|
4641
|
-
const typeIcons = { family: "\uD83C\uDFDB", "primary-home-mortgage": "\uD83C\uDFE1" };
|
|
4660
|
+
const typeIcons = { family: "\uD83C\uDFDB", "primary-home-mortgage": "\uD83C\uDFE1", "primary-home-assignment": "\uD83C\uDFE0" };
|
|
4642
4661
|
const cardsHtml = benefitRows
|
|
4643
4662
|
.map((row, i) => {
|
|
4644
4663
|
const icon = (rawBenefs[i] && typeIcons[rawBenefs[i].type]) || "\u2726";
|
|
4645
4664
|
return `<li class="spieg-benefit-card"><span class="spieg-benefit-icon">${icon}</span><span class="spieg-benefit-label">${escapeHtml(row.label)}</span><strong class="spieg-benefit-amount">${eur(row.amount)}</strong></li>`;
|
|
4646
4665
|
})
|
|
4647
4666
|
.join("");
|
|
4648
|
-
const total = benefitRows
|
|
4667
|
+
const total = getCompensativeBenefitsTotal(benefitRows);
|
|
4649
4668
|
const totalLabel = currentLang === "en" ? "Total allocated benefits" : "Totale benefici allocati";
|
|
4650
4669
|
resultHtml = `
|
|
4651
4670
|
<div class="spieg-no-transfer-badge">⚖️ ${escapeHtml(tr("calcNoTransferSuggested"))}</div>
|
|
@@ -4730,21 +4749,32 @@ const defaultExpenseItems = [
|
|
|
4730
4749
|
const amount = Number(row.amount || 0);
|
|
4731
4750
|
if (row.type === "family") {
|
|
4732
4751
|
const spouse = Number(row.to) === 2 ? name2 : name1;
|
|
4733
|
-
return { label: msg("calcBenefitFamilyAllowance", { spouse }), amount };
|
|
4752
|
+
return { label: msg("calcBenefitFamilyAllowance", { spouse }), amount, includeInTotal: true };
|
|
4734
4753
|
}
|
|
4735
4754
|
if (row.type === "primary-home-mortgage") {
|
|
4736
4755
|
const payer = Number(row.from) === 2 ? name2 : name1;
|
|
4737
4756
|
const receiver = Number(row.to) === 2 ? name2 : name1;
|
|
4738
|
-
return { label: msg("calcBenefitPrimaryHomeMortgage", { payer, receiver }), amount };
|
|
4757
|
+
return { label: msg("calcBenefitPrimaryHomeMortgage", { payer, receiver }), amount, includeInTotal: true };
|
|
4739
4758
|
}
|
|
4740
4759
|
if (row.type === "primary-home-assignment") {
|
|
4741
4760
|
const receiver = Number(row.to) === 2 ? name2 : name1;
|
|
4742
|
-
return {
|
|
4761
|
+
return {
|
|
4762
|
+
label: `${msg("calcBenefitPrimaryHomeAssignment", { receiver })} (${tr("calcBenefitExcludedFromTotal")})`,
|
|
4763
|
+
amount,
|
|
4764
|
+
includeInTotal: false
|
|
4765
|
+
};
|
|
4743
4766
|
}
|
|
4744
|
-
return { label: tr("calcCompBenefitsLabel"), amount };
|
|
4767
|
+
return { label: tr("calcCompBenefitsLabel"), amount, includeInTotal: true };
|
|
4745
4768
|
});
|
|
4746
4769
|
}
|
|
4747
4770
|
|
|
4771
|
+
function getCompensativeBenefitsTotal(rows) {
|
|
4772
|
+
return (rows || []).reduce((sum, row) => {
|
|
4773
|
+
if (!row || row.includeInTotal === false) return sum;
|
|
4774
|
+
return sum + Number(row.amount || 0);
|
|
4775
|
+
}, 0);
|
|
4776
|
+
}
|
|
4777
|
+
|
|
4748
4778
|
function formatCompensativeBenefitsInline(m, name1 = c1n(), name2 = c2n()) {
|
|
4749
4779
|
return getCompensativeBenefitRows(m, name1, name2)
|
|
4750
4780
|
.map((row) => `${row.label}: ${eur(row.amount)}`)
|
|
@@ -4851,14 +4881,14 @@ const defaultExpenseItems = [
|
|
|
4851
4881
|
const rawBenefs = Array.isArray(m.compensativeBenefits)
|
|
4852
4882
|
? m.compensativeBenefits.filter((r) => r && Number(r.amount || 0) > 0.005)
|
|
4853
4883
|
: [];
|
|
4854
|
-
const typeIcons = { family: "\uD83C\uDFDB", "primary-home-mortgage": "\uD83C\uDFE1" };
|
|
4884
|
+
const typeIcons = { family: "\uD83C\uDFDB", "primary-home-mortgage": "\uD83C\uDFE1", "primary-home-assignment": "\uD83C\uDFE0" };
|
|
4855
4885
|
const cardsHtml = benefitRows
|
|
4856
4886
|
.map((row, i) => {
|
|
4857
4887
|
const icon = (rawBenefs[i] && typeIcons[rawBenefs[i].type]) || "\u2726";
|
|
4858
4888
|
return `<li class="spieg-benefit-card"><span class="spieg-benefit-icon">${icon}</span><span class="spieg-benefit-label">${escapeHtml(row.label)}</span><strong class="spieg-benefit-amount">${eur(row.amount)}</strong></li>`;
|
|
4859
4889
|
})
|
|
4860
4890
|
.join("");
|
|
4861
|
-
const total = benefitRows
|
|
4891
|
+
const total = getCompensativeBenefitsTotal(benefitRows);
|
|
4862
4892
|
const totalLabel = currentLang === "en" ? "Total allocated benefits" : "Totale benefici allocati";
|
|
4863
4893
|
benefitCardsHtml = `
|
|
4864
4894
|
<div class="result-benefits-box">
|
|
@@ -5122,12 +5152,12 @@ const defaultExpenseItems = [
|
|
|
5122
5152
|
const rawBenefs = Array.isArray(m.compensativeBenefits)
|
|
5123
5153
|
? m.compensativeBenefits.filter((row) => row && Number(row.amount || 0) > 0.005)
|
|
5124
5154
|
: [];
|
|
5125
|
-
const typeIcons = { family: "\uD83C\uDFDB", "primary-home-mortgage": "\uD83C\uDFE1" };
|
|
5155
|
+
const typeIcons = { family: "\uD83C\uDFDB", "primary-home-mortgage": "\uD83C\uDFE1", "primary-home-assignment": "\uD83C\uDFE0" };
|
|
5126
5156
|
const cardsHtml = compBenefits.map((row, idx) => {
|
|
5127
5157
|
const icon = (rawBenefs[idx] && typeIcons[rawBenefs[idx].type]) || "\u2726";
|
|
5128
5158
|
return `<li class="pdf-explain-benefit-card"><span class="pdf-explain-benefit-icon">${icon}</span><span class="pdf-explain-benefit-label">${escapeHtml(row.label)}</span><strong class="pdf-explain-benefit-amount">${eur(row.amount)}</strong></li>`;
|
|
5129
5159
|
}).join("");
|
|
5130
|
-
const benefitsTotal = compBenefits
|
|
5160
|
+
const benefitsTotal = getCompensativeBenefitsTotal(compBenefits);
|
|
5131
5161
|
explainResultHtml = `
|
|
5132
5162
|
<div class="pdf-explain-no-transfer-badge">⚖️ ${escapeHtml(tr("calcNoTransferSuggested"))}</div>
|
|
5133
5163
|
<div class="pdf-explain-benefits-section">
|
|
@@ -6007,6 +6037,7 @@ ${scenarioLab.length ? `
|
|
|
6007
6037
|
assegnoPagato2: num("assegnoPagato2"),
|
|
6008
6038
|
assegnoFam2: num("assegnoFam2"),
|
|
6009
6039
|
primaCasaMutuoEnabled: document.getElementById("primaCasaMutuoEnabled")?.checked ? 1 : 0,
|
|
6040
|
+
primaCasaValoreLocativo: num("primaCasaValoreLocativo"),
|
|
6010
6041
|
primaCasaMutuoImporto: num("primaCasaMutuoImporto"),
|
|
6011
6042
|
primaCasaAssegnataA: String(document.getElementById("primaCasaAssegnataA")?.value || ""),
|
|
6012
6043
|
primaCasaMutuoPerc1: num("primaCasaMutuoPerc1"),
|
|
@@ -6050,6 +6081,7 @@ ${scenarioLab.length ? `
|
|
|
6050
6081
|
|
|
6051
6082
|
function hydrateState(state) {
|
|
6052
6083
|
if (!state || !state.base || !state.spese) return;
|
|
6084
|
+
let stateSpeseRows = Array.isArray(state.spese) ? state.spese : [];
|
|
6053
6085
|
Object.entries(state.base).forEach(([k, v]) => {
|
|
6054
6086
|
const el = document.getElementById(k);
|
|
6055
6087
|
if (!el) return;
|
|
@@ -6065,11 +6097,15 @@ ${scenarioLab.length ? `
|
|
|
6065
6097
|
speseConvivenzaAutoMode = Math.max(0, Number(state.base && state.base.speseConvivenza || 0)) <= 0.005;
|
|
6066
6098
|
}
|
|
6067
6099
|
if (Array.isArray(state.expenseItems) && state.expenseItems.length) {
|
|
6068
|
-
|
|
6100
|
+
const filteredPairs = state.expenseItems
|
|
6101
|
+
.map((item, idx) => ({ item, row: stateSpeseRows[idx] }))
|
|
6102
|
+
.filter((pair) => !isLegacyLocativeExpenseLabel(pair && pair.item && pair.item.label));
|
|
6103
|
+
expenseItems = filteredPairs.map((pair, idx) => normalizeExpenseItem(pair.item, idx));
|
|
6104
|
+
stateSpeseRows = filteredPairs.map((pair) => pair.row).filter((row) => row !== undefined);
|
|
6069
6105
|
} else {
|
|
6070
6106
|
expenseItems = defaultExpenseItems.map((item) => ({ ...item }));
|
|
6071
6107
|
}
|
|
6072
|
-
while (expenseItems.length <
|
|
6108
|
+
while (expenseItems.length < stateSpeseRows.length) {
|
|
6073
6109
|
expenseItems.push(normalizeExpenseItem(null, expenseItems.length));
|
|
6074
6110
|
}
|
|
6075
6111
|
scenarioLab = normalizeScenarioLabState(state.scenarioLab);
|
|
@@ -6107,7 +6143,7 @@ ${scenarioLab.length ? `
|
|
|
6107
6143
|
updateSpouseLabels();
|
|
6108
6144
|
buildExpenseRows();
|
|
6109
6145
|
syncPermanenza("calendar");
|
|
6110
|
-
|
|
6146
|
+
stateSpeseRows.forEach((row, i) => {
|
|
6111
6147
|
const c1 = document.getElementById(`c1_${i}`);
|
|
6112
6148
|
const c2 = document.getElementById(`c2_${i}`);
|
|
6113
6149
|
const d1 = document.getElementById(`c1d_${i}`);
|
|
@@ -885,21 +885,60 @@
|
|
|
885
885
|
}
|
|
886
886
|
|
|
887
887
|
.extra-box-first-home {
|
|
888
|
+
position: relative;
|
|
889
|
+
overflow: hidden;
|
|
888
890
|
background: linear-gradient(135deg, #ffffff 0%, #f0fffe 50%, #f5faff 100%);
|
|
889
891
|
border: 1px solid rgba(27, 141, 127, 0.15);
|
|
890
892
|
box-shadow: 0 2px 8px rgba(27, 141, 127, 0.08), inset 0 1px 0 rgba(255, 255, 255, 0.8);
|
|
891
893
|
}
|
|
892
894
|
|
|
895
|
+
.extra-box-first-home::after {
|
|
896
|
+
content: "";
|
|
897
|
+
position: absolute;
|
|
898
|
+
inset: -40% -20% auto;
|
|
899
|
+
height: 180px;
|
|
900
|
+
background: radial-gradient(circle at 50% 50%, rgba(61, 181, 160, 0.16), rgba(216, 154, 53, 0.08) 45%, transparent 70%);
|
|
901
|
+
pointer-events: none;
|
|
902
|
+
animation: first-home-aurora 7s ease-in-out infinite alternate;
|
|
903
|
+
}
|
|
904
|
+
|
|
893
905
|
.extra-box-first-home .extra-box-title::before {
|
|
894
906
|
content: '🏠 ';
|
|
895
907
|
margin-right: 4px;
|
|
896
908
|
}
|
|
897
909
|
|
|
898
910
|
.extra-box-first-home .extra-grid {
|
|
911
|
+
position: relative;
|
|
912
|
+
z-index: 1;
|
|
899
913
|
gap: 8px 10px;
|
|
900
914
|
}
|
|
901
915
|
|
|
916
|
+
.extra-box-first-home .field {
|
|
917
|
+
transition: transform 0.2s ease, filter 0.2s ease;
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
.extra-box-first-home .field:focus-within {
|
|
921
|
+
transform: translateY(-1px);
|
|
922
|
+
filter: saturate(1.04);
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
.extra-box-first-home .field input[type="number"],
|
|
926
|
+
.extra-box-first-home .field select {
|
|
927
|
+
border-width: 1.5px;
|
|
928
|
+
box-shadow: 0 1px 0 rgba(255, 255, 255, 0.8) inset;
|
|
929
|
+
transition: border-color 0.2s ease, box-shadow 0.2s ease, background 0.2s ease;
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
.extra-box-first-home .field input[type="number"]:focus,
|
|
933
|
+
.extra-box-first-home .field select:focus {
|
|
934
|
+
border-color: rgba(27, 141, 127, 0.4);
|
|
935
|
+
box-shadow: 0 0 0 3px rgba(27, 141, 127, 0.14), 0 1px 0 rgba(255, 255, 255, 0.85) inset;
|
|
936
|
+
background: linear-gradient(180deg, #ffffff, #f4fffd);
|
|
937
|
+
}
|
|
938
|
+
|
|
902
939
|
.mortgage-split-slider {
|
|
940
|
+
position: relative;
|
|
941
|
+
overflow: hidden;
|
|
903
942
|
margin-top: 12px;
|
|
904
943
|
border: 1.5px solid rgba(27, 141, 127, 0.2);
|
|
905
944
|
border-radius: 16px;
|
|
@@ -916,11 +955,25 @@
|
|
|
916
955
|
transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
|
|
917
956
|
}
|
|
918
957
|
|
|
958
|
+
.mortgage-split-slider::after {
|
|
959
|
+
content: "";
|
|
960
|
+
position: absolute;
|
|
961
|
+
inset: 0;
|
|
962
|
+
background: linear-gradient(112deg, transparent 30%, rgba(255, 255, 255, 0.55) 46%, transparent 58%);
|
|
963
|
+
transform: translateX(-120%);
|
|
964
|
+
transition: transform 0.5s ease;
|
|
965
|
+
pointer-events: none;
|
|
966
|
+
}
|
|
967
|
+
|
|
919
968
|
.mortgage-split-slider:hover {
|
|
920
969
|
box-shadow: 0 8px 20px rgba(27, 141, 127, 0.15), inset 0 1px 0 rgba(255, 255, 255, 0.8);
|
|
921
970
|
border-color: rgba(27, 141, 127, 0.3);
|
|
922
971
|
}
|
|
923
972
|
|
|
973
|
+
.mortgage-split-slider:hover::after {
|
|
974
|
+
transform: translateX(120%);
|
|
975
|
+
}
|
|
976
|
+
|
|
924
977
|
.mortgage-split-slider.is-disabled {
|
|
925
978
|
opacity: 0.55;
|
|
926
979
|
filter: grayscale(0.4);
|
|
@@ -1098,6 +1151,17 @@
|
|
|
1098
1151
|
box-shadow: 0 2px 6px rgba(27, 141, 127, 0.15), inset 0 1px 2px rgba(255, 255, 255, 0.6);
|
|
1099
1152
|
transition: all 0.2s ease;
|
|
1100
1153
|
letter-spacing: 0.2px;
|
|
1154
|
+
animation: split-center-breathe 2.6s ease-in-out infinite;
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
@keyframes first-home-aurora {
|
|
1158
|
+
0% { transform: translate3d(-5%, -12%, 0) scale(1); }
|
|
1159
|
+
100% { transform: translate3d(6%, -6%, 0) scale(1.08); }
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
@keyframes split-center-breathe {
|
|
1163
|
+
0%, 100% { box-shadow: 0 2px 6px rgba(27, 141, 127, 0.15), inset 0 1px 2px rgba(255, 255, 255, 0.6); }
|
|
1164
|
+
50% { box-shadow: 0 5px 14px rgba(27, 141, 127, 0.24), inset 0 1px 2px rgba(255, 255, 255, 0.75); }
|
|
1101
1165
|
}
|
|
1102
1166
|
|
|
1103
1167
|
#primaCasaMutuoSplitInfo {
|