Skip to content

Commit

Permalink
feat: add pie chart visualization for interest and total return
Browse files Browse the repository at this point in the history
closes #2
  • Loading branch information
rocktimsaikia committed Jul 29, 2024
1 parent 8eb73c6 commit c594d4e
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 36 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
"@radix-ui/react-icons": "^1.3.0",
"compounder": "^0.0.1",
"react": "^18.3.1",
"react-dom": "^18.3.1"
"react-dom": "^18.3.1",
"react-minimal-pie-chart": "^8.4.0"
},
"devDependencies": {
"@types/react": "^18.3.3",
Expand Down
20 changes: 20 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

107 changes: 72 additions & 35 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { useEffect, useState } from "react";
import compounder from "compounder";
import AnimatedCounter from "./components/animated-counter";
import { TriangleUpIcon } from "@radix-ui/react-icons";
import { PieChart } from "react-minimal-pie-chart";

type CompoundingFrequency = "quarterly" | "semi-annually" | "annually";

Expand All @@ -23,6 +24,12 @@ function calcPercentageBetween(value: number, min: number, max: number) {
return Math.round(((value - min) / (max - min)) * 100);
}

function clamp(value: number, min = 0, max = 100) {
if (value >= min && value <= max) return value;
if (value < min) return min;
return max;
}

export default function App() {
const [principalAmount, setPrincipalAmount] = useState<number>(100000);
const [interestRate, setInterestRate] = useState<number>(6);
Expand All @@ -49,6 +56,10 @@ export default function App() {
setTotalInterest(total - principalAmount);
}

function getTotalInterestPercentage() {
return (totalInterest / principalAmount) * 100;
}

function calcPrincipalPercent(currentValue: number) {
const { min, max } = PRINCIPAL_AMOUNT_RANGE;
const x = calcPercentageBetween(currentValue, min, max);
Expand Down Expand Up @@ -78,8 +89,8 @@ export default function App() {

return (
<main className="flex flex-col justify-center items-center h-screen font-medium text-gray-800 bg-[#f9fafb]">
<div className="w-[41rem] shadow-lg bg-white rounded-lg p-10">
<div className="flex flex-col space-y-12">
<div className="w-[41rem]">
<div className="flex flex-col space-y-12 shadow-xl bg-white rounded-lg p-12">
<div>
<div className="flex items-center justify-between">
<div>Principal Amount</div>
Expand Down Expand Up @@ -195,41 +206,67 @@ export default function App() {
</div>
</div>

<div className="mt-24 grid grid-cols-3 text-center">
<div>
<p className="text-gray-500">Principal Amount</p>
<div>
<div className="relative">
<PieChart
className="-mt-32 -mb-72"
startAngle={180}
lengthAngle={180}
radius={20}
lineWidth={50}
data={[
{
title: "Total Return",
value: clamp(100 - getTotalInterestPercentage()),
color: "#8A2BE2",
},
{
title: "Interest Return",
value: clamp(getTotalInterestPercentage()),
color: "#4ade80",
},
]}
segmentsShift={0.5}
/>
</div>
<div className="grid grid-cols-3 text-center">
<div>
<AnimatedCounter start={principalAmount} locale="en-IN" />
<p className="text-gray-500">Principal Amount</p>
<div>
<AnimatedCounter start={principalAmount} locale="en-IN" />
</div>
</div>
<div>
<div className="text-gray-500">
<div className="inline-block bg-[#4ade80] h-[11px] w-[11px] mr-2" />
Total Interest
</div>
<p>
<AnimatedCounter start={totalInterest} locale="en-IN" />
<TriangleUpIcon
className="text-green-500 inline-block"
height="22"
width="22"
/>
</p>
</div>
<div>
<div className="text-gray-500">
<div className="inline-block bg-[#8A2BE2] h-[11px] w-[11px] mr-2" />
Total Amount
</div>
<p>
<AnimatedCounter start={totalAmount} locale="en-IN" />
<TriangleUpIcon
className="text-green-500 inline-block"
height="22"
width="22"
/>
</p>
<p className="text-green-600 text-xs">
+ {getTotalInterestPercentage().toFixed(2)} %
</p>
</div>
</div>
<div>
<p className="text-gray-500">Total Interest</p>
<p>
<AnimatedCounter start={totalInterest} locale="en-IN" />
<TriangleUpIcon
className="text-green-500 inline-block"
height="22"
width="22"
/>
</p>
</div>
<div>
<p className="text-gray-500">Total Amount</p>
<p>
<AnimatedCounter start={totalAmount} locale="en-IN" />
<TriangleUpIcon
className="text-green-500 inline-block"
height="22"
width="22"
/>
</p>
<p className="text-green-600 text-xs">
+{" "}
{parseFloat(
String((totalInterest / principalAmount) * 100),
).toFixed(2)}{" "}
%
</p>
</div>
</div>
</div>
Expand Down

0 comments on commit c594d4e

Please sign in to comment.