chore: codify React component style

This commit is contained in:
2026-08-25 15:33:20 +03:00
parent 268acdf2e1
commit d10e1dd182
65 changed files with 711 additions and 365 deletions
@@ -1,10 +1,17 @@
import { Stack, Text, Title } from "@mantine/core";
import type { FC } from "react";
import { useTranslation } from "react-i18next";
import type { BrokerageAccount } from "@/entities/brokerage-account";
import { accountTypeKey } from "@/entities/brokerage-account";
export function AccountHeading({ account }: { account?: BrokerageAccount }) {
type AccountHeadingProps = {
account?: BrokerageAccount;
};
const AccountHeading: FC<AccountHeadingProps> = (props) => {
const { account } = props;
const { t } = useTranslation();
return (
<Stack gap={2}>
<Title order={1}>{account?.name ?? t("portfolio.title")}</Title>
@@ -15,4 +22,6 @@ export function AccountHeading({ account }: { account?: BrokerageAccount }) {
) : null}
</Stack>
);
}
};
export { AccountHeading };
@@ -1,13 +1,16 @@
import { Paper, Text } from "@mantine/core";
import type { FC } from "react";
import type { Portfolio } from "@/entities/portfolio";
import { formatMoney } from "@/entities/portfolio";
import classes from "../AccountPage.module.css";
export function CashBalances({
balances,
}: {
type CashBalancesProps = {
balances: Portfolio["cashBalances"];
}) {
};
const CashBalances: FC<CashBalancesProps> = (props) => {
const { balances } = props;
return (
<>
{balances.map((balance) => (
@@ -18,4 +21,6 @@ export function CashBalances({
))}
</>
);
}
};
export { CashBalances };
@@ -1,17 +1,18 @@
import { Alert, Button, Group, Loader, Stack, Text } from "@mantine/core";
import type { FC } from "react";
import { useTranslation } from "react-i18next";
export function PortfolioRefreshState({
refreshing,
refreshError,
retry,
}: {
type PortfolioRefreshStateProps = {
refreshing: boolean;
refreshError: boolean;
retry?: () => void;
}) {
};
const PortfolioRefreshState: FC<PortfolioRefreshStateProps> = (props) => {
const { refreshing, refreshError, retry } = props;
const { t } = useTranslation();
if (!refreshing && !refreshError) return null;
return (
<>
{refreshing ? (
@@ -34,4 +35,6 @@ export function PortfolioRefreshState({
) : null}
</>
);
}
};
export { PortfolioRefreshState };
@@ -1,13 +1,16 @@
import { Paper, SimpleGrid, Text } from "@mantine/core";
import type { FC } from "react";
import type { Portfolio } from "@/entities/portfolio";
import { formatMoney } from "@/entities/portfolio";
import classes from "../AccountPage.module.css";
export function PortfolioTotals({
totals,
}: {
type PortfolioTotalsProps = {
totals: Portfolio["totalsByCurrency"];
}) {
};
const PortfolioTotals: FC<PortfolioTotalsProps> = (props) => {
const { totals } = props;
return (
<SimpleGrid cols={{ base: 1, sm: 2, md: 3 }}>
{totals.map((total) => (
@@ -22,4 +25,6 @@ export function PortfolioTotals({
))}
</SimpleGrid>
);
}
};
export { PortfolioTotals };
@@ -1,17 +1,18 @@
import { Card, Group, Stack, Text } from "@mantine/core";
import type { FC } from "react";
import { useTranslation } from "react-i18next";
import { formatMoney } from "@/entities/portfolio";
import classes from "../AccountPage.module.css";
import type { AccountPagePosition } from "../AccountPage.vm";
import { PositionLogo } from "./PositionLogo.view";
export function PositionCard({
position,
wide = false,
}: {
type PositionCardProps = {
position: AccountPagePosition;
wide?: boolean;
}) {
};
const PositionCard: FC<PositionCardProps> = (props) => {
const { position, wide = false } = props;
const { t } = useTranslation();
const returnClass = position.returnTone
? classes[position.returnTone]
@@ -78,4 +79,6 @@ export function PositionCard({
) : null}
</Card>
);
}
};
export { PositionCard };
@@ -1,14 +1,16 @@
import { Avatar } from "@mantine/core";
import type { FC } from "react";
import { useState } from "react";
export function PositionLogo({
src,
label,
}: {
type PositionLogoProps = {
src: string | null;
label: string;
}) {
};
const PositionLogo: FC<PositionLogoProps> = (props) => {
const { src, label } = props;
const [failed, setFailed] = useState(false);
if (src && !failed)
return (
<Avatar
@@ -24,4 +26,6 @@ export function PositionLogo({
{(label.trim()[0] ?? "?").toUpperCase()}
</Avatar>
);
}
};
export { PositionLogo };
@@ -1,15 +1,17 @@
import { SimpleGrid } from "@mantine/core";
import type { FC } from "react";
import type { AccountPagePositionSection } from "../AccountPage.vm";
import { PositionCard } from "./PositionCard.view";
import { Section } from "./Section.view";
export function PositionSection({
title,
section,
}: {
type PositionSectionProps = {
title: string;
section: AccountPagePositionSection;
}) {
};
const PositionSection: FC<PositionSectionProps> = (props) => {
const { title, section } = props;
return (
<Section title={title}>
<SimpleGrid cols={section.wide ? 1 : { base: 1, sm: 2, md: 3 }}>
@@ -23,4 +25,6 @@ export function PositionSection({
</SimpleGrid>
</Section>
);
}
};
export { PositionSection };
@@ -1,4 +1,5 @@
import { Alert, Button, Paper, Text } from "@mantine/core";
import type { FC } from "react";
import { useTranslation } from "react-i18next";
import { Link } from "react-router";
import type { BrokerageEvent } from "@/entities/brokerage-event";
@@ -7,8 +8,18 @@ import classes from "../AccountPage.module.css";
import type { AccountPageRecentEvents } from "../AccountPage.vm";
import { Section } from "./Section.view";
export function RecentEvents({ events }: { events: AccountPageRecentEvents }) {
type RecentEventsProps = {
events: AccountPageRecentEvents;
};
type RecentEventProps = {
event: BrokerageEvent;
};
const RecentEvents: FC<RecentEventsProps> = (props) => {
const { events } = props;
const { t } = useTranslation();
return (
<Section title={t("events.recentTitle")}>
{events.state === "loading" ? (
@@ -33,10 +44,12 @@ export function RecentEvents({ events }: { events: AccountPageRecentEvents }) {
</Button>
</Section>
);
}
};
function RecentEvent({ event }: { event: BrokerageEvent }) {
const RecentEvent: FC<RecentEventProps> = (props) => {
const { event } = props;
const { t } = useTranslation();
return (
<Paper className={classes.cash} withBorder>
<Text>{`${t(`events.type.${event.type}`)} · ${event.displayName}`}</Text>
@@ -47,4 +60,6 @@ function RecentEvent({ event }: { event: BrokerageEvent }) {
</Text>
</Paper>
);
}
};
export { RecentEvents };
@@ -1,4 +1,5 @@
import { Alert, Button, Paper, Text } from "@mantine/core";
import type { FC } from "react";
import { useTranslation } from "react-i18next";
import { Link } from "react-router";
import { formatMoney } from "@/entities/portfolio";
@@ -6,12 +7,14 @@ import classes from "../AccountPage.module.css";
import type { AccountPageRecentOperations } from "../AccountPage.vm";
import { Section } from "./Section.view";
export function RecentOperations({
operations,
}: {
type RecentOperationsProps = {
operations: AccountPageRecentOperations;
}) {
};
const RecentOperations: FC<RecentOperationsProps> = (props) => {
const { operations } = props;
const { t } = useTranslation();
return (
<Section title={t("ledger.recentTitle")}>
{operations.state === "loading" ? (
@@ -40,4 +43,6 @@ export function RecentOperations({
</Button>
</Section>
);
}
};
export { RecentOperations };
@@ -1,16 +1,19 @@
import { Stack, Title } from "@mantine/core";
import type { FC, PropsWithChildren } from "react";
export function Section({
title,
children,
}: {
type SectionProps = PropsWithChildren<{
title: string;
children: React.ReactNode;
}) {
}>;
const Section: FC<SectionProps> = (props) => {
const { title, children } = props;
return (
<Stack gap="sm">
<Title order={2}>{title}</Title>
{children}
</Stack>
);
}
};
export { Section };