feat(events): add date filters to upcoming events
This commit is contained in:
@@ -9,12 +9,15 @@ type EventsEndpoint =
|
|||||||
type BrokerageEvent =
|
type BrokerageEvent =
|
||||||
EventsEndpoint["responses"][200]["content"]["application/json"]["events"][number];
|
EventsEndpoint["responses"][200]["content"]["application/json"]["events"][number];
|
||||||
|
|
||||||
function useBrokerageEvents(accountId: string | undefined) {
|
function useBrokerageEvents(
|
||||||
|
accountId: string | undefined,
|
||||||
|
query: EventsEndpoint["parameters"]["query"] = {},
|
||||||
|
) {
|
||||||
return useAuthenticatedQuery({
|
return useAuthenticatedQuery({
|
||||||
...apiQueryClient.queryOptions(
|
...apiQueryClient.queryOptions(
|
||||||
"get",
|
"get",
|
||||||
"/api/v1/brokerage-accounts/{accountId}/events",
|
"/api/v1/brokerage-accounts/{accountId}/events",
|
||||||
{ params: { path: { accountId: accountId ?? "" } } },
|
{ params: { path: { accountId: accountId ?? "" }, query } },
|
||||||
),
|
),
|
||||||
enabled: Boolean(accountId),
|
enabled: Boolean(accountId),
|
||||||
staleTime: 30_000,
|
staleTime: 30_000,
|
||||||
|
|||||||
@@ -0,0 +1,106 @@
|
|||||||
|
import { MantineProvider } from "@mantine/core";
|
||||||
|
import { QueryClientProvider } from "@tanstack/react-query";
|
||||||
|
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
|
||||||
|
import { HttpResponse, http } from "msw";
|
||||||
|
import type { FC } from "react";
|
||||||
|
import {
|
||||||
|
MemoryRouter,
|
||||||
|
Route,
|
||||||
|
Routes,
|
||||||
|
useLocation,
|
||||||
|
useNavigate,
|
||||||
|
} from "react-router";
|
||||||
|
import { describe, expect, it, vi } from "vitest";
|
||||||
|
import { configureAuthClient } from "@/shared/api";
|
||||||
|
import { createTestQueryClient } from "@/test/query";
|
||||||
|
import { server } from "@/test/server";
|
||||||
|
import { AccountEventsPage } from "./AccountEventsPage";
|
||||||
|
|
||||||
|
const Navigation: FC = () => {
|
||||||
|
const location = useLocation();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<output aria-label="URL">{location.search}</output>
|
||||||
|
<button type="button" onClick={() => navigate(-1)}>
|
||||||
|
History back
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
describe("Upcoming event date filters", () => {
|
||||||
|
it.each(["account-1", "all"])(
|
||||||
|
"applies dates only on submit, restores URL dates, and resets from empty results for %s",
|
||||||
|
async (accountId) => {
|
||||||
|
configureAuthClient({
|
||||||
|
accessToken: () => "token",
|
||||||
|
refresh: async () => true,
|
||||||
|
clear: vi.fn(),
|
||||||
|
});
|
||||||
|
const searches: string[] = [];
|
||||||
|
server.use(
|
||||||
|
http.get(
|
||||||
|
"*/api/v1/brokerage-accounts/:accountId/events",
|
||||||
|
({ request }) => {
|
||||||
|
searches.push(new URL(request.url).search);
|
||||||
|
return HttpResponse.json({
|
||||||
|
accountId,
|
||||||
|
calculatedAt: "2099-01-01T00:00:00Z",
|
||||||
|
events: [],
|
||||||
|
incomeTotalsByCurrency: [],
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
render(
|
||||||
|
<QueryClientProvider client={createTestQueryClient()}>
|
||||||
|
<MantineProvider>
|
||||||
|
<MemoryRouter
|
||||||
|
initialEntries={[
|
||||||
|
`/accounts/${accountId}/events?from=2099-02-01&to=2099-02-28`,
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Navigation />
|
||||||
|
<Routes>
|
||||||
|
<Route
|
||||||
|
path="/accounts/:accountId/events"
|
||||||
|
element={<AccountEventsPage />}
|
||||||
|
/>
|
||||||
|
</Routes>
|
||||||
|
</MemoryRouter>
|
||||||
|
</MantineProvider>
|
||||||
|
</QueryClientProvider>,
|
||||||
|
);
|
||||||
|
const from = await screen.findByLabelText("С");
|
||||||
|
const to = screen.getByLabelText("По");
|
||||||
|
expect(from).toHaveValue("01.02.2099");
|
||||||
|
expect(to).toHaveValue("28.02.2099");
|
||||||
|
expect(screen.getByRole("button", { name: "Применить" })).toBeDisabled();
|
||||||
|
expect(searches).toEqual(["?from=2099-02-01&to=2099-02-28"]);
|
||||||
|
fireEvent.change(from, { target: { value: "10.02.2099" } });
|
||||||
|
fireEvent.blur(from);
|
||||||
|
expect(searches).toHaveLength(1);
|
||||||
|
fireEvent.click(screen.getByRole("button", { name: "Применить" }));
|
||||||
|
await waitFor(() =>
|
||||||
|
expect(searches.at(-1)).toBe("?from=2099-02-10&to=2099-02-28"),
|
||||||
|
);
|
||||||
|
expect(await screen.findByLabelText("С")).toHaveValue("10.02.2099");
|
||||||
|
fireEvent.click(screen.getByRole("button", { name: "History back" }));
|
||||||
|
await waitFor(() =>
|
||||||
|
expect(screen.getByLabelText("С")).toHaveValue("01.02.2099"),
|
||||||
|
);
|
||||||
|
expect(screen.getByLabelText("URL")).toHaveTextContent(
|
||||||
|
"?from=2099-02-01&to=2099-02-28",
|
||||||
|
);
|
||||||
|
fireEvent.click(screen.getByRole("button", { name: "Сбросить" }));
|
||||||
|
await waitFor(() => expect(searches.at(-1)).toBe(""));
|
||||||
|
expect(await screen.findByLabelText("С")).toHaveValue("");
|
||||||
|
expect(screen.getByLabelText("По")).toHaveValue("");
|
||||||
|
expect(screen.getByRole("button", { name: "Применить" })).toBeDisabled();
|
||||||
|
expect(
|
||||||
|
screen.getByText("За выбранный период событий не найдено"),
|
||||||
|
).toBeVisible();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
@@ -49,7 +49,7 @@ describe("AccountEventsPage", () => {
|
|||||||
</MemoryRouter>
|
</MemoryRouter>
|
||||||
</MantineProvider>,
|
</MantineProvider>,
|
||||||
);
|
);
|
||||||
expect(query).toHaveBeenCalledWith("selected-account");
|
expect(query).toHaveBeenCalledWith("selected-account", {});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("keeps the loading fallback when no result or account is available", () => {
|
it("keeps the loading fallback when no result or account is available", () => {
|
||||||
@@ -60,7 +60,7 @@ describe("AccountEventsPage", () => {
|
|||||||
refetch: vi.fn(),
|
refetch: vi.fn(),
|
||||||
} as never);
|
} as never);
|
||||||
renderPage();
|
renderPage();
|
||||||
expect(query).toHaveBeenCalledWith(undefined);
|
expect(query).toHaveBeenCalledWith(undefined, {});
|
||||||
expect(screen.getByRole("status", { name: "Загрузка" })).toHaveAttribute(
|
expect(screen.getByRole("status", { name: "Загрузка" })).toHaveAttribute(
|
||||||
"aria-busy",
|
"aria-busy",
|
||||||
"true",
|
"true",
|
||||||
@@ -129,7 +129,7 @@ describe("AccountEventsPage", () => {
|
|||||||
const refetch = vi.fn();
|
const refetch = vi.fn();
|
||||||
const { rerender } = renderPage();
|
const { rerender } = renderPage();
|
||||||
expect(
|
expect(
|
||||||
screen.getByText("На ближайшие 30 дней событий не найдено"),
|
screen.getByText("За выбранный период событий не найдено"),
|
||||||
).toBeInTheDocument();
|
).toBeInTheDocument();
|
||||||
query.mockReturnValue({
|
query.mockReturnValue({
|
||||||
data: undefined,
|
data: undefined,
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import type { FC } from "react";
|
import type { FC } from "react";
|
||||||
import { useAccountEventsPageViewModel } from "./AccountEventsPage.vm";
|
import { useAccountEventsPageViewModel } from "./AccountEventsPage.vm";
|
||||||
import { AccountEventsPageEmpty } from "./components/AccountEventsPageEmpty.view";
|
|
||||||
import { AccountEventsPageError } from "./components/AccountEventsPageError.view";
|
import { AccountEventsPageError } from "./components/AccountEventsPageError.view";
|
||||||
import { AccountEventsPageLoaded } from "./components/AccountEventsPageLoaded.view";
|
import { AccountEventsPageLoaded } from "./components/AccountEventsPageLoaded.view";
|
||||||
import { AccountEventsPageLoading } from "./components/AccountEventsPageLoading.view";
|
import { AccountEventsPageLoading } from "./components/AccountEventsPageLoading.view";
|
||||||
@@ -12,8 +11,6 @@ const AccountEventsPage: FC<AccountEventsPageProps> = (props) => {
|
|||||||
if (viewModel.state === "loading") return <AccountEventsPageLoading />;
|
if (viewModel.state === "loading") return <AccountEventsPageLoading />;
|
||||||
if (viewModel.state === "error")
|
if (viewModel.state === "error")
|
||||||
return <AccountEventsPageError retry={viewModel.retry} />;
|
return <AccountEventsPageError retry={viewModel.retry} />;
|
||||||
if (viewModel.state === "empty")
|
|
||||||
return <AccountEventsPageEmpty {...viewModel} />;
|
|
||||||
return <AccountEventsPageLoaded {...viewModel} />;
|
return <AccountEventsPageLoaded {...viewModel} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,40 +1,81 @@
|
|||||||
import { useParams } from "react-router";
|
import { useEffect, useState } from "react";
|
||||||
|
import { useParams, useSearchParams } from "react-router";
|
||||||
import {
|
import {
|
||||||
type BrokerageEvent,
|
type BrokerageEvent,
|
||||||
useBrokerageEvents,
|
useBrokerageEvents,
|
||||||
} from "@/entities/brokerage-event";
|
} from "@/entities/brokerage-event";
|
||||||
|
|
||||||
type EventsRefreshState = {
|
|
||||||
refreshing?: boolean;
|
|
||||||
refreshError?: boolean;
|
|
||||||
retry?: () => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
type AccountEventsPageViewModel =
|
type AccountEventsPageViewModel =
|
||||||
| { state: "loading" }
|
| { state: "loading" }
|
||||||
| ({ state: "empty" } & EventsRefreshState)
|
|
||||||
| { state: "error"; retry: () => void }
|
| { state: "error"; retry: () => void }
|
||||||
| ({ state: "loaded"; events: BrokerageEvent[] } & EventsRefreshState);
|
| {
|
||||||
|
state: "loaded";
|
||||||
|
events: BrokerageEvent[];
|
||||||
|
refreshing: boolean;
|
||||||
|
refreshError: boolean;
|
||||||
|
retry: () => void;
|
||||||
|
draftFrom: string | null;
|
||||||
|
draftTo: string | null;
|
||||||
|
canApply: boolean;
|
||||||
|
onDraftFromChange: (value: string | null) => void;
|
||||||
|
onDraftToChange: (value: string | null) => void;
|
||||||
|
onApply: () => void;
|
||||||
|
onReset: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
function useAccountEventsPageViewModel(
|
function useAccountEventsPageViewModel(
|
||||||
scopeId?: string,
|
scopeId?: string,
|
||||||
): AccountEventsPageViewModel {
|
): AccountEventsPageViewModel {
|
||||||
const { accountId: routeAccountId } = useParams();
|
const { accountId: routeAccountId } = useParams();
|
||||||
const accountId = scopeId ?? routeAccountId;
|
const accountId = scopeId ?? routeAccountId;
|
||||||
const events = useBrokerageEvents(accountId);
|
const [searchParams, setSearchParams] = useSearchParams();
|
||||||
|
const appliedFrom = searchParams.get("from");
|
||||||
|
const appliedTo = searchParams.get("to");
|
||||||
|
const [draftFrom, setDraftFrom] = useState<string | null>(appliedFrom);
|
||||||
|
const [draftTo, setDraftTo] = useState<string | null>(appliedTo);
|
||||||
|
useEffect(() => {
|
||||||
|
setDraftFrom(appliedFrom);
|
||||||
|
setDraftTo(appliedTo);
|
||||||
|
}, [appliedFrom, appliedTo]);
|
||||||
|
const events = useBrokerageEvents(accountId, {
|
||||||
|
...(appliedFrom ? { from: appliedFrom } : {}),
|
||||||
|
...(appliedTo ? { to: appliedTo } : {}),
|
||||||
|
});
|
||||||
if (events.isLoading) return { state: "loading" };
|
if (events.isLoading) return { state: "loading" };
|
||||||
if (events.isError && !events.data)
|
if (events.isError && !events.data)
|
||||||
return { state: "error", retry: () => events.refetch() };
|
return {
|
||||||
|
state: "error",
|
||||||
|
retry: () => {
|
||||||
|
void events.refetch();
|
||||||
|
},
|
||||||
|
};
|
||||||
if (!events.data) return { state: "loading" };
|
if (!events.data) return { state: "loading" };
|
||||||
const refresh = {
|
return {
|
||||||
|
state: "loaded",
|
||||||
|
events: events.data.events,
|
||||||
refreshing: events.isFetching,
|
refreshing: events.isFetching,
|
||||||
refreshError: events.isError,
|
refreshError: events.isError,
|
||||||
retry: () => {
|
retry: () => {
|
||||||
void events.refetch();
|
void events.refetch();
|
||||||
},
|
},
|
||||||
|
draftFrom,
|
||||||
|
draftTo,
|
||||||
|
canApply:
|
||||||
|
(draftFrom !== appliedFrom || draftTo !== appliedTo) &&
|
||||||
|
(!draftFrom || !draftTo || draftFrom <= draftTo),
|
||||||
|
onDraftFromChange: setDraftFrom,
|
||||||
|
onDraftToChange: setDraftTo,
|
||||||
|
onApply: () =>
|
||||||
|
setSearchParams({
|
||||||
|
...(draftFrom ? { from: draftFrom } : {}),
|
||||||
|
...(draftTo ? { to: draftTo } : {}),
|
||||||
|
}),
|
||||||
|
onReset: () => {
|
||||||
|
setDraftFrom(null);
|
||||||
|
setDraftTo(null);
|
||||||
|
setSearchParams({});
|
||||||
|
},
|
||||||
};
|
};
|
||||||
if (!events.data.events.length) return { state: "empty", ...refresh };
|
|
||||||
return { state: "loaded", events: events.data.events, ...refresh };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export { type EventsRefreshState, useAccountEventsPageViewModel };
|
export { type AccountEventsPageViewModel, useAccountEventsPageViewModel };
|
||||||
|
|||||||
@@ -1,28 +1,11 @@
|
|||||||
import { Alert, Button, Progress, Stack, Text, Title } from "@mantine/core";
|
|
||||||
import type { FC } from "react";
|
import type { FC } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { StateView } from "@/shared/ui";
|
import { StateView } from "@/shared/ui";
|
||||||
import type { EventsRefreshState } from "../AccountEventsPage.vm";
|
|
||||||
|
|
||||||
const AccountEventsPageEmpty: FC<EventsRefreshState> = (props) => {
|
const AccountEventsPageEmpty: FC = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
return (
|
return (
|
||||||
<Stack gap="lg">
|
|
||||||
<Title order={1}>{t("events.title")}</Title>
|
|
||||||
{props.refreshing ? (
|
|
||||||
<Progress value={100} animated aria-label={t("accounts.refreshing")} />
|
|
||||||
) : null}
|
|
||||||
{props.refreshError ? (
|
|
||||||
<Alert color="yellow">
|
|
||||||
{t("accounts.refreshError")}
|
|
||||||
<Button variant="subtle" onClick={props.retry}>
|
|
||||||
{t("accounts.retry")}
|
|
||||||
</Button>
|
|
||||||
</Alert>
|
|
||||||
) : null}
|
|
||||||
<Text c="dimmed">{t("events.hint")}</Text>
|
|
||||||
<StateView title={t("events.emptyTitle")} text={t("events.emptyText")} />
|
<StateView title={t("events.emptyTitle")} text={t("events.emptyText")} />
|
||||||
</Stack>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,33 @@
|
|||||||
import { Alert, Button, Progress, Stack, Text, Title } from "@mantine/core";
|
import {
|
||||||
|
Alert,
|
||||||
|
Button,
|
||||||
|
Group,
|
||||||
|
Progress,
|
||||||
|
Stack,
|
||||||
|
Text,
|
||||||
|
Title,
|
||||||
|
} from "@mantine/core";
|
||||||
|
import { DateInput } from "@mantine/dates";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
import customParseFormat from "dayjs/plugin/customParseFormat";
|
||||||
import type { FC } from "react";
|
import type { FC } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import {
|
import { BrokerageEventCard } from "@/entities/brokerage-event";
|
||||||
type BrokerageEvent,
|
import type { AccountEventsPageViewModel } from "../AccountEventsPage.vm";
|
||||||
BrokerageEventCard,
|
import { AccountEventsPageEmpty } from "./AccountEventsPageEmpty.view";
|
||||||
} from "@/entities/brokerage-event";
|
|
||||||
import type { EventsRefreshState } from "../AccountEventsPage.vm";
|
|
||||||
|
|
||||||
type AccountEventsPageLoadedProps = EventsRefreshState & {
|
dayjs.extend(customParseFormat);
|
||||||
events: BrokerageEvent[];
|
|
||||||
|
const parseDate = (value: string) => {
|
||||||
|
const date = dayjs(value, "DD.MM.YYYY", true);
|
||||||
|
return date.isValid() ? date.format("YYYY-MM-DD") : null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type AccountEventsPageLoadedProps = Extract<
|
||||||
|
AccountEventsPageViewModel,
|
||||||
|
{ state: "loaded" }
|
||||||
|
>;
|
||||||
|
|
||||||
const AccountEventsPageLoaded: FC<AccountEventsPageLoadedProps> = (props) => {
|
const AccountEventsPageLoaded: FC<AccountEventsPageLoadedProps> = (props) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
return (
|
return (
|
||||||
@@ -28,6 +45,37 @@ const AccountEventsPageLoaded: FC<AccountEventsPageLoadedProps> = (props) => {
|
|||||||
</Alert>
|
</Alert>
|
||||||
) : null}
|
) : null}
|
||||||
<Text c="dimmed">{t("events.hint")}</Text>
|
<Text c="dimmed">{t("events.hint")}</Text>
|
||||||
|
<Group align="end">
|
||||||
|
<DateInput
|
||||||
|
label={t("ledger.from")}
|
||||||
|
value={props.draftFrom}
|
||||||
|
onChange={props.onDraftFromChange}
|
||||||
|
maxDate={props.draftTo ?? undefined}
|
||||||
|
valueFormat="DD.MM.YYYY"
|
||||||
|
dateParser={parseDate}
|
||||||
|
allowDeselect
|
||||||
|
popoverProps={{ middlewares: { shift: { crossAxis: true } } }}
|
||||||
|
/>
|
||||||
|
<DateInput
|
||||||
|
label={t("ledger.to")}
|
||||||
|
value={props.draftTo}
|
||||||
|
onChange={props.onDraftToChange}
|
||||||
|
minDate={props.draftFrom ?? undefined}
|
||||||
|
valueFormat="DD.MM.YYYY"
|
||||||
|
dateParser={parseDate}
|
||||||
|
allowDeselect
|
||||||
|
popoverProps={{ middlewares: { shift: { crossAxis: true } } }}
|
||||||
|
/>
|
||||||
|
</Group>
|
||||||
|
<Group>
|
||||||
|
<Button onClick={props.onApply} disabled={!props.canApply}>
|
||||||
|
{t("ledger.apply")}
|
||||||
|
</Button>
|
||||||
|
<Button variant="default" onClick={props.onReset}>
|
||||||
|
{t("ledger.reset")}
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
{props.events.length ? (
|
||||||
<Stack gap="sm">
|
<Stack gap="sm">
|
||||||
{props.events.map((event) => (
|
{props.events.map((event) => (
|
||||||
<BrokerageEventCard
|
<BrokerageEventCard
|
||||||
@@ -44,6 +92,9 @@ const AccountEventsPageLoaded: FC<AccountEventsPageLoadedProps> = (props) => {
|
|||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</Stack>
|
</Stack>
|
||||||
|
) : (
|
||||||
|
<AccountEventsPageEmpty />
|
||||||
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Skeleton } from "@mantine/core";
|
import { Group, Skeleton } from "@mantine/core";
|
||||||
import type { FC } from "react";
|
import type { FC } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { LoadingSkeleton } from "@/shared/ui";
|
import { LoadingSkeleton } from "@/shared/ui";
|
||||||
@@ -9,6 +9,14 @@ const AccountEventsPageLoading: FC = () => {
|
|||||||
<LoadingSkeleton label={t("states.loadingTitle")}>
|
<LoadingSkeleton label={t("states.loadingTitle")}>
|
||||||
<Skeleton height={32} width="40%" />
|
<Skeleton height={32} width="40%" />
|
||||||
<Skeleton height={18} width="70%" />
|
<Skeleton height={18} width="70%" />
|
||||||
|
<Group>
|
||||||
|
<Skeleton height={60} width={220} />
|
||||||
|
<Skeleton height={60} width={220} />
|
||||||
|
</Group>
|
||||||
|
<Group>
|
||||||
|
<Skeleton height={36} width={116} />
|
||||||
|
<Skeleton height={36} width={108} />
|
||||||
|
</Group>
|
||||||
{[0, 1, 2, 3].map((index) => (
|
{[0, 1, 2, 3].map((index) => (
|
||||||
<Skeleton key={index} height={82} radius="md" />
|
<Skeleton key={index} height={82} radius="md" />
|
||||||
))}
|
))}
|
||||||
|
|||||||
Vendored
+12
-2
@@ -3004,7 +3004,12 @@ export interface operations {
|
|||||||
};
|
};
|
||||||
UpcomingEventsController_all: {
|
UpcomingEventsController_all: {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: {
|
||||||
|
/** @description Inclusive start date (YYYY-MM-DD). Defaults to today in UTC. */
|
||||||
|
from?: string;
|
||||||
|
/** @description Inclusive end date (YYYY-MM-DD). Defaults to 30 days after from. */
|
||||||
|
to?: string;
|
||||||
|
};
|
||||||
header?: never;
|
header?: never;
|
||||||
path?: never;
|
path?: never;
|
||||||
cookie?: never;
|
cookie?: never;
|
||||||
@@ -3068,7 +3073,12 @@ export interface operations {
|
|||||||
};
|
};
|
||||||
UpcomingEventsController_get: {
|
UpcomingEventsController_get: {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: {
|
||||||
|
/** @description Inclusive start date (YYYY-MM-DD). Defaults to today in UTC. */
|
||||||
|
from?: string;
|
||||||
|
/** @description Inclusive end date (YYYY-MM-DD). Defaults to 30 days after from. */
|
||||||
|
to?: string;
|
||||||
|
};
|
||||||
header?: never;
|
header?: never;
|
||||||
path: {
|
path: {
|
||||||
accountId: string;
|
accountId: string;
|
||||||
|
|||||||
@@ -2544,7 +2544,28 @@
|
|||||||
"/api/v1/brokerage-accounts/all/events": {
|
"/api/v1/brokerage-accounts/all/events": {
|
||||||
"get": {
|
"get": {
|
||||||
"operationId": "UpcomingEventsController_all",
|
"operationId": "UpcomingEventsController_all",
|
||||||
"parameters": [],
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "from",
|
||||||
|
"required": false,
|
||||||
|
"in": "query",
|
||||||
|
"description": "Inclusive start date (YYYY-MM-DD). Defaults to today in UTC.",
|
||||||
|
"schema": {
|
||||||
|
"format": "date",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "to",
|
||||||
|
"required": false,
|
||||||
|
"in": "query",
|
||||||
|
"description": "Inclusive end date (YYYY-MM-DD). Defaults to 30 days after from.",
|
||||||
|
"schema": {
|
||||||
|
"format": "date",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
"200": {
|
"200": {
|
||||||
"description": "",
|
"description": "",
|
||||||
@@ -2628,6 +2649,26 @@
|
|||||||
"schema": {
|
"schema": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "from",
|
||||||
|
"required": false,
|
||||||
|
"in": "query",
|
||||||
|
"description": "Inclusive start date (YYYY-MM-DD). Defaults to today in UTC.",
|
||||||
|
"schema": {
|
||||||
|
"format": "date",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "to",
|
||||||
|
"required": false,
|
||||||
|
"in": "query",
|
||||||
|
"description": "Inclusive end date (YYYY-MM-DD). Defaults to 30 days after from.",
|
||||||
|
"schema": {
|
||||||
|
"format": "date",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
|
|||||||
@@ -443,7 +443,7 @@ const ru = {
|
|||||||
recentError: "Не удалось загрузить будущие события.",
|
recentError: "Не удалось загрузить будущие события.",
|
||||||
recentEmpty: "На ближайшие 30 дней событий не найдено.",
|
recentEmpty: "На ближайшие 30 дней событий не найдено.",
|
||||||
hint: "Прогноз рассчитан по текущей позиции до налогов. Покрытие зависит от доступных данных MOEX.",
|
hint: "Прогноз рассчитан по текущей позиции до налогов. Покрытие зависит от доступных данных MOEX.",
|
||||||
emptyTitle: "На ближайшие 30 дней событий не найдено",
|
emptyTitle: "За выбранный период событий не найдено",
|
||||||
emptyText:
|
emptyText:
|
||||||
"Доступные источники не сообщили о купонах, дивидендах, погашениях или офертах в этом периоде.",
|
"Доступные источники не сообщили о купонах, дивидендах, погашениях или офертах в этом периоде.",
|
||||||
errorTitle: "Не удалось загрузить будущие события",
|
errorTitle: "Не удалось загрузить будущие события",
|
||||||
|
|||||||
Reference in New Issue
Block a user