Skip to content

Commit a87e37d

Browse files
authored
feat: Add support for investment accounts with current value and XIRR (#134)
1 parent 2ba15d0 commit a87e37d

22 files changed

+1544
-153
lines changed

frontend/components/custom/AccountAnalytics/AccountAnalytics.tsx

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,13 @@ import {
1919
TableRow,
2020
} from "@/components/ui/table";
2121
import type { AccountAnalyticsListResponse } from "@/lib/models/analytics";
22-
import { formatCurrency, getTransactionColor } from "@/lib/utils";
22+
import {
23+
formatCurrency,
24+
formatPercentage,
25+
getTransactionColor,
26+
} from "@/lib/utils";
2327
import { format } from "date-fns";
2428
import { ChevronRight, Plus, Wallet } from "lucide-react";
25-
import { useTheme } from "next-themes";
2629
import Link from "next/link";
2730
import { Fragment, useEffect, useState } from "react";
2831

@@ -49,7 +52,6 @@ const accountColors = [
4952
];
5053

5154
function AccountTransactions({ accountId }: AccountTransactionsProps) {
52-
const { theme } = useTheme();
5355
const { data, isLoading, error } = useTransactions({
5456
account_id: accountId,
5557
page: 1,
@@ -126,8 +128,7 @@ function AccountTransactions({ accountId }: AccountTransactionsProps) {
126128
</div>
127129
<div
128130
className={`text-sm font-semibold ${getTransactionColor(
129-
transaction.amount,
130-
theme
131+
transaction.amount
131132
)}`}
132133
>
133134
{formatCurrency(Math.abs(transaction.amount))}
@@ -258,24 +259,33 @@ export function AccountAnalytics({ data }: AccountAnalyticsProps) {
258259
);
259260
const accountName = accountInfo?.name || `Account ${account.account_id}`;
260261
const initialBalance = accountInfo?.balance || 0;
262+
const isInvestment =
263+
accountInfo?.bank_type === "investment" &&
264+
account.current_value !== null &&
265+
account.current_value !== undefined;
261266

262267
// Calculate the actual balance including initial balance
263268
const actualBalance = account.current_balance + initialBalance;
264269
const absoluteBalance = Math.abs(actualBalance);
270+
const displayValue = isInvestment
271+
? Number(account.current_value)
272+
: absoluteBalance;
265273

266274
return {
267275
...account,
268276
accountName,
269277
initialBalance,
270278
actualBalance,
271279
absoluteBalance,
280+
displayValue,
281+
isInvestment,
272282
color: accountColors[index % accountColors.length],
273283
};
274284
});
275285

276286
// Calculate total balance from the actual balances
277287
const totalBalance = accountsWithBalances.reduce(
278-
(sum, account) => sum + account.absoluteBalance,
288+
(sum, account) => sum + account.displayValue,
279289
0
280290
);
281291

@@ -284,7 +294,7 @@ export function AccountAnalytics({ data }: AccountAnalyticsProps) {
284294
.map((account) => ({
285295
...account,
286296
percentage:
287-
totalBalance > 0 ? (account.absoluteBalance / totalBalance) * 100 : 0,
297+
totalBalance > 0 ? (account.displayValue / totalBalance) * 100 : 0,
288298
}))
289299
.sort((a, b) => b.percentage - a.percentage); // Sort by percentage descending
290300

@@ -464,9 +474,20 @@ export function AccountAnalytics({ data }: AccountAnalyticsProps) {
464474
</div>
465475
</TableCell>
466476
<TableCell className="text-right">
467-
<span className="font-medium">
468-
{formatCurrency(account.absoluteBalance)}
469-
</span>
477+
<div className="flex flex-col items-end">
478+
<span className="font-medium">
479+
{formatCurrency(account.displayValue)}
480+
</span>
481+
{account.isInvestment && (
482+
<span className="text-xs text-muted-foreground">
483+
{formatPercentage(
484+
account.percentage_increase ?? 0
485+
)}
486+
<span className="mx-1"></span>
487+
{formatPercentage(account.xirr ?? 0)}
488+
</span>
489+
)}
490+
</div>
470491
</TableCell>
471492
</TableRow>
472493
{isExpanded && (

frontend/components/custom/CategoryAnalytics/CategoryAnalytics.tsx

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ import type { Category } from "@/lib/models/category";
2222
import { formatCurrency, getTransactionColor } from "@/lib/utils";
2323
import { format } from "date-fns";
2424
import { ChevronRight, FileQuestion, Plus, Tag } from "lucide-react";
25-
import { useTheme } from "next-themes";
2625
import Link from "next/link";
2726
import { Fragment, useEffect, useState } from "react";
2827

@@ -56,7 +55,6 @@ function CategoryTransactions({
5655
categoryId,
5756
isUncategorized,
5857
}: CategoryTransactionsProps) {
59-
const { theme } = useTheme();
6058
const { data, isLoading, error } = useTransactions({
6159
page: 1,
6260
page_size: 5,
@@ -137,8 +135,7 @@ function CategoryTransactions({
137135
</div>
138136
<div
139137
className={`text-sm font-semibold ${getTransactionColor(
140-
transaction.amount,
141-
theme
138+
transaction.amount
142139
)}`}
143140
>
144141
{formatCurrency(Math.abs(transaction.amount))}

0 commit comments

Comments
 (0)