refactor: clarify frontend module ownership
This commit is contained in:
@@ -6,6 +6,8 @@ import { apiClient, configureAuthClient } from "@/shared/api";
|
||||
import { createQueryWrapper } from "@/test/query";
|
||||
import { server } from "@/test/server";
|
||||
|
||||
import type { BankCard, CreateBankOperation } from "./types";
|
||||
|
||||
import {
|
||||
useCreateBankOperation,
|
||||
useDeleteBankOperation,
|
||||
@@ -14,8 +16,6 @@ import {
|
||||
useUpdateExpenseCategory,
|
||||
} from "./mutations";
|
||||
import {
|
||||
type BankCard,
|
||||
type CreateBankOperation,
|
||||
useBankCard,
|
||||
useBankCards,
|
||||
useBankCategories,
|
||||
@@ -106,14 +106,14 @@ function useReadModels() {
|
||||
const categories = useBankCategories();
|
||||
|
||||
const history = useBankOperations("a", {
|
||||
limit: 20,
|
||||
month: "2026-09",
|
||||
page: 2,
|
||||
from: "2026-09-01",
|
||||
limit: 20, page: 2,
|
||||
to: "2026-09-30",
|
||||
});
|
||||
|
||||
const expenses = useBankExpenses("a", { page: 1 });
|
||||
const september = useBankSummary("2026-09");
|
||||
const august = useBankSummary("2026-08", "b");
|
||||
const september = useBankSummary({ from: "2026-09-01", to: "2026-09-30" });
|
||||
const august = useBankSummary({ from: "2026-08-01", to: "2026-08-31" }, "b");
|
||||
const original = useBankOperation("original");
|
||||
|
||||
return {
|
||||
@@ -144,7 +144,8 @@ describe("banking API", () => {
|
||||
const page = Number(url.searchParams.get("page"));
|
||||
pages.push(page);
|
||||
expect(url.searchParams.get("limit")).toBe("100");
|
||||
expect(url.searchParams.get("month")).toBe("2026-09");
|
||||
expect(url.searchParams.get("from")).toBe("2026-09-01");
|
||||
expect(url.searchParams.get("to")).toBe("2026-09-30");
|
||||
if (isFail && page === 2)
|
||||
return HttpResponse.json({ code: "FAILED" }, { status: 500 });
|
||||
|
||||
@@ -158,7 +159,7 @@ describe("banking API", () => {
|
||||
);
|
||||
|
||||
const { result } = renderHook(
|
||||
() => useBankOperations("a", { limit: "all", month: "2026-09", page: 1 }),
|
||||
() => useBankOperations("a", { from: "2026-09-01", limit: "all", page: 1, to: "2026-09-30" }),
|
||||
{ wrapper: createQueryWrapper().wrapper },
|
||||
);
|
||||
|
||||
@@ -171,7 +172,7 @@ describe("banking API", () => {
|
||||
isFail = true;
|
||||
|
||||
const { result: failedResult } = renderHook(
|
||||
() => useBankOperations("a", { limit: "all", month: "2026-09", page: 1 }),
|
||||
() => useBankOperations("a", { from: "2026-09-01", limit: "all", page: 1, to: "2026-09-30" }),
|
||||
{ wrapper: createQueryWrapper().wrapper },
|
||||
);
|
||||
|
||||
@@ -179,7 +180,7 @@ describe("banking API", () => {
|
||||
expect(failedResult.current.data).toBeUndefined();
|
||||
});
|
||||
|
||||
it("uses session-aware banking endpoints and explicit month/page without T-Bank", async () => {
|
||||
it("uses session-aware banking endpoints and explicit period/page without T-Bank", async () => {
|
||||
const counts = new Map<string, number>();
|
||||
server.use(...readHandlers(counts));
|
||||
|
||||
@@ -191,12 +192,15 @@ describe("banking API", () => {
|
||||
expect(result.current.reads.every(read => read.isSuccess)).toBe(true),
|
||||
);
|
||||
expect(counts.keys()).toContain("/api/v1/bank-cards?includeArchived=true");
|
||||
expect(counts.keys()).toContain(
|
||||
"/api/v1/bank-cards/a/operations?limit=20&month=2026-09&page=2",
|
||||
);
|
||||
expect(counts.keys()).toContain(
|
||||
"/api/v1/bank-cards/summary?cardId=b&month=2026-08",
|
||||
);
|
||||
const urls = Array.from(counts.keys(), path => new URL(path, "http://localhost"));
|
||||
const history = urls.find(url => url.pathname === "/api/v1/bank-cards/a/operations");
|
||||
const summary = urls.find(url => url.pathname === "/api/v1/bank-cards/summary" && url.searchParams.get("cardId") === "b");
|
||||
expect(Object.fromEntries(history?.searchParams ?? [])).toEqual({
|
||||
from: "2026-09-01", limit: "20", page: "2", to: "2026-09-30",
|
||||
});
|
||||
expect(Object.fromEntries(summary?.searchParams ?? [])).toEqual({
|
||||
cardId: "b", from: "2026-08-01", to: "2026-08-31",
|
||||
});
|
||||
expect(counts.size).toBe(9);
|
||||
});
|
||||
|
||||
@@ -303,7 +307,7 @@ describe("banking API", () => {
|
||||
expect(result.current.query.data?.balance.value).toBe("100");
|
||||
});
|
||||
|
||||
it("refetches both cards, every month, categories, journal and source expenses after a transfer without invalidating brokerage", async () => {
|
||||
it("refetches both cards, every period, categories, journal and source expenses after a transfer without invalidating brokerage", async () => {
|
||||
const counts = new Map<string, number>();
|
||||
server.use(
|
||||
...readHandlers(counts),
|
||||
|
||||
@@ -14,7 +14,7 @@ import type {
|
||||
UpdateBankCard,
|
||||
UpdateBankOperation,
|
||||
UpdateExpenseCategory,
|
||||
} from "./queries";
|
||||
} from "./types";
|
||||
|
||||
async function mutationData<T>(
|
||||
request: Promise<{ data?: T; error?: unknown }>,
|
||||
|
||||
@@ -1,44 +1,12 @@
|
||||
import {
|
||||
apiClient,
|
||||
apiQueryClient,
|
||||
type paths,
|
||||
useAuthenticatedQuery,
|
||||
} from "@/shared/api";
|
||||
|
||||
import { moneyCents } from "../model/form-validation";
|
||||
import type { BankOperation, BankOperations, BankSummary } from "./types";
|
||||
|
||||
type BankCard
|
||||
= paths["/api/v1/bank-cards/{id}"]["get"]["responses"][200]["content"]["application/json"];
|
||||
|
||||
type BankOperation
|
||||
= paths["/api/v1/bank-cards/operations/{id}"]["get"]["responses"][200]["content"]["application/json"];
|
||||
|
||||
type BankOperations
|
||||
= paths["/api/v1/bank-cards/{id}/operations"]["get"]["responses"][200]["content"]["application/json"];
|
||||
|
||||
type BankSummary
|
||||
= paths["/api/v1/bank-cards/summary"]["get"]["responses"][200]["content"]["application/json"];
|
||||
|
||||
type CreateBankCard
|
||||
= paths["/api/v1/bank-cards"]["post"]["requestBody"]["content"]["application/json"];
|
||||
|
||||
type CreateBankOperation
|
||||
= paths["/api/v1/bank-cards/operations"]["post"]["requestBody"]["content"]["application/json"];
|
||||
|
||||
type CreateExpenseCategory
|
||||
= paths["/api/v1/bank-cards/categories"]["post"]["requestBody"]["content"]["application/json"];
|
||||
|
||||
type ExpenseCategory
|
||||
= paths["/api/v1/bank-cards/categories"]["get"]["responses"][200]["content"]["application/json"][number];
|
||||
|
||||
type UpdateBankCard
|
||||
= paths["/api/v1/bank-cards/{id}"]["patch"]["requestBody"]["content"]["application/json"];
|
||||
|
||||
type UpdateBankOperation
|
||||
= paths["/api/v1/bank-cards/operations/{id}"]["patch"]["requestBody"]["content"]["application/json"];
|
||||
|
||||
type UpdateExpenseCategory
|
||||
= paths["/api/v1/bank-cards/categories/{id}"]["patch"]["requestBody"]["content"]["application/json"];
|
||||
import { moneyCents } from "../model/money";
|
||||
|
||||
const OPERATIONS_PAGE_SIZE = 100;
|
||||
|
||||
@@ -110,7 +78,6 @@ function useBankOperations(
|
||||
query: {
|
||||
from?: string;
|
||||
limit?: "all" | number;
|
||||
month?: string;
|
||||
page: number;
|
||||
to?: string;
|
||||
},
|
||||
@@ -134,7 +101,7 @@ function useBankOperations(
|
||||
|
||||
// eslint-disable-next-line @tanstack/query/exhaustive-deps -- Generated options.queryKey already contains params; retain the shared cache identity.
|
||||
return useAuthenticatedQuery({
|
||||
enabled: Boolean(id && (query.month || (query.from && query.to))),
|
||||
enabled: Boolean(id && query.from && query.to),
|
||||
placeholderData: (
|
||||
previousData: BankOperations | undefined,
|
||||
previousQuery,
|
||||
@@ -144,7 +111,6 @@ function useBankOperations(
|
||||
return !isAll
|
||||
&& previousQuery?.queryKey.length === options.queryKey.length
|
||||
&& previous?.path.id === id
|
||||
&& previous?.query?.month === query.month
|
||||
&& previous?.query?.from === query.from
|
||||
&& previous?.query?.to === query.to
|
||||
&& previous?.query?.limit === query.limit
|
||||
@@ -179,36 +145,19 @@ function useBankOperations(
|
||||
}
|
||||
|
||||
function useBankSummary(
|
||||
period: string
|
||||
| { from: string; to: string },
|
||||
period: { from: string; to: string },
|
||||
cardId?: string,
|
||||
) {
|
||||
const query
|
||||
= typeof period === "string"
|
||||
? { cardId, month: period }
|
||||
: { ...period, cardId };
|
||||
|
||||
return useAuthenticatedQuery({
|
||||
...apiQueryClient.queryOptions("get", "/api/v1/bank-cards/summary", {
|
||||
params: { query },
|
||||
params: { query: { ...period, cardId } },
|
||||
}),
|
||||
enabled: Boolean(period),
|
||||
select: sortBankSummary,
|
||||
});
|
||||
}
|
||||
|
||||
export {
|
||||
type BankCard,
|
||||
type BankOperation,
|
||||
type BankSummary,
|
||||
type CreateBankCard,
|
||||
type CreateBankOperation,
|
||||
type CreateExpenseCategory,
|
||||
type ExpenseCategory,
|
||||
sortBankSummary,
|
||||
type UpdateBankCard,
|
||||
type UpdateBankOperation,
|
||||
type UpdateExpenseCategory,
|
||||
useBankCard,
|
||||
useBankCards,
|
||||
useBankCategories,
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { expect, it } from "vitest";
|
||||
|
||||
import { type BankSummary, sortBankSummary } from "./queries";
|
||||
import type { BankSummary } from "./types";
|
||||
|
||||
import { sortBankSummary } from "./queries";
|
||||
|
||||
const money = (value: string) => ({ currency: "RUB", value });
|
||||
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
import type { paths } from "@/shared/api";
|
||||
|
||||
type BankCard
|
||||
= paths["/api/v1/bank-cards/{id}"]["get"]["responses"][200]["content"]["application/json"];
|
||||
|
||||
type BankOperation
|
||||
= paths["/api/v1/bank-cards/operations/{id}"]["get"]["responses"][200]["content"]["application/json"];
|
||||
|
||||
type BankOperations
|
||||
= paths["/api/v1/bank-cards/{id}/operations"]["get"]["responses"][200]["content"]["application/json"];
|
||||
|
||||
type BankSummary
|
||||
= paths["/api/v1/bank-cards/summary"]["get"]["responses"][200]["content"]["application/json"];
|
||||
|
||||
type CreateBankCard
|
||||
= paths["/api/v1/bank-cards"]["post"]["requestBody"]["content"]["application/json"];
|
||||
|
||||
type CreateBankOperation
|
||||
= paths["/api/v1/bank-cards/operations"]["post"]["requestBody"]["content"]["application/json"];
|
||||
|
||||
type CreateExpenseCategory
|
||||
= paths["/api/v1/bank-cards/categories"]["post"]["requestBody"]["content"]["application/json"];
|
||||
|
||||
type ExpenseCategory
|
||||
= paths["/api/v1/bank-cards/categories"]["get"]["responses"][200]["content"]["application/json"][number];
|
||||
|
||||
type UpdateBankCard
|
||||
= paths["/api/v1/bank-cards/{id}"]["patch"]["requestBody"]["content"]["application/json"];
|
||||
|
||||
type UpdateBankOperation
|
||||
= paths["/api/v1/bank-cards/operations/{id}"]["patch"]["requestBody"]["content"]["application/json"];
|
||||
|
||||
type UpdateExpenseCategory
|
||||
= paths["/api/v1/bank-cards/categories/{id}"]["patch"]["requestBody"]["content"]["application/json"];
|
||||
|
||||
export type {
|
||||
BankCard,
|
||||
BankOperation,
|
||||
BankOperations,
|
||||
BankSummary,
|
||||
CreateBankCard,
|
||||
CreateBankOperation,
|
||||
CreateExpenseCategory,
|
||||
ExpenseCategory,
|
||||
UpdateBankCard,
|
||||
UpdateBankOperation,
|
||||
UpdateExpenseCategory,
|
||||
};
|
||||
Reference in New Issue
Block a user