.
This commit is contained in:
parent
54dc0a8442
commit
b2a8073007
@ -21,5 +21,5 @@ export const StartPage = () => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return <Navigate to={"/quotes"} replace />;
|
return <Navigate to={"/home"} replace />;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -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' />
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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";
|
||||||
|
|||||||
@ -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";
|
||||||
|
|||||||
11
client/src/components/Forms/FormSubmitButton.tsx
Normal file
11
client/src/components/Forms/FormSubmitButton.tsx
Normal 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";
|
||||||
@ -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 />;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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"
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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(),
|
||||||
|
|||||||
@ -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: {
|
||||||
|
|||||||
@ -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",
|
|
||||||
},
|
|
||||||
}))
|
}))
|
||||||
: [];
|
: [];
|
||||||
|
|||||||
@ -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");
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user