148 lines
4.4 KiB
TypeScript
148 lines
4.4 KiB
TypeScript
import { zodResolver } from "@hookform/resolvers/zod";
|
|
import { Button, Group, Modal, Stack, Text } from "@mantine/core";
|
|
import { type FC, useState } from "react";
|
|
import { FormProvider, useForm } from "react-hook-form";
|
|
import { useTranslation } from "react-i18next";
|
|
import { formatDate } from "@/shared/lib";
|
|
import { useBankMutations } from "../api/mutations";
|
|
import { type BankCard, useBankCard } from "../api/queries";
|
|
import {
|
|
type BankCardValues,
|
|
bankCardSchema,
|
|
bankMoneyValue,
|
|
bankToday,
|
|
} from "../model/form-validation";
|
|
import { BankCardFields } from "./BankCardForm.view";
|
|
import { BankFeedback } from "./BankFeedback";
|
|
import classes from "./BankForms.module.css";
|
|
import { useBankFormFocus } from "./useBankFormFocus";
|
|
|
|
type BankCardFormProps = { card?: BankCard; onClose: () => void };
|
|
const BankCardForm: FC<BankCardFormProps> = (props) => {
|
|
useBankFormFocus();
|
|
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>({
|
|
resolver: zodResolver(bankCardSchema),
|
|
defaultValues: {
|
|
name: props.card?.name ?? "",
|
|
bankName: props.card?.bankName ?? "",
|
|
lastFour: props.card?.lastFour ?? "",
|
|
paymentSystem: props.card?.paymentSystem ?? "",
|
|
openingBalance: props.card?.openingBalance.value ?? "0",
|
|
openingDate: props.card?.openingDate ?? bankToday(),
|
|
},
|
|
});
|
|
const pending =
|
|
methods.formState.isSubmitting || (fresh.isFetching && Boolean(error));
|
|
return (
|
|
<Modal
|
|
opened
|
|
onClose={() => !pending && props.onClose()}
|
|
title={t(props.card ? "bank.editCard" : "bank.addCard")}
|
|
closeButtonProps={{ "aria-label": t("bank.close"), disabled: pending }}
|
|
closeOnClickOutside={!pending}
|
|
closeOnEscape={!pending}
|
|
>
|
|
<FormProvider {...methods}>
|
|
<form
|
|
onSubmit={methods.handleSubmit(async (values) => {
|
|
setError(undefined);
|
|
try {
|
|
const metadata = {
|
|
name: values.name,
|
|
bankName: values.bankName || null,
|
|
lastFour: values.lastFour || null,
|
|
paymentSystem: values.paymentSystem || null,
|
|
};
|
|
const financial = {
|
|
openingBalance: {
|
|
currency: "RUB" as const,
|
|
value: bankMoneyValue(values.openingBalance),
|
|
},
|
|
openingDate: values.openingDate,
|
|
};
|
|
if (props.card)
|
|
await mutation.updateCard.mutateAsync({
|
|
id: props.card.id,
|
|
body: {
|
|
...metadata,
|
|
...(!props.card.archivedAt ? financial : {}),
|
|
version,
|
|
},
|
|
});
|
|
else
|
|
await mutation.createCard.mutateAsync({
|
|
id: intent,
|
|
...metadata,
|
|
...financial,
|
|
});
|
|
props.onClose();
|
|
} catch (failure) {
|
|
setError(failure);
|
|
}
|
|
})}
|
|
>
|
|
<fieldset className={classes.fieldset} disabled={pending}>
|
|
<Stack>
|
|
<BankCardFields archived={Boolean(props.card?.archivedAt)} />
|
|
{error ? (
|
|
<>
|
|
<BankFeedback error={error} />
|
|
{props.card && (
|
|
<Button
|
|
variant="default"
|
|
onClick={async () => {
|
|
const response = await fresh.refetch();
|
|
if (response.data && !response.error)
|
|
setLatest(response.data);
|
|
else setError(response.error);
|
|
}}
|
|
>
|
|
{t("bank.reloadRecord")}
|
|
</Button>
|
|
)}
|
|
</>
|
|
) : null}
|
|
{latest && (
|
|
<>
|
|
<Text>
|
|
{t("bank.versionHint", {
|
|
details: `${latest.name} · ${latest.openingBalance.value} RUB · ${formatDate(latest.openingDate)}`,
|
|
})}
|
|
</Text>
|
|
<Button
|
|
variant="default"
|
|
onClick={() => {
|
|
setVersion(latest.version);
|
|
setLatest(undefined);
|
|
setError(undefined);
|
|
}}
|
|
>
|
|
{t("bank.adoptVersion", { version: latest.version })}
|
|
</Button>
|
|
</>
|
|
)}
|
|
<Group>
|
|
<Button type="submit" loading={pending}>
|
|
{t("bank.save")}
|
|
</Button>
|
|
<Button variant="default" onClick={props.onClose}>
|
|
{t("bank.cancel")}
|
|
</Button>
|
|
</Group>
|
|
</Stack>
|
|
</fieldset>
|
|
</form>
|
|
</FormProvider>
|
|
</Modal>
|
|
);
|
|
};
|
|
|
|
export { BankCardForm };
|