refactor: colocate frontend component files
Frontend security / security (push) Successful in 9s
Frontend verify / verify (push) Successful in 5m10s
Conventional commits / commits (pull_request) Successful in 25s
Frontend security / security (pull_request) Successful in 9s
Frontend verify / verify (pull_request) Successful in 5m10s

This commit is contained in:
2026-09-23 08:34:58 +03:00
parent 85fcfe3d07
commit b987fd8e78
190 changed files with 131 additions and 108 deletions
+126
View File
@@ -0,0 +1,126 @@
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { useParams, useSearchParams } from "react-router";
import {
type BankOperation,
bankPeriod,
useBankCard,
useBankCards,
useBankMutations,
useBankOperations,
useBankSummary,
} from "@/entities/bank-card";
import { apiErrorCode } from "@/shared/api";
import { formatDate, formatMoney } from "@/shared/lib";
const PAGE_SIZES = ["5", "10", "20", "50", "100", "all"];
function useCardPageVm() {
const { cardId } = useParams();
const [params, setParams] = useSearchParams();
const { t } = useTranslation();
const period = bankPeriod(params);
const requestedSize = params.get("limit") ?? "10";
const pageSize = PAGE_SIZES.includes(requestedSize) ? requestedSize : "10";
const rawPage = Number(params.get("page") ?? 1);
const page =
pageSize !== "all" && Number.isSafeInteger(rawPage) && rawPage > 0
? rawPage
: 1;
const card = useBankCard(cardId);
const { updateCard } = useBankMutations();
const [archiveError, setArchiveError] = useState<unknown>();
const cards = useBankCards(true);
const history = useBankOperations(cardId, {
...period,
page,
limit: pageSize === "all" ? "all" : Number(pageSize),
});
const summary = useBankSummary(period, cardId);
const [editingCard, setEditingCard] = useState(false);
const [operation, setOperation] = useState<BankOperation | "new" | null>(
null,
);
const [deleting, setDeleting] = useState<BankOperation | null>(null);
const rows = history.data?.items.map((item) => {
const outgoing =
item.kind === "expense" ||
(item.kind === "transfer" && item.cardId === cardId);
return {
item,
color: outgoing ? "red" : "green",
title:
item.kind === "transfer"
? t(outgoing ? "bank.transferTo" : "bank.transferFrom", {
name: outgoing ? item.targetCardName : item.cardName,
})
: t(`bank.kinds.${item.kind}`),
metadata: [formatDate(item.date), item.categoryName, item.description]
.filter(Boolean)
.join(" · "),
value: `${outgoing ? "−" : "+"}${formatMoney(item.amount)}`,
tone: outgoing ? ("negative" as const) : ("positive" as const),
otherCard:
item.kind === "transfer"
? outgoing
? item.targetCardId
: item.cardId
: null,
editable:
Boolean(cards.data) &&
!cards.data?.some(
(c) =>
(c.id === item.cardId || c.id === item.targetCardId) &&
c.archivedAt,
),
};
});
return {
card,
cards,
archiveError,
archivePending: updateCard.isPending,
toggleArchive: async () => {
if (!card.data) return;
setArchiveError(undefined);
try {
await updateCard.mutateAsync({
id: card.data.id,
body: { version: card.data.version, archived: !card.data.archivedAt },
});
} catch (failure) {
setArchiveError(failure);
}
},
history,
summary,
period,
page,
pageSize,
totalPages:
pageSize === "all"
? 1
: Math.max(1, Math.ceil((history.data?.total ?? 0) / Number(pageSize))),
rows,
editingCard,
setEditingCard,
operation,
setOperation,
deleting,
setDeleting,
notFound: apiErrorCode(card.error) === "BANK_NOT_FOUND",
setPeriod: (value: [string | null, string | null]) => {
if (value[0] && value[1])
setParams({ from: value[0], to: value[1], limit: pageSize });
},
setPageSize: (value: string | null) => {
if (value && PAGE_SIZES.includes(value))
setParams({ ...period, limit: value });
},
setPage: (value: number) =>
setParams({ ...period, page: String(value), limit: pageSize }),
};
}
type CardPageVm = ReturnType<typeof useCardPageVm>;
export { type CardPageVm, useCardPageVm };