This commit is contained in:
David Arranz 2024-07-10 11:53:13 +02:00
parent 54dc0a8442
commit b2a8073007
14 changed files with 123 additions and 105 deletions

View File

@ -21,5 +21,5 @@ export const StartPage = () => {
); );
} }
return <Navigate to={"/quotes"} replace />; return <Navigate to={"/home"} replace />;
}; };

View File

@ -1,11 +1,11 @@
import { Container, FormTextField } from "@/components"; import { Container, FormTextField } from "@/components";
import { FormSubmitButton } from "@/components/Forms/FormSubmitButton";
import { UeckoLogo } from "@/components/UeckoLogo/UeckoLogo"; import { UeckoLogo } from "@/components/UeckoLogo/UeckoLogo";
import { useLogin } from "@/lib/hooks"; import { useLogin } from "@/lib/hooks";
import { import {
Alert, Alert,
AlertDescription, AlertDescription,
AlertTitle, AlertTitle,
Button,
Card, Card,
CardContent, CardContent,
CardDescription, CardDescription,
@ -18,7 +18,6 @@ import { ILogin_DTO } from "@shared/contexts";
import { t } from "i18next"; import { t } from "i18next";
import Joi from "joi"; import Joi from "joi";
import { AlertCircleIcon } from "lucide-react"; import { AlertCircleIcon } from "lucide-react";
import { useState } from "react";
import { SubmitHandler, useForm } from "react-hook-form"; import { SubmitHandler, useForm } from "react-hook-form";
import { Trans } from "react-i18next"; import { Trans } from "react-i18next";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
@ -27,7 +26,6 @@ import SpanishJoiMessages from "../../spanish-joi-messages.json";
type LoginDataForm = ILogin_DTO; type LoginDataForm = ILogin_DTO;
export const LoginPage = () => { export const LoginPage = () => {
const [loading, setLoading] = useState(false);
const { mutate: login } = useLogin({ const { mutate: login } = useLogin({
onSuccess: (data) => { onSuccess: (data) => {
const { success, error } = data; const { success, error } = data;
@ -57,12 +55,7 @@ export const LoginPage = () => {
}); });
const onSubmit: SubmitHandler<LoginDataForm> = async (data) => { const onSubmit: SubmitHandler<LoginDataForm> = async (data) => {
try { login({ email: data.email, password: data.password }, {});
setLoading(true);
login({ email: data.email, password: data.password });
} finally {
setLoading(false);
}
}; };
return ( return (
@ -121,9 +114,7 @@ export const LoginPage = () => {
</Alert> </Alert>
)} )}
<Button disabled={loading} type='submit' className='w-full'> <FormSubmitButton className='w-full' label={t("login_page.login")} />
<Trans i18nKey='login_page.login' />
</Button>
<div className='mt-4 text-sm text-center'> <div className='mt-4 text-sm text-center'>
<Trans i18nKey='login_page.become_dealer' /> <Trans i18nKey='login_page.become_dealer' />

View File

@ -1,12 +1,12 @@
import { Card, CardContent } from "@/ui"; import { Badge, Card, CardContent } from "@/ui";
import { DataTableSkeleton, ErrorOverlay, SimpleEmptyState } from "@/components"; import { DataTableSkeleton, ErrorOverlay, SimpleEmptyState } from "@/components";
import { DataTable } from "@/components"; import { DataTable } from "@/components";
import { DataTableToolbar } from "@/components/DataTable/DataTableToolbar"; import { DataTableToolbar } from "@/components/DataTable/DataTableToolbar";
import { useDataTable, useDataTableContext } from "@/lib/hooks"; import { useDataTable, useDataTableContext } from "@/lib/hooks";
import { IListQuotes_Response_DTO, MoneyValue } from "@shared/contexts"; import { IListQuotes_Response_DTO, MoneyValue, UTCDateValue } from "@shared/contexts";
import { ColumnDef, Row } from "@tanstack/react-table"; import { ColumnDef } from "@tanstack/react-table";
import { t } from "i18next"; import { t } from "i18next";
import { useMemo } from "react"; import { useMemo } from "react";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
@ -27,47 +27,51 @@ export const QuotesDataTable = () => {
const columns = useMemo<ColumnDef<IListQuotes_Response_DTO, any>[]>( const columns = useMemo<ColumnDef<IListQuotes_Response_DTO, any>[]>(
() => [ () => [
{ {
id: "id" as const, id: "date" as const,
accessorKey: "id", accessor: "date",
header: () => <>{t("quotes.list.columns.date")}</>,
cell: ({ table, row: { index, original }, column, getValue }) => {
console.log(original.date);
const quoteDate = UTCDateValue.create(original.date);
return quoteDate.isSuccess ? quoteDate.object.toLocaleDateString("es-ES") : "-";
},
enableResizing: false, enableResizing: false,
size: 10, size: 10,
}, },
{ {
id: "article_id" as const, id: "customer_information" as const,
accessorKey: "id_article", accessorKey: "customer_information",
enableResizing: false, header: () => <>{t("quotes.list.columns.customer_information")}</>,
size: 10,
},
{
id: "catalog_name" as const,
accessorKey: "catalog_name",
enableResizing: false,
size: 10,
},
{
id: "description" as const,
accessorKey: "description",
header: () => <>{t("catalog.list.columns.description")}</>,
enableResizing: false,
size: 100,
},
{
id: "points" as const,
accessorKey: "points",
header: () => <div className='text-right'>{t("catalog.list.columns.points")}</div>,
cell: ({ renderValue }: { renderValue: () => any }) => ( cell: ({ renderValue }: { renderValue: () => any }) => (
<div className='text-right'>{renderValue()}</div> <div className='font-semibold'>{renderValue()}</div>
), ),
enableResizing: false, enableResizing: false,
size: 20, size: 10,
}, },
{ {
id: "retail_price" as const, id: "reference" as const,
accessorKey: "retail_price", accessorKey: "reference",
header: () => <div className='text-right'>{t("catalog.list.columns.retail_price")}</div>, header: () => <>{t("quotes.list.columns.reference")}</>,
cell: ({ row }: { row: Row<any> }) => { enableResizing: false,
const price = MoneyValue.create(row.original.retail_price).object; size: 10,
return <div className='text-right'>{price.toFormat()}</div>; },
{
id: "status" as const,
accessorKey: "status",
header: () => <>{t("quotes.list.columns.status")}</>,
cell: ({ renderValue }: { renderValue: () => any }) => <Badge>{renderValue()}</Badge>,
enableResizing: false,
size: 10,
},
{
id: "total_price" as const,
accessor: "total_price",
header: () => <div className='text-right'>{t("quotes.list.columns.total_price")}</div>,
cell: ({ table, row: { index, original }, column, getValue }) => {
const price = MoneyValue.create(original.total_price);
return (
<div className='text-right'>{price.isSuccess ? price.object.toFormat() : "-"}</div>
);
}, },
enableResizing: false, enableResizing: false,
size: 20, size: 20,

View File

@ -19,17 +19,21 @@ const customButtonVariants = cva("", {
}); });
export interface CustomButtonProps extends ButtonProps, VariantProps<typeof customButtonVariants> { export interface CustomButtonProps extends ButtonProps, VariantProps<typeof customButtonVariants> {
icon: LucideIcon; // Propiedad para proporcionar el icono personalizado icon?: LucideIcon; // Propiedad para proporcionar el icono personalizado
label?: string; label?: string;
} }
const CustomButton = React.forwardRef<HTMLButtonElement, CustomButtonProps>( const CustomButton = React.forwardRef<HTMLButtonElement, CustomButtonProps>(
({ className, label, size, icon: Icon, children, ...props }, ref) => ( ({ className, label, size, icon: Icon, children, ...props }, ref) => {
<Button ref={ref} size={size} className={cn("gap-1", className)} {...props}> const hasIcon = !!Icon;
<Icon className={cn(customButtonVariants({ size }))} />
<>{label ? label : children}</> return (
</Button> <Button ref={ref} size={size} className={cn(hasIcon ? "gap-1" : "", className)} {...props}>
) {hasIcon && <Icon className={cn(customButtonVariants({ size }))} />}
<>{label ? label : children}</>
</Button>
);
}
); );
CustomButton.displayName = "CustomButton"; CustomButton.displayName = "CustomButton";

View File

@ -1,13 +1,12 @@
import { ButtonProps } from "@/ui"; import { ButtonProps } from "@/ui";
import { SaveIcon } from "lucide-react";
import { CustomButton } from "./CustomButton"; import { CustomButton } from "./CustomButton";
export interface SubmitButtonProps extends ButtonProps { export interface SubmitButtonProps extends ButtonProps {
label?: string; label?: string;
} }
export const SubmitButton = ({ label = "Guardar", ...props }: SubmitButtonProps) => ( export const SubmitButton = ({ label = "Enviar", ...props }: SubmitButtonProps) => (
<CustomButton type='submit' label={label} icon={SaveIcon} variant='default' {...props} /> <CustomButton type='submit' label={label} variant='default' {...props} />
); );
SubmitButton.displayName = "SubmitButton"; SubmitButton.displayName = "SubmitButton";

View File

@ -0,0 +1,11 @@
import { useFormState } from "react-hook-form";
import { SubmitButton, SubmitButtonProps } from "../Buttons";
export interface FromSubmitButtonProps extends SubmitButtonProps {}
export const FormSubmitButton = (props: FromSubmitButtonProps) => {
const { isSubmitting, isLoading, isValidating } = useFormState();
return <SubmitButton disabled={isSubmitting || isLoading || isValidating} {...props} />;
};
FormSubmitButton.displayName = "FormSubmitButton";

View File

@ -10,13 +10,6 @@ type ProctectRouteProps = {
export const ProtectedRoute = ({ children }: ProctectRouteProps) => { export const ProtectedRoute = ({ children }: ProctectRouteProps) => {
const { isPending, isSuccess, data: { authenticated, redirectTo } = {} } = useIsLoggedIn(); const { isPending, isSuccess, data: { authenticated, redirectTo } = {} } = useIsLoggedIn();
console.debug("ProtectedRouter", {
isPending,
isSuccess,
authenticated,
redirectTo,
});
if (isPending) { if (isPending) {
return <LoadingOverlay />; return <LoadingOverlay />;
} }

View File

@ -76,7 +76,14 @@
}, },
"quotes": { "quotes": {
"list": { "list": {
"title": "Cotizaciones" "title": "Cotizaciones",
"columns": {
"date": "Fecha",
"reference": "Referencia",
"status": "Estado",
"customer_information": "Cliente",
"total_price": "Imp. total"
}
}, },
"status": { "status": {
"draft": "Borrador" "draft": "Borrador"

View File

@ -11,12 +11,12 @@ export const CreateQuotePresenter: ICreateQuotePresenter = {
map: (quote: Quote, context: ISalesContext): ICreateQuote_Response_DTO => { map: (quote: Quote, context: ISalesContext): ICreateQuote_Response_DTO => {
return { return {
id: quote.id.toString(), id: quote.id.toString(),
//reference: quote.refe reference: quote.reference.toString(),
status: quote.status.toString(), status: quote.status.toString(),
date: quote.date.toString(), date: quote.date.toISO8601(),
lang_code: quote.language.toString(), lang_code: quote.language.toString(),
currency_code: quote.currency.toString(), currency_code: quote.currency.toString(),
customer_information: quote.customer, customer_information: quote.customer.toString(),
subtotal: { subtotal: {
amount: 0, amount: 0,
precision: 2, precision: 2,

View File

@ -16,10 +16,10 @@ export const GetQuotePresenter: IGetQuotePresenter = {
return { return {
id: quote.id.toString(), id: quote.id.toString(),
status: quote.status.toString(), status: quote.status.toString(),
date: quote.date.toString(), date: quote.date.toISO8601(),
reference: quote.reference.toString(), reference: quote.reference.toString(),
customer_information: quote.customer.toString(), customer_information: quote.customer.toString(),
lang_code: quote.language.code, lang_code: quote.language.toString(),
currency_code: quote.currency.toString(), currency_code: quote.currency.toString(),
payment_method: quote.paymentMethod.toString(), payment_method: quote.paymentMethod.toString(),

View File

@ -20,10 +20,10 @@ export const ListQuotesPresenter: IListQuotesPresenter = {
return { return {
id: quote.id.toString(), id: quote.id.toString(),
status: quote.status.toString(), status: quote.status.toString(),
date: quote.date.toString(), date: quote.date.toISO8601(),
reference: quote.reference.toString(), reference: quote.reference.toString(),
customer_information: quote.customer.toString(), customer_information: quote.customer.toString(),
lang_code: quote.date.toISO8601(), lang_code: quote.language.toString(),
currency_code: quote.currency.toString(), currency_code: quote.currency.toString(),
subtotal: { subtotal: {

View File

@ -12,20 +12,22 @@ export const UpdateQuotePresenter: IUpdateQuotePresenter = {
return { return {
id: quote.id.toString(), id: quote.id.toString(),
status: quote.status.toString(), status: quote.status.toString(),
date: quote.date.toString(), date: quote.date.toISO8601(),
language_code: quote.date.toString(), reference: quote.reference.toString(),
customer_information: quote.customer.toString(),
lang_code: quote.language.toString(),
currency_code: quote.currency.toString(), currency_code: quote.currency.toString(),
subtotal: {
amount: 0, payment_method: quote.paymentMethod.toString(),
precision: 2, validity: quote.validity.toString(),
currency: "EUR", notes: quote.notes.toString(),
},
total: { subtotal_price: quote.subtotalPrice.toObject(),
amount: 0, discount: quote.discount.toObject(),
precision: 2, total_price: quote.totalPrice.toObject(),
currency: "EUR",
},
items: quoteItemPresenter(quote.items, context), items: quoteItemPresenter(quote.items, context),
dealer_id: quote.dealerId.toString(),
}; };
}, },
}; };
@ -34,23 +36,12 @@ export const UpdateQuotePresenter: IUpdateQuotePresenter = {
const quoteItemPresenter = (items: ICollection<QuoteItem>, context: ISalesContext) => const quoteItemPresenter = (items: ICollection<QuoteItem>, context: ISalesContext) =>
items.totalCount > 0 items.totalCount > 0
? items.items.map((item: QuoteItem) => ({ ? items.items.map((item: QuoteItem) => ({
article_id: item.articleId,
description: item.description.toString(), description: item.description.toString(),
quantity: item.quantity.toString(), quantity: item.quantity.toObject(),
unit_measure: "", unit_price: item.unitPrice.toObject(),
unit_price: { subtotal_price: item.subtotalPrice.toObject(),
amount: 0, discount: item.discount.toObject(),
precision: 2, total_price: item.totalPrice.toObject(),
currency: "EUR",
},
subtotal: {
amount: 0,
precision: 2,
currency: "EUR",
},
total: {
amount: 0,
precision: 2,
currency: "EUR",
},
})) }))
: []; : [];

View File

@ -55,7 +55,25 @@ export class UTCDateValue extends ValueObject<Date> {
return this.isValid() ? this.props.toISOString() : ""; return this.isValid() ? this.props.toISOString() : "";
}; };
public toDateString = (): string => {
// Tue Jul 09 2024
return this.isValid() ? this.props.toDateString() : "";
};
public toLocaleDateString = (
locales?: Intl.LocalesArgument,
options?: Intl.DateTimeFormatOptions
): string => {
// DD/MM/YYYY
return this.isValid() ? this.props.toLocaleDateString(locales, options) : "";
};
public toLocaleTimeString = (): string => {
return this.isValid() ? this.props.toLocaleTimeString() : "";
};
public toString(): string { public toString(): string {
// YYYY-MM-DD
if (!this.isEmpty()) { if (!this.isEmpty()) {
const year = this.props.getFullYear(); const year = this.props.getFullYear();
const month = String(this.props.getMonth() + 1).padStart(2, "0"); const month = String(this.props.getMonth() + 1).padStart(2, "0");

View File

@ -1,4 +1,4 @@
import { IQuantuty_Response_DTO } from "../../../../../common"; import { IMoney_Response_DTO } from "../../../../../common";
export interface IListQuotes_Response_DTO { export interface IListQuotes_Response_DTO {
id: string; id: string;
@ -9,6 +9,6 @@ export interface IListQuotes_Response_DTO {
lang_code: string; lang_code: string;
currency_code: string; currency_code: string;
subtotal: IQuantuty_Response_DTO; subtotal_price: IMoney_Response_DTO;
total: IQuantuty_Response_DTO; total_price: IMoney_Response_DTO;
} }