File: /var/www/quadcode.com/src/components/blocks/investment-calculator/Calculator.svelte
<script lang="ts">
import { t } from '$lib/translations';
import { investmentCalculator } from '../../../store';
import { type IInvestmentCalculator } from '$type/investmentCalculator';
import CalculatorInput from '$components/blocks/investment-calculator/CalculatorInput.svelte';
import CalculatorSliderInput from '$components/blocks/investment-calculator/CalculatorSliderInput.svelte';
import { onMount } from 'svelte';
let investmentCalculatorData: IInvestmentCalculator;
investmentCalculator.subscribe((newValue: IInvestmentCalculator): void => {
investmentCalculatorData = newValue;
});
let mode: string = 'basic';
const toggleMode = (newMode: string) => {
mode = newMode;
investmentCalculatorData.mode = newMode;
investmentCalculator.set(investmentCalculatorData);
calculate();
};
onMount(() => {
calculate();
});
const calculate = () => {
const {
initialInvestment,
timeHorizon: months,
monthlyContribution,
returnRate: annualReturn,
annualInflationRate: annualInflation,
taxRate,
investmentFees: annualFeeRate,
annualContributionIncrease,
} = investmentCalculatorData;
if (mode === 'basic') {
const monthlyReturnRate = (1 + annualReturn / 100) ** (1 / 12) - 1;
let portfolio = initialInvestment;
let totalContributions = initialInvestment;
const chartData = [];
for (let month = 1; month <= months; month++) {
portfolio += monthlyContribution;
totalContributions += monthlyContribution;
portfolio *= (1 + monthlyReturnRate);
if (month % 12 === 0) {
chartData.push({
year: month / 12,
totalInvested: Math.ceil(totalContributions),
totalInterest: Math.ceil(portfolio - totalContributions),
finalBalance: Math.ceil(portfolio),
});
}
}
if (months % 12 !== 0) {
chartData.push({
year: +(months / 12).toFixed(1),
totalInvested: Math.ceil(totalContributions),
totalInterest: Math.ceil(portfolio - totalContributions),
finalBalance: Math.ceil(portfolio),
});
}
const nominalValue = Math.ceil(portfolio);
const totalInvested = Math.ceil(totalContributions);
const totalInterest = Math.ceil(portfolio - totalContributions);
const finalBalance = nominalValue;
investmentCalculatorData = {
...investmentCalculatorData,
nominalValue,
totalInvested,
totalInterest,
finalBalance,
chartData,
};
investmentCalculator.set(investmentCalculatorData);
} else {
const i = annualInflation / 100;
const t = taxRate / 100;
const g = annualContributionIncrease / 100;
const monthlyReturnRate = (1 + annualReturn / 100) ** (1 / 12) - 1;
const monthlyFeeRate = (1 + annualFeeRate / 100) ** (1 / 12) - 1;
let portfolioGross = initialInvestment;
let portfolioNet = initialInvestment;
let totalContributions = initialInvestment;
let currentMonthlyContribution = monthlyContribution;
for (let month = 1; month <= months; month++) {
if (month > 1 && (month - 1) % 12 === 0) {
currentMonthlyContribution *= (1 + g);
}
portfolioGross += currentMonthlyContribution;
portfolioNet += currentMonthlyContribution;
totalContributions += currentMonthlyContribution;
portfolioGross *= (1 + monthlyReturnRate);
portfolioNet *= (1 + monthlyReturnRate) / (1 + monthlyFeeRate);
}
const investmentGain = portfolioNet - totalContributions;
const estimatedTaxes = Math.max(0, investmentGain) * t;
const afterTaxValue = portfolioNet - estimatedTaxes;
const inflationFactor = (1 + i) ** (months / 12);
const nominalValue = Math.ceil(portfolioNet);
const realValue = Math.ceil(afterTaxValue / inflationFactor);
const inflationImpact = Math.ceil(portfolioNet - portfolioNet / inflationFactor);
const managementFees = Math.ceil(portfolioGross - portfolioNet);
investmentCalculatorData = {
...investmentCalculatorData,
nominalValue,
realValue,
estimatedTaxes: Math.ceil(estimatedTaxes),
inflationImpact,
managementFees,
};
investmentCalculator.set(investmentCalculatorData);
}
};
</script>
<div class="block-calculator">
<div class="container">
<div class="investmentProfitCalculator__header">
<h2>{$t('IC.Investment Calculator: Check Your Potential Earnings')}</h2>
<p>{$t('IC.Use smart investment calculator to see how much your money can grow over time.')}</p>
</div>
<div class="investmentProfitCalculator__toggle {mode === 'advanced' ? 'item2' : ''}">
<button
class="investmentProfitCalculator__item {mode === 'basic' ? 'active' : ''}"
on:click={() => toggleMode('basic')}
>
{$t('IC.Basic')}
</button>
<button
class="investmentProfitCalculator__item {mode === 'advanced' ? 'active' : ''}"
on:click={() => toggleMode('advanced')}
>
{$t('IC.Advanced')}
</button>
</div>
<div class="investmentProfitCalculator__inputForm {'mode-' + mode}">
<div class="investmentProfitCalculator__basic">
<CalculatorInput
id="initialInvestment"
title={$t('IC.Initial investment')}
hint={$t('IC.How much money do you have today?')}
value="65"
className="money"
/>
<CalculatorSliderInput
id="timeHorizon"
title={$t('IC.Time horizon')}
hint={$t('IC.How long are you investing?')}
value={140}
range={[0, 250]}
name={$t('IC.month|months|months')}
/>
<CalculatorInput
id="monthlyContribution"
title={$t('IC.Monthly contribution')}
hint={$t('IC.How much will you add each month?')}
value="65"
className="money"
/>
<CalculatorSliderInput
id="returnRate"
title={$t('IC.Expected annual return')}
hint={$t('IC.Choose your investment strategy')}
value={7}
range={[0, 100]}
name={$t('%|%|%')}
/>
</div>
<div class="investmentProfitCalculator__advanced {mode !== 'advanced' ? 'disabled' : ''}">
<CalculatorInput
id="annualInflationRate"
title={$t('IC.Annual inflation rate')}
hint={$t('IC.Average price increase per year (usually 2-4%)')}
value="2"
className="percent"
/>
<CalculatorInput
id="taxRate"
title={$t('IC.Tax rate')}
hint={$t('IC.Your estimated tax on investment gains')}
value="13"
className="percent"
/>
<CalculatorInput
id="investmentFees"
title={$t('IC.Investment fees %')}
hint={$t('IC.Annual management fees or expense ratios')}
value="0.05"
className="percent"
/>
<CalculatorInput
id="annualContributionIncrease"
title={$t('IC.Annual contribution increase')}
hint={$t('IC.Percentage by which you will increase your monthly deposit each year.')}
value="30"
className="percent"
/>
</div>
</div>
<button
class="investmentProfitCalculator__calculate"
on:click={calculate}
>
{$t('IC.Calculate')}
</button>
</div>
</div>
<style lang="scss">
@import 'src/scss/variables';
@import 'src/scss/media';
@import 'src/scss/mixins';
.block-calculator {
.container {
max-width: 1181px;
padding: 32px;
background-color: #FFFFFF;
border-radius: 16px;
@include breakpoint-down('tabM') {
border-radius: 32px;
padding: 20px;
}
}
.investmentProfitCalculator {
&__header {
h2 {
font-weight: 500;
font-size: 40px;
line-height: 46px;
letter-spacing: -3%;
@include breakpoint-down('tabM') {
font-weight: 500;
font-size: 24px;
line-height: 32px;
}
}
p {
font-weight: 400;
font-size: 14px;
line-height: 20px;
margin: 4px 0 24px;
@include breakpoint-down('tabM') {
font-weight: 400;
font-size: 12px;
line-height: 14px;
margin: 4px 0 16px;
}
}
}
&__toggle {
background: $qc-bg-osc;
border-radius: 8px;
display: flex;
padding: 2px;
gap: 12px;
height: 38px;
justify-content: center;
align-items: center;
width: 100%;
position: relative;
margin-bottom: 20px;
&:before {
background: white;
content: ' ';
width: calc(50% - 6px);
height: 34px;
border-radius: 6px;
box-shadow: 0 1px 4px 0 #00000014;
position: absolute;
z-index: 1;
left: 2px;
transition: all 0.3s ease-in-out;
}
&.item2 {
&:before {
left: calc(50% + 4px);
}
}
}
&__item {
background: none;
border: 0;
font-family: $Suisse;
font-weight: 400;
font-size: 14px;
line-height: 18px;
text-align: center;
width: calc(50% - 6px);
z-index: 2;
color: #445667;
cursor: pointer;
&.active {
color: #141414;
font-weight: 700;
}
}
&__inputForm {
display: flex;
justify-content: space-between;
@include breakpoint-down('tabM') {
flex-direction: column;
justify-content: initial;
}
&.mode-basic {
.investmentProfitCalculator__basic {
@include breakpoint-down('deskM') {
width: 100%;
}
}
}
&.mode-advanced {
& + .investmentProfitCalculator__calculate {
width: 100%;
}
}
}
&__basic {
width: calc(50% - 16px);
@include breakpoint-down('tabM') {
width: 100%;
}
}
&__advanced {
width: calc(50% - 16px);
@include breakpoint-down('tabM') {
width: 100%;
}
&.disabled {
pointer-events: none;
opacity: 0.2;
@include breakpoint-down('deskM') {
display: none;
}
}
}
&__calculate {
cursor: pointer;
background: $qc-accent;
border: 0;
color: white;
display: flex;
justify-content: center;
align-items: center;
width: calc(50% - 16px);
height: 42px;
border-radius: 40px;
margin-top: 12px;
font-family: $Suisse;
font-weight: 500;
font-size: 14px;
line-height: 18px;
position: relative;
z-index: 2;
@include breakpoint-down('deskM') {
width: 100%;
}
}
}
}
</style>