feat: agregar soporte para selección de idioma en la creación de cotizaciones

This commit is contained in:
David Arranz 2026-10-08 12:36:03 +02:00
parent 08f01e2b3b
commit b4a6ac18f7
2 changed files with 63 additions and 32 deletions

View File

@ -7,8 +7,6 @@ import { useMemo } from "react";
import { useFormContext, useWatch } from "react-hook-form"; import { useFormContext, useWatch } from "react-hook-form";
const calculateQuoteTotals = (prices: any) => { const calculateQuoteTotals = (prices: any) => {
console.debug("calculateQuoteTotals", prices);
const discountOrError = Percentage.create( const discountOrError = Percentage.create(
prices[1] || { prices[1] || {
amount: null, amount: null,

View File

@ -10,35 +10,53 @@ import { t } from "i18next";
import { SubmitButton } from "@/components"; import { SubmitButton } from "@/components";
import { useUnsavedChangesNotifier } from "@/lib/hooks"; import { useUnsavedChangesNotifier } from "@/lib/hooks";
import { Button, Form, Label, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Separator } from "@/ui"; import { documentedLanguages } from "@/lib/languages";
import {
Button,
Form,
Label,
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
Separator,
} from "@/ui";
import { useToast } from "@/ui/use-toast"; import { useToast } from "@/ui/use-toast";
import { joiResolver } from "@hookform/resolvers/joi"; import { joiResolver } from "@hookform/resolvers/joi";
import { ICreateQuote_Request_DTO } from "@shared/contexts"; import { ICreateQuote_Request_DTO } from "@shared/contexts";
import Joi from "joi"; import Joi from "joi";
import { useMemo } from "react"; import { useEffect, useMemo, useState } from "react";
import { Controller, SubmitHandler, useForm } from "react-hook-form"; import { Controller, SubmitHandler, useForm } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import SpanishJoiMessages from "../../spanish-joi-messages.json"; import SpanishJoiMessages from "../../spanish-joi-messages.json";
import { useQuotes } from "./hooks"; import { useQuotes } from "./hooks";
import { documentedLanguages } from "@/lib/languages";
interface QuoteDataForm extends ICreateQuote_Request_DTO {} interface QuoteDataForm extends ICreateQuote_Request_DTO {}
export const QuoteCreate = () => { export const QuoteCreate = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const { i18n } = useTranslation();
const { toast } = useToast(); const { toast } = useToast();
const { useCreate } = useQuotes(); const { useCreate } = useQuotes();
const { mutate, isPending } = useCreate(); const { mutateAsync, isPending } = useCreate();
const [createdQuoteId, setCreatedQuoteId] = useState<string>();
const interfaceLanguage = i18n.resolvedLanguage ?? i18n.language;
const documentLanguage = documentedLanguages.some(({ code }) => code === interfaceLanguage)
? interfaceLanguage
: "es";
const defaultValues = useMemo( const defaultValues = useMemo(
() => ({ () => ({
date: new Date(Date.now()).toUTCString(), date: new Date(Date.now()).toUTCString(),
lang_code: "es", lang_code: documentLanguage,
customer_reference: "", customer_reference: "",
customer_information: "", customer_information: "",
//reference: "", //reference: "",
}), }),
[] [documentLanguage]
); );
const form = useForm<QuoteDataForm>({ const form = useForm<QuoteDataForm>({
@ -49,6 +67,9 @@ export const QuoteCreate = () => {
customer_reference: Joi.string(), customer_reference: Joi.string(),
date: Joi.date().required(), date: Joi.date().required(),
customer_information: Joi.string().required(), customer_information: Joi.string().required(),
lang_code: Joi.string()
.valid(...documentedLanguages.map(({ code }) => code))
.required(),
}), }),
{ {
messages: { messages: {
@ -58,35 +79,35 @@ export const QuoteCreate = () => {
), ),
}); });
const { formState, reset, getValues, handleSubmit } = form; const { formState, reset, handleSubmit } = form;
const { isSubmitting, isDirty } = formState; const { isSubmitting, isDirty } = formState;
useUnsavedChangesNotifier({ useUnsavedChangesNotifier({
isDirty, isDirty,
}); });
useEffect(() => {
if (createdQuoteId) {
navigate(`/quotes/edit/${createdQuoteId}`, { relative: "path" });
}
}, [createdQuoteId, navigate]);
const onSubmit: SubmitHandler<QuoteDataForm> = async (formData) => { const onSubmit: SubmitHandler<QuoteDataForm> = async (formData) => {
try { try {
mutate(formData, { const data = await mutateAsync(formData);
onError: (error) => { reset(formData);
console.debug(error); toast({
toast({ title: t("quotes.notifications.created"),
title: t("common.error"), variant: "default",
description: error.message, });
variant: "destructive", setCreatedQuoteId(data.id);
}); } catch (error) {
}, console.debug(error);
onSuccess: (data) => { toast({
reset(getValues()); title: t("common.error"),
toast({ description: (error as Error).message,
title: t("quotes.notifications.created"), variant: "destructive",
variant: "default",
});
navigate(`/quotes/edit/${data.id}`, { relative: "path" });
},
}); });
} finally {
//setLoading(false);
} }
}; };
@ -120,7 +141,12 @@ export const QuoteCreate = () => {
description={t("quotes.create.form_groups.general.desc")} description={t("quotes.create.form_groups.general.desc")}
footerActions={ footerActions={
<div className='flex items-stretch justify-between flex-1'> <div className='flex items-stretch justify-between flex-1'>
<Button size='sm' variant={"outline"} onClick={() => navigate("/quotes")}> <Button
type='button'
size='sm'
variant={"outline"}
onClick={() => navigate("/quotes")}
>
{t("common.discard")} {t("common.discard")}
</Button> </Button>
<SubmitButton size='sm' label={t("common.continue")}></SubmitButton> <SubmitButton size='sm' label={t("common.continue")}></SubmitButton>
@ -144,13 +170,18 @@ export const QuoteCreate = () => {
/> />
<div className='grid gap-2'> <div className='grid gap-2'>
<Label htmlFor='lang_code'>{t("quotes.form_fields.lang_code.label")}</Label> <Label htmlFor='lang_code' className='font-semibold'>
{t("quotes.form_fields.lang_code.label")}
</Label>
<Controller <Controller
control={form.control} control={form.control}
name='lang_code' name='lang_code'
render={({ field }) => ( render={({ field }) => (
<Select value={field.value || "es"} onValueChange={field.onChange}> <Select value={field.value || "es"} onValueChange={field.onChange}>
<SelectTrigger id='lang_code' aria-label={t("quotes.form_fields.lang_code.label")}> <SelectTrigger
id='lang_code'
aria-label={t("quotes.form_fields.lang_code.label")}
>
<SelectValue /> <SelectValue />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
@ -164,7 +195,9 @@ export const QuoteCreate = () => {
</Select> </Select>
)} )}
/> />
<p className='text-sm text-muted-foreground'>{t("quotes.form_fields.lang_code.desc")}</p> <p className='text-sm text-muted-foreground'>
{t("quotes.form_fields.lang_code.desc")}
</p>
</div> </div>
<Separator /> <Separator />