feat: add агегку events

This commit is contained in:
2026-08-14 15:50:25 +03:00
parent 719a6ed8b7
commit da55de5b50
13 changed files with 855 additions and 6 deletions
+22
View File
@@ -141,4 +141,26 @@ describe("AccountPage states", () => {
);
expect(screen.getByText("Вторая")).toBeInTheDocument();
});
it("keeps the portfolio visible when upcoming events fail", async () => {
server.use(
http.get("*/api/v1/brokerage-accounts/:accountId/events", () =>
HttpResponse.json({ code: "MOEX_UNAVAILABLE" }, { status: 503 }),
),
);
renderPage(
<AccountPageLoaded
accountId="account-1"
accessToken="token"
portfolio={portfolio}
/>,
);
await waitFor(() =>
expect(
screen.getByText("Не удалось загрузить будущие события."),
).toBeInTheDocument(),
);
expect(screen.getByText("Вторая")).toBeInTheDocument();
});
});
+55
View File
@@ -14,6 +14,10 @@ import { Link } from "react-router";
import { useBrokerageOperations } from "@/entities/broker-operation";
import type { BrokerageAccount } from "@/entities/brokerage-account";
import { accountTypeKey } from "@/entities/brokerage-account";
import {
type BrokerageEvent,
useBrokerageEvents,
} from "@/entities/brokerage-event";
import type { Portfolio } from "@/entities/portfolio";
import { formatMoney, formatSnapshotTime } from "@/entities/portfolio";
import { StateView } from "@/shared/ui";
@@ -99,6 +103,7 @@ export function AccountPageEmpty({
text={t("portfolio.emptyText")}
/>
<RecentOperations accountId={accountId} accessToken={accessToken} />
<RecentEvents accountId={accountId} accessToken={accessToken} />
</Stack>
);
}
@@ -166,6 +171,7 @@ export function AccountPageLoaded({
accountId={accountId}
accessToken={accessToken ?? null}
/>
<RecentEvents accountId={accountId} accessToken={accessToken ?? null} />
</Stack>
);
}
@@ -211,6 +217,55 @@ function RecentOperations({
</Section>
);
}
function RecentEvents({
accountId,
accessToken,
}: {
accountId: string | undefined;
accessToken: string | null;
}) {
const { t } = useTranslation();
const events = useBrokerageEvents(accountId, accessToken);
return (
<Section title={t("events.recentTitle")}>
{events.isLoading ? (
<Text c="dimmed">{t("events.recentLoading")}</Text>
) : null}
{events.isError ? (
<Alert color="red">{t("events.recentError")}</Alert>
) : null}
{events.data?.events.length === 0 ? (
<Text c="dimmed">{t("events.recentEmpty")}</Text>
) : null}
{events.data?.events.slice(0, 5).map((event) => (
<RecentEvent
key={`${event.type}-${event.date}-${event.ticker}`}
event={event}
/>
))}
<Button
component={Link}
to={`/accounts/${accountId}/events`}
variant="subtle"
>
{t("events.open")}
</Button>
</Section>
);
}
function RecentEvent({ event }: { event: BrokerageEvent }) {
const { t } = useTranslation();
return (
<Paper className={classes.cash} withBorder>
<Text>{`${t(`events.type.${event.type}`)} · ${event.displayName}`}</Text>
<Text fw={700}>
{event.amount && event.currency
? formatMoney({ currency: event.currency, value: event.amount })
: t("events.noAmount")}
</Text>
</Paper>
);
}
function PositionSection({
title,
positions,