Presupuestador_web/client/src/app/quotes/edit.tsx
2024-07-17 20:10:07 +02:00

276 lines
8.2 KiB
TypeScript

import {
BackHistoryButton,
CancelButton,
ErrorOverlay,
LoadingOverlay,
SubmitButton,
} from "@/components";
import { calculateItemTotals } from "@/lib/calc";
import { useUrlId } from "@/lib/hooks/useUrlId";
import { Badge, Button, Form, Tabs, TabsContent, TabsList, TabsTrigger } from "@/ui";
import { CurrencyData, IGetQuote_Response_DTO, Language, MoneyValue } from "@shared/contexts";
import { t } from "i18next";
import { useEffect, useMemo, useState } from "react";
import { SubmitHandler, useForm } from "react-hook-form";
import { useNavigate } from "react-router-dom";
import { toast } from "react-toastify";
import { QuoteDetailsCardEditor, QuoteGeneralCardEditor } from "./components/editors";
import { useQuotes } from "./hooks";
/*type QuoteDataForm = Omit<IGetQuote_Response_DTO, "items"> & {
items: IGetQuote_QuoteItem_Response_DTO;
};*/
type QuoteDataForm = IGetQuote_Response_DTO;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export const QuoteEdit = () => {
const navigate = useNavigate();
const quoteId = useUrlId();
const [quoteCurrency, setQuoteCurrency] = useState<CurrencyData>(
CurrencyData.createDefaultCode().object
);
const [quoteLanguage, setQuoteLanguage] = useState<Language>(Language.createDefaultCode().object);
/*const { data: userIdentity } = useGetIdentity();
console.log(userIdentity);
const { flag } = useLocalization({
locale: userIdentity?.language ?? "es-es",
});
console.log(flag);*/
const { useOne, useUpdate } = useQuotes();
const { data, status, error: queryError } = useOne(quoteId);
const defaultValues = useMemo(
() => ({
date: "",
reference: "",
customer_information: "",
lang_code: "",
currency_code: "",
payment_method: "",
notes: "",
validity: "",
subtotal_price: {
amount: undefined,
scale: 2,
currency_code: data?.currency_code ?? quoteCurrency.code,
},
discount: {
amount: undefined,
scale: 0,
},
total_price: {
amount: undefined,
scale: 2,
currency_code: data?.currency_code ?? quoteCurrency.code,
},
items: [
{
description: "",
quantity: {
amount: 1,
scale: 0,
},
unit_price: {
amount: null,
scale: 4,
currency_code: data?.currency_code ?? quoteCurrency.code,
},
subtotal_price: {
amount: null,
scale: 4,
currency_code: data?.currency_code ?? quoteCurrency.code,
},
discount: {
amount: null,
scale: 2,
},
total_price: {
amount: null,
scale: 4,
currency_code: data?.currency_code ?? quoteCurrency.code,
},
},
],
}),
[data, quoteCurrency]
);
const { mutate } = useUpdate(String(quoteId));
const form = useForm<QuoteDataForm>({
mode: "onBlur",
values: data,
defaultValues,
});
const { watch, getValues, setValue, formState } = form;
/*const { clear } = useFormPersist(
"quote-edit",
{
watch,
setValue,
},
{
storage: window.localStorage, // default window.sessionStorage
//exclude: ['foo']
}
);*/
const { isSubmitting } = formState;
const onSubmit: SubmitHandler<QuoteDataForm> = async (data) => {
// Transformación del form -> typo de request
mutate(data, {
onError: (error) => {
console.debug(error);
toast.error(error.message);
//alert(error.message);
},
//onSettled: () => {},
onSuccess: () => {
toast("Guardado!");
//clear();
},
});
};
useEffect(() => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { unsubscribe } = watch((_, { name, type }) => {
const value = getValues();
if (name) {
if (name === "currency_code") {
setQuoteCurrency(
CurrencyData.createFromCode(value.currency_code ?? CurrencyData.DEFAULT_CURRENCY_CODE)
.object
);
}
if (name === "lang_code") {
setQuoteLanguage(
Language.createFromCode(value.lang_code ?? Language.DEFAULT_LANGUAGE_CODE).object
);
}
if (name === "items") {
const { items } = value;
let quoteSubtotal = MoneyValue.create().object;
// Recálculo líneas
items &&
items.map((item, index) => {
const itemTotals = calculateItemTotals(item);
if (itemTotals === null) {
return;
}
quoteSubtotal = quoteSubtotal.add(itemTotals.totalPrice);
setValue(`items.${index}.subtotal_price`, itemTotals.subtotalPrice.toObject());
setValue(`items.${index}.total_price`, itemTotals.totalPrice.toObject());
});
// Recálculo completo
setValue("subtotal_price", quoteSubtotal.toObject());
}
if (name.endsWith("quantity") || name.endsWith("unit_price") || name.endsWith("discount")) {
const { items } = value;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [, indexString, fieldName] = String(name).split(".");
const index = parseInt(indexString);
const itemTotals = calculateItemTotals(items[index]);
if (itemTotals === null) {
return;
}
setValue(`items.${index}.subtotal_price`, itemTotals.subtotalPrice.toObject());
setValue(`items.${index}.total_price`, itemTotals.totalPrice.toObject());
// Recálculo completo
}
}
});
return () => unsubscribe();
}, [watch, getValues, setValue]);
if (isSubmitting) {
return <LoadingOverlay />;
}
if (status === "error") {
return <ErrorOverlay errorMessage={queryError.message} />;
}
if (status !== "success") {
return <LoadingOverlay />;
}
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
<div className='mx-auto grid max-w-[90rem] flex-1 auto-rows-max gap-6'>
<div className='flex items-center gap-4'>
<BackHistoryButton />
<h1 className='flex-1 text-xl font-semibold tracking-tight shrink-0 whitespace-nowrap sm:grow-0'>
{t("quotes.edit.title")}
</h1>
<Badge variant='default' className='ml-auto sm:ml-0'>
{data.status}
</Badge>
<div className='items-center hidden gap-2 md:ml-auto md:flex'>
<CancelButton variant='outline' size='sm' onClick={() => navigate("/quotes")}>
{t("common.cancel")}
</CancelButton>
<SubmitButton
variant={form.formState.isDirty ? "default" : "outline"}
size='sm'
disabled={formState.isSubmitting || formState.isLoading || formState.isValidating}
>
{t("common.save")}
</SubmitButton>
</div>
</div>
<Tabs defaultValue='items' className='space-y-4'>
<TabsList>
<TabsTrigger value='general'>{t("quotes.create.tabs.general")}</TabsTrigger>
<TabsTrigger value='items'>{t("quotes.create.tabs.items")}</TabsTrigger>
{/* <TabsTrigger value='history'>{t("quotes.create.tabs.history")}</TabsTrigger>*/}
</TabsList>
<TabsContent value='general'>
<QuoteGeneralCardEditor />
</TabsContent>
<TabsContent value='items'>
<QuoteDetailsCardEditor
currency={quoteCurrency}
language={quoteLanguage}
defaultValues={defaultValues}
/>
</TabsContent>
<TabsContent value='history'></TabsContent>
</Tabs>
<div className='flex items-center justify-center gap-2 md:hidden'>
<Button variant='outline' size='sm'>
{t("quotes.create.buttons.discard")}
</Button>
<Button size='sm'>{t("quotes.create.buttons.save_quote")}</Button>
</div>
</div>
</form>
</Form>
);
};