@jjlmoya/utils-finance 1.1.0

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.
Files changed (133) hide show
  1. package/package.json +64 -0
  2. package/src/category/i18n/en.ts +159 -0
  3. package/src/category/i18n/es.ts +159 -0
  4. package/src/category/i18n/fr.ts +159 -0
  5. package/src/category/index.ts +24 -0
  6. package/src/category/seo.astro +15 -0
  7. package/src/components/PreviewNavSidebar.astro +116 -0
  8. package/src/components/PreviewToolbar.astro +143 -0
  9. package/src/data.ts +11 -0
  10. package/src/env.d.ts +5 -0
  11. package/src/index.ts +32 -0
  12. package/src/layouts/PreviewLayout.astro +117 -0
  13. package/src/pages/[locale]/[slug].astro +146 -0
  14. package/src/pages/[locale].astro +251 -0
  15. package/src/pages/index.astro +4 -0
  16. package/src/tests/faq_count.test.ts +19 -0
  17. package/src/tests/locale_completeness.test.ts +39 -0
  18. package/src/tests/mocks/astro_mock.js +2 -0
  19. package/src/tests/no_h1_in_components.test.ts +48 -0
  20. package/src/tests/schemas_fulfillment.test.ts +23 -0
  21. package/src/tests/seo_length.test.ts +23 -0
  22. package/src/tests/title_quality.test.ts +55 -0
  23. package/src/tests/tool_validation.test.ts +17 -0
  24. package/src/tool/compoundInterest/bibliography.astro +14 -0
  25. package/src/tool/compoundInterest/component.astro +697 -0
  26. package/src/tool/compoundInterest/i18n/en.ts +212 -0
  27. package/src/tool/compoundInterest/i18n/es.ts +212 -0
  28. package/src/tool/compoundInterest/i18n/fr.ts +212 -0
  29. package/src/tool/compoundInterest/index.ts +34 -0
  30. package/src/tool/compoundInterest/logic.ts +40 -0
  31. package/src/tool/compoundInterest/seo.astro +14 -0
  32. package/src/tool/compoundInterest/ui.ts +19 -0
  33. package/src/tool/courtFeeCalculator/bibliography.astro +14 -0
  34. package/src/tool/courtFeeCalculator/component.astro +427 -0
  35. package/src/tool/courtFeeCalculator/i18n/en.ts +268 -0
  36. package/src/tool/courtFeeCalculator/i18n/es.ts +268 -0
  37. package/src/tool/courtFeeCalculator/i18n/fr.ts +268 -0
  38. package/src/tool/courtFeeCalculator/index.ts +33 -0
  39. package/src/tool/courtFeeCalculator/logic.ts +86 -0
  40. package/src/tool/courtFeeCalculator/seo.astro +14 -0
  41. package/src/tool/courtFeeCalculator/ui.ts +31 -0
  42. package/src/tool/debtSnowball/bibliography.astro +14 -0
  43. package/src/tool/debtSnowball/component.astro +1052 -0
  44. package/src/tool/debtSnowball/i18n/en.ts +288 -0
  45. package/src/tool/debtSnowball/i18n/es.ts +288 -0
  46. package/src/tool/debtSnowball/i18n/fr.ts +288 -0
  47. package/src/tool/debtSnowball/index.ts +37 -0
  48. package/src/tool/debtSnowball/logic.ts +156 -0
  49. package/src/tool/debtSnowball/seo.astro +14 -0
  50. package/src/tool/debtSnowball/ui.ts +49 -0
  51. package/src/tool/fireCalculator/bibliography.astro +14 -0
  52. package/src/tool/fireCalculator/component.astro +689 -0
  53. package/src/tool/fireCalculator/i18n/en.ts +261 -0
  54. package/src/tool/fireCalculator/i18n/es.ts +261 -0
  55. package/src/tool/fireCalculator/i18n/fr.ts +261 -0
  56. package/src/tool/fireCalculator/index.ts +33 -0
  57. package/src/tool/fireCalculator/logic.ts +108 -0
  58. package/src/tool/fireCalculator/seo.astro +14 -0
  59. package/src/tool/fireCalculator/ui.ts +33 -0
  60. package/src/tool/ibanBicSwiftConverter/bibliography.astro +14 -0
  61. package/src/tool/ibanBicSwiftConverter/component.astro +581 -0
  62. package/src/tool/ibanBicSwiftConverter/i18n/en.ts +167 -0
  63. package/src/tool/ibanBicSwiftConverter/i18n/es.ts +216 -0
  64. package/src/tool/ibanBicSwiftConverter/i18n/fr.ts +167 -0
  65. package/src/tool/ibanBicSwiftConverter/index.ts +33 -0
  66. package/src/tool/ibanBicSwiftConverter/logic.ts +101 -0
  67. package/src/tool/ibanBicSwiftConverter/seo.astro +14 -0
  68. package/src/tool/ibanBicSwiftConverter/ui.ts +17 -0
  69. package/src/tool/inflation/bibliography.astro +14 -0
  70. package/src/tool/inflation/component.astro +400 -0
  71. package/src/tool/inflation/i18n/en.ts +301 -0
  72. package/src/tool/inflation/i18n/es.ts +301 -0
  73. package/src/tool/inflation/i18n/fr.ts +301 -0
  74. package/src/tool/inflation/index.ts +34 -0
  75. package/src/tool/inflation/logic.ts +92 -0
  76. package/src/tool/inflation/seo.astro +14 -0
  77. package/src/tool/inflation/ui.ts +17 -0
  78. package/src/tool/lateInterest/bibliography.astro +14 -0
  79. package/src/tool/lateInterest/component.astro +655 -0
  80. package/src/tool/lateInterest/i18n/en.ts +255 -0
  81. package/src/tool/lateInterest/i18n/es.ts +224 -0
  82. package/src/tool/lateInterest/i18n/fr.ts +224 -0
  83. package/src/tool/lateInterest/index.ts +34 -0
  84. package/src/tool/lateInterest/logic.ts +89 -0
  85. package/src/tool/lateInterest/seo.astro +14 -0
  86. package/src/tool/lateInterest/ui.ts +33 -0
  87. package/src/tool/legalInterestRate/bibliography.astro +14 -0
  88. package/src/tool/legalInterestRate/component.astro +650 -0
  89. package/src/tool/legalInterestRate/i18n/en.ts +159 -0
  90. package/src/tool/legalInterestRate/i18n/es.ts +159 -0
  91. package/src/tool/legalInterestRate/i18n/fr.ts +159 -0
  92. package/src/tool/legalInterestRate/index.ts +33 -0
  93. package/src/tool/legalInterestRate/logic.ts +29 -0
  94. package/src/tool/legalInterestRate/seo.astro +14 -0
  95. package/src/tool/legalInterestRate/ui.ts +23 -0
  96. package/src/tool/lotteryOptimizer/bibliography.astro +14 -0
  97. package/src/tool/lotteryOptimizer/component.astro +863 -0
  98. package/src/tool/lotteryOptimizer/i18n/en.ts +188 -0
  99. package/src/tool/lotteryOptimizer/i18n/es.ts +281 -0
  100. package/src/tool/lotteryOptimizer/i18n/fr.ts +188 -0
  101. package/src/tool/lotteryOptimizer/index.ts +33 -0
  102. package/src/tool/lotteryOptimizer/logic.ts +133 -0
  103. package/src/tool/lotteryOptimizer/seo.astro +14 -0
  104. package/src/tool/lotteryOptimizer/ui.ts +33 -0
  105. package/src/tool/mortgage/bibliography.astro +14 -0
  106. package/src/tool/mortgage/component.astro +1030 -0
  107. package/src/tool/mortgage/i18n/en.ts +227 -0
  108. package/src/tool/mortgage/i18n/es.ts +227 -0
  109. package/src/tool/mortgage/i18n/fr.ts +227 -0
  110. package/src/tool/mortgage/index.ts +34 -0
  111. package/src/tool/mortgage/logic.ts +118 -0
  112. package/src/tool/mortgage/seo.astro +14 -0
  113. package/src/tool/mortgage/ui.ts +35 -0
  114. package/src/tool/percentageCalculator/bibliography.astro +14 -0
  115. package/src/tool/percentageCalculator/component.astro +640 -0
  116. package/src/tool/percentageCalculator/i18n/en.ts +233 -0
  117. package/src/tool/percentageCalculator/i18n/es.ts +233 -0
  118. package/src/tool/percentageCalculator/i18n/fr.ts +233 -0
  119. package/src/tool/percentageCalculator/index.ts +34 -0
  120. package/src/tool/percentageCalculator/logic.ts +48 -0
  121. package/src/tool/percentageCalculator/seo.astro +14 -0
  122. package/src/tool/percentageCalculator/ui.ts +30 -0
  123. package/src/tool/rentIncreaseCalculator/bibliography.astro +14 -0
  124. package/src/tool/rentIncreaseCalculator/component.astro +587 -0
  125. package/src/tool/rentIncreaseCalculator/i18n/en.ts +159 -0
  126. package/src/tool/rentIncreaseCalculator/i18n/es.ts +220 -0
  127. package/src/tool/rentIncreaseCalculator/i18n/fr.ts +159 -0
  128. package/src/tool/rentIncreaseCalculator/index.ts +33 -0
  129. package/src/tool/rentIncreaseCalculator/logic.ts +43 -0
  130. package/src/tool/rentIncreaseCalculator/seo.astro +14 -0
  131. package/src/tool/rentIncreaseCalculator/ui.ts +19 -0
  132. package/src/tools.ts +16 -0
  133. package/src/types.ts +74 -0
