mantenimento-app 2.2.5 → 2.2.7
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 +34 -14
- package/backend/calculate-model.js +6 -5
- package/frontend/public/app.js +34 -14
- package/frontend/public/index.html +1 -1
- package/frontend/public/styles.css +111 -6
- package/package.json +1 -1
package/app.js
CHANGED
|
@@ -3457,15 +3457,17 @@ const defaultExpenseItems = [
|
|
|
3457
3457
|
const housingUtilityNonColl = collocatario === 1 ? housingUtility2 : housingUtility1;
|
|
3458
3458
|
|
|
3459
3459
|
const baseDuplicazione = speseConvivenza > 0 ? (speseTot - speseConvivenza) : null;
|
|
3460
|
+
// OECD-modified equivalence logic suggests split-household extra burden is material but not full duplication.
|
|
3461
|
+
const HOUSING_UTILITY_ADJ_FACTOR = 0.35;
|
|
3460
3462
|
const separationAdjustmentHousingUtilities = baseDuplicazione !== null && baseDuplicazione <= 0.005
|
|
3461
|
-
? Math.max(0, housingUtilityNonColl)
|
|
3463
|
+
? Math.max(0, housingUtilityNonColl * HOUSING_UTILITY_ADJ_FACTOR)
|
|
3462
3464
|
: 0;
|
|
3463
|
-
const speseConvivenzaEffettive = speseConvivenza > 0
|
|
3464
|
-
|
|
3465
|
+
const speseConvivenzaEffettive = speseConvivenza > 0 ? speseConvivenza : null;
|
|
3466
|
+
const costoSeparazioneMensile = baseDuplicazione !== null
|
|
3467
|
+
? Math.max(0, baseDuplicazione + separationAdjustmentHousingUtilities)
|
|
3465
3468
|
: null;
|
|
3466
|
-
const costoSeparazioneMensile = speseConvivenzaEffettive !== null ? (speseTot - speseConvivenzaEffettive) : null;
|
|
3467
3469
|
const nettoInsiemeCombinato = speseConvivenzaEffettive !== null ? (r1 + r2 - speseConvivenzaEffettive) : null;
|
|
3468
|
-
const nettoSeparatoTotale = post1 + post2;
|
|
3470
|
+
const nettoSeparatoTotale = (post1 + post2) - separationAdjustmentHousingUtilities;
|
|
3469
3471
|
const perditaMensile = nettoInsiemeCombinato !== null ? nettoInsiemeCombinato - nettoSeparatoTotale : null;
|
|
3470
3472
|
const perditaAnnua = perditaMensile !== null ? perditaMensile * 12 : null;
|
|
3471
3473
|
const totReddito = Math.max(0.001, r1 + r2);
|
|
@@ -4673,24 +4675,42 @@ const defaultExpenseItems = [
|
|
|
4673
4675
|
<strong class="sep-cost-value ${cls}">${formatted}</strong>
|
|
4674
4676
|
</div>`;
|
|
4675
4677
|
};
|
|
4678
|
+
const statHtml = (label, value, tone) => {
|
|
4679
|
+
const cls = lossClass(value);
|
|
4680
|
+
const formatted = value === null ? "—" : eur(value);
|
|
4681
|
+
return `<div class="sep-cost-stat ${tone || ""}">
|
|
4682
|
+
<span class="sep-cost-stat-label">${label}</span>
|
|
4683
|
+
<strong class="sep-cost-stat-value ${cls}">${formatted}</strong>
|
|
4684
|
+
</div>`;
|
|
4685
|
+
};
|
|
4686
|
+
const pillHtml = (label, value) => {
|
|
4687
|
+
const cls = lossClass(value);
|
|
4688
|
+
const formatted = value === null ? "—" : eur(value);
|
|
4689
|
+
return `<div class="sep-cost-pill">
|
|
4690
|
+
<span class="sep-cost-pill-label">${label}</span>
|
|
4691
|
+
<strong class="sep-cost-pill-value ${cls}">${formatted}</strong>
|
|
4692
|
+
</div>`;
|
|
4693
|
+
};
|
|
4676
4694
|
|
|
4677
4695
|
panel.innerHTML = `
|
|
4678
4696
|
<div class="sep-cost-panel">
|
|
4679
|
-
<h3 class="sep-cost-title">${escapeHtml(tr("sepCostPanelTitle"))}</h3>
|
|
4680
|
-
<div class="sep-cost-
|
|
4697
|
+
<h3 class="sep-cost-title"><span class="sep-cost-title-icon">💔</span>${escapeHtml(tr("sepCostPanelTitle"))}</h3>
|
|
4698
|
+
<div class="sep-cost-hero">
|
|
4699
|
+
${statHtml(tr("sepCostLossMonthly"), m.perditaMensile, "sep-cost-stat--primary")}
|
|
4700
|
+
${statHtml(tr("sepCostLossAnnually"), m.perditaAnnua, "sep-cost-stat--secondary")}
|
|
4701
|
+
</div>
|
|
4702
|
+
<div class="sep-cost-section sep-cost-section--grid">
|
|
4681
4703
|
${rowHtml(tr("sepCostNetTogether"), m.nettoInsiemeCombinato, false)}
|
|
4682
4704
|
${rowHtml(tr("sepCostNetSeparated"), m.nettoSeparatoTotale, false)}
|
|
4683
|
-
</div>
|
|
4684
|
-
<div class="sep-cost-divider"></div>
|
|
4685
|
-
<div class="sep-cost-section">
|
|
4686
4705
|
${m.separationAdjustmentHousingUtilities > 0
|
|
4687
4706
|
? rowHtml(tr("sepCostHousingUtilityAdj"), m.separationAdjustmentHousingUtilities, false)
|
|
4688
4707
|
: ""}
|
|
4689
4708
|
${rowHtml(tr("sepCostDuplication"), m.costoSeparazioneMensile, false)}
|
|
4690
|
-
|
|
4691
|
-
|
|
4692
|
-
|
|
4693
|
-
${
|
|
4709
|
+
</div>
|
|
4710
|
+
<div class="sep-cost-divider"></div>
|
|
4711
|
+
<div class="sep-cost-pill-wrap">
|
|
4712
|
+
${pillHtml(msg("sepCostLossSpouse", { spouse: c1NameEsc }), m.perditaSpouse1)}
|
|
4713
|
+
${pillHtml(msg("sepCostLossSpouse", { spouse: c2NameEsc }), m.perditaSpouse2)}
|
|
4694
4714
|
</div>
|
|
4695
4715
|
</div>
|
|
4696
4716
|
`;
|
|
@@ -132,15 +132,16 @@ function calculateModel(input) {
|
|
|
132
132
|
const housingUtility2 = c2Spese.reduce((acc, n, idx) => acc + (housingIdx.has(idx) ? toNumber(n) : 0), 0) + quotaMutuoSpese2;
|
|
133
133
|
const housingUtilityNonColl = collocatario === 1 ? housingUtility2 : housingUtility1;
|
|
134
134
|
const baseDuplicazione = speseConvivenza > 0 ? (speseTot - speseConvivenza) : null;
|
|
135
|
+
const HOUSING_UTILITY_ADJ_FACTOR = 0.35;
|
|
135
136
|
const separationAdjustmentHousingUtilities = baseDuplicazione !== null && baseDuplicazione <= 0.005
|
|
136
|
-
? Math.max(0, housingUtilityNonColl)
|
|
137
|
+
? Math.max(0, housingUtilityNonColl * HOUSING_UTILITY_ADJ_FACTOR)
|
|
137
138
|
: 0;
|
|
138
|
-
const speseConvivenzaEffettive = speseConvivenza > 0
|
|
139
|
-
|
|
139
|
+
const speseConvivenzaEffettive = speseConvivenza > 0 ? speseConvivenza : null;
|
|
140
|
+
const costoSeparazioneMensile = baseDuplicazione !== null
|
|
141
|
+
? Math.max(0, baseDuplicazione + separationAdjustmentHousingUtilities)
|
|
140
142
|
: null;
|
|
141
|
-
const costoSeparazioneMensile = speseConvivenzaEffettive !== null ? (speseTot - speseConvivenzaEffettive) : null;
|
|
142
143
|
const nettoInsiemeCombinato = speseConvivenzaEffettive !== null ? (r1 + r2 - speseConvivenzaEffettive) : null;
|
|
143
|
-
const nettoSeparatoTotale = post1 + post2;
|
|
144
|
+
const nettoSeparatoTotale = (post1 + post2) - separationAdjustmentHousingUtilities;
|
|
144
145
|
const perditaMensile = nettoInsiemeCombinato !== null ? nettoInsiemeCombinato - nettoSeparatoTotale : null;
|
|
145
146
|
const perditaAnnua = perditaMensile !== null ? perditaMensile * 12 : null;
|
|
146
147
|
const totReddito = Math.max(0.001, r1 + r2);
|
package/frontend/public/app.js
CHANGED
|
@@ -3457,15 +3457,17 @@ const defaultExpenseItems = [
|
|
|
3457
3457
|
const housingUtilityNonColl = collocatario === 1 ? housingUtility2 : housingUtility1;
|
|
3458
3458
|
|
|
3459
3459
|
const baseDuplicazione = speseConvivenza > 0 ? (speseTot - speseConvivenza) : null;
|
|
3460
|
+
// OECD-modified equivalence logic suggests split-household extra burden is material but not full duplication.
|
|
3461
|
+
const HOUSING_UTILITY_ADJ_FACTOR = 0.35;
|
|
3460
3462
|
const separationAdjustmentHousingUtilities = baseDuplicazione !== null && baseDuplicazione <= 0.005
|
|
3461
|
-
? Math.max(0, housingUtilityNonColl)
|
|
3463
|
+
? Math.max(0, housingUtilityNonColl * HOUSING_UTILITY_ADJ_FACTOR)
|
|
3462
3464
|
: 0;
|
|
3463
|
-
const speseConvivenzaEffettive = speseConvivenza > 0
|
|
3464
|
-
|
|
3465
|
+
const speseConvivenzaEffettive = speseConvivenza > 0 ? speseConvivenza : null;
|
|
3466
|
+
const costoSeparazioneMensile = baseDuplicazione !== null
|
|
3467
|
+
? Math.max(0, baseDuplicazione + separationAdjustmentHousingUtilities)
|
|
3465
3468
|
: null;
|
|
3466
|
-
const costoSeparazioneMensile = speseConvivenzaEffettive !== null ? (speseTot - speseConvivenzaEffettive) : null;
|
|
3467
3469
|
const nettoInsiemeCombinato = speseConvivenzaEffettive !== null ? (r1 + r2 - speseConvivenzaEffettive) : null;
|
|
3468
|
-
const nettoSeparatoTotale = post1 + post2;
|
|
3470
|
+
const nettoSeparatoTotale = (post1 + post2) - separationAdjustmentHousingUtilities;
|
|
3469
3471
|
const perditaMensile = nettoInsiemeCombinato !== null ? nettoInsiemeCombinato - nettoSeparatoTotale : null;
|
|
3470
3472
|
const perditaAnnua = perditaMensile !== null ? perditaMensile * 12 : null;
|
|
3471
3473
|
const totReddito = Math.max(0.001, r1 + r2);
|
|
@@ -4673,24 +4675,42 @@ const defaultExpenseItems = [
|
|
|
4673
4675
|
<strong class="sep-cost-value ${cls}">${formatted}</strong>
|
|
4674
4676
|
</div>`;
|
|
4675
4677
|
};
|
|
4678
|
+
const statHtml = (label, value, tone) => {
|
|
4679
|
+
const cls = lossClass(value);
|
|
4680
|
+
const formatted = value === null ? "—" : eur(value);
|
|
4681
|
+
return `<div class="sep-cost-stat ${tone || ""}">
|
|
4682
|
+
<span class="sep-cost-stat-label">${label}</span>
|
|
4683
|
+
<strong class="sep-cost-stat-value ${cls}">${formatted}</strong>
|
|
4684
|
+
</div>`;
|
|
4685
|
+
};
|
|
4686
|
+
const pillHtml = (label, value) => {
|
|
4687
|
+
const cls = lossClass(value);
|
|
4688
|
+
const formatted = value === null ? "—" : eur(value);
|
|
4689
|
+
return `<div class="sep-cost-pill">
|
|
4690
|
+
<span class="sep-cost-pill-label">${label}</span>
|
|
4691
|
+
<strong class="sep-cost-pill-value ${cls}">${formatted}</strong>
|
|
4692
|
+
</div>`;
|
|
4693
|
+
};
|
|
4676
4694
|
|
|
4677
4695
|
panel.innerHTML = `
|
|
4678
4696
|
<div class="sep-cost-panel">
|
|
4679
|
-
<h3 class="sep-cost-title">${escapeHtml(tr("sepCostPanelTitle"))}</h3>
|
|
4680
|
-
<div class="sep-cost-
|
|
4697
|
+
<h3 class="sep-cost-title"><span class="sep-cost-title-icon">💔</span>${escapeHtml(tr("sepCostPanelTitle"))}</h3>
|
|
4698
|
+
<div class="sep-cost-hero">
|
|
4699
|
+
${statHtml(tr("sepCostLossMonthly"), m.perditaMensile, "sep-cost-stat--primary")}
|
|
4700
|
+
${statHtml(tr("sepCostLossAnnually"), m.perditaAnnua, "sep-cost-stat--secondary")}
|
|
4701
|
+
</div>
|
|
4702
|
+
<div class="sep-cost-section sep-cost-section--grid">
|
|
4681
4703
|
${rowHtml(tr("sepCostNetTogether"), m.nettoInsiemeCombinato, false)}
|
|
4682
4704
|
${rowHtml(tr("sepCostNetSeparated"), m.nettoSeparatoTotale, false)}
|
|
4683
|
-
</div>
|
|
4684
|
-
<div class="sep-cost-divider"></div>
|
|
4685
|
-
<div class="sep-cost-section">
|
|
4686
4705
|
${m.separationAdjustmentHousingUtilities > 0
|
|
4687
4706
|
? rowHtml(tr("sepCostHousingUtilityAdj"), m.separationAdjustmentHousingUtilities, false)
|
|
4688
4707
|
: ""}
|
|
4689
4708
|
${rowHtml(tr("sepCostDuplication"), m.costoSeparazioneMensile, false)}
|
|
4690
|
-
|
|
4691
|
-
|
|
4692
|
-
|
|
4693
|
-
${
|
|
4709
|
+
</div>
|
|
4710
|
+
<div class="sep-cost-divider"></div>
|
|
4711
|
+
<div class="sep-cost-pill-wrap">
|
|
4712
|
+
${pillHtml(msg("sepCostLossSpouse", { spouse: c1NameEsc }), m.perditaSpouse1)}
|
|
4713
|
+
${pillHtml(msg("sepCostLossSpouse", { spouse: c2NameEsc }), m.perditaSpouse2)}
|
|
4694
4714
|
</div>
|
|
4695
4715
|
</div>
|
|
4696
4716
|
`;
|
|
@@ -1847,19 +1847,78 @@
|
|
|
1847
1847
|
|
|
1848
1848
|
.sep-cost-panel {
|
|
1849
1849
|
margin-top: 10px;
|
|
1850
|
-
border-radius:
|
|
1850
|
+
border-radius: 16px;
|
|
1851
1851
|
border: 1.5px solid #e2a5a5;
|
|
1852
|
-
background:
|
|
1853
|
-
|
|
1854
|
-
|
|
1852
|
+
background:
|
|
1853
|
+
radial-gradient(120% 180% at 0% -20%, rgba(255, 241, 230, 0.75), transparent 58%),
|
|
1854
|
+
radial-gradient(120% 180% at 100% -20%, rgba(255, 228, 228, 0.72), transparent 56%),
|
|
1855
|
+
linear-gradient(145deg, #fff5f3, #ffeceb 52%, #fff6f0);
|
|
1856
|
+
box-shadow: 0 10px 24px rgba(130, 33, 33, 0.14);
|
|
1857
|
+
padding: 14px;
|
|
1855
1858
|
}
|
|
1856
1859
|
|
|
1857
1860
|
.sep-cost-title {
|
|
1858
|
-
margin: 0 0
|
|
1859
|
-
font-size:
|
|
1861
|
+
margin: 0 0 10px;
|
|
1862
|
+
font-size: 1.01rem;
|
|
1860
1863
|
font-weight: 900;
|
|
1861
1864
|
color: #842d2d;
|
|
1862
1865
|
letter-spacing: 0.2px;
|
|
1866
|
+
display: flex;
|
|
1867
|
+
align-items: center;
|
|
1868
|
+
gap: 8px;
|
|
1869
|
+
}
|
|
1870
|
+
|
|
1871
|
+
.sep-cost-title-icon {
|
|
1872
|
+
width: 24px;
|
|
1873
|
+
height: 24px;
|
|
1874
|
+
border-radius: 999px;
|
|
1875
|
+
background: rgba(140, 32, 32, 0.12);
|
|
1876
|
+
display: inline-flex;
|
|
1877
|
+
align-items: center;
|
|
1878
|
+
justify-content: center;
|
|
1879
|
+
font-size: 0.85rem;
|
|
1880
|
+
}
|
|
1881
|
+
|
|
1882
|
+
.sep-cost-hero {
|
|
1883
|
+
display: grid;
|
|
1884
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
1885
|
+
gap: 8px;
|
|
1886
|
+
margin-bottom: 9px;
|
|
1887
|
+
}
|
|
1888
|
+
|
|
1889
|
+
.sep-cost-stat {
|
|
1890
|
+
border-radius: 12px;
|
|
1891
|
+
border: 1px solid rgba(138, 44, 44, 0.16);
|
|
1892
|
+
background: rgba(255,255,255,0.82);
|
|
1893
|
+
padding: 8px 10px;
|
|
1894
|
+
display: grid;
|
|
1895
|
+
gap: 2px;
|
|
1896
|
+
min-width: 0;
|
|
1897
|
+
}
|
|
1898
|
+
|
|
1899
|
+
.sep-cost-stat--primary {
|
|
1900
|
+
background: linear-gradient(130deg, rgba(255, 233, 232, 0.95), rgba(255, 244, 232, 0.92));
|
|
1901
|
+
border-color: rgba(170, 67, 46, 0.35);
|
|
1902
|
+
}
|
|
1903
|
+
|
|
1904
|
+
.sep-cost-stat--secondary {
|
|
1905
|
+
background: linear-gradient(130deg, rgba(255, 245, 240, 0.95), rgba(255, 235, 231, 0.92));
|
|
1906
|
+
border-color: rgba(164, 61, 61, 0.28);
|
|
1907
|
+
}
|
|
1908
|
+
|
|
1909
|
+
.sep-cost-stat-label {
|
|
1910
|
+
font-size: 0.72rem;
|
|
1911
|
+
font-weight: 800;
|
|
1912
|
+
color: #7a3d3d;
|
|
1913
|
+
text-transform: uppercase;
|
|
1914
|
+
letter-spacing: 0.04em;
|
|
1915
|
+
}
|
|
1916
|
+
|
|
1917
|
+
.sep-cost-stat-value {
|
|
1918
|
+
font-size: 1.12rem;
|
|
1919
|
+
font-weight: 900;
|
|
1920
|
+
font-variant-numeric: tabular-nums;
|
|
1921
|
+
white-space: nowrap;
|
|
1863
1922
|
}
|
|
1864
1923
|
|
|
1865
1924
|
.sep-cost-section {
|
|
@@ -1867,6 +1926,10 @@
|
|
|
1867
1926
|
gap: 7px;
|
|
1868
1927
|
}
|
|
1869
1928
|
|
|
1929
|
+
.sep-cost-section--grid {
|
|
1930
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
1931
|
+
}
|
|
1932
|
+
|
|
1870
1933
|
.sep-cost-divider {
|
|
1871
1934
|
height: 1px;
|
|
1872
1935
|
margin: 8px 0;
|
|
@@ -1893,6 +1956,7 @@
|
|
|
1893
1956
|
font-size: 0.82rem;
|
|
1894
1957
|
color: #6a3939;
|
|
1895
1958
|
font-weight: 700;
|
|
1959
|
+
min-width: 0;
|
|
1896
1960
|
}
|
|
1897
1961
|
|
|
1898
1962
|
.sep-cost-value {
|
|
@@ -1903,6 +1967,47 @@
|
|
|
1903
1967
|
color: #5d2e2e;
|
|
1904
1968
|
}
|
|
1905
1969
|
|
|
1970
|
+
.sep-cost-pill-wrap {
|
|
1971
|
+
display: grid;
|
|
1972
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
1973
|
+
gap: 8px;
|
|
1974
|
+
}
|
|
1975
|
+
|
|
1976
|
+
.sep-cost-pill {
|
|
1977
|
+
border: 1px solid rgba(134, 40, 40, 0.18);
|
|
1978
|
+
border-radius: 11px;
|
|
1979
|
+
background: rgba(255,255,255,0.82);
|
|
1980
|
+
padding: 8px 10px;
|
|
1981
|
+
display: grid;
|
|
1982
|
+
gap: 2px;
|
|
1983
|
+
}
|
|
1984
|
+
|
|
1985
|
+
.sep-cost-pill-label {
|
|
1986
|
+
font-size: 0.76rem;
|
|
1987
|
+
color: #714343;
|
|
1988
|
+
font-weight: 700;
|
|
1989
|
+
line-height: 1.2;
|
|
1990
|
+
}
|
|
1991
|
+
|
|
1992
|
+
.sep-cost-pill-value {
|
|
1993
|
+
font-size: 1rem;
|
|
1994
|
+
font-weight: 900;
|
|
1995
|
+
font-variant-numeric: tabular-nums;
|
|
1996
|
+
white-space: nowrap;
|
|
1997
|
+
}
|
|
1998
|
+
|
|
1999
|
+
@media (max-width: 720px) {
|
|
2000
|
+
.sep-cost-hero,
|
|
2001
|
+
.sep-cost-section--grid,
|
|
2002
|
+
.sep-cost-pill-wrap {
|
|
2003
|
+
grid-template-columns: 1fr;
|
|
2004
|
+
}
|
|
2005
|
+
.sep-cost-stat-value,
|
|
2006
|
+
.sep-cost-pill-value {
|
|
2007
|
+
white-space: normal;
|
|
2008
|
+
}
|
|
2009
|
+
}
|
|
2010
|
+
|
|
1906
2011
|
.sep-loss-negative {
|
|
1907
2012
|
color: #9b1f1f;
|
|
1908
2013
|
}
|