2024-08-20 15:23:25 +00:00
|
|
|
import {
|
|
|
|
|
ColorBadge,
|
|
|
|
|
DataTable,
|
|
|
|
|
DataTableSkeleton,
|
2024-08-20 21:40:37 +00:00
|
|
|
DownloadDialog,
|
2024-08-20 15:23:25 +00:00
|
|
|
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,
|
|
|
|
|
ResizableHandle,
|
|
|
|
|
ResizablePanel,
|
|
|
|
|
ResizablePanelGroup,
|
|
|
|
|
Tooltip,
|
|
|
|
|
TooltipContent,
|
|
|
|
|
TooltipTrigger,
|
|
|
|
|
} from "@/ui";
|
2024-07-10 09:53:13 +00:00
|
|
|
import { IListQuotes_Response_DTO, MoneyValue, 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-08-20 11:44:04 +00:00
|
|
|
import { FilePenLineIcon, MoreVerticalIcon } from "lucide-react";
|
2024-08-20 15:16:41 +00:00
|
|
|
import { useEffect, useMemo, useState } from "react";
|
2024-06-29 19:39:25 +00:00
|
|
|
import { useNavigate } from "react-router-dom";
|
2024-08-20 21:40:37 +00:00
|
|
|
import useDownloader from "react-use-downloader";
|
2024-07-11 18:31:01 +00:00
|
|
|
import { useQuotes } from "../hooks";
|
2024-08-19 16:13:07 +00:00
|
|
|
import { QuotePDFPreview } from "./QuotePDFPreview";
|
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();
|
|
|
|
|
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-20 21:40:37 +00:00
|
|
|
const { useList, 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-06-29 19:39:25 +00:00
|
|
|
const columns = useMemo<ColumnDef<IListQuotes_Response_DTO, any>[]>(
|
|
|
|
|
() => [
|
|
|
|
|
{
|
2024-07-10 09:53:13 +00:00
|
|
|
id: "date" as const,
|
|
|
|
|
accessor: "date",
|
|
|
|
|
header: () => <>{t("quotes.list.columns.date")}</>,
|
2024-08-19 09:33:42 +00:00
|
|
|
cell: ({ row: { original } }) => {
|
2024-07-10 09:53:13 +00:00
|
|
|
const quoteDate = UTCDateValue.create(original.date);
|
2024-08-20 11:44:04 +00:00
|
|
|
return (
|
|
|
|
|
<div className='text-right text-ellipsis'>
|
|
|
|
|
{quoteDate.isSuccess ? quoteDate.object.toLocaleDateString("es-ES") : "-"}
|
|
|
|
|
</div>
|
|
|
|
|
);
|
2024-07-10 09:53:13 +00:00
|
|
|
},
|
2024-06-29 19:39:25 +00:00
|
|
|
enableResizing: false,
|
|
|
|
|
},
|
|
|
|
|
{
|
2024-07-10 09:53:13 +00:00
|
|
|
id: "customer_information" as const,
|
|
|
|
|
accessorKey: "customer_information",
|
|
|
|
|
header: () => <>{t("quotes.list.columns.customer_information")}</>,
|
2024-07-10 10:41:00 +00:00
|
|
|
cell: ({ row: { original } }) => (
|
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-06-29 19:39:25 +00:00
|
|
|
enableResizing: false,
|
2024-08-20 11:44:04 +00:00
|
|
|
size: 640,
|
2024-06-29 19:39:25 +00:00
|
|
|
},
|
|
|
|
|
{
|
2024-07-10 09:53:13 +00:00
|
|
|
id: "reference" as const,
|
|
|
|
|
accessorKey: "reference",
|
|
|
|
|
header: () => <>{t("quotes.list.columns.reference")}</>,
|
2024-08-20 15:23:25 +00:00
|
|
|
cell: ({ renderValue }) => <div className='text-left text-ellipsis'>{renderValue()}</div>,
|
2024-06-29 19:39:25 +00:00
|
|
|
enableResizing: false,
|
|
|
|
|
},
|
|
|
|
|
{
|
2024-07-10 09:53:13 +00:00
|
|
|
id: "status" as const,
|
|
|
|
|
accessorKey: "status",
|
|
|
|
|
header: () => <>{t("quotes.list.columns.status")}</>,
|
2024-08-19 09:33:42 +00:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
2024-08-20 15:23:25 +00:00
|
|
|
cell: ({ row: { original } }) => <ColorBadge label={original.status} />,
|
2024-06-29 19:39:25 +00:00
|
|
|
enableResizing: false,
|
|
|
|
|
},
|
|
|
|
|
{
|
2024-07-10 09:53:13 +00:00
|
|
|
id: "total_price" as const,
|
|
|
|
|
accessor: "total_price",
|
|
|
|
|
header: () => <div className='text-right'>{t("quotes.list.columns.total_price")}</div>,
|
2024-08-16 14:57:44 +00:00
|
|
|
cell: ({ row: { original } }) => {
|
2024-07-10 09:53:13 +00:00
|
|
|
const price = MoneyValue.create(original.total_price);
|
|
|
|
|
return (
|
|
|
|
|
<div className='text-right'>{price.isSuccess ? price.object.toFormat() : "-"}</div>
|
|
|
|
|
);
|
2024-06-29 19:39:25 +00:00
|
|
|
},
|
|
|
|
|
enableResizing: false,
|
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-08-19 09:33:42 +00:00
|
|
|
cell: ({ row }: { row: Row<IListQuotes_Response_DTO> }) => (
|
2024-08-20 11:44:04 +00:00
|
|
|
<div className='flex items-center gap-1 ml-auto'>
|
|
|
|
|
<Tooltip>
|
2024-08-20 15:16:41 +00:00
|
|
|
<TooltipTrigger asChild>
|
2024-08-20 11:44:04 +00:00
|
|
|
<Button
|
|
|
|
|
size='sm'
|
|
|
|
|
variant='outline'
|
|
|
|
|
className='h-8 gap-1'
|
|
|
|
|
onClick={(e) => {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
navigate(`/quotes/edit/${row.original.id}`, { relative: "path" });
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<FilePenLineIcon className='h-3.5 w-3.5' />
|
|
|
|
|
<span className='lg:sr-only xl:not-sr-only xl:whitespace-nowrap'>
|
2024-08-20 15:16:41 +00:00
|
|
|
{t("quotes.list.columns.actions.edit")}
|
2024-08-20 11:44:04 +00:00
|
|
|
</span>
|
|
|
|
|
</Button>
|
|
|
|
|
</TooltipTrigger>
|
|
|
|
|
<TooltipContent>
|
2024-08-20 15:16:41 +00:00
|
|
|
<p>{t("quotes.list.columns.actions.edit")}</p>
|
2024-08-20 11:44:04 +00:00
|
|
|
</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'>More</span>
|
|
|
|
|
</Button>
|
|
|
|
|
</DropdownMenuTrigger>
|
|
|
|
|
<DropdownMenuContent align='end'>
|
|
|
|
|
<DropdownMenuItem>Edit</DropdownMenuItem>
|
2024-08-20 21:40:37 +00:00
|
|
|
<DropdownMenuItem
|
|
|
|
|
onClick={(e) => {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
download(row.original.id, getQuotePDFFilename(row.original));
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
Download
|
|
|
|
|
</DropdownMenuItem>
|
2024-08-20 11:44:04 +00:00
|
|
|
<DropdownMenuSeparator />
|
|
|
|
|
<DropdownMenuItem>Trash</DropdownMenuItem>
|
|
|
|
|
</DropdownMenuContent>
|
|
|
|
|
</DropdownMenu>
|
|
|
|
|
</div>
|
2024-07-10 10:41:00 +00:00
|
|
|
),
|
|
|
|
|
enableResizing: false,
|
2024-06-29 19:39:25 +00:00
|
|
|
},
|
|
|
|
|
],
|
2024-08-19 09:33:42 +00:00
|
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
2024-06-29 19:39:25 +00:00
|
|
|
[]
|
|
|
|
|
);
|
|
|
|
|
|
2024-08-20 15:16:41 +00:00
|
|
|
const handleOnPaginationChange = () => {
|
|
|
|
|
//setActiveRow(undefined);
|
|
|
|
|
};
|
|
|
|
|
|
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
|
|
|
onPaginationChange: handleOnPaginationChange,
|
2024-06-29 19:39:25 +00:00
|
|
|
});
|
|
|
|
|
|
2024-08-18 20:39:06 +00:00
|
|
|
const handleOnRowClick = (row: Row<IListQuotes_Response_DTO>) => {
|
2024-08-20 15:16:41 +00:00
|
|
|
setActiveRow(row);
|
2024-08-18 20:39:06 +00:00
|
|
|
};
|
|
|
|
|
|
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 (
|
|
|
|
|
<SimpleEmptyState
|
|
|
|
|
subtitle='Empieza creando alguna cotización'
|
|
|
|
|
buttonText=''
|
|
|
|
|
onButtonClick={() => navigate("add", { relative: "path" })}
|
|
|
|
|
/>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (
|
2024-08-20 21:40:37 +00:00
|
|
|
<>
|
|
|
|
|
<ResizablePanelGroup direction='horizontal' className='flex items-stretch flex-1 gap-4'>
|
|
|
|
|
<ResizablePanel defaultSize={preview ? 66 : 100} className='flex items-stretch flex-1'>
|
|
|
|
|
<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-20 11:44:04 +00:00
|
|
|
</ResizablePanel>
|
2024-08-20 21:40:37 +00:00
|
|
|
{preview && <ResizableHandle withHandle />}
|
|
|
|
|
{preview && (
|
|
|
|
|
<ResizablePanel defaultSize={33} className='flex items-stretch flex-1'>
|
|
|
|
|
<QuotePDFPreview quote={activeRow?.original} className='flex-1' />
|
|
|
|
|
</ResizablePanel>
|
|
|
|
|
)}
|
|
|
|
|
</ResizablePanelGroup>
|
|
|
|
|
<DownloadDialog {...downloadProps} />
|
|
|
|
|
</>
|
2024-06-29 19:39:25 +00:00
|
|
|
);
|
|
|
|
|
};
|