Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
193 changes: 105 additions & 88 deletions src/pages/webapp/Dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import {DecisionHttpService} from "@/services/decision-http-service.ts";
import {UsersHttpService} from "@/services/users-http-service.ts";
import {Tooltip, TooltipContent, TooltipProvider, TooltipTrigger} from "@/components/ui/tooltip.tsx";
import {Badge} from "@/components/ui/badge.tsx";
import {Card, CardDescription, CardHeader, CardTitle} from "@/components/ui/card.tsx";
import {Card, CardContent, CardDescription, CardHeader, CardTitle} from "@/components/ui/card.tsx";
import {NewsItem} from "@/model/NewsItem.ts";
import {Alert} from "@/model/Alert.ts";
import {NewsHttpService} from "@/services/news-http-service.ts";
Expand Down Expand Up @@ -75,6 +75,7 @@ function Summary() {
const alerts = await alertsHttpService.getAlerts();
setAlerts(alerts);
}

fetchInitialData().then();
}, []);

Expand All @@ -97,7 +98,8 @@ function Summary() {

return (
<div className={"flex flex-col gap-5"}>
<Card onClick={() => navigate("/news")} className={"md:flex-1 bg-neutral-100 shadow-none border-none cursor-pointer"}>
<Card onClick={() => navigate("/news")}
className={"md:flex-1 bg-neutral-100 shadow-none border-none cursor-pointer"}>
<CardHeader>
<div className={"rounded-full bg-neutral-200 w-fit box-border p-3"}>
<NewspaperIcon className={"size-6"}/>
Expand All @@ -109,10 +111,11 @@ function Summary() {
</CardDescription>
</CardHeader>
</Card>
<Card onClick={() => navigate("/alerts")} className={"md:flex-1 bg-neutral-100 shadow-none border-none cursor-pointer"}>
<Card onClick={() => navigate("/alerts")}
className={"md:flex-1 bg-neutral-100 shadow-none border-none cursor-pointer"}>
<CardHeader>
<div className={"rounded-full bg-neutral-200 w-fit box-border p-3"}>
<ExclamationTriangleIcon className={"size-6"} />
<ExclamationTriangleIcon className={"size-6"}/>
</div>
<CardTitle className={"pt-3"}>Title</CardTitle>
<CardDescription>
Expand Down Expand Up @@ -187,6 +190,7 @@ function Portfolio() {
const holdings = await portfolioHttpService.getPortfolioHoldings();
setHoldings(holdings);
}

fetchInitialData().then();
}, []);

Expand All @@ -211,91 +215,103 @@ function Portfolio() {
}));
setAssetHoldings(assets);
}

fetchInitialData().then();
}, [holdings]);

return (
<div className={"flex flex-col gap-5 items-end"}>
<HoldingCreator onSubmit={onAddHolding}></HoldingCreator>
<Table className={"mb-5"}>
<TableHeader>
<TableRow>
<TableHead className="w-[100px]">Ticker</TableHead>
<TableHead>Shares</TableHead>
<TableHead>Value per share</TableHead>
<TableHead>Total value</TableHead>
<TableHead>Last decision</TableHead>
<TableHead className="text-right">Change (%)</TableHead>
<TableHead colSpan={1}></TableHead>
</TableRow>
</TableHeader>
<TableBody>
{assetHoldings.map(holding => {
const difference = percentageChange(holding.valueShare, holding.buyPrice);
return (
<TableRow key={holding.assetId} className={""}>
<TableCell>
<span className={'text-neutral-400'}>{holding.mic}</span>
<span className="ps-2 font-medium">{holding.ticker}</span>
</TableCell>
<TableCell>{holding.shares}</TableCell>
<TableCell>{toUSD(holding.valueShare)}</TableCell>
<TableCell>{toUSD(holding.valueShare * holding.shares)}</TableCell>
<TableCell>
<TooltipProvider>
<Tooltip>
<TooltipTrigger>
<Badge className={`${holding.lastDecision === "BUY"
? "bg-green-700"
: (holding.lastDecision === "SELL"
? "bg-red-700"
: "")}`} variant={"default"}>
{holding.lastDecision}
</Badge>
</TooltipTrigger>
<TooltipContent>
<p>{holding.lastDecisionDate.toLocaleString('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric',
hour: 'numeric',
minute: '2-digit',
second: '2-digit',
hour12: true,
})}</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</TableCell>
<TableCell
className={`text-right font-medium ${difference.includes('-') ? 'text-red-700' : 'text-green-700'}`}>{difference}</TableCell>
<TableCell>
<AssetDrawer
assetId={holding.assetId}
buyPrice={holding.buyPrice}
shares={holding.shares}
onSubmit={onUpdateHolding}
onDelete={onDeleteHolding}>
<PencilIcon className={"size-4"}></PencilIcon>
</AssetDrawer>
</TableCell>
<>
<Card className={"shadow-none box-border p-5"}>
<div className={"flex flex-col gap-5 items-end"}>
<Table>
<TableHeader>
<TableRow>
<TableHead className="w-[100px]">Ticker</TableHead>
<TableHead>Shares</TableHead>
<TableHead>Value per share</TableHead>
<TableHead>Total value</TableHead>
<TableHead>Last decision</TableHead>
<TableHead className="text-right">Change (%)</TableHead>
<TableHead colSpan={1}></TableHead>
</TableRow>
)
})}
</TableBody>
<TableFooter>
<TableRow>
<TableCell colSpan={3}>Total</TableCell>
<TableCell>{
toUSD(assetHoldings
.map(holding => holding.shares * holding.valueShare)
.reduce((previous, current) => previous + current, 0))
}</TableCell>
<TableCell colSpan={3}></TableCell>
</TableRow>
</TableFooter>
</Table>
</div>
</TableHeader>
<TableBody>
{assetHoldings.map(holding => {
const difference = percentageChange(holding.valueShare, holding.buyPrice);
return (
<TableRow key={holding.assetId} className={""}>
<TableCell>
<span className={'text-neutral-400'}>{holding.mic}</span>
<span className="ps-2 font-medium">{holding.ticker}</span>
</TableCell>
<TableCell>{holding.shares}</TableCell>
<TableCell>{toUSD(holding.valueShare)}</TableCell>
<TableCell>{toUSD(holding.valueShare * holding.shares)}</TableCell>
<TableCell>
<TooltipProvider>
<Tooltip>
<TooltipTrigger>
<Badge className={`${holding.lastDecision === "BUY"
? "bg-green-700"
: (holding.lastDecision === "SELL"
? "bg-red-700"
: "")}`} variant={"default"}>
{holding.lastDecision}
</Badge>
</TooltipTrigger>
<TooltipContent>
<p>{holding.lastDecisionDate.toLocaleString('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric',
hour: 'numeric',
minute: '2-digit',
second: '2-digit',
hour12: true,
})}</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</TableCell>
<TableCell
className={`text-right font-medium ${difference.includes('-') ? 'text-red-700' : 'text-green-700'}`}>{difference}</TableCell>
<TableCell>
<AssetDrawer
assetId={holding.assetId}
buyPrice={holding.buyPrice}
shares={holding.shares}
onSubmit={onUpdateHolding}
onDelete={onDeleteHolding}>
<PencilIcon className={"size-4"}></PencilIcon>
</AssetDrawer>
</TableCell>
</TableRow>
)
})}
</TableBody>
<TableFooter>
<TableRow>
<TableCell colSpan={3}>Total</TableCell>
<TableCell>{
toUSD(assetHoldings
.map(holding => holding.shares * holding.valueShare)
.reduce((previous, current) => previous + current, 0))
}</TableCell>
<TableCell colSpan={3}></TableCell>
</TableRow>
</TableFooter>
</Table>
</div>
</Card>
<Card className={"shadow-none box-border mt-5"}>
<CardHeader>
<CardTitle>Add new asset to portfolio</CardTitle>
</CardHeader>
<CardContent>
<HoldingCreator onSubmit={onAddHolding}></HoldingCreator>
</CardContent>
</Card>
</>
)
}

