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-11-13 16:28:58 +00:00
|
|
|
import { Button, Card, CardContent, 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-11-13 16:28:58 +00:00
|
|
|
import { ArchiveIcon, CopyIcon, DownloadIcon, EditIcon } 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-11-14 16:46:31 +00:00
|
|
|
import { QuoteSentToEditor, QuoteStatusEditor } from "./editors";
|
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-11-14 15:44:41 +00:00
|
|
|
const { useList, useDownloader, useSetStatus, useDuplicate, getQuotePDFFilename } = useQuotes();
|
2024-11-13 16:28:58 +00:00
|
|
|
|
|
|
|
|
const { mutate: setStatusMutation } = useSetStatus();
|
2024-11-14 15:44:41 +00:00
|
|
|
const { mutate: duplicate } = useDuplicate();
|
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]
|
|
|
|
|
);
|
|
|
|
|
|
2024-11-14 15:44:41 +00:00
|
|
|
const handleDuplicateQuote = (id: string) => {
|
|
|
|
|
duplicate(
|
|
|
|
|
{
|
|
|
|
|
id,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
onSuccess(data) {
|
|
|
|
|
toast({
|
|
|
|
|
description: t("quotes.duplicate_action.toast_success"),
|
|
|
|
|
variant: "success",
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
navigate(`/quotes/edit/${data.id}`, { relative: "path" });
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
2024-11-14 16:46:31 +00:00
|
|
|
const handleArchiveQuote = (id: string) => {
|
|
|
|
|
setStatusMutation(
|
|
|
|
|
{ id, newStatus: "archived" },
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
onSuccess: () => {
|
|
|
|
|
toast({
|
|
|
|
|
description: t("quotes.quote_status_editor.toast_status_changed", {
|
|
|
|
|
newStatus: t("quotes.status.archived"),
|
|
|
|
|
}),
|
|
|
|
|
variant: "success",
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
2024-11-13 16:28:58 +00:00
|
|
|
const handleOnChangeStatus = (id: string, newStatus: string) => {
|
|
|
|
|
setStatusMutation(
|
|
|
|
|
{ id, newStatus },
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
onSuccess: () => {
|
|
|
|
|
toast({
|
2024-11-14 15:44:41 +00:00
|
|
|
description: t("quotes.quote_status_editor.toast_status_changed", {
|
|
|
|
|
newStatus: t(`quotes.status.${newStatus}`),
|
|
|
|
|
}),
|
|
|
|
|
variant: "success",
|
2024-11-13 16:28:58 +00:00
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
2024-09-02 15:44:49 +00:00
|
|
|
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-11-13 16:28:58 +00:00
|
|
|
<QuoteStatusEditor
|
|
|
|
|
type='badge'
|
|
|
|
|
status={original.status}
|
2024-11-14 16:46:31 +00:00
|
|
|
onChangeStatus={(newStatus: string) => handleOnChangeStatus(original.id, newStatus)}
|
2024-11-13 16:28:58 +00:00
|
|
|
/>
|
2024-08-28 18:38:20 +00:00
|
|
|
),
|
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);
|
2024-11-14 16:46:31 +00:00
|
|
|
const isSuccess = quoteDate.isSuccess && !quoteDate.object.isEmpty();
|
|
|
|
|
|
2024-10-25 09:49:10 +00:00
|
|
|
return (
|
|
|
|
|
<div className='text-left text-ellipsis'>
|
2024-11-14 16:46:31 +00:00
|
|
|
{isSuccess ? (
|
|
|
|
|
<ColorBadge
|
|
|
|
|
label={quoteDate.object.toLocaleDateString("es-ES")}
|
|
|
|
|
className='text-green-800 bg-green-100 hover:text-green-800 hover:bg-green-100'
|
|
|
|
|
/>
|
2024-10-25 09:49:10 +00:00
|
|
|
) : (
|
2024-11-14 16:46:31 +00:00
|
|
|
<></>
|
2024-10-25 09:49:10 +00:00
|
|
|
)}
|
|
|
|
|
</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-11-14 16:46:31 +00:00
|
|
|
size: 500,
|
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;
|
2024-11-13 16:28:58 +00:00
|
|
|
const isSent = original?.status === "accepted" && !!original?.date_sent;
|
2024-11-14 16:46:31 +00:00
|
|
|
const isArchived = original?.status === "archived";
|
2024-10-25 09:49:10 +00:00
|
|
|
|
|
|
|
|
return (
|
2024-11-14 16:46:31 +00:00
|
|
|
<div className='flex gap-1'>
|
|
|
|
|
<ButtonGroup className='gap-0'>
|
|
|
|
|
<Tooltip>
|
|
|
|
|
<TooltipTrigger asChild>
|
|
|
|
|
<Button
|
|
|
|
|
variant='ghost'
|
|
|
|
|
size='icon'
|
|
|
|
|
disabled={isSent}
|
|
|
|
|
onClick={(e) => {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
handleEditQuote(original);
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<EditIcon className='w-4 h-4' />
|
2026-10-05 11:58:03 +00:00
|
|
|
<span className='sr-only'>{t("common.edit")}</span>
|
2024-11-14 16:46:31 +00:00
|
|
|
</Button>
|
|
|
|
|
</TooltipTrigger>
|
|
|
|
|
<TooltipContent>
|
2026-10-05 11:58:03 +00:00
|
|
|
<p>{t("common.edit")}</p>
|
2024-11-14 16:46:31 +00:00
|
|
|
</TooltipContent>
|
|
|
|
|
</Tooltip>
|
2024-10-25 09:49:10 +00:00
|
|
|
|
2024-11-14 16:46:31 +00:00
|
|
|
<Tooltip>
|
|
|
|
|
<TooltipTrigger asChild>
|
|
|
|
|
<Button
|
|
|
|
|
variant='ghost'
|
|
|
|
|
size='icon'
|
|
|
|
|
onClick={(e) => {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
handleDuplicateQuote(original.id);
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<CopyIcon className='w-4 h-4' />
|
2026-10-05 11:58:03 +00:00
|
|
|
<span className='sr-only'>{t("common.duplicate")}</span>
|
2024-11-14 16:46:31 +00:00
|
|
|
</Button>
|
|
|
|
|
</TooltipTrigger>
|
|
|
|
|
<TooltipContent>
|
2026-10-05 11:58:03 +00:00
|
|
|
<p>{t("common.duplicate")}</p>
|
2024-11-14 16:46:31 +00:00
|
|
|
</TooltipContent>
|
|
|
|
|
</Tooltip>
|
2024-11-13 16:28:58 +00:00
|
|
|
|
2024-11-14 16:46:31 +00:00
|
|
|
<Tooltip>
|
|
|
|
|
<TooltipTrigger asChild>
|
|
|
|
|
<Button
|
|
|
|
|
variant='ghost'
|
|
|
|
|
size='icon'
|
|
|
|
|
onClick={() => {
|
|
|
|
|
download(original.id, getQuotePDFFilename(original));
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<DownloadIcon className='w-4 h-4' />
|
2026-10-05 11:58:03 +00:00
|
|
|
<span className='sr-only'>{t("common.download")}</span>
|
2024-11-14 16:46:31 +00:00
|
|
|
</Button>
|
|
|
|
|
</TooltipTrigger>
|
|
|
|
|
<TooltipContent>
|
2026-10-05 11:58:03 +00:00
|
|
|
<p>{t("common.download")}</p>
|
2024-11-14 16:46:31 +00:00
|
|
|
</TooltipContent>
|
|
|
|
|
</Tooltip>
|
2024-11-13 16:28:58 +00:00
|
|
|
|
2024-11-14 16:46:31 +00:00
|
|
|
<Tooltip>
|
|
|
|
|
<TooltipTrigger asChild>
|
|
|
|
|
<Button
|
|
|
|
|
variant='ghost'
|
|
|
|
|
size='icon'
|
|
|
|
|
disabled={isArchived || isSent}
|
|
|
|
|
onClick={(e) => {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
handleArchiveQuote(original.id);
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<ArchiveIcon className='w-4 h-4' />
|
2026-10-05 11:58:03 +00:00
|
|
|
<span className='sr-only'>{t("common.archive")}</span>
|
2024-11-14 16:46:31 +00:00
|
|
|
</Button>
|
|
|
|
|
</TooltipTrigger>
|
|
|
|
|
<TooltipContent>
|
2026-10-05 11:58:03 +00:00
|
|
|
<p>{t("common.archive")}</p>
|
2024-11-14 16:46:31 +00:00
|
|
|
</TooltipContent>
|
|
|
|
|
</Tooltip>
|
|
|
|
|
</ButtonGroup>
|
|
|
|
|
<ButtonGroup>
|
|
|
|
|
<QuoteSentToEditor
|
|
|
|
|
id={original.id}
|
|
|
|
|
onSentTo={() => null}
|
|
|
|
|
disabled={isSent || !allowToSent}
|
|
|
|
|
/>
|
|
|
|
|
</ButtonGroup>
|
|
|
|
|
</div>
|
2024-10-25 09:49:10 +00:00
|
|
|
);
|
|
|
|
|
},
|
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
|
2026-10-05 11:58:03 +00:00
|
|
|
subtitle={t("quotes.list.empty_state.subtitle")}
|
2024-08-26 15:53:58 +00:00
|
|
|
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
|
|
|
);
|
|
|
|
|
};
|