refactor: enforce frontend view model boundaries
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { useParams, useSearchParams } from "react-router";
|
||||
import { z } from "zod";
|
||||
|
||||
import {
|
||||
type BrokerageEvent,
|
||||
useBrokerageEvents,
|
||||
} from "@/entities/brokerage-event";
|
||||
import { useScreenSync } from "@/features/tbank-connection";
|
||||
import { isValidPeriod } from "@/shared/lib";
|
||||
|
||||
type AccountEventsPageViewModel
|
||||
= | {
|
||||
@@ -38,8 +38,15 @@ function useAccountEventsPageViewModel(
|
||||
const urlFrom = searchParameters.get("from");
|
||||
const urlTo = searchParameters.get("to");
|
||||
const isValidUrlPeriod = isValidPeriod(urlFrom, urlTo);
|
||||
const appliedFrom = isValidUrlPeriod ? urlFrom : /* eslint-disable-line unicorn/no-null -- Preserve the nullable domain value consumed by this view model. */ null;
|
||||
const appliedTo = isValidUrlPeriod ? urlTo : /* eslint-disable-line unicorn/no-null -- Preserve the nullable domain value consumed by this view model. */ null;
|
||||
|
||||
const appliedFrom = isValidUrlPeriod
|
||||
? urlFrom
|
||||
: null; // eslint-disable-line unicorn/no-null -- Preserve the nullable domain value consumed by this view model.
|
||||
|
||||
const appliedTo = isValidUrlPeriod
|
||||
? urlTo
|
||||
: null; // eslint-disable-line unicorn/no-null -- Preserve the nullable domain value consumed by this view model.
|
||||
|
||||
const [draftFrom, setDraftFrom] = useState<null | string>(appliedFrom);
|
||||
const [draftTo, setDraftTo] = useState<null | string>(appliedTo);
|
||||
useEffect(() => {
|
||||
@@ -49,19 +56,23 @@ function useAccountEventsPageViewModel(
|
||||
normalized.delete("to");
|
||||
setSearchParameters(normalized, { replace: true });
|
||||
}, [isValidUrlPeriod, searchParameters, setSearchParameters]);
|
||||
|
||||
const [previousPeriod, setPreviousPeriod] = useState([
|
||||
appliedFrom,
|
||||
appliedTo,
|
||||
]);
|
||||
|
||||
if (previousPeriod[0] !== appliedFrom || previousPeriod[1] !== appliedTo) {
|
||||
setPreviousPeriod([appliedFrom, appliedTo]);
|
||||
setDraftFrom(appliedFrom);
|
||||
setDraftTo(appliedTo);
|
||||
}
|
||||
|
||||
const events = useBrokerageEvents(accountId, {
|
||||
...(appliedFrom && { from: appliedFrom }),
|
||||
...(appliedTo && { to: appliedTo }),
|
||||
});
|
||||
|
||||
const sync = useScreenSync(
|
||||
{
|
||||
accountId,
|
||||
@@ -71,12 +82,15 @@ function useAccountEventsPageViewModel(
|
||||
},
|
||||
[events],
|
||||
);
|
||||
|
||||
if (events.isLoading) return { state: "loading" };
|
||||
|
||||
const onReset = () => {
|
||||
setDraftFrom(null);
|
||||
setDraftTo(null);
|
||||
setSearchParameters({});
|
||||
};
|
||||
|
||||
if ((events.isError || events.meta?.sync.state === "failed") && !events.data)
|
||||
return {
|
||||
onReset,
|
||||
@@ -86,6 +100,7 @@ function useAccountEventsPageViewModel(
|
||||
state: "error",
|
||||
};
|
||||
if (!events.data) return { state: "loading" };
|
||||
|
||||
return {
|
||||
canApply:
|
||||
(draftFrom !== appliedFrom || draftTo !== appliedTo)
|
||||
@@ -115,14 +130,4 @@ function useAccountEventsPageViewModel(
|
||||
};
|
||||
}
|
||||
|
||||
const calendarDate = z.iso.date();
|
||||
function isValidPeriod(from: null | string, to: null | string) {
|
||||
return (
|
||||
[from, to].every(
|
||||
date => date === null || calendarDate.safeParse(date).success,
|
||||
)
|
||||
&& (!from || !to || from <= to)
|
||||
);
|
||||
}
|
||||
|
||||
export { type AccountEventsPageViewModel, useAccountEventsPageViewModel };
|
||||
|
||||
Reference in New Issue
Block a user