2024-08-20 15:23:25 +00:00
|
|
|
import {
|
2024-08-25 20:06:24 +00:00
|
|
|
ButtonGroup,
|
2024-08-20 15:23:25 +00:00
|
|
|
ColorBadge,
|
|
|
|
|
DataTable,
|
|
|
|
|
DataTableSkeleton,
|
|
|
|
|
ErrorOverlay,
|
|
|
|
|
SimpleEmptyState,
|
|
|
|
|
} from "@/components";
|
2024-06-29 19:39:25 +00:00
|
|
|
import { DataTableToolbar } from "@/components/DataTable/DataTableToolbar";
|
|
|
|
|
import { useDataTable, useDataTableContext } from "@/lib/hooks";
|
2024-08-20 11:44:04 +00:00
|
|
|
import {
|
|
|
|
|
Button,
|
|
|
|
|
Card,
|
|
|
|
|
CardContent,
|
|
|
|
|
DropdownMenu,
|
|
|
|
|
DropdownMenuContent,
|
|
|
|
|
DropdownMenuItem,
|
|
|
|
|
DropdownMenuSeparator,
|
|
|
|
|
DropdownMenuTrigger,
|
|
|
|
|
Tooltip,
|
|
|
|
|
TooltipContent,
|
|
|
|
|
TooltipTrigger,
|
|
|
|
|
} from "@/ui";
|
2024-08-21 09:30:47 +00:00
|
|
|
import { useToast } from "@/ui/use-toast";
|
2024-08-26 18:29:41 +00:00
|
|
|
import { IListQuotes_Response_DTO, UTCDateValue } from "@shared/contexts";
|
2024-08-19 09:33:42 +00:00
|
|
|
import { ColumnDef, Row } from "@tanstack/react-table";
|
2024-06-29 19:39:25 +00:00
|
|
|
import { t } from "i18next";
|
2024-10-25 09:49:10 +00:00
|
|
|
import { FilePenLineIcon, MoreVerticalIcon, SendIcon } from "lucide-react";
|
2024-08-22 17:23:12 +00:00
|
|
|
import { useCallback, useEffect, useId, useMemo, useState } from "react";
|
2024-06-29 19:39:25 +00:00
|
|
|
import { useNavigate } from "react-router-dom";
|
2024-07-11 18:31:01 +00:00
|
|
|
import { useQuotes } from "../hooks";
|
2024-08-21 09:30:47 +00:00
|
|
|
import { DownloadQuoteDialog } from "./DownloadQuoteDialog";
|
2024-08-26 18:29:41 +00:00
|
|
|
import { QuoteResume } from "./QuoteResume";
|
2024-06-29 19:39:25 +00:00
|
|
|
|
2024-08-20 11:44:04 +00:00
|
|
|
export const QuotesDataTable = ({
|
|
|
|
|
status = "all",
|
|
|
|
|
preview = false,
|
|
|
|
|
}: {
|
|
|
|
|
status?: string;
|
|
|
|
|
preview?: boolean;
|
|
|
|
|
}) => {
|
2024-06-29 19:39:25 +00:00
|
|
|
const navigate = useNavigate();
|
2024-08-21 09:30:47 +00:00
|
|
|
const { toast } = useToast();
|
2024-08-22 17:23:12 +00:00
|
|
|
|
|
|
|
|
const tableId = useId();
|
|
|
|
|
const previewId = useId();
|
|
|
|
|
|
2024-06-29 19:39:25 +00:00
|
|
|
const { pagination, globalFilter, isFiltered } = useDataTableContext();
|
2024-08-20 15:16:41 +00:00
|
|
|
|
|
|
|
|
const [activeRow, setActiveRow] = useState<Row<IListQuotes_Response_DTO> | undefined>(undefined);
|
|
|
|
|
|
2024-08-21 11:16:56 +00:00
|
|
|
const { useList, useDownloader, getQuotePDFFilename } = useQuotes();
|
2024-07-11 18:31:01 +00:00
|
|
|
|
|
|
|
|
const { data, isPending, isError, error } = useList({
|
2024-06-29 19:39:25 +00:00
|
|
|
pagination: {
|
|
|
|
|
pageIndex: pagination.pageIndex,
|
|
|
|
|
pageSize: pagination.pageSize,
|
|
|
|
|
},
|
2024-07-11 18:31:01 +00:00
|
|
|
status,
|
|
|
|
|
quickSearchTerm: globalFilter,
|
2024-06-29 19:39:25 +00:00
|
|
|
});
|
|
|
|
|
|
2024-08-20 21:40:37 +00:00
|
|
|
const { download, ...downloadProps } = useDownloader();
|
|
|
|
|
|
2024-09-02 15:44:49 +00:00
|
|
|
const handleOnRowClick = (row: Row<IListQuotes_Response_DTO>) => {
|
|
|
|
|
setActiveRow(row);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleFinishDownload = useCallback(() => {
|
|
|
|
|
toast({
|
|
|
|
|
description: t("quotes.downloading_dialog.toast_success"),
|
|
|
|
|
});
|
|
|
|
|
}, [toast]);
|
|
|
|
|
|
|
|
|
|
const handleEditQuote = useCallback(
|
|
|
|
|
(quote: IListQuotes_Response_DTO) => {
|
|
|
|
|
navigate(`/quotes/edit/${quote.id}`, { relative: "path" });
|
|
|
|
|
},
|
|
|
|
|
[navigate, toast]
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const columns = useMemo<ColumnDef<IListQuotes_Response_DTO, unknown>[]>(() => {
|
2024-08-27 18:58:35 +00:00
|
|
|
const columns = [
|
2024-08-23 16:47:51 +00:00
|
|
|
{
|
|
|
|
|
id: "reference" as const,
|
|
|
|
|
accessorKey: "reference",
|
|
|
|
|
header: () => <>{t("quotes.list.columns.reference")}</>,
|
2024-09-02 15:44:49 +00:00
|
|
|
cell: ({
|
|
|
|
|
row: { original },
|
|
|
|
|
renderValue,
|
|
|
|
|
}: {
|
|
|
|
|
row: { original: IListQuotes_Response_DTO };
|
|
|
|
|
renderValue: () => any;
|
2024-10-25 09:49:10 +00:00
|
|
|
}) => {
|
2024-11-12 11:58:58 +00:00
|
|
|
//const allowToSent = original?.status === "accepted" && !original?.date_sent;
|
2024-10-25 09:49:10 +00:00
|
|
|
const isSent = Boolean(original?.status === "accepted" && original?.date_sent);
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<Button
|
|
|
|
|
size='sm'
|
|
|
|
|
variant='link'
|
2024-11-12 09:53:42 +00:00
|
|
|
disabled={isSent}
|
2024-10-25 09:49:10 +00:00
|
|
|
className='h-8 gap-1 px-0 text-left text-ellipsis'
|
|
|
|
|
onClick={(e) => {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
handleEditQuote(original);
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<div className=''>{renderValue()}</div>
|
|
|
|
|
</Button>
|
|
|
|
|
);
|
|
|
|
|
},
|
2024-08-23 16:47:51 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
id: "status" as const,
|
|
|
|
|
accessorKey: "status",
|
|
|
|
|
header: () => <>{t("quotes.list.columns.status")}</>,
|
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
2024-09-02 15:44:49 +00:00
|
|
|
cell: ({ row: { original } }: { row: { original: IListQuotes_Response_DTO } }) => (
|
2024-08-28 18:38:20 +00:00
|
|
|
<ColorBadge label={t(`quotes.status.${original.status}`)} />
|
|
|
|
|
),
|
2024-08-23 16:47:51 +00:00
|
|
|
},
|
2024-10-25 09:49:10 +00:00
|
|
|
{
|
|
|
|
|
id: "date_sent" as const,
|
|
|
|
|
accessor: "date_sent",
|
|
|
|
|
header: () => (
|
|
|
|
|
<div className='text-left text-ellipsis'>{t("quotes.list.columns.date_sent")}</div>
|
|
|
|
|
),
|
|
|
|
|
cell: ({ row: { original } }: { row: { original: IListQuotes_Response_DTO } }) => {
|
|
|
|
|
const quoteDate = UTCDateValue.create(original.date_sent);
|
|
|
|
|
return (
|
|
|
|
|
<div className='text-left text-ellipsis'>
|
|
|
|
|
{quoteDate.isSuccess ? (
|
|
|
|
|
<ColorBadge label={quoteDate.object.toLocaleDateString("es-ES")} />
|
|
|
|
|
) : (
|
|
|
|
|
<>-</>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
},
|
2024-06-29 19:39:25 +00:00
|
|
|
{
|
2024-07-10 09:53:13 +00:00
|
|
|
id: "date" as const,
|
|
|
|
|
accessor: "date",
|
2024-08-23 16:47:51 +00:00
|
|
|
header: () => (
|
2024-10-25 09:49:10 +00:00
|
|
|
<div className='text-left text-ellipsis'>{t("quotes.list.columns.date")}</div>
|
2024-08-23 16:47:51 +00:00
|
|
|
),
|
2024-09-02 15:44:49 +00:00
|
|
|
cell: ({ row: { original } }: { row: { original: IListQuotes_Response_DTO } }) => {
|
2024-07-10 09:53:13 +00:00
|
|
|
const quoteDate = UTCDateValue.create(original.date);
|
2024-08-20 11:44:04 +00:00
|
|
|
return (
|
2024-10-25 09:49:10 +00:00
|
|
|
<div className='text-left text-ellipsis'>
|
2024-08-20 11:44:04 +00:00
|
|
|
{quoteDate.isSuccess ? quoteDate.object.toLocaleDateString("es-ES") : "-"}
|
|
|
|
|
</div>
|
|
|
|
|
);
|
2024-07-10 09:53:13 +00:00
|
|
|
},
|
2024-06-29 19:39:25 +00:00
|
|
|
},
|
2024-08-23 16:47:51 +00:00
|
|
|
{
|
|
|
|
|
id: "customer_reference" as const,
|
|
|
|
|
accessorKey: "customer_reference",
|
|
|
|
|
header: () => <>{t("quotes.list.columns.customer_reference")}</>,
|
2024-09-02 15:44:49 +00:00
|
|
|
cell: ({ renderValue }: { renderValue: () => any }) => (
|
|
|
|
|
<div className='text-left text-ellipsis'>{renderValue()}</div>
|
|
|
|
|
),
|
2024-08-23 16:47:51 +00:00
|
|
|
},
|
2024-06-29 19:39:25 +00:00
|
|
|
{
|
2024-07-10 09:53:13 +00:00
|
|
|
id: "customer_information" as const,
|
|
|
|
|
accessorKey: "customer_information",
|
|
|
|
|
header: () => <>{t("quotes.list.columns.customer_information")}</>,
|
2024-09-02 15:44:49 +00:00
|
|
|
cell: ({ row: { original } }: { row: { original: IListQuotes_Response_DTO } }) => (
|
2024-08-20 11:44:04 +00:00
|
|
|
<div className='text-left text-ellipsis'>
|
2024-07-10 10:41:00 +00:00
|
|
|
{original.customer_information.split("\n").map((item, index) => {
|
|
|
|
|
return (
|
2024-08-16 14:57:44 +00:00
|
|
|
<span
|
|
|
|
|
key={index}
|
|
|
|
|
className={
|
|
|
|
|
index === 0 ? "font-medium" : "hidden text-sm text-muted-foreground md:inline"
|
|
|
|
|
}
|
|
|
|
|
>
|
2024-07-10 10:41:00 +00:00
|
|
|
{item}
|
|
|
|
|
<br />
|
|
|
|
|
</span>
|
|
|
|
|
);
|
|
|
|
|
})}
|
|
|
|
|
</div>
|
2024-07-10 09:53:13 +00:00
|
|
|
),
|
2024-08-25 20:06:24 +00:00
|
|
|
size: 600,
|
2024-06-29 19:39:25 +00:00
|
|
|
},
|
2024-10-25 09:49:10 +00:00
|
|
|
|
2024-08-26 18:29:41 +00:00
|
|
|
/*{
|
2024-08-27 18:58:35 +00:00
|
|
|
id: "total_price" as const,
|
|
|
|
|
accessor: "total_price",
|
|
|
|
|
header: () => <div className='text-right'>{t("quotes.list.columns.total_price")}</div>,
|
|
|
|
|
cell: ({ row: { original } }) => {
|
|
|
|
|
const price = MoneyValue.create(original.total_price);
|
|
|
|
|
return (
|
|
|
|
|
<div className='text-right'>{price.isSuccess ? price.object.toFormat() : "-"}</div>
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
},*/
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
const actionColumn = [
|
2024-07-10 10:41:00 +00:00
|
|
|
{
|
2024-08-20 11:44:04 +00:00
|
|
|
id: "row-actions",
|
2024-07-10 10:41:00 +00:00
|
|
|
header: () => null,
|
2024-10-25 09:49:10 +00:00
|
|
|
cell: ({ row: { original } }: { row: { original: IListQuotes_Response_DTO } }) => {
|
|
|
|
|
const allowToSent = original?.status === "accepted" && !original?.date_sent;
|
|
|
|
|
const isSent = original?.status === "accepted" && original?.date_sent;
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<ButtonGroup>
|
|
|
|
|
<Tooltip>
|
|
|
|
|
<TooltipTrigger asChild>
|
|
|
|
|
<>
|
|
|
|
|
{allowToSent && !isSent && (
|
|
|
|
|
<Button
|
|
|
|
|
size='sm'
|
|
|
|
|
variant='default'
|
|
|
|
|
className='h-8 gap-1'
|
|
|
|
|
onClick={(e) => {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
//handleSentToUecko(original);
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<SendIcon className='h-3.5 w-3.5' />
|
|
|
|
|
<span className='lg:sr-only xl:not-sr-only xl:whitespace-nowrap'>
|
|
|
|
|
{t("quotes.list.columns.actions.sent_to")}
|
|
|
|
|
</span>
|
|
|
|
|
</Button>
|
|
|
|
|
)}
|
|
|
|
|
|
2024-11-12 09:53:42 +00:00
|
|
|
{!isSent && (
|
2024-10-25 09:49:10 +00:00
|
|
|
<Button
|
|
|
|
|
size='sm'
|
|
|
|
|
variant='outline'
|
|
|
|
|
className='h-8 gap-1'
|
|
|
|
|
onClick={(e) => {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
handleEditQuote(original);
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<FilePenLineIcon className='h-3.5 w-3.5' />
|
|
|
|
|
<span className='lg:sr-only xl:not-sr-only xl:whitespace-nowrap'>
|
|
|
|
|
{t("quotes.list.columns.actions.edit")}
|
|
|
|
|
</span>
|
|
|
|
|
</Button>
|
|
|
|
|
)}
|
|
|
|
|
</>
|
|
|
|
|
</TooltipTrigger>
|
|
|
|
|
<TooltipContent>
|
|
|
|
|
<p>{t("quotes.list.columns.actions.sent_to_uecko")}</p>
|
|
|
|
|
</TooltipContent>
|
|
|
|
|
</Tooltip>
|
|
|
|
|
|
|
|
|
|
<DropdownMenu>
|
|
|
|
|
<DropdownMenuTrigger asChild>
|
|
|
|
|
<Button size='icon' variant='outline' className='w-8 h-8'>
|
|
|
|
|
<MoreVerticalIcon className='h-3.5 w-3.5' />
|
|
|
|
|
<span className='sr-only'>{t("common.more")}</span>
|
2024-10-03 14:23:46 +00:00
|
|
|
</Button>
|
2024-10-25 09:49:10 +00:00
|
|
|
</DropdownMenuTrigger>
|
|
|
|
|
<DropdownMenuContent align='end'>
|
|
|
|
|
<DropdownMenuItem
|
|
|
|
|
onClick={() => {
|
|
|
|
|
download(original.id, getQuotePDFFilename(original));
|
2024-10-03 14:23:46 +00:00
|
|
|
}}
|
|
|
|
|
>
|
2024-10-25 09:49:10 +00:00
|
|
|
Download
|
|
|
|
|
</DropdownMenuItem>
|
|
|
|
|
<DropdownMenuSeparator />
|
|
|
|
|
<DropdownMenuItem>{t("common.archive")}</DropdownMenuItem>
|
|
|
|
|
</DropdownMenuContent>
|
|
|
|
|
</DropdownMenu>
|
|
|
|
|
</ButtonGroup>
|
|
|
|
|
);
|
|
|
|
|
},
|
2024-06-29 19:39:25 +00:00
|
|
|
},
|
2024-08-27 18:58:35 +00:00
|
|
|
];
|
|
|
|
|
|
|
|
|
|
return preview ? columns : [...columns, ...actionColumn];
|
|
|
|
|
}, [preview]);
|
2024-06-29 19:39:25 +00:00
|
|
|
|
|
|
|
|
const { table } = useDataTable({
|
|
|
|
|
data: data?.items ?? [],
|
|
|
|
|
columns: columns,
|
|
|
|
|
pageCount: data?.total_pages ?? -1,
|
|
|
|
|
});
|
|
|
|
|
|
2024-08-20 15:16:41 +00:00
|
|
|
useEffect(() => {
|
|
|
|
|
if (table && data && data?.total_pages > 0) {
|
|
|
|
|
setActiveRow(table.getRowModel().rows[0]);
|
|
|
|
|
}
|
|
|
|
|
}, [data, table]);
|
|
|
|
|
|
2024-06-29 19:39:25 +00:00
|
|
|
if (isError) {
|
|
|
|
|
return <ErrorOverlay subtitle={(error as Error).message} />;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (isPending) {
|
|
|
|
|
return (
|
2024-08-18 20:39:06 +00:00
|
|
|
<div className='grid items-start flex-1 gap-4 sm:py-0 md:gap-8 lg:grid-cols-3 xl:grid-cols-3'>
|
|
|
|
|
<Card className='grid items-start gap-4 auto-rows-max md:gap-8 lg:col-span-2'>
|
|
|
|
|
<CardContent>
|
|
|
|
|
<DataTableSkeleton
|
|
|
|
|
columnCount={6}
|
|
|
|
|
searchableColumnCount={1}
|
|
|
|
|
filterableColumnCount={2}
|
|
|
|
|
//cellWidths={["10rem", "40rem", "12rem", "12rem", "8rem"]}
|
|
|
|
|
shrinkZero
|
|
|
|
|
/>
|
|
|
|
|
</CardContent>
|
|
|
|
|
</Card>
|
|
|
|
|
<div></div>
|
|
|
|
|
</div>
|
2024-06-29 19:39:25 +00:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (data?.total_items === 0 && !isFiltered) {
|
|
|
|
|
return (
|
2024-08-26 15:53:58 +00:00
|
|
|
<Card>
|
|
|
|
|
<CardContent>
|
|
|
|
|
<SimpleEmptyState
|
|
|
|
|
subtitle='Empieza creando alguna cotización'
|
|
|
|
|
buttonText={t("quotes.create.title")}
|
|
|
|
|
onButtonClick={() => navigate("add", { relative: "path" })}
|
|
|
|
|
/>
|
|
|
|
|
</CardContent>
|
|
|
|
|
</Card>
|
2024-06-29 19:39:25 +00:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (
|
2024-08-20 21:40:37 +00:00
|
|
|
<>
|
2024-08-28 10:19:33 +00:00
|
|
|
<div className='flex flex-col items-stretch flex-1 gap-4 xl:flex-row'>
|
|
|
|
|
<div id={tableId} className='flex items-stretch flex-1'>
|
2024-08-20 21:40:37 +00:00
|
|
|
<DataTable
|
|
|
|
|
table={table}
|
|
|
|
|
paginationOptions={{ visible: true }}
|
|
|
|
|
className='grid items-start flex-1 gap-4 auto-rows-max md:gap-8 lg:col-span-2'
|
|
|
|
|
onRowClick={handleOnRowClick}
|
|
|
|
|
activeRowIndex={activeRow?.index}
|
|
|
|
|
>
|
|
|
|
|
<DataTableToolbar table={table} />
|
|
|
|
|
</DataTable>
|
2024-08-28 10:19:33 +00:00
|
|
|
</div>
|
|
|
|
|
|
2024-08-20 21:40:37 +00:00
|
|
|
{preview && (
|
2024-08-28 18:38:20 +00:00
|
|
|
<div id={previewId} className='flex items-stretch '>
|
2024-08-26 18:29:41 +00:00
|
|
|
<QuoteResume quoteId={activeRow?.original.id} />
|
2024-08-28 10:19:33 +00:00
|
|
|
</div>
|
2024-08-20 21:40:37 +00:00
|
|
|
)}
|
2024-08-28 10:19:33 +00:00
|
|
|
</div>
|
2024-08-21 09:30:47 +00:00
|
|
|
<DownloadQuoteDialog {...downloadProps} onFinishDownload={handleFinishDownload} />
|
2024-08-20 21:40:37 +00:00
|
|
|
</>
|
2024-06-29 19:39:25 +00:00
|
|
|
);
|
|
|
|
|
};
|