@@ -0,0 +1,34 @@
1
+ import type { FinanceToolEntry, ToolLocaleContent, ToolDefinition } from '../../types';
2
+ import MortgageComponent from './component.astro';
3
+ import MortgageSEO from './seo.astro';
4
+ import MortgageBibliography from './bibliography.astro';
5
+
6
+ import type { MortgageUI } from './ui';
7
+
8
+ export type MortgageLocaleContent = ToolLocaleContent<MortgageUI>;
9
+
10
+ import { content as es } from './i18n/es';
11
+ import { content as en } from './i18n/en';
12
+ import { content as fr } from './i18n/fr';
13
+
14
+ export const mortgage: FinanceToolEntry<MortgageUI> = {
15
+ id: 'hipoteca',
16
+ icons: {
17
+ bg: 'mdi:home-percent',
18
+ fg: 'mdi:percent',
19
+ },
20
+ i18n: {
21
+ es: async () => es,
22
+ en: async () => en,
23
+ fr: async () => fr,
24
+ },
25
+ };
26
+
27
+ export { MortgageComponent, MortgageSEO, MortgageBibliography };
28
+
29
+ export const MORTGAGE_TOOL: ToolDefinition = {
30
+ entry: mortgage,
31
+ Component: MortgageComponent,
32
+ SEOComponent: MortgageSEO,
33
+ BibliographyComponent: MortgageBibliography,
34
+ };
@@ -0,0 +1,118 @@
1
+ export interface MortgageInput {
2
+ amount: number;
3
+ annualInterestRate: number;
4
+ years: number;
5
+ monthlyExtraPayment?: number;
6
+ }
7
+
8
+ export interface MortgageResult {
9
+ monthlyPayment: number;
10
+ totalInterest: number;
11
+ totalPaid: number;
12
+ amortizationTable: AmortizationRow[];
13
+ yearsSaved: number;
14
+ monthsSaved: number;
15
+ interestSaved: number;
16
+ originalTotalInterest: number;
17
+ actualDurationMonths: number;
18
+ }
19
+
20
+ export interface AmortizationRow {
21
+ month: number;
22
+ payment: number;
23
+ interest: number;
24
+ principal: number;
25
+ extraPayment: number;
26
+ remainingBalance: number;
27
+ }
28
+
29
+ export class MortgageCalculator {
30
+ private static calculateMonthlyPayment(amount: number, monthlyRate: number, totalMonths: number): number {
31
+ if (monthlyRate === 0) {
32
+ return amount / totalMonths;
33
+ }
34
+ const power = Math.pow(1 + monthlyRate, totalMonths);
35
+ return (amount * (monthlyRate * power)) / (power - 1);
36
+ }
37
+
38
+ private static calculateOriginalInterest(amount: number, monthlyRate: number, totalMonths: number, monthlyPaymentBase: number): number {
39
+ let originalTotalInterest = 0;
40
+ let tempBalance = amount;
41
+ for (let i = 0; i < totalMonths; i++) {
42
+ const interest = tempBalance * monthlyRate;
43
+ originalTotalInterest += interest;
44
+ tempBalance -= monthlyPaymentBase - interest;
45
+ }
46
+ return originalTotalInterest;
47
+ }
48
+
49
+ private static processMonthlyPayment(balance: number, monthlyRate: number, monthlyPaymentBase: number, monthlyExtraPayment: number): { interest: number; principal: number; extra: number; newBalance: number } {
50
+ const interestPayment = balance * monthlyRate;
51
+ let principalPayment = monthlyPaymentBase - interestPayment;
52
+ let currentExtra = monthlyExtraPayment;
53
+
54
+ if (principalPayment + currentExtra > balance) {
55
+ if (principalPayment > balance) {
56
+ principalPayment = balance;
57
+ currentExtra = 0;
58
+ } else {
59
+ currentExtra = balance - principalPayment;
60
+ }
61
+ return { interest: interestPayment, principal: principalPayment, extra: currentExtra, newBalance: 0 };
62
+ }
63
+
64
+ const newBalance = balance - principalPayment - currentExtra;
65
+ return { interest: interestPayment, principal: principalPayment, extra: currentExtra, newBalance: newBalance < 0.01 ? 0 : newBalance };
66
+ }
67
+
68
+ private static buildAmortizationTable(config: { amount: number; monthlyRate: number; totalMonths: number; monthlyPaymentBase: number; monthlyExtraPayment: number }): { table: AmortizationRow[]; totalInterest: number; actualMonths: number } {
69
+ const { amount, monthlyRate, totalMonths, monthlyPaymentBase, monthlyExtraPayment } = config;
70
+ const table: AmortizationRow[] = [];
71
+ let remainingBalance = amount;
72
+ let totalInterest = 0;
73
+ let actualMonths = 0;
74
+
75
+ for (let month = 1; month <= totalMonths; month++) {
76
+ const payment = this.processMonthlyPayment(remainingBalance, monthlyRate, monthlyPaymentBase, monthlyExtraPayment);
77
+
78
+ totalInterest += payment.interest;
79
+ actualMonths = month;
80
+ remainingBalance = payment.newBalance;
81
+
82
+ table.push({
83
+ month,
84
+ payment: Number((payment.principal + payment.interest + payment.extra).toFixed(2)),
85
+ interest: Number(payment.interest.toFixed(2)),
86
+ principal: Number(payment.principal.toFixed(2)),
87
+ extraPayment: Number(payment.extra.toFixed(2)),
88
+ remainingBalance: Number(remainingBalance.toFixed(2)),
89
+ });
90
+
91
+ if (remainingBalance <= 0) break;
92
+ }
93
+
94
+ return { table, totalInterest, actualMonths };
95
+ }
96
+
97
+ static calculate(input: MortgageInput): MortgageResult {
98
+ const { amount, annualInterestRate, years, monthlyExtraPayment = 0 } = input;
99
+ const monthlyRate = annualInterestRate / 100 / 12;
100
+ const totalMonths = years * 12;
101
+
102
+ const monthlyPaymentBase = this.calculateMonthlyPayment(amount, monthlyRate, totalMonths);
103
+ const originalTotalInterest = this.calculateOriginalInterest(amount, monthlyRate, totalMonths, monthlyPaymentBase);
104
+ const { table: amortizationTable, totalInterest, actualMonths } = this.buildAmortizationTable({ amount, monthlyRate, totalMonths, monthlyPaymentBase, monthlyExtraPayment });
105
+
106
+ return {
107
+ monthlyPayment: Number(monthlyPaymentBase.toFixed(2)),
108
+ totalInterest: Number(totalInterest.toFixed(2)),
109
+ totalPaid: Number((amount + totalInterest).toFixed(2)),
110
+ amortizationTable,
111
+ originalTotalInterest: Number(originalTotalInterest.toFixed(2)),
112
+ interestSaved: Number((originalTotalInterest - totalInterest).toFixed(2)),
113
+ actualDurationMonths: actualMonths,
114
+ monthsSaved: totalMonths - actualMonths,
115
+ yearsSaved: Number(((totalMonths - actualMonths) / 12).toFixed(1)),
116
+ };
117
+ }
118
+ }
@@ -0,0 +1,14 @@
1
+ ---
2
+ import { SEORenderer } from '@jjlmoya/utils-shared';
3
+ import { mortgage } from './index';
4
+ import type { KnownLocale } from '../../types';
5
+
6
+ interface Props {
7
+ locale?: KnownLocale;
8
+ }
9
+
10
+ const { locale = 'es' } = Astro.props;
11
+ const content = await mortgage.i18n[locale]?.();
12
+ if (!content) return null;
13
+ ---
14
+ <SEORenderer content={{ locale: locale as string, sections: content.seo }} />
@@ -0,0 +1,35 @@
1
+ export interface MortgageUI {
2
+ labelTitle: string;
3
+ labelLoanAmount: string;
4
+ labelInterestRate: string;
5
+ labelYears: string;
6
+ labelMonthlyExtra: string;
7
+ labelMonthlyPayment: string;
8
+ labelMonthCount: string;
9
+ labelSavingsCard: string;
10
+ labelSavingsInterest: string;
11
+ labelSavingsTime: string;
12
+ labelTimelineComparison: string;
13
+ labelTimelineOriginal: string;
14
+ labelTimelineOptimized: string;
15
+ labelCostBreakdown: string;
16
+ labelBorrowed: string;
17
+ labelTotalBorrowed: string;
18
+ labelTotalInterest: string;
19
+ labelAmortizationTable: string;
20
+ labelTableHeader: string;
21
+ labelViewAll: string;
22
+ labelPaid: string;
23
+ labelTableMonth: string;
24
+ labelTableInterest: string;
25
+ labelTablePrincipal: string;
26
+ labelTableExtra: string;
27
+ labelTableBalance: string;
28
+ labelMoreMonths: string;
29
+ labelMonth: string;
30
+ currencySymbol: string;
31
+ currencyCode: string;
32
+ currencyLocale: string;
33
+ monthPlural: string;
34
+ yearPlural: string;
35
+ }
@@ -0,0 +1,14 @@
1
+ ---
2
+ import { Bibliography as SharedBibliography } from '@jjlmoya/utils-shared';
3
+ import { percentageCalculator } from './index';
4
+ import type { KnownLocale } from '../../types';
5
+
6
+ interface Props {
7
+ locale?: KnownLocale;
8
+ }
9
+
10
+ const { locale = 'es' } = Astro.props;
11
+ const content = await percentageCalculator.i18n[locale]?.();
12
+ ---
13
+
14
+ {content && <SharedBibliography links={content.bibliography} />}