refactor: tighten types and reuse instrument UI
Frontend verify / commits (pull_request) Successful in 24s
Frontend verify / security (pull_request) Failing after 9s
Frontend verify / verify (pull_request) Skipped

This commit is contained in:
2026-09-29 06:15:31 +03:00
parent b404c1802a
commit 7c28a423dd
67 changed files with 380 additions and 347 deletions
+2 -2
View File
@@ -403,7 +403,7 @@ describe("banking API", () => {
);
await waitFor(() =>
expect(result.current.categories.data?.[0].name).toBe("Food"),
expect(result.current.categories.data?.[0]?.name).toBe("Food"),
);
await act(() =>
result.current.updateCategory.mutateAsync({
@@ -412,7 +412,7 @@ describe("banking API", () => {
}),
);
await waitFor(() =>
expect(result.current.categories.data?.[0].name).toBe("Groceries"),
expect(result.current.categories.data?.[0]?.name).toBe("Groceries"),
);
await act(() =>
result.current.updateOperation.mutateAsync({
+10 -10
View File
@@ -46,16 +46,16 @@ function useBankMutationOptions() {
return {
onError: (error: unknown) => {
const errorCode = apiErrorCode(error);
if (
[
ApiErrorCode.BankDependentRefunds,
ApiErrorCode.BankIntentConflict,
ApiErrorCode.BankRefundLimit,
ApiErrorCode.BankVersionConflict,
ApiErrorCode.BankWriteConflict,
].some(code => isApiErrorCode(errorCode, code))
)
return invalidate();
return [
ApiErrorCode.BankDependentRefunds,
ApiErrorCode.BankIntentConflict,
ApiErrorCode.BankRefundLimit,
ApiErrorCode.BankVersionConflict,
ApiErrorCode.BankWriteConflict,
].some(code => isApiErrorCode(errorCode, code))
? invalidate()
: undefined;
},
onSuccess: invalidate,
retry: false,
+2 -1
View File
@@ -143,7 +143,7 @@ function useBankOperations(
return !isAll
&& previousQuery?.queryKey.length === options.queryKey.length
&& previous?.path?.id === id
&& previous?.path.id === id
&& previous?.query?.month === query.month
&& previous?.query?.from === query.from
&& previous?.query?.to === query.to
@@ -165,6 +165,7 @@ function useBankOperations(
// eslint-disable-next-line @typescript-eslint/only-throw-error -- Preserve generated ProblemDetails for apiErrorCode and field-error consumers.
if (error) throw error;
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- An empty HTTP body can violate the generated response contract.
if (!data) throw new Error("Missing bank operations response");
if (!isAll) return data;
items.push(...data.items);
@@ -1,4 +1,4 @@
import { Button, Group, Modal, Stack, Text } from "@mantine/core";
import { Button, Fieldset, Group, Modal, Stack, Text } from "@mantine/core";
import { type FC } from "react";
import { FormProvider } from "react-hook-form";
@@ -6,7 +6,6 @@ import { formatDate } from "@/shared/lib";
import { type BankCard } from "../../api/queries";
import { BankFeedback } from "../BankFeedback";
import classes from "../BankForms.module.css";
import { BankCardFields } from "./BankCardForm.view";
import { useBankCardFormVm } from "./BankCardForm.vm";
@@ -40,7 +39,7 @@ const BankCardForm: FC<BankCardFormProps> = (props) => {
>
<FormProvider {...methods}>
<form onSubmit={onSubmit}>
<fieldset className={classes.fieldset} disabled={isPending}>
<Fieldset disabled={isPending} variant="unstyled">
<Stack>
<BankCardFields archived={Boolean(props.card?.archivedAt)} />
@@ -80,7 +79,7 @@ const BankCardForm: FC<BankCardFormProps> = (props) => {
</Button>
</Group>
</Stack>
</fieldset>
</Fieldset>
</form>
</FormProvider>
</Modal>
@@ -1,7 +1,7 @@
import type { DateInput } from "@mantine/dates";
import type { ComponentProps } from "react";
import type { Control } from "react-hook-form";
import { DateInput } from "@mantine/dates";
import { useController } from "react-hook-form";
import { bankDateInputValue } from "../../model/form-validation";
@@ -1,6 +0,0 @@
.fieldset {
border: 0;
padding: 0;
margin: 0;
min-width: 0;
}
@@ -78,6 +78,8 @@ const BankSummaryViewCategories: FC<BankSummaryViewProps> = (props) => {
</LoadingSkeleton>
);
}
return null;
};
const BankSummaryView: FC<BankSummaryViewProps> = (props) => {
@@ -8,7 +8,7 @@ import {
waitFor,
} from "@testing-library/react";
import { http, HttpResponse } from "msw";
import { describe, expect, it } from "vitest";
import { assert, describe, expect, it } from "vitest";
import { configureAuthClient } from "@/shared/api";
import { createTestQueryClient } from "@/test/query";
@@ -78,15 +78,16 @@ describe("CategoryManager", () => {
name: "Переименовать",
});
assert.isDefined(renameButtons[0]);
fireEvent.click(renameButtons[0]);
fireEvent.click(screen.getByRole("button", { name: "Сохранить" }));
fireEvent.click(
await screen.findByRole("button", { name: "Перечитать запись" }),
);
await waitFor(() => expect(reads).toBe(3));
fireEvent.click(
screen.getAllByRole("button", { name: "Переименовать" })[1],
);
const secondRename = screen.getAllByRole("button", { name: "Переименовать" })[1];
assert.isDefined(secondRename);
fireEvent.click(secondRename);
fireEvent.change(screen.getByLabelText(/Название категории/), {
target: { value: "Новая вторая" },
});
@@ -1,6 +1,6 @@
import {
Badge,
import { Badge,
Button,
Fieldset,
Group,
Modal,
Stack,
@@ -14,7 +14,6 @@ import { useTranslation } from "react-i18next";
import { bankOperationAppearance } from "../../model/operation-appearance";
import { BankFeedback } from "../BankFeedback";
import classes from "../BankForms.module.css";
import { useCategoryManagerVm } from "./CategoryManager.vm";
type CategoryManagerProps = {
@@ -76,7 +75,7 @@ const CategoryManager: FC<CategoryManagerProps> = (props) => {
>
<FormProvider {...methods}>
<form onSubmit={onSubmit}>
<fieldset className={classes.fieldset} disabled={isPending}>
<Fieldset disabled={isPending} variant="unstyled">
<Stack>
{categoryRows?.map((category) => {
return (
@@ -149,7 +148,7 @@ const CategoryManager: FC<CategoryManagerProps> = (props) => {
</Button>
</Group>
</Stack>
</fieldset>
</Fieldset>
</form>
</FormProvider>
</Modal>
@@ -34,7 +34,7 @@ const BrokerageEventCard: FC<BrokerageEventCardProps> = (props) => {
<EntryCard
imageSrc={instrumentLogoUrl(event.logoName)}
initial={(
(event.ticker ?? event.displayName).trim()[0] ?? "?"
event.ticker.trim()[0] ?? "?"
).toUpperCase()}
metadata={metadata}
title={[t(`events.type.${event.type}`), event.displayName].join(" · ")}