Files
frontend/src/pages/account-events/ui/AccountEventsPage/AccountEventsPage.vm.ts
T
ksv741 6b1b26aa34
Frontend verify / commits (pull_request) Successful in 45s
Frontend verify / security (pull_request) Successful in 14s
Frontend verify / verify (pull_request) Successful in 6m45s
feat: render hourly investment states
2026-10-09 19:56:20 +03:00

128 lines
3.1 KiB
TypeScript

import { useState } from "react";
import { useParams } from "react-router";
import type { InvestmentResourceMeta } from "@/shared/api";
import {
type BrokerageEvent,
useBrokerageEvents,
} from "@/entities/brokerage-event";
import { useScreenSync } from "@/features/tbank-connection";
import {
defaultPeriod,
isValidPeriod,
moscowDate,
useInitialAvailability,
useUrlPeriod,
} from "@/shared/lib";
type AccountEventsPageViewModel
= | {
canApply: boolean;
draftPeriod: [null | string, null | string];
events: BrokerageEvent[];
maxDate: string;
onApply: () => void;
onPeriodChange: (value: [null | string, null | string]) => void;
onPeriodPopoverOpen: () => void;
onReset: () => void;
partial?: boolean;
source?: InvestmentResourceMeta;
state: "loaded";
}
| { onReset: () => void; state: "error" }
| { state: "loading" };
function useAccountEventsPageViewModel(): AccountEventsPageViewModel {
const { accountId = "" } = useParams();
const {
from: appliedFrom,
setParameters: setSearchParameters,
to: appliedTo,
} = useUrlPeriod();
const [draftPeriod, setDraftPeriod] = useState<[
null | string,
null | string,
]>([appliedFrom, appliedTo]);
const [draftFrom, draftTo] = draftPeriod;
const [maxDate, setMaxDate] = useState(() => moscowDate(new Date()));
const scope = JSON.stringify([accountId, appliedFrom, appliedTo]);
const [previousScope, setPreviousScope] = useState(scope);
if (previousScope !== scope) {
setPreviousScope(scope);
setDraftPeriod([appliedFrom, appliedTo]);
}
const events = useBrokerageEvents(accountId, {
...(appliedFrom && { from: appliedFrom }),
...(appliedTo && { to: appliedTo }),
});
useScreenSync(
{
accountId,
screen: "events",
...(appliedFrom && { from: appliedFrom }),
...(appliedTo && { to: appliedTo }),
},
[events],
);
const availability = useInitialAvailability({
hasContent: !!events.data,
identity: scope,
isError: events.isError || events.meta?.lastAttempt.state === "failed",
isFetching: events.isFetching,
});
if (availability === "loading" && events.isLoading)
return { state: "loading" };
const onReset = () => {
setDraftPeriod([appliedFrom, appliedTo]);
setSearchParameters({});
};
if ((events.isError || events.meta?.lastAttempt.state === "failed") && !events.data)
return {
onReset,
state: "error",
};
if (!events.data)
return availability === "unavailable"
? { onReset, state: "error" }
: { state: "loading" };
return {
canApply:
(draftFrom !== appliedFrom || draftTo !== appliedTo)
&& isValidPeriod(draftFrom, draftTo),
draftPeriod,
events: events.data.events,
maxDate,
onApply: () => {
if (!isValidPeriod(draftFrom, draftTo)) return;
setSearchParameters({
...(draftFrom && { from: draftFrom }),
...(draftTo && { to: draftTo }),
});
},
onPeriodChange: value => setDraftPeriod(
value[0] === null && value[1] === null ? defaultPeriod() : value,
),
onPeriodPopoverOpen: () => setMaxDate(moscowDate(new Date())),
onReset,
partial: events.meta?.completeness === "incomplete",
source: events.meta,
state: "loaded",
};
}
export { type AccountEventsPageViewModel, useAccountEventsPageViewModel };