Expand Down Expand Up @@ -323,7 +339,7 @@ function HoldingCreator({onSubmit}: {
}, []);

return (
<div className={"flex gap-3"}>
<div className={"flex justify-between"}>
<Select onValueChange={value => {
setAsset(availableAssets.find(asset => asset.assetId === value));
}}>
Expand All @@ -343,9 +359,10 @@ function HoldingCreator({onSubmit}: {
})}
</SelectContent>
</Select>
{asset ? <AssetDrawer assetId={asset.assetId} onSubmit={onSubmit}>
<Button>Add holding</Button>
</AssetDrawer> : ""}
{asset && <AssetDrawer assetId={asset.assetId} onSubmit={onSubmit}>
<Button className={"cursor-pointer"}>Add holding</Button>
</AssetDrawer>}
{!asset && <Button disabled>Add holding</Button>}
</div>
)
}
Expand Down
20 changes: 10 additions & 10 deletions src/pages/webapp/Profile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ export default function Profile() {
<div className={"flex flex-row gap-5"}>
<div className={"flex-1"}>
<h2 className={"text-2xl font-medium py-6"}>Personal information</h2>
<Input type="email" disabled={true} value={user.email} />
<Input type="email" disabled={true} value={user.email}/>
<div className={"flex flex-row gap-4 mt-4"}>
<Input type="firstName" disabled={true} value={user.firstName} />
<Input type="lastName" disabled={true} value={user.lastName} />
<Input type="firstName" disabled={true} value={user.firstName}/>
<Input type="lastName" disabled={true} value={user.lastName}/>
</div>
</div>
<div className={"flex-1"}>
Expand All @@ -37,9 +37,9 @@ export default function Profile() {
variant="outline" value={user.riskLevel.toString()}
onValueChange={newValue => {
if (newValue) {
setUser(user => ({...user, riskLevel: parseInt(newValue)} as User))}
}
}
setUser(user => ({...user, riskLevel: parseInt(newValue)} as User))
}
}}
>
{/* TODO: Fetch risk levels from back */}
{[1, 2, 3, 4].map((riskLevelItem, index) => (
Expand All @@ -53,10 +53,10 @@ export default function Profile() {
))}
</ToggleGroup>
<Badge className={"mt-2"} variant="secondary">
{ user.riskLevel === 1 && "Conservative" }
{ user.riskLevel === 2 && "Moderate" }
{ user.riskLevel === 3 && "Aggressive" }
{ user.riskLevel === 4 && "Very aggressive" }
{user.riskLevel === 1 && "Conservative"}
{user.riskLevel === 2 && "Moderate"}
{user.riskLevel === 3 && "Aggressive"}
{user.riskLevel === 4 && "Very aggressive"}
</Badge>
</div>
</div>
Expand Down
Loading
Loading