@jjlmoya/utils-finance 1.16.0 → 1.18.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 +3 -1
- package/src/entries.ts +7 -1
- package/src/tests/locale_completeness.test.ts +1 -1
- package/src/tests/tool_validation.test.ts +2 -2
- package/src/tool/financialFreedom/bibliography.astro +14 -0
- package/src/tool/financialFreedom/bibliography.ts +12 -0
- package/src/tool/financialFreedom/component.astro +253 -0
- package/src/tool/financialFreedom/entry.ts +27 -0
- package/src/tool/financialFreedom/financial-freedom-calculator.css +346 -0
- package/src/tool/financialFreedom/i18n/de.ts +123 -0
- package/src/tool/financialFreedom/i18n/en.ts +182 -0
- package/src/tool/financialFreedom/i18n/es.ts +182 -0
- package/src/tool/financialFreedom/i18n/fr.ts +123 -0
- package/src/tool/financialFreedom/i18n/id.ts +123 -0
- package/src/tool/financialFreedom/i18n/it.ts +123 -0
- package/src/tool/financialFreedom/i18n/ja.ts +123 -0
- package/src/tool/financialFreedom/i18n/ko.ts +123 -0
- package/src/tool/financialFreedom/i18n/nl.ts +123 -0
- package/src/tool/financialFreedom/i18n/pl.ts +123 -0
- package/src/tool/financialFreedom/i18n/pt.ts +123 -0
- package/src/tool/financialFreedom/i18n/ru.ts +123 -0
- package/src/tool/financialFreedom/i18n/sv.ts +123 -0
- package/src/tool/financialFreedom/i18n/tr.ts +123 -0
- package/src/tool/financialFreedom/i18n/zh.ts +123 -0
- package/src/tool/financialFreedom/index.ts +10 -0
- package/src/tool/financialFreedom/logic.ts +68 -0
- package/src/tool/financialFreedom/seo.astro +13 -0
- package/src/tool/financialFreedom/ui.ts +26 -0
- package/src/tool/profitability/bibliography.astro +14 -0
- package/src/tool/profitability/bibliography.ts +12 -0
- package/src/tool/profitability/component.astro +249 -0
- package/src/tool/profitability/entry.ts +27 -0
- package/src/tool/profitability/i18n/de.ts +116 -0
- package/src/tool/profitability/i18n/en.ts +165 -0
- package/src/tool/profitability/i18n/es.ts +152 -0
- package/src/tool/profitability/i18n/fr.ts +112 -0
- package/src/tool/profitability/i18n/id.ts +112 -0
- package/src/tool/profitability/i18n/it.ts +112 -0
- package/src/tool/profitability/i18n/ja.ts +112 -0
- package/src/tool/profitability/i18n/ko.ts +112 -0
- package/src/tool/profitability/i18n/nl.ts +112 -0
- package/src/tool/profitability/i18n/pl.ts +112 -0
- package/src/tool/profitability/i18n/pt.ts +112 -0
- package/src/tool/profitability/i18n/ru.ts +112 -0
- package/src/tool/profitability/i18n/sv.ts +112 -0
- package/src/tool/profitability/i18n/tr.ts +112 -0
- package/src/tool/profitability/i18n/zh.ts +112 -0
- package/src/tool/profitability/index.ts +10 -0
- package/src/tool/profitability/logic.ts +43 -0
- package/src/tool/profitability/profitability-calculator.css +263 -0
- package/src/tool/profitability/seo.astro +15 -0
- package/src/tool/profitability/ui.ts +20 -0
- package/src/tools.ts +3 -1
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import type { ProfitabilityUI } from '../ui';
|
|
2
|
+
import type { ToolLocaleContent } from '../../../types';
|
|
3
|
+
|
|
4
|
+
export const ui: ProfitabilityUI = {
|
|
5
|
+
labelInitialInvestment: 'Başlangıç Yatırımı',
|
|
6
|
+
labelFinalValue: 'Final Değeri / Mevcut Bakiye',
|
|
7
|
+
labelTimeSelection: 'Yatırım Süresi',
|
|
8
|
+
labelYears: 'Yıl',
|
|
9
|
+
labelMonths: 'Ay',
|
|
10
|
+
labelDays: 'Gün',
|
|
11
|
+
labelROI: 'Yatırım Getirisi (ROI)',
|
|
12
|
+
labelCAGR: 'Yıllık Büyüme Oranı (CAGR)',
|
|
13
|
+
labelNetProfit: 'Net Kar',
|
|
14
|
+
labelLinearGrowth: 'Doğrusal Büyüme (Tahmini)',
|
|
15
|
+
labelExponentialGrowth: 'Bileşik Büyüme (CAGR)',
|
|
16
|
+
labelPositiveResult: 'Karlı Yatırım',
|
|
17
|
+
labelNegativeResult: 'Zarar Pozisyonu',
|
|
18
|
+
labelCopyResult: 'Sonuçları Kopyala',
|
|
19
|
+
labelCopied: 'Kopyalandı!',
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const slug = 'yatirim-karlilik-hesaplama-roi-cagr';
|
|
23
|
+
const title = 'Karlılık Hesaplayıcı: ROI & CAGR';
|
|
24
|
+
const description = 'Yatırım performansınızı analiz edin. Gerçek kazançlarınızı anlamak için ROI ve CAGR hesaplayın.';
|
|
25
|
+
|
|
26
|
+
const faq = [
|
|
27
|
+
{
|
|
28
|
+
question: 'İyi bir CAGR nedir?',
|
|
29
|
+
answer: 'Tarihsel olarak S&P 500 yıllık yaklaşık %7-10 getiri sağlar.',
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
question: 'CAGR neden toplam ROI\'den farklıdır?',
|
|
33
|
+
answer: 'ROI zamandan bağımsız toplam getiridir. CAGR ise yıllık orandır.',
|
|
34
|
+
},
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
const howTo = [
|
|
38
|
+
{
|
|
39
|
+
name: 'Başlangıç sermayesini girin',
|
|
40
|
+
text: 'Orijinal yatırım miktarını girin.',
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
name: 'Final değerini girin',
|
|
44
|
+
text: 'Mevcut piyasa değerini girin.',
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
name: 'Süreyi belirleyin',
|
|
48
|
+
text: 'Yatırımı ne kadar süre tuttuğunuzu belirtin.',
|
|
49
|
+
},
|
|
50
|
+
];
|
|
51
|
+
|
|
52
|
+
export const content: ToolLocaleContent<ProfitabilityUI> = {
|
|
53
|
+
slug,
|
|
54
|
+
title,
|
|
55
|
+
description,
|
|
56
|
+
ui,
|
|
57
|
+
seo: [
|
|
58
|
+
{
|
|
59
|
+
type: 'title',
|
|
60
|
+
text: 'ROI vs CAGR: Hangi metrik daha iyi?',
|
|
61
|
+
level: 2,
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
type: 'paragraph',
|
|
65
|
+
html: '<strong>ROI</strong> toplam getiri yüzdesini gösterir ancak zamanı dikkate almaz. <strong>CAGR</strong> uzun vadeli analiz için daha iyidir.',
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
faq,
|
|
69
|
+
bibliography: [
|
|
70
|
+
{
|
|
71
|
+
name: 'ROI Tanımı - Investopedia',
|
|
72
|
+
url: 'https://www.investopedia.com/terms/r/returnoninvestment.asp',
|
|
73
|
+
},
|
|
74
|
+
],
|
|
75
|
+
howTo,
|
|
76
|
+
schemas: [
|
|
77
|
+
{
|
|
78
|
+
'@context': 'https://schema.org',
|
|
79
|
+
'@type': 'FinancialProduct',
|
|
80
|
+
name: title,
|
|
81
|
+
description,
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
'@context': 'https://schema.org',
|
|
85
|
+
'@type': 'FAQPage',
|
|
86
|
+
mainEntity: faq.map((f) => ({
|
|
87
|
+
'@type': 'Question',
|
|
88
|
+
name: f.question,
|
|
89
|
+
acceptedAnswer: { '@type': 'Answer', text: f.answer },
|
|
90
|
+
})),
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
'@context': 'https://schema.org',
|
|
94
|
+
'@type': 'HowTo',
|
|
95
|
+
name: title,
|
|
96
|
+
step: howTo.map((h, i) => ({
|
|
97
|
+
'@type': 'HowToStep',
|
|
98
|
+
position: i + 1,
|
|
99
|
+
name: h.name,
|
|
100
|
+
text: h.text,
|
|
101
|
+
})),
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
'@context': 'https://schema.org',
|
|
105
|
+
'@type': 'SoftwareApplication',
|
|
106
|
+
name: title,
|
|
107
|
+
applicationCategory: 'FinanceApplication',
|
|
108
|
+
operatingSystem: 'All',
|
|
109
|
+
offers: { '@type': 'Offer', price: '0', priceCurrency: 'USD' },
|
|
110
|
+
},
|
|
111
|
+
],
|
|
112
|
+
};
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import type { ProfitabilityUI } from '../ui';
|
|
2
|
+
import type { ToolLocaleContent } from '../../../types';
|
|
3
|
+
|
|
4
|
+
export const ui: ProfitabilityUI = {
|
|
5
|
+
labelInitialInvestment: '初始投资',
|
|
6
|
+
labelFinalValue: '最终价值 / 当前余额',
|
|
7
|
+
labelTimeSelection: '投资期限',
|
|
8
|
+
labelYears: '年',
|
|
9
|
+
labelMonths: '个月',
|
|
10
|
+
labelDays: '天',
|
|
11
|
+
labelROI: '投资回报率 (ROI)',
|
|
12
|
+
labelCAGR: '年化增长率 (CAGR)',
|
|
13
|
+
labelNetProfit: '净利润',
|
|
14
|
+
labelLinearGrowth: '线性增长(估计)',
|
|
15
|
+
labelExponentialGrowth: '复利增长 (CAGR)',
|
|
16
|
+
labelPositiveResult: '盈利投资',
|
|
17
|
+
labelNegativeResult: '亏损状态',
|
|
18
|
+
labelCopyResult: '复制结果',
|
|
19
|
+
labelCopied: '已复制!',
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const slug = 'profitability-calculator-roi-cagr';
|
|
23
|
+
const title = '投资盈利计算器:ROI & CAGR';
|
|
24
|
+
const description = '精确分析您的投资绩效。计算 ROI 和 CAGR 以了解您的真实年化收益。';
|
|
25
|
+
|
|
26
|
+
const faq = [
|
|
27
|
+
{
|
|
28
|
+
question: '多少 CAGR 才算好?',
|
|
29
|
+
answer: '从历史来看,标普 500 指数的年化回报率约为 7-10%。',
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
question: '为什么我的 CAGR 不同于总 ROI?',
|
|
33
|
+
answer: 'ROI 是不考虑时间的总收益率。CAGR 是将回报“年化”后的结果。',
|
|
34
|
+
},
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
const howTo = [
|
|
38
|
+
{
|
|
39
|
+
name: '输入初始资本',
|
|
40
|
+
text: '输入您最初投资的金额。',
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
name: '输入最终价值',
|
|
44
|
+
text: '输入当前市场价值。',
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
name: '设置投资期限',
|
|
48
|
+
text: '指定您持有投资的时间。',
|
|
49
|
+
},
|
|
50
|
+
];
|
|
51
|
+
|
|
52
|
+
export const content: ToolLocaleContent<ProfitabilityUI> = {
|
|
53
|
+
slug,
|
|
54
|
+
title,
|
|
55
|
+
description,
|
|
56
|
+
ui,
|
|
57
|
+
seo: [
|
|
58
|
+
{
|
|
59
|
+
type: 'title',
|
|
60
|
+
text: 'ROI 与 CAGR:哪个指标更好?',
|
|
61
|
+
level: 2,
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
type: 'paragraph',
|
|
65
|
+
html: '<strong>ROI</strong> 显示总收益百分比,但忽略了时间。<strong>CAGR</strong> 对于长期分析更具优势,因为它将回报“年化”。',
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
faq,
|
|
69
|
+
bibliography: [
|
|
70
|
+
{
|
|
71
|
+
name: 'ROI 定义 - Investopedia',
|
|
72
|
+
url: 'https://www.investopedia.com/terms/r/returnoninvestment.asp',
|
|
73
|
+
},
|
|
74
|
+
],
|
|
75
|
+
howTo,
|
|
76
|
+
schemas: [
|
|
77
|
+
{
|
|
78
|
+
'@context': 'https://schema.org',
|
|
79
|
+
'@type': 'FinancialProduct',
|
|
80
|
+
name: title,
|
|
81
|
+
description,
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
'@context': 'https://schema.org',
|
|
85
|
+
'@type': 'FAQPage',
|
|
86
|
+
mainEntity: faq.map((f) => ({
|
|
87
|
+
'@type': 'Question',
|
|
88
|
+
name: f.question,
|
|
89
|
+
acceptedAnswer: { '@type': 'Answer', text: f.answer },
|
|
90
|
+
})),
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
'@context': 'https://schema.org',
|
|
94
|
+
'@type': 'HowTo',
|
|
95
|
+
name: title,
|
|
96
|
+
step: howTo.map((h, i) => ({
|
|
97
|
+
'@type': 'HowToStep',
|
|
98
|
+
position: i + 1,
|
|
99
|
+
name: h.name,
|
|
100
|
+
text: h.text,
|
|
101
|
+
})),
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
'@context': 'https://schema.org',
|
|
105
|
+
'@type': 'SoftwareApplication',
|
|
106
|
+
name: title,
|
|
107
|
+
applicationCategory: 'FinanceApplication',
|
|
108
|
+
operatingSystem: 'All',
|
|
109
|
+
offers: { '@type': 'Offer', price: '0', priceCurrency: 'USD' },
|
|
110
|
+
},
|
|
111
|
+
],
|
|
112
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { profitability } from './entry';
|
|
2
|
+
import type { ToolDefinition } from '../../types';
|
|
3
|
+
|
|
4
|
+
export * from './entry';
|
|
5
|
+
export const PROFITABILITY_TOOL: ToolDefinition = {
|
|
6
|
+
entry: profitability,
|
|
7
|
+
Component: () => import('./component.astro'),
|
|
8
|
+
SEOComponent: () => import('./seo.astro'),
|
|
9
|
+
BibliographyComponent: () => import('./bibliography.astro'),
|
|
10
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export interface ProfitabilityInput {
|
|
2
|
+
initialInvestment: number;
|
|
3
|
+
finalValue: number;
|
|
4
|
+
timeValue: number;
|
|
5
|
+
timeUnit: 'days' | 'months' | 'years';
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface ProfitabilityResult {
|
|
9
|
+
roi: number;
|
|
10
|
+
cagr: number;
|
|
11
|
+
netProfit: number;
|
|
12
|
+
isPositive: boolean;
|
|
13
|
+
years: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export class ProfitabilityCalculator {
|
|
17
|
+
static calculate(input: ProfitabilityInput): ProfitabilityResult {
|
|
18
|
+
const { initialInvestment, finalValue, timeValue, timeUnit } = input;
|
|
19
|
+
|
|
20
|
+
let years = 0;
|
|
21
|
+
if (timeUnit === 'days') years = timeValue / 365;
|
|
22
|
+
else if (timeUnit === 'months') years = timeValue / 12;
|
|
23
|
+
else years = timeValue;
|
|
24
|
+
|
|
25
|
+
const netProfit = finalValue - initialInvestment;
|
|
26
|
+
const isPositive = netProfit >= 0;
|
|
27
|
+
|
|
28
|
+
const roi = initialInvestment !== 0 ? (netProfit / initialInvestment) * 100 : 0;
|
|
29
|
+
|
|
30
|
+
let cagr = 0;
|
|
31
|
+
if (initialInvestment > 0 && finalValue > 0 && years > 0) {
|
|
32
|
+
cagr = (Math.pow(finalValue / initialInvestment, 1 / years) - 1) * 100;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
roi: Number(roi.toFixed(2)),
|
|
37
|
+
cagr: Number(cagr.toFixed(2)),
|
|
38
|
+
netProfit: Number(netProfit.toFixed(2)),
|
|
39
|
+
isPositive,
|
|
40
|
+
years
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
.prof-root {
|
|
2
|
+
--prof-brand: #10b981;
|
|
3
|
+
--prof-brand-light: #34d399;
|
|
4
|
+
--prof-brand-dark: #059669;
|
|
5
|
+
--prof-danger: #ef4444;
|
|
6
|
+
--prof-bg: var(--surface-1);
|
|
7
|
+
--prof-card-bg: var(--surface-2);
|
|
8
|
+
--prof-input-bg: var(--surface-3);
|
|
9
|
+
--prof-text: var(--text-1);
|
|
10
|
+
--prof-text-muted: var(--text-2);
|
|
11
|
+
--prof-border: var(--border-color);
|
|
12
|
+
|
|
13
|
+
display: flex;
|
|
14
|
+
flex-direction: column;
|
|
15
|
+
gap: 2rem;
|
|
16
|
+
width: 100%;
|
|
17
|
+
max-width: 1000px;
|
|
18
|
+
margin: 0 auto;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.theme-dark .prof-root {
|
|
22
|
+
--prof-input-bg: #064e3b;
|
|
23
|
+
--prof-card-bg: #022c22;
|
|
24
|
+
--prof-border: #065f46;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.prof-card {
|
|
28
|
+
background: var(--prof-card-bg);
|
|
29
|
+
border: 2px solid var(--prof-border);
|
|
30
|
+
border-radius: 2.5rem;
|
|
31
|
+
padding: 3.5rem;
|
|
32
|
+
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.2);
|
|
33
|
+
transition: all 0.5s cubic-bezier(0.4, 0, 0.2, 1);
|
|
34
|
+
position: relative;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.prof-card.positive {
|
|
38
|
+
border-color: var(--prof-brand);
|
|
39
|
+
box-shadow: 0 25px 50px -12px rgba(16, 185, 129, 0.2);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.prof-card.negative {
|
|
43
|
+
border-color: var(--prof-danger);
|
|
44
|
+
box-shadow: 0 25px 50px -12px rgba(239, 68, 68, 0.2);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.prof-grid {
|
|
48
|
+
display: grid;
|
|
49
|
+
grid-template-columns: 1fr 1fr;
|
|
50
|
+
gap: 5rem;
|
|
51
|
+
align-items: center;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
@media (max-width: 900px) {
|
|
55
|
+
.prof-grid {
|
|
56
|
+
grid-template-columns: 1fr;
|
|
57
|
+
gap: 3rem;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.prof-input-group {
|
|
62
|
+
margin-bottom: 2rem;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.prof-label-row {
|
|
66
|
+
display: flex;
|
|
67
|
+
justify-content: space-between;
|
|
68
|
+
align-items: center;
|
|
69
|
+
margin-bottom: 0.75rem;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.prof-currency-select {
|
|
73
|
+
background: none;
|
|
74
|
+
border: none;
|
|
75
|
+
color: var(--prof-brand);
|
|
76
|
+
font-weight: 900;
|
|
77
|
+
font-size: 1.1rem;
|
|
78
|
+
cursor: pointer;
|
|
79
|
+
padding-right: 1.5rem;
|
|
80
|
+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='%2310b981' stroke-width='4' %3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M19 9l-7 7-7-7'%3E%3C/path%3E%3C/svg%3E");
|
|
81
|
+
background-repeat: no-repeat;
|
|
82
|
+
background-position: right center;
|
|
83
|
+
background-size: 1rem;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.prof-currency-select:focus {
|
|
87
|
+
outline: none;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
:global(.theme-dark) .prof-currency-select {
|
|
91
|
+
color: #34d399;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.prof-label {
|
|
95
|
+
display: block;
|
|
96
|
+
font-size: 0.95rem;
|
|
97
|
+
font-weight: 800;
|
|
98
|
+
color: var(--prof-text-muted);
|
|
99
|
+
margin-bottom: 0.75rem;
|
|
100
|
+
text-transform: uppercase;
|
|
101
|
+
letter-spacing: 0.05em;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.prof-input-field {
|
|
105
|
+
width: 100%;
|
|
106
|
+
background: var(--prof-input-bg);
|
|
107
|
+
border: 2px solid var(--prof-border);
|
|
108
|
+
border-radius: 1.25rem;
|
|
109
|
+
padding: 1.25rem 1.5rem;
|
|
110
|
+
font-size: 1.75rem;
|
|
111
|
+
font-weight: 900;
|
|
112
|
+
color: var(--prof-text);
|
|
113
|
+
transition: all 0.3s;
|
|
114
|
+
box-shadow: inset 0 2px 4px rgba(0,0,0,0.1);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.prof-input-field:hover {
|
|
118
|
+
border-color: var(--prof-brand-light);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.prof-input-field:focus {
|
|
122
|
+
border-color: var(--prof-brand);
|
|
123
|
+
background: var(--prof-card-bg);
|
|
124
|
+
outline: none;
|
|
125
|
+
box-shadow: 0 0 0 6px rgba(16, 185, 129, 0.2);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.prof-time-row {
|
|
129
|
+
display: flex;
|
|
130
|
+
gap: 1rem;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.prof-unit-select {
|
|
134
|
+
appearance: none;
|
|
135
|
+
background-color: var(--prof-input-bg);
|
|
136
|
+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='%2310b981' stroke-width='3' %3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M19 9l-7 7-7-7'%3E%3C/path%3E%3C/svg%3E");
|
|
137
|
+
background-repeat: no-repeat;
|
|
138
|
+
background-position: right 1rem center;
|
|
139
|
+
background-size: 1.25rem;
|
|
140
|
+
border: 2px solid var(--prof-border);
|
|
141
|
+
border-radius: 1.25rem;
|
|
142
|
+
color: var(--prof-text);
|
|
143
|
+
cursor: pointer;
|
|
144
|
+
font-weight: 800;
|
|
145
|
+
padding: 0 3.5rem 0 1.5rem;
|
|
146
|
+
transition: all 0.2s;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.prof-unit-select:hover {
|
|
150
|
+
border-color: var(--prof-brand);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.prof-unit-select option {
|
|
154
|
+
background-color: var(--prof-card-bg);
|
|
155
|
+
color: var(--prof-text);
|
|
156
|
+
padding: 1rem;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
:global(.theme-dark) .prof-unit-select {
|
|
160
|
+
background-color: #064e3b;
|
|
161
|
+
color: #ecfdf5;
|
|
162
|
+
border-color: #065f46;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
:global(.theme-dark) .prof-unit-select option {
|
|
166
|
+
background-color: #064e3b;
|
|
167
|
+
color: #ecfdf5;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.prof-results {
|
|
171
|
+
display: flex;
|
|
172
|
+
flex-direction: column;
|
|
173
|
+
gap: 2rem;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
.prof-status-badge {
|
|
177
|
+
display: inline-flex;
|
|
178
|
+
padding: 0.75rem 1.5rem;
|
|
179
|
+
border-radius: 2rem;
|
|
180
|
+
font-weight: 900;
|
|
181
|
+
font-size: 1rem;
|
|
182
|
+
text-transform: uppercase;
|
|
183
|
+
margin-bottom: 1.5rem;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
.prof-status-badge.positive {
|
|
187
|
+
background: var(--prof-brand);
|
|
188
|
+
color: white;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
.prof-status-badge.negative {
|
|
192
|
+
background: var(--prof-danger);
|
|
193
|
+
color: white;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
.prof-main-metric {
|
|
197
|
+
margin-bottom: 2rem;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
.prof-metric-val {
|
|
201
|
+
font-size: 5.5rem;
|
|
202
|
+
font-weight: 900;
|
|
203
|
+
line-height: 1;
|
|
204
|
+
letter-spacing: -0.06em;
|
|
205
|
+
margin: 0.5rem 0;
|
|
206
|
+
filter: drop-shadow(0 4px 8px rgba(0,0,0,0.1));
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.positive .prof-metric-val { color: var(--prof-brand); }
|
|
210
|
+
.negative .prof-metric-val { color: var(--prof-danger); }
|
|
211
|
+
|
|
212
|
+
.prof-submetrics {
|
|
213
|
+
display: grid;
|
|
214
|
+
grid-template-columns: 1fr 1fr;
|
|
215
|
+
gap: 1.5rem;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
.prof-submetric-card {
|
|
219
|
+
background: var(--prof-input-bg);
|
|
220
|
+
padding: 1.75rem;
|
|
221
|
+
border-radius: 1.5rem;
|
|
222
|
+
border: 1px solid var(--prof-border);
|
|
223
|
+
text-align: center;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.prof-submetric-val {
|
|
227
|
+
display: block;
|
|
228
|
+
font-size: 2rem;
|
|
229
|
+
font-weight: 900;
|
|
230
|
+
color: var(--prof-text);
|
|
231
|
+
margin-top: 0.5rem;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
.prof-copy-btn {
|
|
235
|
+
display: flex;
|
|
236
|
+
align-items: center;
|
|
237
|
+
justify-content: center;
|
|
238
|
+
gap: 0.75rem;
|
|
239
|
+
width: 100%;
|
|
240
|
+
padding: 1.5rem;
|
|
241
|
+
background: var(--prof-text);
|
|
242
|
+
color: var(--prof-card-bg);
|
|
243
|
+
border: none;
|
|
244
|
+
border-radius: 1.5rem;
|
|
245
|
+
font-weight: 900;
|
|
246
|
+
font-size: 1.25rem;
|
|
247
|
+
cursor: pointer;
|
|
248
|
+
transition: all 0.3s;
|
|
249
|
+
margin-top: 1rem;
|
|
250
|
+
box-shadow: 0 10px 20px rgba(0,0,0,0.2);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
.prof-copy-btn:hover {
|
|
254
|
+
transform: translateY(-3px);
|
|
255
|
+
box-shadow: 0 15px 30px rgba(0,0,0,0.3);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
.prof-chart-wrap {
|
|
259
|
+
margin-top: 4rem;
|
|
260
|
+
padding-top: 3.5rem;
|
|
261
|
+
border-top: 2px solid var(--prof-border);
|
|
262
|
+
height: 400px;
|
|
263
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { SEORenderer } from '@jjlmoya/utils-shared';
|
|
3
|
+
import { profitability } from './entry';
|
|
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 profitability.i18n[locale]?.();
|
|
12
|
+
if (!content) return null;
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
{content.seo?.length > 0 && <SEORenderer content={{ locale, sections: content.seo }} />}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface ProfitabilityUI extends Record<string, string> {
|
|
2
|
+
labelTitle: string;
|
|
3
|
+
labelInitialInvestment: string;
|
|
4
|
+
labelFinalValue: string;
|
|
5
|
+
labelTimePeriod: string;
|
|
6
|
+
labelYears: string;
|
|
7
|
+
labelMonths: string;
|
|
8
|
+
labelDays: string;
|
|
9
|
+
labelROI: string;
|
|
10
|
+
labelCAGR: string;
|
|
11
|
+
labelNetProfit: string;
|
|
12
|
+
labelPositiveResult: string;
|
|
13
|
+
labelNegativeResult: string;
|
|
14
|
+
labelCopyResult: string;
|
|
15
|
+
labelCopied: string;
|
|
16
|
+
labelGrowthChart: string;
|
|
17
|
+
labelLinearGrowth: string;
|
|
18
|
+
labelExponentialGrowth: string;
|
|
19
|
+
labelTimeSelection: string;
|
|
20
|
+
}
|
package/src/tools.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export { ALL_ENTRIES } from './entries';
|
|
2
2
|
import type { ToolDefinition } from './types';
|
|
3
3
|
import { COMPOUND_INTEREST_TOOL } from './tool/compoundInterest';
|
|
4
|
+
import { FINANCIAL_FREEDOM_TOOL } from './tool/financialFreedom';
|
|
5
|
+
import { PROFITABILITY_TOOL } from './tool/profitability';
|
|
4
6
|
import { MORTGAGE_TOOL } from './tool/mortgage';
|
|
5
7
|
import { INFLATION_TOOL } from './tool/inflation';
|
|
6
8
|
import { PERCENTAGE_CALCULATOR_TOOL } from './tool/percentageCalculator';
|
|
@@ -13,6 +15,6 @@ import { LEGAL_INTEREST_RATE_TOOL } from './tool/legalInterestRate';
|
|
|
13
15
|
import { FIRE_CALCULATOR_TOOL } from './tool/fireCalculator';
|
|
14
16
|
import { DEBT_SNOWBALL_TOOL } from './tool/debtSnowball';
|
|
15
17
|
|
|
16
|
-
export const ALL_TOOLS: ToolDefinition[] = [COMPOUND_INTEREST_TOOL, MORTGAGE_TOOL, INFLATION_TOOL, PERCENTAGE_CALCULATOR_TOOL, LATE_INTEREST_TOOL, IBAN_BIC_SWIFT_TOOL, RENT_INCREASE_TOOL, LOTTERY_OPTIMIZER_TOOL, COURT_FEE_CALCULATOR_TOOL, LEGAL_INTEREST_RATE_TOOL, FIRE_CALCULATOR_TOOL, DEBT_SNOWBALL_TOOL];
|
|
18
|
+
export const ALL_TOOLS: ToolDefinition[] = [COMPOUND_INTEREST_TOOL, FINANCIAL_FREEDOM_TOOL, PROFITABILITY_TOOL, MORTGAGE_TOOL, INFLATION_TOOL, PERCENTAGE_CALCULATOR_TOOL, LATE_INTEREST_TOOL, IBAN_BIC_SWIFT_TOOL, RENT_INCREASE_TOOL, LOTTERY_OPTIMIZER_TOOL, COURT_FEE_CALCULATOR_TOOL, LEGAL_INTEREST_RATE_TOOL, FIRE_CALCULATOR_TOOL, DEBT_SNOWBALL_TOOL];
|
|
17
19
|
|
|
18
20
|
|