refactor: tighten types and reuse instrument UI
This commit is contained in:
@@ -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({
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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(" · ")}
|
||||
|
||||
Reference in New Issue
Block a user