Refactor portfolio hook and page components

- Add functions to calculate total investment and get latest investment
- Update graph data calculations for overall and four-year graphs
- Update recent funds component to display latest investment deals
- Minor code formatting and error handling improvements
This commit is contained in:
Pattadon 2024-11-01 14:32:33 +07:00
parent 22fe709ce8
commit 7356831e2e
3 changed files with 114 additions and 49 deletions

View File

@ -1,6 +1,38 @@
import { SupabaseClient } from "@supabase/supabase-js"; import { SupabaseClient } from "@supabase/supabase-js";
import { getProjectTag, getTagName } from "@/lib/data/query"; import { getProjectTag, getTagName } from "@/lib/data/query";
function getTotalInvestment(deals: { deal_amount : number}[]) {
let total = 0;
for (let index = 0; index < deals.length; index++) {
total += deals[index].deal_amount;
}
return total;
}
async function getLatestInvestment(
supabase: SupabaseClient,
deals: { project_id: number; deal_amount: number; created_time: Date }[]
) {
const llist = [];
const count = 8;
for (let i = deals.length - 1; i >= 0 && llist.length < count; --i) {
let { data: project, error } = await supabase
.from("project")
.select("project_name")
.eq("id", deals[i].project_id);
if (error) {
console.error(error);
}
llist.push({
name: project?.[0]?.project_name,
amount: deals[i].deal_amount,
date: new Date(deals[i].created_time),
});
}
return llist;
}
async function checkForInvest(supabase: SupabaseClient, userId: string) { async function checkForInvest(supabase: SupabaseClient, userId: string) {
let { count, error } = await supabase let { count, error } = await supabase
.from("investment_deal") .from("investment_deal")
@ -30,7 +62,6 @@ function countValues(arr: { value: string }[][]): Record<string, number> {
return counts; return counts;
} }
async function getBusinessTypeName( async function getBusinessTypeName(
supabase: SupabaseClient, supabase: SupabaseClient,
projectId: number projectId: number
@ -75,32 +106,38 @@ interface GraphData {
} }
function overAllGraphData(deals: Deal[]): GraphData[] { function overAllGraphData(deals: Deal[]): GraphData[] {
return deals // Initialize all months with value 0
? deals const months = [
.filter((item: Deal) => new Date(item.created_time) >= yearAgo(1)) "Jan",
.reduce((acc: GraphData[], item: Deal) => { "Feb",
// get the first three initial letter of the month "Mar",
const monthName = getMonthName(item.created_time.toString()).slice( "Apr",
0, "May",
3 "Jun",
); "Jul",
const existingMonth = acc.find( "Aug",
(entry: GraphData) => entry.name === monthName "Sep",
); "Oct",
"Nov",
"Dec",
];
const acc: GraphData[] = months.map((month) => ({ name: month, value: 0 }));
if (existingMonth) { deals
existingMonth.value += item.deal_amount; .filter((item: Deal) => new Date(item.created_time) >= yearAgo(1))
} .forEach((item: Deal) => {
// if month doesnt exist yet, create new record const monthName = getMonthName(item.created_time.toString()).slice(0, 3);
else { const monthEntry = acc.find((entry) => entry.name === monthName);
acc.push({ name: monthName, value: item.deal_amount });
}
return acc; if (monthEntry) {
}, [] as GraphData[]) monthEntry.value += item.deal_amount;
: []; }
});
return acc;
} }
interface Deal { interface Deal {
created_time: string | number | Date; created_time: string | number | Date;
deal_amount: any; deal_amount: any;
@ -112,24 +149,26 @@ interface GraphData {
} }
function fourYearGraphData(deals: Deal[]): GraphData[] { function fourYearGraphData(deals: Deal[]): GraphData[] {
return deals const currentYear = new Date().getFullYear();
const acc: GraphData[] = Array.from({ length: 4 }, (_, i) => ({
name: (currentYear - i).toString(),
value: 0,
})).reverse();
deals
.filter((item: Deal) => new Date(item.created_time) >= yearAgo(3)) .filter((item: Deal) => new Date(item.created_time) >= yearAgo(3))
.reduce((acc: GraphData[], item: Deal) => { .forEach((item: Deal) => {
const year = new Date(item.created_time).getFullYear(); const year = new Date(item.created_time).getFullYear().toString();
const existingYear = acc.find( const yearEntry = acc.find((entry) => entry.name === year);
(entry: GraphData) => entry.name === year.toString()
);
if (existingYear) { if (yearEntry) {
existingYear.value += item.deal_amount; yearEntry.value += item.deal_amount;
} else {
acc.push({ name: year.toString(), value: item.deal_amount });
} }
});
return acc; return acc;
}, [] as GraphData[]);
} }
interface DayOfWeekData { interface DayOfWeekData {
name: string; name: string;
value: number; value: number;
@ -238,4 +277,6 @@ export {
getBusinessTypeName, getBusinessTypeName,
countValues, countValues,
checkForInvest, checkForInvest,
getLatestInvestment,
getTotalInvestment,
}; };

View File

@ -11,6 +11,8 @@ import {
getBusinessTypeName, getBusinessTypeName,
countValues, countValues,
checkForInvest, checkForInvest,
getLatestInvestment,
getTotalInvestment,
} from "./hook"; } from "./hook";
import CountUpComponent from "@/components/countUp"; import CountUpComponent from "@/components/countUp";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
@ -24,6 +26,7 @@ import { RecentFunds } from "@/components/recent-funds";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import QuestionMarkIcon from "@/components/icon/questionMark"; import QuestionMarkIcon from "@/components/icon/questionMark";
import { NoDataAlert } from "@/components/alert/noData/alert"; import { NoDataAlert } from "@/components/alert/noData/alert";
import { error } from "console";
export default async function Portfolio({ export default async function Portfolio({
params, params,
@ -33,7 +36,6 @@ export default async function Portfolio({
const supabase = createSupabaseClient(); const supabase = createSupabaseClient();
// if user hasn't invest in anything // if user hasn't invest in anything
if (!(await checkForInvest(supabase, params.uid))) { if (!(await checkForInvest(supabase, params.uid))) {
return ( return (
<div> <div>
<NoDataAlert /> <NoDataAlert />
@ -47,10 +49,22 @@ export default async function Portfolio({
if (investorDealError) { if (investorDealError) {
console.error(investorDealError); console.error(investorDealError);
} }
const {
data: { user },
error: userError,
} = await supabase.auth.getUser();
if (userError) {
console.error("Error while fetching user" + error);
}
const username = user ? user.user_metadata.name : "Anonymous";
// console.log(username)
const overAllData = deals ? overAllGraphData(deals) : []; const overAllData = deals ? overAllGraphData(deals) : [];
const fourYearData = deals ? fourYearGraphData(deals) : []; const fourYearData = deals ? fourYearGraphData(deals) : [];
const dayOfWeekData = deals ? dayOftheWeekData(deals) : []; const dayOfWeekData = deals ? dayOftheWeekData(deals) : [];
const tags = deals ? await getInvestorProjectTag(supabase, deals) : []; const tags = deals ? await getInvestorProjectTag(supabase, deals) : [];
const latestDeals = deals ? await getLatestInvestment(supabase, deals) : [];
const totalInvestment = deals ? getTotalInvestment(deals) : 0;
// console.log(latestDeals);
const tagCount = countTags(tags); const tagCount = countTags(tags);
// console.log(investedBusinessIds); // console.log(investedBusinessIds);
const businessType = deals const businessType = deals
@ -80,9 +94,22 @@ export default async function Portfolio({
<div>{totalInvest}</div> <div>{totalInvest}</div>
</div> */} </div> */}
{/* <CountUpComponent end={100} duration={3} /> */} {/* <CountUpComponent end={100} duration={3} /> */}
<div className="text-center py-4">
<h1 className="text-2xl font-semibold">
Welcome to your Portfolio, {username}!
</h1>
<p className="text-lg text-muted-foreground">
Here's an overview of your investment journey and progress.
</p>
<p className="text-xl font-medium text-green-400">
Total Investment: $
<CountUpComponent end={totalInvestment} duration={1} />
</p>
</div>
<div className="flex flew-rows-3 gap-10 mt-5 w-full"> <div className="flex flew-rows-3 gap-10 mt-5 w-full">
<Tabs defaultValue="daily" className="space-y-4 w-full"> <Tabs defaultValue="daily" className="space-y-4 w-full">
<TabsList className="grid w-full grid-cols-3"> <TabsList className="grid w-96 grid-cols-3">
<TabsTrigger value="daily">Daily</TabsTrigger> <TabsTrigger value="daily">Daily</TabsTrigger>
<TabsTrigger value="monthly">Monthly</TabsTrigger> <TabsTrigger value="monthly">Monthly</TabsTrigger>
<TabsTrigger value="yearly">Yearly</TabsTrigger> <TabsTrigger value="yearly">Yearly</TabsTrigger>
@ -246,7 +273,7 @@ export default async function Portfolio({
</CardTitle> </CardTitle>
</CardHeader> </CardHeader>
<CardContent className="mt-5"> <CardContent className="mt-5">
<RecentFunds /> <RecentFunds data={latestDeals} />
</CardContent> </CardContent>
</Card> </Card>
</div> </div>

View File

@ -38,26 +38,23 @@ const data = [
}, },
]; ];
interface RecentFundsProps{ interface RecentFundsProps {
name?: string; data?: { name?: string; amount?: number; avatar?: string ; date?: Date}[];
email?: string;
amount?: number;
avatar?: string;
} }
export function RecentFunds(props: RecentFundsProps) { export function RecentFunds(props: RecentFundsProps) {
return ( return (
<div className="space-y-8"> <div className="space-y-8">
{data.map((person, index) => ( {(props?.data || []).map((deal, index) => (
<div className="flex items-center" key={index}> <div className="flex items-center" key={index}>
<Avatar className="h-9 w-9"> <Avatar className="h-9 w-9">
<AvatarImage src={person.avatar} alt={person.name} /> <AvatarImage src={deal.avatar} alt={deal.name} />
<AvatarFallback>{person.initials}</AvatarFallback> <AvatarFallback>{(deal.name ?? "").slice(0, 3)}</AvatarFallback>
</Avatar> </Avatar>
<div className="ml-4 space-y-1"> <div className="ml-4 space-y-1">
<p className="text-sm font-medium leading-none">{person.name}</p> <p className="text-sm font-medium leading-none">{deal.name}</p>
<p className="text-sm text-muted-foreground">{person.email}</p> <p className="text-xs text-muted-foreground">{deal?.date?.toLocaleDateString()}</p>
</div> </div>
<div className="ml-auto font-medium">+${person.amount}</div> <div className="ml-auto font-medium">+${deal.amount}</div>
</div> </div>
))} ))}
</div> </div>