import { useState } from "react"; import { useTranslation } from "react-i18next"; import { useParams, useSearchParams } from "react-router"; import { type BankOperation, bankPeriod, useBankCard, useBankCards, useBankOperations, useBankSummary, useUpdateBankCard, } from "@/entities/bank-card"; import { ApiErrorCode, apiErrorCode, isApiErrorCode } from "@/shared/api"; import { formatDate, formatMoney } from "@/shared/lib"; const PAGE_SIZES = new Set(["5", "10", "20", "50", "100", "all"]); type CardPageVm = ReturnType; function useCardPageVm() { const { cardId } = useParams(); const [parameters, setParameters] = useSearchParams(); const { t } = useTranslation(); const period = bankPeriod(parameters); const requestedSize = parameters.get("limit") ?? "10"; const pageSize = PAGE_SIZES.has(requestedSize) ? requestedSize : "10"; const rawPage = Number(parameters.get("page") ?? 1); const page = pageSize !== "all" && Number.isSafeInteger(rawPage) && rawPage > 0 ? rawPage : 1; const card = useBankCard(cardId); const updateCard = useUpdateBankCard(); const cards = useBankCards(true); const history = useBankOperations(cardId, { ...period, limit: pageSize === "all" ? "all" : Number(pageSize), page, }); const summary = useBankSummary(period, cardId); const [editingCard, setEditingCard] = useState(false); const [operation, setOperation] = useState<"new" | BankOperation | null>( null, ); const [deleting, setDeleting] = useState(null); const rows = history.data?.items.map((item) => { const isOutgoing = item.kind === "expense" || (item.kind === "transfer" && item.cardId === cardId); const otherCardId = isOutgoing ? item.targetCardId : item.cardId; let title = t(`bank.kinds.${item.kind}`); if (item.kind === "transfer") title = t(isOutgoing ? "bank.transferTo" : "bank.transferFrom", { name: isOutgoing ? item.targetCardName : item.cardName, }); return { color: isOutgoing ? "red" : "green", editable: !history.isPlaceholderData && !card.data?.archivedAt && Boolean(cards.data) && !cards.data?.some( c => (c.id === item.cardId || c.id === item.targetCardId) && c.archivedAt, ), item, metadata: [formatDate(item.date), item.categoryName, item.description] .filter(Boolean) .join(" · "), onDelete: () => setDeleting(item), onEdit: () => setOperation(item), otherCard: item.kind === "transfer" ? otherCardId : null, title, tone: isOutgoing ? ("negative" as const) : ("positive" as const), value: `${isOutgoing ? "−" : "+"}${formatMoney(item.amount)}`, }; }); return { archiveError: updateCard.isError ? updateCard.error : undefined, archivePending: updateCard.isPending, canAddOperation: !card.data?.archivedAt && Boolean(cards.data), card: card.data, cardError: card.error, cardLoading: card.isLoading, cardRefreshError: card.isError, cardRefreshing: card.isFetching, cardRetry: () => card.refetch(), cards: cards.data, cardsFeedback: cards.isError ? { cached: Boolean(cards.data), error: cards.error } : undefined, cardsRetry: () => cards.refetch(), closeDeletion: () => setDeleting(null), closeEditing: () => setEditingCard(false), closeOperation: () => setOperation(null), deleting, editableOperation: operation === "new" ? undefined : (operation ?? undefined), editingCard, history: { changingPage: history.isPlaceholderData, error: history.error, loading: history.isLoading, page: history.data?.page, refetch: () => history.refetch(), }, notFound: isApiErrorCode( apiErrorCode(card.error), ApiErrorCode.BankNotFound, ), openCardEditor: () => setEditingCard(true), openNewOperation: () => setOperation("new"), operation, page, pageSize, period, rows, setPage: (value: number) => setParameters({ ...period, limit: pageSize, page: String(value) }), setPageSize: (value: null | string) => { if (value && PAGE_SIZES.has(value)) setParameters({ ...period, limit: value }); }, setPeriod: (value: [null | string, null | string]) => { if (value[0] && value[1]) setParameters({ from: value[0], limit: pageSize, to: value[1] }); }, summary: { data: summary.data, error: summary.error, fetching: summary.isFetching, loading: summary.isLoading, refetch: () => summary.refetch(), }, toggleArchive: () => { if (!card.data) return; updateCard.mutate({ body: { archived: !card.data.archivedAt, version: card.data.version }, id: card.data.id, }); }, totalPages: pageSize === "all" ? 1 : Math.max(1, Math.ceil((history.data?.total ?? 0) / Number(pageSize))), }; } export { type CardPageVm, useCardPageVm };