feat: default journal period to last 7 days
This commit is contained in:
@@ -20,6 +20,7 @@ import {
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { configureAuthClient } from "@/shared/api";
|
||||
import { defaultPeriod, formatDate } from "@/shared/lib";
|
||||
import { createTestQueryClient } from "@/test/query";
|
||||
import { resource } from "@/test/resource";
|
||||
import { server } from "@/test/server";
|
||||
@@ -157,6 +158,7 @@ describe("Upcoming event date filters", () => {
|
||||
it.each(["account-1", "all"])(
|
||||
"normalizes invalid links and history while preserving one-sided future dates for %s",
|
||||
async (accountId) => {
|
||||
const [defaultFrom, defaultTo] = defaultPeriod();
|
||||
const searches: string[] = [];
|
||||
server.use(
|
||||
http.get(
|
||||
@@ -195,20 +197,24 @@ describe("Upcoming event date filters", () => {
|
||||
),
|
||||
);
|
||||
expect(screen.getByLabelText("Период")).toHaveTextContent("01.02.2099");
|
||||
expect(searches).toEqual(["", "?from=2099-02-01", "?to=2099-02-28"]);
|
||||
expect(searches[0]).toBe("");
|
||||
expect(searches).toContain(`?from=${defaultFrom}&to=${defaultTo}`);
|
||||
expect(searches).toContain("?from=2099-02-01");
|
||||
expect(searches).toContain("?to=2099-02-28");
|
||||
},
|
||||
);
|
||||
|
||||
it.each(["account-1", "all"])(
|
||||
"lets users reset filters after a request failure for %s",
|
||||
async (accountId) => {
|
||||
const [defaultFrom] = defaultPeriod();
|
||||
server.use(
|
||||
http.get(
|
||||
"*/api/v1/brokerage-accounts/:accountId/events",
|
||||
({ request }) =>
|
||||
new URL(request.url).searchParams.has("from")
|
||||
? HttpResponse.json({ code: "VALIDATION_ERROR" }, { status: 400 })
|
||||
: HttpResponse.json(resource({ events: [] })),
|
||||
new URL(request.url).searchParams.get("from") === defaultFrom
|
||||
? HttpResponse.json(resource({ events: [] }))
|
||||
: HttpResponse.json({ code: "VALIDATION_ERROR" }, { status: 400 }),
|
||||
),
|
||||
);
|
||||
renderEvents(accountId, ["?from=2099-02-01"]);
|
||||
@@ -222,6 +228,7 @@ 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) => {
|
||||
const [defaultFrom, defaultTo] = defaultPeriod();
|
||||
const searches: string[] = [];
|
||||
server.use(
|
||||
http.get(
|
||||
@@ -240,38 +247,42 @@ describe("Upcoming event date filters", () => {
|
||||
},
|
||||
),
|
||||
);
|
||||
renderEvents(accountId, ["?from=2099-02-01&to=2099-02-28"]);
|
||||
renderEvents(accountId, ["?from=2026-02-01&to=2026-02-28"]);
|
||||
const from = await screen.findByLabelText("Период");
|
||||
expect(from).toHaveTextContent("01.02.2099 – 28.02.2099");
|
||||
expect(from).toHaveTextContent("01.02.2026 – 28.02.2026");
|
||||
expect(screen.getByRole("button", { name: "Применить" })).toBeDisabled();
|
||||
expect(searches).toEqual(["?from=2099-02-01&to=2099-02-28"]);
|
||||
expect(searches).toEqual(["?from=2026-02-01&to=2026-02-28"]);
|
||||
fireEvent.click(from);
|
||||
fireEvent.click(
|
||||
await screen.findByRole("button", { name: /10 February 2099/i }),
|
||||
await screen.findByRole("button", { name: /10 February 2026/i }),
|
||||
);
|
||||
fireEvent.click(
|
||||
await screen.findByRole("button", { name: /28 February 2099/i }),
|
||||
await screen.findByRole("button", { name: /28 February 2026/i }),
|
||||
);
|
||||
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(searches.at(-1)).toBe("?from=2026-02-10&to=2026-02-28"),
|
||||
);
|
||||
expect(await screen.findByLabelText("Период")).toHaveTextContent(
|
||||
"10.02.2099 – 28.02.2099",
|
||||
"10.02.2026 – 28.02.2026",
|
||||
);
|
||||
fireEvent.click(screen.getByRole("button", { name: "History back" }));
|
||||
await waitFor(() =>
|
||||
expect(screen.getByLabelText("Период")).toHaveTextContent(
|
||||
"01.02.2099 – 28.02.2099",
|
||||
"01.02.2026 – 28.02.2026",
|
||||
),
|
||||
);
|
||||
expect(screen.getByLabelText("URL")).toHaveTextContent(
|
||||
"?from=2099-02-01&to=2099-02-28",
|
||||
"?from=2026-02-01&to=2026-02-28",
|
||||
);
|
||||
fireEvent.click(screen.getByRole("button", { name: "Сбросить" }));
|
||||
await waitFor(() => expect(searches.at(-1)).toBe(""));
|
||||
expect(await screen.findByLabelText("Период")).toHaveTextContent("");
|
||||
await waitFor(() =>
|
||||
expect(searches.at(-1)).toBe(`?from=${defaultFrom}&to=${defaultTo}`),
|
||||
);
|
||||
expect(await screen.findByLabelText("Период")).toHaveTextContent(
|
||||
`${formatDate(defaultFrom)} – ${formatDate(defaultTo)}`,
|
||||
);
|
||||
expect(screen.getByRole("button", { name: "Применить" })).toBeDisabled();
|
||||
expect(
|
||||
screen.getByText("За выбранный период событий не найдено"),
|
||||
|
||||
@@ -6,6 +6,7 @@ import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type * as BrokerageEvents from "@/entities/brokerage-event";
|
||||
|
||||
import { useBrokerageEvents } from "@/entities/brokerage-event";
|
||||
import { defaultPeriod } from "@/shared/lib";
|
||||
|
||||
import { AccountEventsPage } from "./AccountEventsPage";
|
||||
|
||||
@@ -71,7 +72,10 @@ describe("AccountEventsPage", () => {
|
||||
</MemoryRouter>
|
||||
</MantineProvider>,
|
||||
);
|
||||
expect(query).toHaveBeenCalledWith("selected-account", {});
|
||||
expect(query).toHaveBeenCalledWith("selected-account", {
|
||||
from: defaultPeriod()[0],
|
||||
to: defaultPeriod()[1],
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps the loading fallback when no result or account is available", () => {
|
||||
@@ -88,7 +92,10 @@ describe("AccountEventsPage", () => {
|
||||
} as never,
|
||||
);
|
||||
renderPage();
|
||||
expect(query).toHaveBeenCalledWith("", {});
|
||||
expect(query).toHaveBeenCalledWith("", {
|
||||
from: defaultPeriod()[0],
|
||||
to: defaultPeriod()[1],
|
||||
});
|
||||
expect(screen.getByRole("status", { name: "Загрузка" })).toHaveAttribute(
|
||||
"aria-busy",
|
||||
"true",
|
||||
|
||||
@@ -6,15 +6,23 @@ import {
|
||||
useBrokerageEvents,
|
||||
} from "@/entities/brokerage-event";
|
||||
import { useScreenSync } from "@/features/tbank-connection";
|
||||
import { isValidPeriod, useInitialAvailability, useUrlPeriod } from "@/shared/lib";
|
||||
import {
|
||||
defaultPeriod,
|
||||
isValidPeriod,
|
||||
moscowDate,
|
||||
useInitialAvailability,
|
||||
useUrlPeriod,
|
||||
} from "@/shared/lib";
|
||||
|
||||
type AccountEventsPageViewModel
|
||||
= | {
|
||||
canApply: boolean;
|
||||
draftPeriod: [null | string, null | string];
|
||||
events: BrokerageEvent[];
|
||||
maxDate: string;
|
||||
onApply: () => void;
|
||||
onPeriodChange: (value: [null | string, null | string]) => void;
|
||||
onPeriodPopoverOpen: () => void;
|
||||
onReset: () => void;
|
||||
partial?: boolean;
|
||||
state: "loaded";
|
||||
@@ -38,6 +46,8 @@ function useAccountEventsPageViewModel(): AccountEventsPageViewModel {
|
||||
|
||||
const [draftFrom, draftTo] = draftPeriod;
|
||||
|
||||
const [maxDate, setMaxDate] = useState(() => moscowDate(new Date()));
|
||||
|
||||
const scope = JSON.stringify([accountId, appliedFrom, appliedTo]);
|
||||
const [previousScope, setPreviousScope] = useState(scope);
|
||||
|
||||
@@ -72,7 +82,7 @@ function useAccountEventsPageViewModel(): AccountEventsPageViewModel {
|
||||
return { state: "loading" };
|
||||
|
||||
const onReset = () => {
|
||||
setDraftPeriod([null, null]);
|
||||
setDraftPeriod([appliedFrom, appliedTo]);
|
||||
setSearchParameters({});
|
||||
};
|
||||
|
||||
@@ -92,6 +102,7 @@ function useAccountEventsPageViewModel(): AccountEventsPageViewModel {
|
||||
&& isValidPeriod(draftFrom, draftTo),
|
||||
draftPeriod,
|
||||
events: events.data.events,
|
||||
maxDate,
|
||||
onApply: () => {
|
||||
if (!isValidPeriod(draftFrom, draftTo)) return;
|
||||
setSearchParameters({
|
||||
@@ -99,7 +110,10 @@ function useAccountEventsPageViewModel(): AccountEventsPageViewModel {
|
||||
...(draftTo && { to: draftTo }),
|
||||
});
|
||||
},
|
||||
onPeriodChange: setDraftPeriod,
|
||||
onPeriodChange: value => setDraftPeriod(
|
||||
value[0] === null && value[1] === null ? defaultPeriod() : value,
|
||||
),
|
||||
onPeriodPopoverOpen: () => setMaxDate(moscowDate(new Date())),
|
||||
onReset,
|
||||
partial: events.meta?.availability !== "complete",
|
||||
state: "loaded",
|
||||
|
||||
+2
@@ -65,9 +65,11 @@ const AccountEventsPageLoaded: FC<AccountEventsPageLoadedProps> = (props) => {
|
||||
clearable
|
||||
clearButtonProps={{ "aria-label": t("ledger.clearPeriod") }}
|
||||
label={t("ledger.period")}
|
||||
maxDate={props.maxDate}
|
||||
onChange={props.onPeriodChange}
|
||||
popoverProps={{
|
||||
middlewares: { shift: { crossAxis: true } },
|
||||
onOpen: props.onPeriodPopoverOpen,
|
||||
returnFocus: true,
|
||||
}}
|
||||
type="range"
|
||||
|
||||
Reference in New Issue
Block a user