@jjlmoya/utils-finance 1.38.0 → 1.39.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.
- package/package.json +1 -1
- package/src/category/index.ts +2 -1
- package/src/entries.ts +4 -1
- package/src/tests/locale_completeness.test.ts +2 -2
- package/src/tests/tool_validation.test.ts +2 -2
- package/src/tool/subscriptionCostAnnualizer/bibliography.astro +11 -0
- package/src/tool/subscriptionCostAnnualizer/bibliography.ts +6 -0
- package/src/tool/subscriptionCostAnnualizer/component.astro +58 -0
- package/src/tool/subscriptionCostAnnualizer/content.ts +716 -0
- package/src/tool/subscriptionCostAnnualizer/controller.ts +72 -0
- package/src/tool/subscriptionCostAnnualizer/dom-views.ts +38 -0
- package/src/tool/subscriptionCostAnnualizer/entry.ts +28 -0
- package/src/tool/subscriptionCostAnnualizer/i18n/de.ts +2 -0
- package/src/tool/subscriptionCostAnnualizer/i18n/en.ts +2 -0
- package/src/tool/subscriptionCostAnnualizer/i18n/es.ts +2 -0
- package/src/tool/subscriptionCostAnnualizer/i18n/fr.ts +2 -0
- package/src/tool/subscriptionCostAnnualizer/i18n/id.ts +2 -0
- package/src/tool/subscriptionCostAnnualizer/i18n/it.ts +2 -0
- package/src/tool/subscriptionCostAnnualizer/i18n/ja.ts +2 -0
- package/src/tool/subscriptionCostAnnualizer/i18n/ko.ts +2 -0
- package/src/tool/subscriptionCostAnnualizer/i18n/nl.ts +2 -0
- package/src/tool/subscriptionCostAnnualizer/i18n/pl.ts +2 -0
- package/src/tool/subscriptionCostAnnualizer/i18n/pt.ts +2 -0
- package/src/tool/subscriptionCostAnnualizer/i18n/ru.ts +2 -0
- package/src/tool/subscriptionCostAnnualizer/i18n/sv.ts +2 -0
- package/src/tool/subscriptionCostAnnualizer/i18n/tr.ts +2 -0
- package/src/tool/subscriptionCostAnnualizer/i18n/zh.ts +2 -0
- package/src/tool/subscriptionCostAnnualizer/index.ts +12 -0
- package/src/tool/subscriptionCostAnnualizer/logic.test.ts +37 -0
- package/src/tool/subscriptionCostAnnualizer/logic.ts +120 -0
- package/src/tool/subscriptionCostAnnualizer/seo.astro +11 -0
- package/src/tool/subscriptionCostAnnualizer/storage.ts +28 -0
- package/src/tool/subscriptionCostAnnualizer/subscription-cost-annualizer.css +408 -0
- package/src/tool/subscriptionCostAnnualizer/ui.ts +38 -0
- package/src/tools.ts +2 -1
package/package.json
CHANGED
package/src/category/index.ts
CHANGED
|
@@ -18,10 +18,11 @@ import { pesetaValue } from '../tool/pesetaValue/entry';
|
|
|
18
18
|
import { salaryEmployerCost } from '../tool/salaryEmployerCost/entry';
|
|
19
19
|
import { dividendYield } from '../tool/dividendYield/entry';
|
|
20
20
|
import { inflationPurchasingPower } from '../tool/inflationPurchasingPower/entry';
|
|
21
|
+
import { subscriptionCostAnnualizer } from '../tool/subscriptionCostAnnualizer/entry';
|
|
21
22
|
|
|
22
23
|
export const financeCategory: FinanceCategoryEntry = {
|
|
23
24
|
icon: 'mdi:finance',
|
|
24
|
-
tools: [compoundInterest, financialFreedom, profitability, mortgage, inflation, percentageCalculator, lateInterest, ibanBicSwift, rentIncrease, lotteryOptimizer, courtFeeCalculator, legalInterestRate, fireCalculator, debtSnowball, revolvingCardCalculator, pesetaValue, salaryEmployerCost, dividendYield, inflationPurchasingPower],
|
|
25
|
+
tools: [compoundInterest, financialFreedom, profitability, mortgage, inflation, percentageCalculator, lateInterest, ibanBicSwift, rentIncrease, lotteryOptimizer, courtFeeCalculator, legalInterestRate, fireCalculator, debtSnowball, revolvingCardCalculator, pesetaValue, salaryEmployerCost, dividendYield, inflationPurchasingPower, subscriptionCostAnnualizer],
|
|
25
26
|
i18n: {
|
|
26
27
|
en: () => import('./i18n/en').then((m) => m.content),
|
|
27
28
|
es: () => import('./i18n/es').then((m) => m.content),
|
package/src/entries.ts
CHANGED
|
@@ -36,6 +36,8 @@ export { dividendYield } from './tool/dividendYield/entry';
|
|
|
36
36
|
export type { DividendYieldLocaleContent } from './tool/dividendYield/entry';
|
|
37
37
|
export { inflationPurchasingPower } from './tool/inflationPurchasingPower/entry';
|
|
38
38
|
export type { InflationPurchasingPowerLocaleContent } from './tool/inflationPurchasingPower/entry';
|
|
39
|
+
export { subscriptionCostAnnualizer } from './tool/subscriptionCostAnnualizer/entry';
|
|
40
|
+
export type { SubscriptionCostAnnualizerLocaleContent } from './tool/subscriptionCostAnnualizer/entry';
|
|
39
41
|
export { financeCategory } from './category';
|
|
40
42
|
import { compoundInterest } from './tool/compoundInterest/entry';
|
|
41
43
|
import { financialFreedom } from './tool/financialFreedom/entry';
|
|
@@ -57,4 +59,5 @@ import { salaryEmployerCost } from './tool/salaryEmployerCost/entry';
|
|
|
57
59
|
import { dividendYield } from './tool/dividendYield/entry';
|
|
58
60
|
import { mortgageExtraPaymentPlanner } from './tool/mortgageExtraPaymentPlanner/entry';
|
|
59
61
|
import { inflationPurchasingPower } from './tool/inflationPurchasingPower/entry';
|
|
60
|
-
|
|
62
|
+
import { subscriptionCostAnnualizer } from './tool/subscriptionCostAnnualizer/entry';
|
|
63
|
+
export const ALL_ENTRIES = [compoundInterest, financialFreedom, profitability, courtFeeCalculator, debtSnowball, fireCalculator, ibanBicSwift, inflation, lateInterest, legalInterestRate, lotteryOptimizer, mortgage, percentageCalculator, rentIncrease, revolvingCardCalculator, pesetaValue, salaryEmployerCost, dividendYield, mortgageExtraPaymentPlanner, inflationPurchasingPower, subscriptionCostAnnualizer];
|
|
@@ -4,8 +4,8 @@ import { financeCategory } from '../data';
|
|
|
4
4
|
|
|
5
5
|
describe('Tool Validation Suite', () => {
|
|
6
6
|
describe('Library Registration', () => {
|
|
7
|
-
it('should have
|
|
8
|
-
expect(ALL_TOOLS.length).toBe(
|
|
7
|
+
it('should have 21 tools in ALL_TOOLS', () => {
|
|
8
|
+
expect(ALL_TOOLS.length).toBe(21);
|
|
9
9
|
});
|
|
10
10
|
|
|
11
11
|
it('financeCategory should be defined', () => {
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { Bibliography as SharedBibliography } from '@jjlmoya/utils-shared';
|
|
3
|
+
import { subscriptionCostAnnualizer } from './entry';
|
|
4
|
+
import type { KnownLocale } from '../../types';
|
|
5
|
+
|
|
6
|
+
interface Props { locale?: KnownLocale; }
|
|
7
|
+
const { locale = 'en' } = Astro.props;
|
|
8
|
+
const content = await subscriptionCostAnnualizer.i18n[locale]?.();
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
{content && <SharedBibliography links={content.bibliography} />}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { BibliographyEntry } from '../../types';
|
|
2
|
+
|
|
3
|
+
export const bibliography: BibliographyEntry[] = [
|
|
4
|
+
{ name: 'Consumer Financial Protection Bureau, Managing your subscriptions', url: 'https://www.consumerfinance.gov/consumer-tools/bank-accounts/' },
|
|
5
|
+
{ name: 'Banco de España, Financial education resources', url: 'https://clientebancario.bde.es/pcb/en/menu-horizontal/actualidadeduca/' },
|
|
6
|
+
];
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { SubscriptionCostAnnualizerUI } from './ui';
|
|
3
|
+
import type { SubscriptionCalculatorInput } from './logic';
|
|
4
|
+
import { calculateSubscriptionCosts } from './logic';
|
|
5
|
+
import { renderSubscriptionResults, renderSubscriptionScene } from './dom-views';
|
|
6
|
+
import './subscription-cost-annualizer.css';
|
|
7
|
+
|
|
8
|
+
interface Props { ui: SubscriptionCostAnnualizerUI; locale?: string; }
|
|
9
|
+
|
|
10
|
+
const { ui } = Astro.props;
|
|
11
|
+
const initial: SubscriptionCalculatorInput = {
|
|
12
|
+
currency: 'USD',
|
|
13
|
+
subscriptions: [
|
|
14
|
+
{ id: 'streaming', name: 'Streaming', amount: 12, frequency: 'monthly', renewalDate: '2026-10-12' },
|
|
15
|
+
{ id: 'cloud', name: 'Cloud backup', amount: 4, frequency: 'monthly', renewalDate: '2026-10-20' },
|
|
16
|
+
{ id: 'magazine', name: 'Magazine', amount: 24, frequency: 'quarterly', renewalDate: '2026-11-05' },
|
|
17
|
+
],
|
|
18
|
+
};
|
|
19
|
+
const initialResult = calculateSubscriptionCosts(initial);
|
|
20
|
+
const config = JSON.stringify({ ui, initial });
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
<div class="subscription-cost-annualizer-tool" data-sca-tool>
|
|
24
|
+
<form class="sca-controls" aria-label={ui.subscriptionLabel}>
|
|
25
|
+
<div class="sca-currency-field">
|
|
26
|
+
<label for="sca-currency">{ui.currencyLabel}</label>
|
|
27
|
+
<select id="sca-currency" data-sca-currency>
|
|
28
|
+
<option value="USD">USD — US dollar</option>
|
|
29
|
+
<option value="EUR">EUR — Euro</option>
|
|
30
|
+
<option value="GBP">GBP — Pound sterling</option>
|
|
31
|
+
<option value="CAD">CAD — Canadian dollar</option>
|
|
32
|
+
<option value="AUD">AUD — Australian dollar</option>
|
|
33
|
+
<option value="JPY">JPY — Japanese yen</option>
|
|
34
|
+
</select>
|
|
35
|
+
<small>{ui.currencyHelp}</small>
|
|
36
|
+
</div>
|
|
37
|
+
<div class="sca-list-heading"><h2>{ui.subscriptionLabel}</h2><span>{ui.reorderHint}</span></div>
|
|
38
|
+
<div class="sca-list" data-sca-list></div>
|
|
39
|
+
<div class="sca-actions"><button type="button" class="sca-primary-button" data-sca-add>{ui.addButton}</button><button type="button" class="sca-secondary-button" data-sca-reset>{ui.resetButton}</button></div>
|
|
40
|
+
</form>
|
|
41
|
+
|
|
42
|
+
<section class="sca-output" aria-live="polite">
|
|
43
|
+
<div class="sca-scene-wrap" data-sca-scene set:html={initialResult ? renderSubscriptionScene(initialResult, ui) : ''}></div>
|
|
44
|
+
<div class="sca-results-wrap" data-sca-results set:html={initialResult ? renderSubscriptionResults(initialResult, ui) : ''}></div>
|
|
45
|
+
<p class="sca-error" data-sca-error hidden>{ui.emptyMessage}</p>
|
|
46
|
+
</section>
|
|
47
|
+
|
|
48
|
+
<p class="sca-privacy">{ui.privacyNote}</p>
|
|
49
|
+
<details class="sca-method"><summary>{ui.methodTitle}</summary><p>{ui.methodText}</p><p>{ui.limitationText}</p></details>
|
|
50
|
+
</div>
|
|
51
|
+
|
|
52
|
+
<script is:inline type="application/json" data-sca-config set:html={config}></script>
|
|
53
|
+
<script>
|
|
54
|
+
import { initSubscriptionCostAnnualizer } from './controller';
|
|
55
|
+
const root = document.querySelector<HTMLElement>('[data-sca-tool]');
|
|
56
|
+
const configElement = document.querySelector<HTMLScriptElement>('[data-sca-config]');
|
|
57
|
+
if (root && configElement?.textContent) initSubscriptionCostAnnualizer(root, JSON.parse(configElement.textContent));
|
|
58
|
+
</script>
|