This commit is contained in:
David Arranz 2024-07-11 19:43:08 +02:00
parent e84c03247c
commit bdcda617fb
9 changed files with 67 additions and 69 deletions

View File

@ -46,7 +46,7 @@ export const QuotesDataTable = () => {
<div className='text-ellipsis'> <div className='text-ellipsis'>
{original.customer_information.split("\n").map((item, index) => { {original.customer_information.split("\n").map((item, index) => {
return ( return (
<span className={index === 0 ? "font-semibold" : "font-medium"}> <span key={index} className={index === 0 ? "font-semibold" : "font-medium"}>
{item} {item}
<br /> <br />
</span> </span>
@ -93,7 +93,7 @@ export const QuotesDataTable = () => {
variant='secondary' variant='secondary'
onClick={(e) => { onClick={(e) => {
e.preventDefault(); e.preventDefault();
navigate(`/quotes/edit/${row.original.id}`, { relative: "path", replace: true }); navigate(`/quotes/edit/${row.original.id}`, { relative: "path" });
}} }}
> >
<Trans i18nKey={"common.edit"} /> <Trans i18nKey={"common.edit"} />

View File

@ -6,11 +6,9 @@ import {
} from "@/components"; } from "@/components";
import { t } from "i18next"; import { t } from "i18next";
import { ChevronLeft } from "lucide-react";
import { SubmitButton } from "@/components"; import { SubmitButton } from "@/components";
import { useWarnAboutChange } from "@/lib/hooks"; import { useWarnAboutChange } from "@/lib/hooks";
import { Button, Form } from "@/ui"; import { Form } from "@/ui";
import { ICreateQuote_Request_DTO } from "@shared/contexts"; import { ICreateQuote_Request_DTO } from "@shared/contexts";
import { useEffect } from "react"; import { useEffect } from "react";
import { FieldErrors, SubmitErrorHandler, SubmitHandler, useForm } from "react-hook-form"; import { FieldErrors, SubmitErrorHandler, SubmitHandler, useForm } from "react-hook-form";
@ -52,7 +50,7 @@ export const QuoteCreate = () => {
setWarnWhen(false); setWarnWhen(false);
mutate(formData, { mutate(formData, {
onSuccess: (data) => { onSuccess: (data) => {
navigate(`/quotes/edit/${data.id}`, { relative: "path", replace: true }); navigate(`/quotes/edit/${data.id}`, { relative: "path" });
}, },
}); });
} finally { } finally {
@ -71,15 +69,7 @@ export const QuoteCreate = () => {
<form onSubmit={handleSubmit(onSubmit, onErrors)}> <form onSubmit={handleSubmit(onSubmit, onErrors)}>
<div className='mx-auto grid max-w-[90rem] flex-1 auto-rows-max gap-6'> <div className='mx-auto grid max-w-[90rem] flex-1 auto-rows-max gap-6'>
<div className='flex items-center gap-4'> <div className='flex items-center gap-4'>
<Button <BackHistoryButton />
variant='outline'
size='icon'
className='h-7 w-7'
onClick={() => navigate("/quotes")}
>
<ChevronLeft className='w-4 h-4' />
<span className='sr-only'>{t("quotes.common.back")}</span>
</Button>
<h1 className='flex-1 text-xl font-semibold tracking-tight shrink-0 whitespace-nowrap sm:grow-0'> <h1 className='flex-1 text-xl font-semibold tracking-tight shrink-0 whitespace-nowrap sm:grow-0'>
{t("quotes.create.title")} {t("quotes.create.title")}
</h1> </h1>

View File

@ -1,12 +1,19 @@
import { ErrorOverlay, FormCurrencyField, LoadingOverlay, SubmitButton } from "@/components"; import {
BackHistoryButton,
CancelButton,
ErrorOverlay,
FormCurrencyField,
LoadingOverlay,
SubmitButton,
} from "@/components";
import { calculateItemTotals } from "@/lib/calc"; import { calculateItemTotals } from "@/lib/calc";
import { useUrlId } from "@/lib/hooks/useUrlId"; import { useUrlId } from "@/lib/hooks/useUrlId";
import { Badge, Button, Form, Tabs, TabsContent, TabsList, TabsTrigger } from "@/ui"; import { Badge, Button, Form, Tabs, TabsContent, TabsList, TabsTrigger } from "@/ui";
import { CurrencyData, IUpdateQuote_Request_DTO, MoneyValue } from "@shared/contexts"; import { CurrencyData, IUpdateQuote_Request_DTO, MoneyValue } from "@shared/contexts";
import { t } from "i18next"; import { t } from "i18next";
import { ChevronLeftIcon } from "lucide-react";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { SubmitHandler, useForm } from "react-hook-form"; import { SubmitHandler, useForm } from "react-hook-form";
import { useNavigate } from "react-router-dom";
import { QuoteDetailsCardEditor, QuoteGeneralCardEditor } from "./components/editors"; import { QuoteDetailsCardEditor, QuoteGeneralCardEditor } from "./components/editors";
import { useQuotes } from "./hooks"; import { useQuotes } from "./hooks";
@ -14,6 +21,7 @@ interface QuoteDataForm extends IUpdateQuote_Request_DTO {}
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
export const QuoteEdit = () => { export const QuoteEdit = () => {
const navigate = useNavigate();
const quoteId = useUrlId(); const quoteId = useUrlId();
const [quoteCurrency, setQuoteCurrency] = useState<CurrencyData>( const [quoteCurrency, setQuoteCurrency] = useState<CurrencyData>(
CurrencyData.createDefaultCode().object CurrencyData.createDefaultCode().object
@ -46,6 +54,20 @@ export const QuoteEdit = () => {
payment_method: "", payment_method: "",
notes: "", notes: "",
validity: "", validity: "",
subtotal_price: {
amount: undefined,
precision: 2,
currency_code: data?.currency_code,
},
discount: {
amount: undefined,
precision: 0,
},
total_price: {
amount: undefined,
precision: 2,
currency_code: data?.currency_code,
},
items: [], items: [],
}, },
}); });
@ -140,10 +162,7 @@ export const QuoteEdit = () => {
<form onSubmit={form.handleSubmit(onSubmit)}> <form onSubmit={form.handleSubmit(onSubmit)}>
<div className='mx-auto grid max-w-[90rem] flex-1 auto-rows-max gap-6'> <div className='mx-auto grid max-w-[90rem] flex-1 auto-rows-max gap-6'>
<div className='flex items-center gap-4'> <div className='flex items-center gap-4'>
<Button variant='outline' size='icon' className='h-7 w-7'> <BackHistoryButton />
<ChevronLeftIcon className='w-4 h-4' />
<span className='sr-only'>{t("quotes.common.back")}</span>
</Button>
<h1 className='flex-1 text-xl font-semibold tracking-tight shrink-0 whitespace-nowrap sm:grow-0'> <h1 className='flex-1 text-xl font-semibold tracking-tight shrink-0 whitespace-nowrap sm:grow-0'>
{t("quotes.edit.title")} {t("quotes.edit.title")}
</h1> </h1>
@ -151,9 +170,9 @@ export const QuoteEdit = () => {
{data.status} {data.status}
</Badge> </Badge>
<div className='items-center hidden gap-2 md:ml-auto md:flex'> <div className='items-center hidden gap-2 md:ml-auto md:flex'>
<Button variant='outline' size='sm'> <CancelButton variant='outline' size='sm' onClick={() => navigate("/quotes")}>
{t("common.cancel")} {t("common.cancel")}
</Button> </CancelButton>
<SubmitButton variant={form.formState.isDirty ? "default" : "outline"} size='sm'> <SubmitButton variant={form.formState.isDirty ? "default" : "outline"} size='sm'>
{t("common.save")} {t("common.save")}
</SubmitButton> </SubmitButton>

View File

@ -58,11 +58,14 @@ export const useQuotes = () => {
useSave<IUpdateQuote_Response_DTO, TDataSourceError, IUpdateQuote_Request_DTO>({ useSave<IUpdateQuote_Response_DTO, TDataSourceError, IUpdateQuote_Request_DTO>({
mutationKey: keys().data().resource("quotes").action("one").id(id).params().get(), mutationKey: keys().data().resource("quotes").action("one").id(id).params().get(),
mutationFn: (data) => { mutationFn: (data) => {
const { date: quoteDate } = data;
return dataSource.updateOne({ return dataSource.updateOne({
resource: "quotes", resource: "quotes",
id, id,
data: { data: {
...data, ...data,
date: new Date(quoteDate).toISOString().slice(0, 10),
}, },
}); });
}, },

View File

@ -1,45 +1,15 @@
import { Button, ButtonProps } from "@/ui"; import { Button } from "@/ui";
import { ChevronLeft } from "lucide-react"; import { t } from "i18next";
import { To, useNavigate } from "react-router-dom"; import { ChevronLeftIcon } from "lucide-react";
import { CustomButton } from "../Buttons/CustomButton"; import { useNavigate } from "react-router-dom";
export interface BackHistoryButtonProps extends ButtonProps { export const BackHistoryButton = () => {
label?: string;
url?: To;
}
export const BackHistoryButton = ({
label = "Volver atrás",
size,
url = undefined,
...props
}: BackHistoryButtonProps): JSX.Element => {
const navigate = useNavigate(); const navigate = useNavigate();
return ( return (
<CustomButton <Button variant='outline' size='icon' className='h-7 w-7' onClick={() => navigate(-1)}>
type='button' <ChevronLeftIcon className='w-4 h-4' />
label={label} <span className='sr-only'>{t("quotes.common.back")}</span>
icon={ChevronLeft}
variant='ghost'
onClick={() => {
url ? navigate(url) : navigate(-1);
}}
{...props}
/>
);
return (
<Button
variant='ghost'
onClick={() => {
url ? navigate(url) : navigate(-1);
}}
size={size}
{...props}
>
<ChevronLeft className='w-4 h-4' />
<span className={size === "icon" ? "sr-only" : "ml-2"}>{label}</span>
</Button> </Button>
); );
}; };

View File

@ -33,11 +33,16 @@ export function DataTablePagination<TData>({
return ( return (
<div className={cn("flex items-center justify-between px-2", className)}> <div className={cn("flex items-center justify-between px-2", className)}>
<div className='flex-1 text-base text-muted-foreground'> <div className='flex-1 text-base text-muted-foreground'>
{t("common.rows_selected", { {table.getSelectedRowModel().rows.length > 0 && (
count: table.getFilteredSelectedRowModel().rows.length, <>
total: table.getFilteredRowModel().rows.length, {t("common.rows_selected", {
})} count: table.getFilteredSelectedRowModel().rows.length,
total: table.getFilteredRowModel().rows.length,
})}
</>
)}
</div> </div>
<div className='flex items-center space-x-6 lg:space-x-8'> <div className='flex items-center space-x-6 lg:space-x-8'>
<div className='flex items-center space-x-2'> <div className='flex items-center space-x-2'>
<p className='text-base font-medium'>{t("common.rows_per_page")}</p> <p className='text-base font-medium'>{t("common.rows_per_page")}</p>

View File

@ -94,7 +94,7 @@ export const FormDatePickerField = React.forwardRef<
field.onChange(e); field.onChange(e);
setIsPopoverOpen(false); setIsPopoverOpen(false);
}} }}
disabled={(date) => date < new Date("2024-06-01")} disabled={(date) => date < new Date("1980-01-01")}
weekStartsOn={1} weekStartsOn={1}
fixedWeeks={true} fixedWeeks={true}
fromYear={2024} fromYear={2024}

View File

@ -75,6 +75,11 @@ export class QuoteRepository extends SequelizeRepository<Quote> implements IQuot
public async findAll(queryCriteria?: IQueryCriteria): Promise<ICollection<any>> { public async findAll(queryCriteria?: IQueryCriteria): Promise<ICollection<any>> {
const { rows, count } = await this._findAll("Quote_Model", queryCriteria, { const { rows, count } = await this._findAll("Quote_Model", queryCriteria, {
include: [], // esto es para quitar las asociaciones al hacer la consulta include: [], // esto es para quitar las asociaciones al hacer la consulta
order: [
["date", "DESC"],
["customer_information", "ASC"],
["status", "ASC"],
],
}); });
return this.mapper.mapArrayAndCountToDomain(rows, count); return this.mapper.mapArrayAndCountToDomain(rows, count);

View File

@ -49,20 +49,26 @@ export function ensureUpdateQuote_Request_DTOIsValid(quoteDTO: IUpdateQuote_Requ
notes: Joi.string().optional().allow(null).allow("").default(""), notes: Joi.string().optional().allow(null).allow("").default(""),
validity: Joi.string().optional().allow(null).allow("").default(""), validity: Joi.string().optional().allow(null).allow("").default(""),
subtotal: Joi.object({ subtotal_price: Joi.object({
amount: Joi.number(), amount: Joi.number(),
precision: Joi.number(), precision: Joi.number(),
currency_code: Joi.string(), currency_code: Joi.string(),
}), }).unknown(),
discount: Joi.object({ discount: Joi.object({
amount: Joi.number(), amount: Joi.number(),
precision: Joi.number(), precision: Joi.number(),
}).unknown(), }).unknown(),
total_price: Joi.object({
amount: Joi.number(),
precision: Joi.number(),
currency_code: Joi.string(),
}).unknown(),
items: Joi.array().items( items: Joi.array().items(
Joi.object({ Joi.object({
article_id: Joi.string(), article_id: Joi.string().optional().allow(null).allow("").default(""),
quantity: Joi.object({ quantity: Joi.object({
amount: Joi.number(), amount: Joi.number(),
precision: Joi.number(), precision: Joi.number(),