refactor: enforce frontend view model boundaries
This commit is contained in:
@@ -0,0 +1,127 @@
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { useBankMutations } from "../../api/mutations";
|
||||
import { type BankCard, useBankCard } from "../../api/queries";
|
||||
import { bankMoneyValue, bankToday } from "../../model/form-validation";
|
||||
import { bankCardSchema, type BankCardValues } from "../../model/form.schema";
|
||||
import { useBankFormFocus } from "../useBankFormFocus";
|
||||
|
||||
type BankCardFormProps = { card?: BankCard; onClose: () => void };
|
||||
|
||||
function useBankCardFormVm(props: BankCardFormProps) {
|
||||
useBankFormFocus();
|
||||
const [opened, setOpened] = useState(true);
|
||||
const { t } = useTranslation();
|
||||
const [intent] = useState(() => crypto.randomUUID());
|
||||
const [version, setVersion] = useState(props.card?.version ?? 1);
|
||||
const [error, setError] = useState<unknown>();
|
||||
const [latest, setLatest] = useState<BankCard>();
|
||||
const fresh = useBankCard(props.card?.id);
|
||||
const mutation = useBankMutations();
|
||||
|
||||
const methods = useForm<BankCardValues>({
|
||||
defaultValues: {
|
||||
bankName: props.card?.bankName ?? "",
|
||||
lastFour: props.card?.lastFour ?? "",
|
||||
name: props.card?.name ?? "",
|
||||
openingBalance: props.card?.openingBalance.value ?? "0",
|
||||
openingDate: props.card?.openingDate ?? bankToday(),
|
||||
paymentSystem: props.card?.paymentSystem ?? "",
|
||||
},
|
||||
resolver: zodResolver(bankCardSchema),
|
||||
});
|
||||
|
||||
const isPending
|
||||
= !opened
|
||||
|| methods.formState.isSubmitting
|
||||
|| (fresh.isFetching && Boolean(error));
|
||||
|
||||
const onRequestClose = () => !isPending && setOpened(false);
|
||||
const dialogTitle = t(props.card ? "bank.editCard" : "bank.addCard");
|
||||
|
||||
const onSubmit = methods.handleSubmit(async (values) => {
|
||||
setError(undefined);
|
||||
|
||||
try {
|
||||
const metadata = {
|
||||
bankName:
|
||||
values.bankName
|
||||
// eslint-disable-next-line unicorn/no-null -- PATCH uses null to clear these optional card fields.
|
||||
|| null,
|
||||
lastFour:
|
||||
values.lastFour
|
||||
// eslint-disable-next-line unicorn/no-null -- PATCH uses null to clear these optional card fields.
|
||||
|| null,
|
||||
name: values.name,
|
||||
paymentSystem:
|
||||
values.paymentSystem
|
||||
// eslint-disable-next-line unicorn/no-null -- PATCH uses null to clear these optional card fields.
|
||||
|| null,
|
||||
};
|
||||
|
||||
const financial = {
|
||||
openingBalance: {
|
||||
currency: "RUB" as const,
|
||||
value: bankMoneyValue(values.openingBalance),
|
||||
},
|
||||
openingDate: values.openingDate,
|
||||
};
|
||||
|
||||
if (props.card)
|
||||
await mutation.updateCard.mutateAsync({
|
||||
body: {
|
||||
...metadata,
|
||||
...(!props.card.archivedAt && financial),
|
||||
version,
|
||||
},
|
||||
id: props.card.id,
|
||||
});
|
||||
else
|
||||
await mutation.createCard.mutateAsync({
|
||||
id: intent,
|
||||
...metadata,
|
||||
...financial,
|
||||
});
|
||||
setOpened(false);
|
||||
}
|
||||
catch (error_) {
|
||||
setError(error_);
|
||||
}
|
||||
});
|
||||
|
||||
const onReloadRecord = async () => {
|
||||
const response = await fresh.refetch();
|
||||
if (response.data && !response.error) setLatest(response.data);
|
||||
else setError(response.error);
|
||||
};
|
||||
|
||||
const onAdoptVersion = () => {
|
||||
if (!latest) return;
|
||||
setVersion(latest.version);
|
||||
setLatest(undefined);
|
||||
setError(undefined);
|
||||
};
|
||||
|
||||
const onCancel = () => setOpened(false);
|
||||
|
||||
return {
|
||||
dialogTitle,
|
||||
error,
|
||||
isPending,
|
||||
latest,
|
||||
methods,
|
||||
onAdoptVersion,
|
||||
onCancel,
|
||||
onReloadRecord,
|
||||
onRequestClose,
|
||||
onSubmit,
|
||||
opened,
|
||||
t,
|
||||
version,
|
||||
};
|
||||
}
|
||||
|
||||
export { useBankCardFormVm };
|
||||
Reference in New Issue
Block a user