feat: replace account page accordion with tabs and show open position result (#112)
This commit was merged in pull request #113.
This commit is contained in:
@@ -1,6 +1,12 @@
|
||||
import type { FC } from "react";
|
||||
|
||||
import { Accordion, Alert, Stack } from "@mantine/core";
|
||||
import {
|
||||
Alert,
|
||||
Group,
|
||||
SegmentedControl,
|
||||
Stack,
|
||||
Tabs,
|
||||
Text,
|
||||
} from "@mantine/core";
|
||||
import { type FC, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import type { BrokerageAccount } from "@/entities/brokerage-account";
|
||||
@@ -8,6 +14,7 @@ import type { Portfolio } from "@/entities/portfolio";
|
||||
import type { InvestmentResourceMeta } from "@/shared/api";
|
||||
|
||||
import { BrokerageEventsPreview } from "@/entities/brokerage-event";
|
||||
import { formatMoney } from "@/shared/lib";
|
||||
import { SourceSyncStatus } from "@/shared/ui";
|
||||
import { PortfolioStructure } from "@/widgets/portfolio-structure";
|
||||
|
||||
@@ -35,18 +42,27 @@ type AccountPageLoadedProps = {
|
||||
recentEvents: AccountPageRecentEvents;
|
||||
recentOperations: AccountPageRecentOperations;
|
||||
source?: InvestmentResourceMeta;
|
||||
unrealizedReturns: Portfolio["totalsByCurrency"];
|
||||
};
|
||||
|
||||
const AccountPageLoaded: FC<AccountPageLoadedProps> = (props) => {
|
||||
const { t } = useTranslation();
|
||||
const [filter, setFilter] = useState<string>();
|
||||
|
||||
const defaultValue = [
|
||||
...props.positionSections.map(section => section.kind),
|
||||
"cash",
|
||||
"recentOperations",
|
||||
"recentEvents",
|
||||
"incomeSummary",
|
||||
];
|
||||
const activeSection = props.positionSections.find(
|
||||
section => section.kind === filter,
|
||||
) ?? props.positionSections[0];
|
||||
|
||||
const positionKindLabels: Record<AccountPagePositionSection["kind"], string> = {
|
||||
bonds: t("portfolio.positionKinds.bonds"),
|
||||
other: t("portfolio.positionKinds.other"),
|
||||
stocks: t("portfolio.positionKinds.stocks"),
|
||||
};
|
||||
|
||||
const filterOptions = props.positionSections.map(section => ({
|
||||
label: positionKindLabels[section.kind],
|
||||
value: section.kind,
|
||||
}));
|
||||
|
||||
return (
|
||||
<Stack gap="lg">
|
||||
@@ -62,6 +78,26 @@ const AccountPageLoaded: FC<AccountPageLoadedProps> = (props) => {
|
||||
? <Alert role="status">{t("investment.partial")}</Alert>
|
||||
: <CurrencyCards amounts={props.portfolio.totalsByCurrency} />}
|
||||
|
||||
{!props.partial && props.unrealizedReturns.length > 0 && (
|
||||
<Stack
|
||||
aria-label={t("portfolio.unrealizedResult")}
|
||||
component="section"
|
||||
gap={4}
|
||||
>
|
||||
<Text fw={600}>{t("portfolio.unrealizedResult")}</Text>
|
||||
|
||||
<Group gap="md">
|
||||
{props.unrealizedReturns.map(total => (
|
||||
<Text key={total.currency}>{formatMoney(total)}</Text>
|
||||
))}
|
||||
</Group>
|
||||
|
||||
<Text c="dimmed" size="xs">
|
||||
{t("portfolio.unrealizedResultCaption")}
|
||||
</Text>
|
||||
</Stack>
|
||||
)}
|
||||
|
||||
{!props.partial && (
|
||||
<PortfolioStructure
|
||||
key={props.accountId}
|
||||
@@ -69,70 +105,73 @@ const AccountPageLoaded: FC<AccountPageLoadedProps> = (props) => {
|
||||
/>
|
||||
)}
|
||||
|
||||
<Accordion
|
||||
classNames={{
|
||||
chevron: classes.accordionChevron,
|
||||
control: classes.accordionControl,
|
||||
label: classes.accordionLabel,
|
||||
panel: classes.accordionPanel,
|
||||
}}
|
||||
defaultValue={defaultValue}
|
||||
multiple
|
||||
<Tabs
|
||||
classNames={{ list: classes.tabsList }}
|
||||
defaultValue="positions"
|
||||
keepMounted={false}
|
||||
>
|
||||
{props.positionSections.map(section => (
|
||||
<Accordion.Item key={section.kind} value={section.kind}>
|
||||
<Accordion.Control>
|
||||
{t(positionSectionTitleKey(section.kind))}
|
||||
</Accordion.Control>
|
||||
<Tabs.List aria-label={t("portfolio.sections")} mb="md">
|
||||
<Tabs.Tab value="positions">
|
||||
{t("portfolio.tabs.positions")}
|
||||
</Tabs.Tab>
|
||||
|
||||
<Accordion.Panel>
|
||||
<PositionSection section={section} />
|
||||
</Accordion.Panel>
|
||||
</Accordion.Item>
|
||||
))}
|
||||
<Tabs.Tab value="cash">{t("portfolio.tabs.cash")}</Tabs.Tab>
|
||||
|
||||
<Accordion.Item value="cash">
|
||||
<Accordion.Control>{t("portfolio.cashBalances")}</Accordion.Control>
|
||||
<Tabs.Tab value="operations">
|
||||
{t("portfolio.tabs.operations")}
|
||||
</Tabs.Tab>
|
||||
|
||||
<Accordion.Panel>
|
||||
<CurrencyCards amounts={props.portfolio.cashBalances} />
|
||||
</Accordion.Panel>
|
||||
</Accordion.Item>
|
||||
<Tabs.Tab value="events">{t("portfolio.tabs.events")}</Tabs.Tab>
|
||||
|
||||
<Accordion.Item value="recentOperations">
|
||||
<Accordion.Control>{t("ledger.recentTitle")}</Accordion.Control>
|
||||
<Tabs.Tab value="income">{t("portfolio.tabs.income")}</Tabs.Tab>
|
||||
</Tabs.List>
|
||||
|
||||
<Accordion.Panel>
|
||||
<RecentOperations operations={props.recentOperations} />
|
||||
</Accordion.Panel>
|
||||
</Accordion.Item>
|
||||
|
||||
<Accordion.Item value="recentEvents">
|
||||
<Accordion.Control>{t("events.recentTitle")}</Accordion.Control>
|
||||
|
||||
<Accordion.Panel>
|
||||
<BrokerageEventsPreview events={props.recentEvents} />
|
||||
</Accordion.Panel>
|
||||
</Accordion.Item>
|
||||
|
||||
<Accordion.Item value="incomeSummary">
|
||||
<Accordion.Control>{t("income.summaryTitle")}</Accordion.Control>
|
||||
|
||||
<Accordion.Panel>
|
||||
{!!props.incomeSummary && (
|
||||
<IncomeSummary {...props.incomeSummary} />
|
||||
<Tabs.Panel value="positions">
|
||||
<Stack gap="md">
|
||||
{props.positionSections.length > 1 && (
|
||||
<div className={classes.positionFilter}>
|
||||
<SegmentedControl
|
||||
aria-label={t("portfolio.positionFilter")}
|
||||
data={filterOptions}
|
||||
onChange={setFilter}
|
||||
value={activeSection?.kind}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</Accordion.Panel>
|
||||
</Accordion.Item>
|
||||
</Accordion>
|
||||
|
||||
{activeSection && <PositionSection section={activeSection} />}
|
||||
</Stack>
|
||||
</Tabs.Panel>
|
||||
|
||||
<Tabs.Panel value="cash">
|
||||
<CurrencyCards amounts={props.portfolio.cashBalances} />
|
||||
</Tabs.Panel>
|
||||
|
||||
<Tabs.Panel value="operations">
|
||||
<RecentOperations
|
||||
moreLabel={t("portfolio.more")}
|
||||
operations={props.recentOperations}
|
||||
/>
|
||||
</Tabs.Panel>
|
||||
|
||||
<Tabs.Panel value="events">
|
||||
<BrokerageEventsPreview
|
||||
events={props.recentEvents}
|
||||
moreLabel={t("portfolio.more")}
|
||||
/>
|
||||
</Tabs.Panel>
|
||||
|
||||
<Tabs.Panel value="income">
|
||||
{!!props.incomeSummary && (
|
||||
<IncomeSummary
|
||||
{...props.incomeSummary}
|
||||
moreLabel={t("portfolio.more")}
|
||||
/>
|
||||
)}
|
||||
</Tabs.Panel>
|
||||
</Tabs>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
function positionSectionTitleKey(kind: AccountPagePositionSection["kind"]) {
|
||||
if (kind === "stocks") return "portfolio.stocks";
|
||||
|
||||
return kind === "bonds" ? "portfolio.bonds" : "portfolio.otherPositions";
|
||||
}
|
||||
|
||||
export { AccountPageLoaded };
|
||||
|
||||
Reference in New Issue
Block a user