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

View File

@ -6,11 +6,9 @@ import {
} from "@/components";
import { t } from "i18next";
import { ChevronLeft } from "lucide-react";
import { SubmitButton } from "@/components";
import { useWarnAboutChange } from "@/lib/hooks";
import { Button, Form } from "@/ui";
import { Form } from "@/ui";
import { ICreateQuote_Request_DTO } from "@shared/contexts";
import { useEffect } from "react";
import { FieldErrors, SubmitErrorHandler, SubmitHandler, useForm } from "react-hook-form";
@ -52,7 +50,7 @@ export const QuoteCreate = () => {
setWarnWhen(false);
mutate(formData, {
onSuccess: (data) => {
navigate(`/quotes/edit/${data.id}`, { relative: "path", replace: true });
navigate(`/quotes/edit/${data.id}`, { relative: "path" });
},
});
} finally {
@ -71,15 +69,7 @@ export const QuoteCreate = () => {
<form onSubmit={handleSubmit(onSubmit, onErrors)}>
<div className='mx-auto grid max-w-[90rem] flex-1 auto-rows-max gap-6'>
<div className='flex items-center gap-4'>
<Button
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>
<BackHistoryButton />
<h1 className='flex-1 text-xl font-semibold tracking-tight shrink-0 whitespace-nowrap sm:grow-0'>
{t("quotes.create.title")}
</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 { useUrlId } from "@/lib/hooks/useUrlId";
import { Badge, Button, Form, Tabs, TabsContent, TabsList, TabsTrigger } from "@/ui";
import { CurrencyData, IUpdateQuote_Request_DTO, MoneyValue } from "@shared/contexts";
import { t } from "i18next";
import { ChevronLeftIcon } from "lucide-react";
import { useEffect, useState } from "react";
import { SubmitHandler, useForm } from "react-hook-form";
import { useNavigate } from "react-router-dom";
import { QuoteDetailsCardEditor, QuoteGeneralCardEditor } from "./components/editors";
import { useQuotes } from "./hooks";
@ -14,6 +21,7 @@ interface QuoteDataForm extends IUpdateQuote_Request_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
@ -46,6 +54,20 @@ export const QuoteEdit = () => {
payment_method: "",
notes: "",
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: [],
},
});
@ -140,10 +162,7 @@ export const QuoteEdit = () => {
<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'>
<Button variant='outline' size='icon' className='h-7 w-7'>
<ChevronLeftIcon className='w-4 h-4' />
<span className='sr-only'>{t("quotes.common.back")}</span>
</Button>
<BackHistoryButton />
<h1 className='flex-1 text-xl font-semibold tracking-tight shrink-0 whitespace-nowrap sm:grow-0'>
{t("quotes.edit.title")}
</h1>
@ -151,9 +170,9 @@ export const QuoteEdit = () => {
{data.status}
</Badge>
<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")}
</Button>
</CancelButton>
<SubmitButton variant={form.formState.isDirty ? "default" : "outline"} size='sm'>
{t("common.save")}
</SubmitButton>

View File

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

View File

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

View File

@ -33,11 +33,16 @@ export function DataTablePagination<TData>({
return (
<div className={cn("flex items-center justify-between px-2", className)}>
<div className='flex-1 text-base text-muted-foreground'>
{t("common.rows_selected", {
count: table.getFilteredSelectedRowModel().rows.length,
total: table.getFilteredRowModel().rows.length,
})}
{table.getSelectedRowModel().rows.length > 0 && (
<>
{t("common.rows_selected", {
count: table.getFilteredSelectedRowModel().rows.length,
total: table.getFilteredRowModel().rows.length,
})}
</>
)}
</div>
<div className='flex items-center space-x-6 lg:space-x-8'>
<div className='flex items-center space-x-2'>
<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);
setIsPopoverOpen(false);
}}
disabled={(date) => date < new Date("2024-06-01")}
disabled={(date) => date < new Date("1980-01-01")}
weekStartsOn={1}
fixedWeeks={true}
fromYear={2024}

View File

@ -75,6 +75,11 @@ export class QuoteRepository extends SequelizeRepository<Quote> implements IQuot
public async findAll(queryCriteria?: IQueryCriteria): Promise<ICollection<any>> {
const { rows, count } = await this._findAll("Quote_Model", queryCriteria, {
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);

View File

@ -49,20 +49,26 @@ export function ensureUpdateQuote_Request_DTOIsValid(quoteDTO: IUpdateQuote_Requ
notes: 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(),
precision: Joi.number(),
currency_code: Joi.string(),
}),
}).unknown(),
discount: Joi.object({
amount: Joi.number(),
precision: Joi.number(),
}).unknown(),
total_price: Joi.object({
amount: Joi.number(),
precision: Joi.number(),
currency_code: Joi.string(),
}).unknown(),
items: Joi.array().items(
Joi.object({
article_id: Joi.string(),
article_id: Joi.string().optional().allow(null).allow("").default(""),
quantity: Joi.object({
amount: Joi.number(),
precision: Joi.number(),