.
This commit is contained in:
parent
4853c4a0bb
commit
fe816b73f8
@ -57,12 +57,8 @@ export const QuotePDFPreview = ({
|
|||||||
return (
|
return (
|
||||||
<Card className={cn("overflow-hidden flex flex-col", className)}>
|
<Card className={cn("overflow-hidden flex flex-col", className)}>
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle>
|
<Skeleton className='w-full h-8' />
|
||||||
<Skeleton className='w-full h-8' />
|
<Skeleton className='w-full h-8' />
|
||||||
</CardTitle>
|
|
||||||
<CardDescription>
|
|
||||||
<Skeleton className='w-full h-8' />
|
|
||||||
</CardDescription>
|
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className='py-4'>
|
<CardContent className='py-4'>
|
||||||
<Skeleton className='w-full aspect-[3/4] relative bg-white shadow flex-1' />
|
<Skeleton className='w-full aspect-[3/4] relative bg-white shadow flex-1' />
|
||||||
@ -85,7 +81,7 @@ export const QuotePDFPreview = ({
|
|||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
printJS({
|
printJS({
|
||||||
printable: file,
|
printable: file?.data,
|
||||||
type: "pdf",
|
type: "pdf",
|
||||||
showModal: false,
|
showModal: false,
|
||||||
modalMessage: "Cargando...",
|
modalMessage: "Cargando...",
|
||||||
|
|||||||
@ -1,17 +1,40 @@
|
|||||||
import { DataTable, DataTableSkeleton, ErrorOverlay, SimpleEmptyState } from "@/components";
|
import { DataTable, DataTableSkeleton, ErrorOverlay, SimpleEmptyState } 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 { Badge, Button, Card, CardContent } from "@/ui";
|
import {
|
||||||
|
Badge,
|
||||||
|
Button,
|
||||||
|
Card,
|
||||||
|
CardContent,
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuSeparator,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
ResizableHandle,
|
||||||
|
ResizablePanel,
|
||||||
|
ResizablePanelGroup,
|
||||||
|
Tooltip,
|
||||||
|
TooltipContent,
|
||||||
|
TooltipTrigger,
|
||||||
|
} from "@/ui";
|
||||||
import { IListQuotes_Response_DTO, MoneyValue, UTCDateValue } from "@shared/contexts";
|
import { IListQuotes_Response_DTO, MoneyValue, UTCDateValue } from "@shared/contexts";
|
||||||
import { ColumnDef, Row } from "@tanstack/react-table";
|
import { ColumnDef, Row } from "@tanstack/react-table";
|
||||||
import { t } from "i18next";
|
import { t } from "i18next";
|
||||||
|
import { FilePenLineIcon, MoreVerticalIcon } from "lucide-react";
|
||||||
import { useMemo, useState } from "react";
|
import { useMemo, useState } from "react";
|
||||||
import { Trans } from "react-i18next";
|
import { Trans } from "react-i18next";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { useQuotes } from "../hooks";
|
import { useQuotes } from "../hooks";
|
||||||
import { QuotePDFPreview } from "./QuotePDFPreview";
|
import { QuotePDFPreview } from "./QuotePDFPreview";
|
||||||
|
|
||||||
export const QuotesDataTable = ({ status = "all" }: { status?: string }) => {
|
export const QuotesDataTable = ({
|
||||||
|
status = "all",
|
||||||
|
preview = false,
|
||||||
|
}: {
|
||||||
|
status?: string;
|
||||||
|
preview?: boolean;
|
||||||
|
}) => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { pagination, globalFilter, isFiltered } = useDataTableContext();
|
const { pagination, globalFilter, isFiltered } = useDataTableContext();
|
||||||
const [focusedQuote, setFocusedQuote] = useState<IListQuotes_Response_DTO | undefined>(undefined);
|
const [focusedQuote, setFocusedQuote] = useState<IListQuotes_Response_DTO | undefined>(undefined);
|
||||||
@ -34,17 +57,20 @@ export const QuotesDataTable = ({ status = "all" }: { status?: string }) => {
|
|||||||
header: () => <>{t("quotes.list.columns.date")}</>,
|
header: () => <>{t("quotes.list.columns.date")}</>,
|
||||||
cell: ({ row: { original } }) => {
|
cell: ({ row: { original } }) => {
|
||||||
const quoteDate = UTCDateValue.create(original.date);
|
const quoteDate = UTCDateValue.create(original.date);
|
||||||
return quoteDate.isSuccess ? quoteDate.object.toLocaleDateString("es-ES") : "-";
|
return (
|
||||||
|
<div className='text-right text-ellipsis'>
|
||||||
|
{quoteDate.isSuccess ? quoteDate.object.toLocaleDateString("es-ES") : "-"}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
},
|
},
|
||||||
enableResizing: false,
|
enableResizing: false,
|
||||||
size: 10,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "customer_information" as const,
|
id: "customer_information" as const,
|
||||||
accessorKey: "customer_information",
|
accessorKey: "customer_information",
|
||||||
header: () => <>{t("quotes.list.columns.customer_information")}</>,
|
header: () => <>{t("quotes.list.columns.customer_information")}</>,
|
||||||
cell: ({ row: { original } }) => (
|
cell: ({ row: { original } }) => (
|
||||||
<div className='text-ellipsis'>
|
<div className='text-left text-ellipsis'>
|
||||||
{original.customer_information.split("\n").map((item, index) => {
|
{original.customer_information.split("\n").map((item, index) => {
|
||||||
return (
|
return (
|
||||||
<span
|
<span
|
||||||
@ -61,14 +87,16 @@ export const QuotesDataTable = ({ status = "all" }: { status?: string }) => {
|
|||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
enableResizing: false,
|
enableResizing: false,
|
||||||
size: 10,
|
size: 640,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "reference" as const,
|
id: "reference" as const,
|
||||||
accessorKey: "reference",
|
accessorKey: "reference",
|
||||||
header: () => <>{t("quotes.list.columns.reference")}</>,
|
header: () => <>{t("quotes.list.columns.reference")}</>,
|
||||||
|
cell: ({ row: { original } }) => (
|
||||||
|
<div className='text-left text-ellipsis'>{original.reference}</div>
|
||||||
|
),
|
||||||
enableResizing: false,
|
enableResizing: false,
|
||||||
size: 10,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "status" as const,
|
id: "status" as const,
|
||||||
@ -77,7 +105,6 @@ export const QuotesDataTable = ({ status = "all" }: { status?: string }) => {
|
|||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
cell: ({ renderValue }: { renderValue: () => any }) => <Badge>{renderValue()}</Badge>,
|
cell: ({ renderValue }: { renderValue: () => any }) => <Badge>{renderValue()}</Badge>,
|
||||||
enableResizing: false,
|
enableResizing: false,
|
||||||
size: 10,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "total_price" as const,
|
id: "total_price" as const,
|
||||||
@ -90,25 +117,53 @@ export const QuotesDataTable = ({ status = "all" }: { status?: string }) => {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
enableResizing: false,
|
enableResizing: false,
|
||||||
size: 20,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "edit-acion",
|
id: "row-actions",
|
||||||
header: () => null,
|
header: () => null,
|
||||||
cell: ({ row }: { row: Row<IListQuotes_Response_DTO> }) => (
|
cell: ({ row }: { row: Row<IListQuotes_Response_DTO> }) => (
|
||||||
<Button
|
<div className='flex items-center gap-1 ml-auto'>
|
||||||
variant='secondary'
|
<Tooltip>
|
||||||
onClick={(e) => {
|
<TooltipTrigger>
|
||||||
e.preventDefault();
|
<Button
|
||||||
navigate(`/quotes/edit/${row.original.id}`, { relative: "path" });
|
size='sm'
|
||||||
}}
|
variant='outline'
|
||||||
>
|
className='h-8 gap-1'
|
||||||
<Trans i18nKey={"common.edit"} />
|
onClick={(e) => {
|
||||||
<span className='sr-only'>, {row.original.id}</span>
|
e.preventDefault();
|
||||||
</Button>
|
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'>
|
||||||
|
<Trans i18nKey={"quotes.list.columns.actions.edit"} />
|
||||||
|
</span>
|
||||||
|
</Button>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>
|
||||||
|
<p>
|
||||||
|
<Trans i18nKey={"quotes.list.columns.actions.edit"} />
|
||||||
|
</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'>More</span>
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent align='end'>
|
||||||
|
<DropdownMenuItem>Edit</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem>Export</DropdownMenuItem>
|
||||||
|
<DropdownMenuSeparator />
|
||||||
|
<DropdownMenuItem>Trash</DropdownMenuItem>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</div>
|
||||||
),
|
),
|
||||||
enableResizing: false,
|
enableResizing: false,
|
||||||
size: 20,
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
@ -159,17 +214,23 @@ export const QuotesDataTable = ({ status = "all" }: { status?: string }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='grid items-stretch flex-1 gap-4 sm:py-0 md:gap-8 lg:grid-cols-3 xl:grid-cols-3'>
|
<ResizablePanelGroup direction='horizontal' className='flex items-stretch flex-1 gap-4'>
|
||||||
<DataTable
|
<ResizablePanel defaultSize={75} className='flex items-stretch flex-1'>
|
||||||
table={table}
|
<DataTable
|
||||||
paginationOptions={{ visible: true }}
|
table={table}
|
||||||
className='grid items-start gap-4 auto-rows-max md:gap-8 lg:col-span-2'
|
paginationOptions={{ visible: true }}
|
||||||
onRowClick={handleOnRowClick}
|
className='grid items-start flex-1 gap-4 auto-rows-max md:gap-8 lg:col-span-2'
|
||||||
>
|
onRowClick={handleOnRowClick}
|
||||||
<DataTableToolbar table={table} />
|
>
|
||||||
</DataTable>
|
<DataTableToolbar table={table} />
|
||||||
|
</DataTable>
|
||||||
<QuotePDFPreview quote={focusedQuote} className='flex-1 ' />
|
</ResizablePanel>
|
||||||
</div>
|
{preview && <ResizableHandle withHandle />}
|
||||||
|
{preview && (
|
||||||
|
<ResizablePanel defaultSize={25} className='flex items-stretch flex-1'>
|
||||||
|
<QuotePDFPreview quote={focusedQuote} className='flex-1' />
|
||||||
|
</ResizablePanel>
|
||||||
|
)}
|
||||||
|
</ResizablePanelGroup>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -2,12 +2,15 @@ import { DataTableProvider } from "@/lib/hooks";
|
|||||||
import { Trans } from "react-i18next";
|
import { Trans } from "react-i18next";
|
||||||
import { QuotesDataTable } from "./components";
|
import { QuotesDataTable } from "./components";
|
||||||
|
|
||||||
import { Button, Tabs, TabsContent, TabsList, TabsTrigger } from "@/ui";
|
import { Button, Tabs, TabsContent, TabsList, TabsTrigger, Toggle } from "@/ui";
|
||||||
|
import { useToggle } from "@wojtekmaj/react-hooks";
|
||||||
import { t } from "i18next";
|
import { t } from "i18next";
|
||||||
|
import { InfoIcon } from "lucide-react";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
export const QuotesList = () => {
|
export const QuotesList = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const [enabledPreview, toggleEnabledPreview] = useToggle(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DataTableProvider>
|
<DataTableProvider>
|
||||||
@ -27,33 +30,45 @@ export const QuotesList = () => {
|
|||||||
|
|
||||||
<Tabs defaultValue='all'>
|
<Tabs defaultValue='all'>
|
||||||
<div className='flex items-center'>
|
<div className='flex items-center'>
|
||||||
|
<span className='mr-4 font-medium'>Status</span>
|
||||||
<TabsList>
|
<TabsList>
|
||||||
<TabsTrigger value='all'>
|
<TabsTrigger value='all'>
|
||||||
<Trans i18nKey='quotes.list.tabs.all' />
|
<Trans i18nKey='quotes.list.tabs.all' />
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
<TabsTrigger value='emitted'>
|
|
||||||
<Trans i18nKey='quotes.list.tabs.emitted' />
|
|
||||||
</TabsTrigger>
|
|
||||||
<TabsTrigger value='draft'>
|
<TabsTrigger value='draft'>
|
||||||
<Trans i18nKey='quotes.list.tabs.draft' />
|
<Trans i18nKey='quotes.list.tabs.draft' />
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
|
<TabsTrigger value='emitted'>
|
||||||
|
<Trans i18nKey='quotes.list.tabs.emitted' />
|
||||||
|
</TabsTrigger>
|
||||||
<TabsTrigger value='archived' className='hidden sm:flex'>
|
<TabsTrigger value='archived' className='hidden sm:flex'>
|
||||||
<Trans i18nKey='quotes.list.tabs.archived' />
|
<Trans i18nKey='quotes.list.tabs.archived' />
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
</TabsList>
|
</TabsList>
|
||||||
<div className='flex items-center gap-2 ml-auto'></div>
|
<div className='flex items-center gap-2 ml-auto'>
|
||||||
|
<Toggle
|
||||||
|
aria-label='Show quote preview'
|
||||||
|
variant={"outline"}
|
||||||
|
defaultPressed={false}
|
||||||
|
pressed={enabledPreview}
|
||||||
|
onPressedChange={toggleEnabledPreview}
|
||||||
|
>
|
||||||
|
<InfoIcon className='w-4 h-4 mr-2' />
|
||||||
|
Quote preview
|
||||||
|
</Toggle>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<TabsContent value='all'>
|
<TabsContent value='all'>
|
||||||
<QuotesDataTable status='all' />
|
<QuotesDataTable status='all' preview={enabledPreview} />
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
<TabsContent value='draft'>
|
<TabsContent value='draft'>
|
||||||
<QuotesDataTable status='draft' />
|
<QuotesDataTable status='draft' preview={enabledPreview} />
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
<TabsContent value='archived'>
|
<TabsContent value='archived'>
|
||||||
<QuotesDataTable status='archived' />
|
<QuotesDataTable status='archived' preview={enabledPreview} />
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
<TabsContent value='emitted'>
|
<TabsContent value='emitted'>
|
||||||
<QuotesDataTable status='emitted' />
|
<QuotesDataTable status='emitted' preview={enabledPreview} />
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</DataTableProvider>
|
</DataTableProvider>
|
||||||
|
|||||||
@ -192,9 +192,9 @@ export function useDataTable<TData, TValue>({
|
|||||||
debugColumns: false,
|
debugColumns: false,
|
||||||
|
|
||||||
defaultColumn: {
|
defaultColumn: {
|
||||||
size: 5, //starting column size
|
size: 96, //starting column size
|
||||||
minSize: 0, //enforced during column resizing
|
minSize: 96, //enforced during column resizing
|
||||||
maxSize: 96, //enforced during column resizing
|
maxSize: 500, //enforced during column resizing
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -113,7 +113,10 @@
|
|||||||
"reference": "Reference",
|
"reference": "Reference",
|
||||||
"status": "Status",
|
"status": "Status",
|
||||||
"customer_information": "Customer",
|
"customer_information": "Customer",
|
||||||
"total_price": "Imp. total"
|
"total_price": "Imp. total",
|
||||||
|
"actions": {
|
||||||
|
"edit": "Edit quote"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"preview": {
|
"preview": {
|
||||||
"quote": "Quote",
|
"quote": "Quote",
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user