.
This commit is contained in:
parent
bb3ab3dbb4
commit
09cbfc12c5
@ -2,6 +2,7 @@ import { Outlet, RouterProvider, createBrowserRouter } from "react-router-dom";
|
||||
import {
|
||||
DealerLayout,
|
||||
DealersList,
|
||||
ErrorPage,
|
||||
LoginPage,
|
||||
LogoutPage,
|
||||
QuoteCreate,
|
||||
@ -25,6 +26,13 @@ export const Routes = () => {
|
||||
},
|
||||
];
|
||||
|
||||
const routesForErrors = [
|
||||
{
|
||||
path: "*",
|
||||
Component: ErrorPage,
|
||||
},
|
||||
];
|
||||
|
||||
// Define routes accessible only to authenticated users
|
||||
const routesForAuthenticatedOnly = [
|
||||
{
|
||||
@ -123,7 +131,12 @@ export const Routes = () => {
|
||||
|
||||
// Combine and conditionally include routes based on authentication status
|
||||
const router = createBrowserRouter(
|
||||
[...routesForPublic, ...routesForAuthenticatedOnly, ...routesForNotAuthenticatedOnly],
|
||||
[
|
||||
...routesForPublic,
|
||||
...routesForAuthenticatedOnly,
|
||||
...routesForNotAuthenticatedOnly,
|
||||
...routesForErrors,
|
||||
],
|
||||
{
|
||||
//basename: "/app",
|
||||
}
|
||||
|
||||
@ -25,6 +25,29 @@ export const ErrorPage = (props: ErrorPageProps) => {
|
||||
try again.
|
||||
</p>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className='flex min-h-[100dvh] flex-col items-center justify-center bg-background px-4 py-12 sm:px-6 lg:px-8'>
|
||||
<div className='max-w-md mx-auto text-center'>
|
||||
<div className='w-12 h-12 mx-auto text-primary' />
|
||||
<h1 className='mt-4 text-3xl font-bold tracking-tight text-foreground sm:text-4xl'>
|
||||
Oops, page not found!
|
||||
</h1>
|
||||
<p className='mt-4 text-muted-foreground'>
|
||||
The page you're looking for doesn't exist or has been moved.
|
||||
</p>
|
||||
<div className='mt-6'>
|
||||
<Button
|
||||
className='inline-flex items-center px-4 py-2 text-sm font-medium transition-colors rounded-md shadow-sm bg-primary text-primary-foreground hover:bg-primary/90 focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2'
|
||||
prefetch={false}
|
||||
>
|
||||
Go to Homepage
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<section id='Error' className='flex flex-col items-center w-full h-full'>
|
||||
{msg}
|
||||
|
||||
@ -0,0 +1,31 @@
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Button, ButtonProps } from "@/ui";
|
||||
import { t } from "i18next";
|
||||
import { PackagePlusIcon } from "lucide-react";
|
||||
|
||||
export interface AppendCatalogArticleRowButtonProps extends ButtonProps {
|
||||
label?: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const AppendCatalogArticleRowButton = ({
|
||||
label = t("common.append_article"),
|
||||
className,
|
||||
...props
|
||||
}: AppendCatalogArticleRowButtonProps): JSX.Element => (
|
||||
<Button
|
||||
type='button'
|
||||
variant='outline'
|
||||
size='icon'
|
||||
className={cn(
|
||||
"w-full gap-1 border-dashed text-muted-foreground border-muted-foreground/50",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<PackagePlusIcon className={label ? "w-4 h-4 mr-2" : "w-4 h-4"} />
|
||||
{label && <>{label}</>}
|
||||
</Button>
|
||||
);
|
||||
|
||||
AppendCatalogArticleRowButton.displayName = "AddNewRowButton";
|
||||
@ -1,17 +1,18 @@
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Button, ButtonProps } from "@/ui";
|
||||
import { t } from "i18next";
|
||||
import { PlusCircleIcon } from "lucide-react";
|
||||
|
||||
export interface AddNewRowButtonProps extends ButtonProps {
|
||||
export interface AppendEmptyRowButtonProps extends ButtonProps {
|
||||
label?: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const AddNewRowButton = ({
|
||||
label = "Añade nueva fila",
|
||||
export const AppendEmptyRowButton = ({
|
||||
label = t("common.append_empty_row"),
|
||||
className,
|
||||
...props
|
||||
}: AddNewRowButtonProps): JSX.Element => (
|
||||
}: AppendEmptyRowButtonProps): JSX.Element => (
|
||||
<Button
|
||||
type='button'
|
||||
variant='outline'
|
||||
@ -27,4 +28,4 @@ export const AddNewRowButton = ({
|
||||
</Button>
|
||||
);
|
||||
|
||||
AddNewRowButton.displayName = "AddNewRowButton";
|
||||
AppendEmptyRowButton.displayName = "AddNewRowButton";
|
||||
@ -1,5 +1,3 @@
|
||||
import { Card, CardContent } from "@/ui";
|
||||
|
||||
import { DataTableSkeleton, ErrorOverlay, SimpleEmptyState } from "@/components";
|
||||
|
||||
import { useCatalogList } from "@/app/catalog/hooks";
|
||||
@ -13,7 +11,7 @@ import { t } from "i18next";
|
||||
import { useMemo } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
export const CatalogPickerDataTable = ({ onClick }: { onClick: (data: unknown) => void }) => {
|
||||
export const CatalogPickerDataTable = ({ onSelect }: { onSelect: (data: unknown) => void }) => {
|
||||
const navigate = useNavigate();
|
||||
const { pagination, globalFilter, isFiltered } = useDataTableContext();
|
||||
|
||||
@ -48,7 +46,7 @@ export const CatalogPickerDataTable = ({ onClick }: { onClick: (data: unknown) =
|
||||
onClick={
|
||||
(event) => {
|
||||
event.preventDefault();
|
||||
onClick && onClick(row.original);
|
||||
onSelect && onSelect(row.original);
|
||||
}
|
||||
/*setMail({
|
||||
...mail,
|
||||
@ -57,10 +55,10 @@ export const CatalogPickerDataTable = ({ onClick }: { onClick: (data: unknown) =
|
||||
}
|
||||
>
|
||||
<div className='flex flex-row justify-between w-full space-x-6'>
|
||||
<div className='text-left grow line-clamp-2 text-muted-foreground hover:text-foreground'>
|
||||
<div className='w-3/4 text-left grow line-clamp-2 text-muted-foreground hover:text-foreground'>
|
||||
{renderValue()}
|
||||
</div>
|
||||
<div className='text-right'>
|
||||
<div className='w-1/4 text-right'>
|
||||
<dl className='flex flex-row justify-end space-x-1'>
|
||||
<dt className='text-xs font-medium text-accent-foreground/75'>
|
||||
{t("catalog.list.columns.points")}:
|
||||
@ -94,17 +92,13 @@ export const CatalogPickerDataTable = ({ onClick }: { onClick: (data: unknown) =
|
||||
|
||||
if (isPending) {
|
||||
return (
|
||||
<Card>
|
||||
<CardContent>
|
||||
<DataTableSkeleton
|
||||
columnCount={6}
|
||||
searchableColumnCount={1}
|
||||
filterableColumnCount={2}
|
||||
//cellWidths={["10rem", "40rem", "12rem", "12rem", "8rem"]}
|
||||
shrinkZero
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<DataTableSkeleton
|
||||
columnCount={6}
|
||||
searchableColumnCount={1}
|
||||
filterableColumnCount={2}
|
||||
//cellWidths={["10rem", "40rem", "12rem", "12rem", "8rem"]}
|
||||
shrinkZero
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@ -120,10 +114,12 @@ export const CatalogPickerDataTable = ({ onClick }: { onClick: (data: unknown) =
|
||||
|
||||
return (
|
||||
<DataTable
|
||||
className='bg-transparent border-0 shadow-none'
|
||||
table={table}
|
||||
headerOptions={{ visible: false }}
|
||||
paginationOptions={{ visible: true }}
|
||||
title='Catálogo'
|
||||
paginationOptions={{ visible: true, enablePageSizeSelector: false }}
|
||||
contentClassName='p-0'
|
||||
footerClassName='p-0'
|
||||
rowClassName='border-b-0'
|
||||
cellClassName='px-0'
|
||||
>
|
||||
|
||||
@ -38,10 +38,11 @@ import {
|
||||
getCoreRowModel,
|
||||
useReactTable,
|
||||
} from "@tanstack/react-table";
|
||||
import { useCallback, useMemo, useState } from "react";
|
||||
import { useMemo, useState } from "react";
|
||||
import { createPortal } from "react-dom";
|
||||
import { FieldValues, UseFieldArrayReturn } from "react-hook-form";
|
||||
import { AddNewRowButton } from "./AddNewRowButton";
|
||||
import { AppendCatalogArticleRowButton } from "./AppendCatalogArticleRowButton";
|
||||
import { AppendEmptyRowButton } from "./AppendEmptyRowButton";
|
||||
import { QuoteItemsSortableDataTableToolbar } from "./QuoteItemsSortableDataTableToolbar";
|
||||
import { QuoteItemsSortableTableRow } from "./QuoteItemsSortableTableRow";
|
||||
|
||||
@ -49,6 +50,7 @@ declare module "@tanstack/react-table" {
|
||||
interface TableMeta<TData extends RowData> {
|
||||
insertItem: (rowIndex: number, data?: unknown) => void;
|
||||
appendItem: (data?: unknown) => void;
|
||||
pickCatalogArticle: () => void;
|
||||
duplicateItems: (rowIndex?: number) => void;
|
||||
deleteItems: (rowIndex?: number | number[]) => void;
|
||||
updateItem: (rowIndex: number, rowData: TData, fieldName: string, value: unknown) => void;
|
||||
@ -63,7 +65,7 @@ export type QuoteItemsSortableDataTableProps = {
|
||||
columns: ColumnDef<unknown, unknown>[];
|
||||
data: Record<"id", string>[];
|
||||
defaultValues: Readonly<{ [x: string]: any }> | undefined;
|
||||
actions: Omit<UseFieldArrayReturn<FieldValues, "items">, "fields">;
|
||||
actions: Omit<UseFieldArrayReturn<FieldValues, "items">, "fields"> & Record<string, unknown>;
|
||||
};
|
||||
|
||||
const measuringConfig = {
|
||||
@ -139,6 +141,9 @@ export function QuoteItemsSortableDataTable({
|
||||
appendItem: (data?: unknown) => {
|
||||
actions.append(data || defaultValues?.items[0], { shouldFocus: true });
|
||||
},
|
||||
pickCatalogArticle: () => {
|
||||
actions.pickCatalogArticle();
|
||||
},
|
||||
duplicateItems: (rowIndex?: number) => {
|
||||
if (rowIndex != undefined) {
|
||||
const originalData = table.getRowModel().rows[rowIndex].original;
|
||||
@ -146,6 +151,7 @@ export function QuoteItemsSortableDataTable({
|
||||
} else if (table.getSelectedRowModel().rows.length) {
|
||||
const lastIndex =
|
||||
table.getSelectedRowModel().rows[table.getSelectedRowModel().rows.length - 1].index;
|
||||
|
||||
const data = table
|
||||
.getSelectedRowModel()
|
||||
.rows.map((row) => ({ ...row.original, id: undefined }));
|
||||
@ -238,70 +244,6 @@ export function QuoteItemsSortableDataTable({
|
||||
setActiveId(null);
|
||||
}
|
||||
|
||||
const hadleNewItem = useCallback(() => {
|
||||
actions.append([
|
||||
{
|
||||
article_id: 2000004503,
|
||||
description: "Lacquered Norma with 1 spline for lacquered panel up to 700 mm (27.56 in)",
|
||||
quantity: { amount: "15", scale: 0 },
|
||||
unit_price: {
|
||||
amount: "150000",
|
||||
scale: 4,
|
||||
currency_code: "EUR",
|
||||
},
|
||||
discount: {
|
||||
amount: 3500,
|
||||
scale: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
article_id: 2000005891,
|
||||
description:
|
||||
"Split walnut HPL 3 elephant gray faux-leather central earring tray compartment 150x410x50 mm (5.91 in x 16.14 in x 1.97 in)",
|
||||
quantity: { amount: "8", scale: 0 },
|
||||
unit_price: {
|
||||
amount: "384560",
|
||||
scale: 4,
|
||||
currency_code: "EUR",
|
||||
},
|
||||
discount: {
|
||||
amount: null,
|
||||
scale: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
article_id: 2000007412,
|
||||
description:
|
||||
"Nara H=3000 mm (118.11 in) fabric-covered glass panel up to 600 mm (23.62 in) wide",
|
||||
quantity: { amount: "4", scale: 0 },
|
||||
unit_price: {
|
||||
amount: "8450000",
|
||||
scale: 4,
|
||||
currency_code: "EUR",
|
||||
},
|
||||
discount: {
|
||||
amount: 500,
|
||||
scale: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
article_id: 2000002589,
|
||||
description:
|
||||
"Panoramic anodized sliding H=2600 mm (102.36 in) GR3 glass panel up to 1200 mm (47.24 in) wide",
|
||||
quantity: { amount: "25", scale: 0 },
|
||||
unit_price: {
|
||||
amount: "67481",
|
||||
scale: 4,
|
||||
currency_code: "EUR",
|
||||
},
|
||||
discount: {
|
||||
amount: 100,
|
||||
scale: 2,
|
||||
},
|
||||
},
|
||||
]);
|
||||
}, [actions]);
|
||||
|
||||
function filterItems(items: string[] | Row<unknown>[]) {
|
||||
if (!activeId) {
|
||||
return items;
|
||||
@ -357,9 +299,16 @@ export function QuoteItemsSortableDataTable({
|
||||
</TableBody>
|
||||
<TableFooter className='bg-default'>
|
||||
<TableRow className='hover:bg-default'>
|
||||
<TableCell colSpan={6} className='py-6'>
|
||||
<AddNewRowButton onClick={hadleNewItem} />
|
||||
<TableCell colSpan={3} className='py-6'></TableCell>
|
||||
<TableCell colSpan={5} className='py-6'>
|
||||
<div className='grid grid-cols-2 gap-6'>
|
||||
<AppendEmptyRowButton onClick={() => table.options.meta?.appendItem()} />
|
||||
<AppendCatalogArticleRowButton
|
||||
onClick={() => table.options.meta?.pickCatalogArticle()}
|
||||
/>
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell className='py-6'></TableCell>
|
||||
</TableRow>
|
||||
</TableFooter>
|
||||
</Table>
|
||||
|
||||
@ -8,81 +8,95 @@ export const QuoteItemsSortableDataTableToolbar = ({ table }: { table: Table<unk
|
||||
|
||||
if (selectedRowsCount) {
|
||||
return (
|
||||
<div className='flex items-center h-12 p-1 text-white rounded-md bg-primary '>
|
||||
<nav className='sticky z-10 pt-4 bg-background top-16'>
|
||||
<div className='flex items-center h-12 p-1 rounded-md text-muted-foreground bg-primary '>
|
||||
<div className='flex items-center gap-2'>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
type='button'
|
||||
variant='ghost'
|
||||
disabled={!table.getSelectedRowModel().rows.length}
|
||||
onClick={() => table.options.meta?.duplicateItems()}
|
||||
>
|
||||
<CopyPlusIcon className='w-4 h-4 sm:mr-2' />
|
||||
<span className='sr-only sm:not-sr-only'>
|
||||
{t("common.duplicate_selected_rows")}
|
||||
</span>
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>{t("common.duplicate_selected_rows_tooltip")}</TooltipContent>
|
||||
</Tooltip>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
type='button'
|
||||
variant='ghost'
|
||||
disabled={!table.getSelectedRowModel().rows.length}
|
||||
onClick={() => table.options.meta?.deleteItems()}
|
||||
>
|
||||
<Trash2Icon className='w-4 h-4 sm:mr-2' />
|
||||
<span className='sr-only sm:not-sr-only'>{t("common.remove_selected_rows")}</span>
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>{t("common.remove_selected_rows_tooltip")}</TooltipContent>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
type='button'
|
||||
variant='ghost'
|
||||
disabled={!table.getSelectedRowModel().rows.length}
|
||||
onClick={() => table.resetRowSelection()}
|
||||
>
|
||||
<ScanIcon className='w-4 h-4 sm:mr-2' />
|
||||
<span className='sr-only sm:not-sr-only'>{t("common.reset_selected_rows")}</span>
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>{t("common.reset_selected_rows_tooltip")}</TooltipContent>
|
||||
</Tooltip>
|
||||
<Separator orientation='vertical' className='h-6 mx-1 bg-muted-foreground' />
|
||||
<p>{t("common.rows_selected", { count: selectedRowsCount })}</p>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<nav className='sticky z-10 pt-4 bg-background top-16'>
|
||||
<div className='flex items-center h-12 p-1 rounded-md bg-accent text-muted-foreground'>
|
||||
<div className='flex items-center gap-2'>
|
||||
<Button>{`${selectedRowsCount} filas seleccionadas`}</Button>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
type='button'
|
||||
variant='ghost'
|
||||
size='icon'
|
||||
disabled={!table.getSelectedRowModel().rows.length}
|
||||
onClick={() => table.options.meta?.duplicateItems()}
|
||||
onClick={() => table.options.meta?.appendItem()}
|
||||
>
|
||||
<CopyPlusIcon className='w-4 h-4' />
|
||||
<span className='sm:sr-only'>{t("common.duplicate_rows")}</span>
|
||||
<CirclePlusIcon className='w-4 h-4 mr-2' />
|
||||
<span>{t("common.append_empty_row")}</span>
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>{t("common.duplicate_rows_tooltip")}</TooltipContent>
|
||||
<TooltipContent>{t("common.append_empty_row_tooltip")}</TooltipContent>
|
||||
</Tooltip>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
type='button'
|
||||
variant='ghost'
|
||||
size='icon'
|
||||
disabled={!table.getSelectedRowModel().rows.length}
|
||||
onClick={() => table.options.meta?.deleteItems()}
|
||||
onClick={() => table.options.meta?.pickCatalogArticle()}
|
||||
>
|
||||
<Trash2Icon className='w-4 h-4' />
|
||||
<span className='sm:sr-only'>Eliminar</span>
|
||||
<PackagePlusIcon className='w-4 h-4 mr-2' />
|
||||
<span>{t("common.append_article")}</span>
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Elimina las fila(s) seleccionada(s)</TooltipContent>
|
||||
</Tooltip>
|
||||
<Separator orientation='vertical' className='h-6 mx-1 bg-muted/50' />
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
variant='ghost'
|
||||
size='sm'
|
||||
disabled={!table.getSelectedRowModel().rows.length}
|
||||
onClick={() => table.resetRowSelection()}
|
||||
>
|
||||
<ScanIcon className='w-4 h-4 md:mr-2' />
|
||||
<span className='sm:sr-only'>Quitar selección</span>
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Quita la selección</TooltipContent>
|
||||
<TooltipContent>{t("common.append_article_tooltip")}</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div className='flex items-center gap-2 ml-auto'></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='flex items-center h-12 p-1 rounded-md bg-accent/50 text-muted-foreground'>
|
||||
<div className='flex items-center gap-2'>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button type='button' variant='ghost' onClick={() => table.options.meta?.appendItem()}>
|
||||
<CirclePlusIcon className='w-4 h-4 mr-2' />
|
||||
<span>{t("common.append_empty_row")}</span>
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>{t("common.append_empty_row_tooltip")}</TooltipContent>
|
||||
</Tooltip>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button type='button' variant='ghost' onClick={() => table.options.meta?.appendItem()}>
|
||||
<PackagePlusIcon className='w-4 h-4 mr-2' />
|
||||
<span>{t("common.append_article")}</span>
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>{t("common.append_article_tooltip")}</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div className='flex items-center gap-2 ml-auto'></div>
|
||||
</div>
|
||||
</nav>
|
||||
);
|
||||
};
|
||||
|
||||
@ -0,0 +1,28 @@
|
||||
import { Button, Dialog, DialogContent, DialogFooter } from "@/ui";
|
||||
|
||||
import { DataTableProvider } from "@/lib/hooks";
|
||||
import { CatalogPickerDataTable } from "../CatalogPickerDataTable";
|
||||
|
||||
export const CatalogPickerDialog = ({
|
||||
isOpen,
|
||||
onOpenChange,
|
||||
onSelect,
|
||||
}: {
|
||||
isOpen: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
onSelect: (data: unknown) => void;
|
||||
}) => {
|
||||
return (
|
||||
<Dialog modal open={isOpen} onOpenChange={onOpenChange}>
|
||||
<DialogContent className='w-11/12 max-w-full'>
|
||||
<DataTableProvider syncWithLocation={false} initialPageSize={5}>
|
||||
<CatalogPickerDataTable onSelect={onSelect} />
|
||||
</DataTableProvider>
|
||||
|
||||
<DialogFooter>
|
||||
<Button type='submit'>Choose</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
@ -4,6 +4,7 @@ import {
|
||||
FormQuantityField,
|
||||
FormTextAreaField,
|
||||
} from "@/components";
|
||||
|
||||
import { DataTableProvider } from "@/lib/hooks";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from "@/ui";
|
||||
@ -13,7 +14,9 @@ import { t } from "i18next";
|
||||
import { useCallback, useState } from "react";
|
||||
import { useFieldArray, useFormContext } from "react-hook-form";
|
||||
import { useDetailColumns } from "../../hooks";
|
||||
import { CatalogPickerDataTable } from "../CatalogPickerDataTable";
|
||||
import { QuoteItemsSortableDataTable } from "../QuoteItemsSortableDataTable";
|
||||
import { CatalogPickerDialog } from "./CatalogPickerDialog";
|
||||
|
||||
export const QuoteDetailsCardEditor = ({
|
||||
currency,
|
||||
@ -26,6 +29,10 @@ export const QuoteDetailsCardEditor = ({
|
||||
}) => {
|
||||
const { control, register } = useFormContext();
|
||||
|
||||
const [pickerMode] = useState<"dialog" | "panel">("dialog");
|
||||
|
||||
const [pickerDialogOpen, setPickerDialogOpen] = useState<boolean>(false);
|
||||
|
||||
const { fields, ...fieldActions } = useFieldArray({
|
||||
control,
|
||||
name: "items",
|
||||
@ -192,15 +199,15 @@ export const QuoteDetailsCardEditor = ({
|
||||
}
|
||||
);
|
||||
|
||||
const handleInsertArticle = useCallback(
|
||||
(newArticle: any) => {
|
||||
const handleAppendCatalogArticle = useCallback(
|
||||
(article: any) => {
|
||||
fieldActions.append({
|
||||
...newArticle,
|
||||
...article,
|
||||
quantity: {
|
||||
amount: 100,
|
||||
scale: Quantity.DEFAULT_SCALE,
|
||||
},
|
||||
unit_price: newArticle.retail_price,
|
||||
unit_price: article.retail_price,
|
||||
});
|
||||
},
|
||||
[fieldActions]
|
||||
@ -211,24 +218,26 @@ export const QuoteDetailsCardEditor = ({
|
||||
const defaultLayout = [265, 440, 655];
|
||||
const navCollapsedSize = 4;
|
||||
|
||||
return (
|
||||
<>
|
||||
<QuoteItemsSortableDataTable
|
||||
actions={fieldActions}
|
||||
columns={columns}
|
||||
data={fields}
|
||||
defaultValues={defaultValues}
|
||||
/>
|
||||
<FormCurrencyField
|
||||
variant='outline'
|
||||
currency={currency}
|
||||
language={language}
|
||||
scale={4}
|
||||
className='text-right'
|
||||
{...register("subtotal_price")}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
if (pickerMode === "dialog") {
|
||||
return (
|
||||
<div className='relative'>
|
||||
<QuoteItemsSortableDataTable
|
||||
actions={{
|
||||
...fieldActions,
|
||||
pickCatalogArticle: () => setPickerDialogOpen(true),
|
||||
}}
|
||||
columns={columns}
|
||||
data={fields}
|
||||
defaultValues={defaultValues}
|
||||
/>
|
||||
<CatalogPickerDialog
|
||||
onSelect={handleAppendCatalogArticle}
|
||||
isOpen={pickerDialogOpen}
|
||||
onOpenChange={setPickerDialogOpen}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<ResizablePanelGroup
|
||||
@ -250,12 +259,17 @@ export const QuoteDetailsCardEditor = ({
|
||||
}}
|
||||
className={cn(isCollapsed && "min-w-[50px] transition-all duration-300 ease-in-out")}
|
||||
>
|
||||
<QuoteItemsSortableDataTable actions={fieldActions} columns={columns} data={fields} />
|
||||
<QuoteItemsSortableDataTable
|
||||
actions={fieldActions}
|
||||
columns={columns}
|
||||
data={fields}
|
||||
defaultValues={defaultValues}
|
||||
/>
|
||||
</ResizablePanel>
|
||||
<ResizableHandle withHandle className='mx-3' />
|
||||
<ResizablePanel defaultSize={defaultLayout[1]} minSize={10}>
|
||||
<DataTableProvider syncWithLocation={false}>
|
||||
<CatalogPickerDataTable onClick={handleInsertArticle} />
|
||||
<CatalogPickerDataTable onSelect={handleAppendCatalogArticle} />
|
||||
</DataTableProvider>
|
||||
</ResizablePanel>
|
||||
</ResizablePanelGroup>
|
||||
|
||||
@ -26,7 +26,7 @@ export type DataTableColumnProps<TData, TValue> = ColumnDef<TData, TValue>;
|
||||
|
||||
export type DataTablePaginationOptionsProps<TData> = Pick<
|
||||
DataTablePaginationProps<TData>,
|
||||
"visible"
|
||||
"visible" | "enablePageSizeSelector"
|
||||
>;
|
||||
|
||||
export type DataTableHeaderOptionsProps = {
|
||||
@ -41,6 +41,8 @@ export type DataTableProps<TData> = PropsWithChildren<{
|
||||
paginationOptions?: DataTablePaginationOptionsProps<TData>;
|
||||
headerOptions?: DataTableHeaderOptionsProps;
|
||||
className?: string;
|
||||
contentClassName?: string;
|
||||
footerClassName?: string;
|
||||
rowClassName?: string;
|
||||
cellClassName?: string;
|
||||
}>;
|
||||
@ -54,6 +56,8 @@ export function DataTable<TData>({
|
||||
headerOptions = { visible: true },
|
||||
children,
|
||||
className,
|
||||
contentClassName,
|
||||
footerClassName,
|
||||
rowClassName,
|
||||
cellClassName,
|
||||
}: DataTableProps<TData>) {
|
||||
@ -67,7 +71,7 @@ export function DataTable<TData>({
|
||||
<CardDescription>{description}</CardDescription>
|
||||
</CardHeader>
|
||||
)}
|
||||
<CardContent className='pt-6'>
|
||||
<CardContent className={cn("pt-6", contentClassName)}>
|
||||
{children && (
|
||||
<>
|
||||
<div className='flex space-x-2'>{children}</div>
|
||||
@ -124,12 +128,8 @@ export function DataTable<TData>({
|
||||
</TableBody>
|
||||
</Table>
|
||||
</CardContent>
|
||||
<CardFooter>
|
||||
<DataTablePagination
|
||||
className='flex-1'
|
||||
visible={paginationOptions?.visible}
|
||||
table={table}
|
||||
/>
|
||||
<CardFooter className={footerClassName}>
|
||||
<DataTablePagination className='flex-1' table={table} {...paginationOptions} />
|
||||
</CardFooter>
|
||||
</Card>
|
||||
);
|
||||
|
||||
@ -15,12 +15,14 @@ import { useMemo } from "react";
|
||||
export type DataTablePaginationProps<TData> = {
|
||||
table: Table<TData>;
|
||||
className?: string;
|
||||
enablePageSizeSelector?: boolean;
|
||||
visible?: boolean | "auto";
|
||||
};
|
||||
|
||||
export function DataTablePagination<TData>({
|
||||
table,
|
||||
className,
|
||||
enablePageSizeSelector = true,
|
||||
visible = "auto",
|
||||
}: DataTablePaginationProps<TData>) {
|
||||
const isVisible = useMemo(() => visible === true, [visible]);
|
||||
@ -31,11 +33,11 @@ export function DataTablePagination<TData>({
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={cn("flex items-center justify-between px-2", className)}>
|
||||
<div className={className}>
|
||||
<div className='flex-1 text-base text-muted-foreground'>
|
||||
{table.getSelectedRowModel().rows.length > 0 && (
|
||||
<>
|
||||
{t("common.rows_selected", {
|
||||
{t("common.rows_selected_of_total", {
|
||||
count: table.getFilteredSelectedRowModel().rows.length,
|
||||
total: table.getFilteredRowModel().rows.length,
|
||||
})}
|
||||
@ -43,75 +45,86 @@ export function DataTablePagination<TData>({
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className='flex items-center space-x-6 lg:space-x-8'>
|
||||
<div className='flex items-center space-x-2'>
|
||||
<p className='text-base font-medium'>{t("common.rows_per_page")}</p>
|
||||
<div className='flex justify-between space-x-6 lg:space-x-8'>
|
||||
{enablePageSizeSelector && (
|
||||
<div className='flex items-center space-x-2 grow'>
|
||||
<p className='text-sm font-medium'>{t("common.rows_per_page")}</p>
|
||||
|
||||
<Select
|
||||
value={`${table.getState().pagination.pageSize}`}
|
||||
onValueChange={(value) => {
|
||||
table.setPageSize(Number(value));
|
||||
}}
|
||||
>
|
||||
<SelectTrigger className='h-8 w-[70px]'>
|
||||
<SelectValue placeholder={table.getState().pagination.pageSize} />
|
||||
</SelectTrigger>
|
||||
<SelectContent side='top'>
|
||||
{DEFAULT_PAGE_SIZES.map((pageSize) => (
|
||||
<SelectItem key={pageSize} value={`${pageSize}`}>
|
||||
{pageSize}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div className='flex w-[100px] items-center justify-center text-base font-medium'>
|
||||
{t("common.num_page_of_total", {
|
||||
count: table.getState().pagination.pageIndex + 1,
|
||||
total: table.getPageCount(),
|
||||
})}
|
||||
</div>
|
||||
<div className='flex items-center space-x-2'>
|
||||
<Button
|
||||
type='button'
|
||||
variant='outline'
|
||||
className='hidden w-8 h-8 p-0 lg:flex'
|
||||
onClick={() => table.setPageIndex(INITIAL_PAGE_INDEX)}
|
||||
disabled={!table.getCanPreviousPage()}
|
||||
>
|
||||
<span className='sr-only'>{t("common.go_to_first_page")}</span>
|
||||
<ChevronsLeftIcon className='w-4 h-4' />
|
||||
</Button>
|
||||
<Button
|
||||
type='button'
|
||||
variant='outline'
|
||||
className='w-8 h-8 p-0'
|
||||
onClick={() => table.previousPage()}
|
||||
disabled={!table.getCanPreviousPage()}
|
||||
>
|
||||
<span className='sr-only'>{t("common.go_to_prev_page")}</span>
|
||||
<ChevronLeftIcon className='w-4 h-4' />
|
||||
</Button>
|
||||
<Button
|
||||
type='button'
|
||||
variant='outline'
|
||||
className='w-8 h-8 p-0'
|
||||
onClick={() => table.nextPage()}
|
||||
disabled={!table.getCanNextPage()}
|
||||
>
|
||||
<span className='sr-only'>{t("common.go_to_next_page")}</span>
|
||||
<ChevronRightIcon className='w-4 h-4' />
|
||||
</Button>
|
||||
<Button
|
||||
type='button'
|
||||
variant='outline'
|
||||
className='hidden w-8 h-8 p-0 lg:flex'
|
||||
onClick={() => table.setPageIndex(table.getPageCount() + 1)}
|
||||
disabled={!table.getCanNextPage()}
|
||||
>
|
||||
<span className='sr-only'>{t("common.go_to_last_page")}</span>
|
||||
<ChevronsRightIcon className='w-4 h-4' />
|
||||
</Button>
|
||||
<Select
|
||||
value={`${table.getState().pagination.pageSize}`}
|
||||
onValueChange={(value) => {
|
||||
table.setPageSize(Number(value));
|
||||
}}
|
||||
>
|
||||
<SelectTrigger className='h-8 w-[70px]'>
|
||||
<SelectValue placeholder={table.getState().pagination.pageSize} />
|
||||
</SelectTrigger>
|
||||
<SelectContent side='top'>
|
||||
{DEFAULT_PAGE_SIZES.map((pageSize) => (
|
||||
<SelectItem key={pageSize} value={`${pageSize}`}>
|
||||
{pageSize}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
className={cn(
|
||||
"flex space-x-2 flex-1",
|
||||
enablePageSizeSelector ? "justify-end" : "justify-between"
|
||||
)}
|
||||
>
|
||||
<div className='flex w-[100px] items-center justify-center'>
|
||||
<p className='text-sm font-medium '>
|
||||
{t("common.num_page_of_total", {
|
||||
count: table.getState().pagination.pageIndex + 1,
|
||||
total: table.getPageCount(),
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
<div className='flex items-center space-x-2'>
|
||||
<Button
|
||||
type='button'
|
||||
variant='outline'
|
||||
className='hidden w-8 h-8 p-0 lg:flex'
|
||||
onClick={() => table.setPageIndex(INITIAL_PAGE_INDEX)}
|
||||
disabled={!table.getCanPreviousPage()}
|
||||
>
|
||||
<span className='sr-only'>{t("common.go_to_first_page")}</span>
|
||||
<ChevronsLeftIcon className='w-4 h-4' />
|
||||
</Button>
|
||||
<Button
|
||||
type='button'
|
||||
variant='outline'
|
||||
className='w-8 h-8 p-0'
|
||||
onClick={() => table.previousPage()}
|
||||
disabled={!table.getCanPreviousPage()}
|
||||
>
|
||||
<span className='sr-only'>{t("common.go_to_prev_page")}</span>
|
||||
<ChevronLeftIcon className='w-4 h-4' />
|
||||
</Button>
|
||||
<Button
|
||||
type='button'
|
||||
variant='outline'
|
||||
className='w-8 h-8 p-0'
|
||||
onClick={() => table.nextPage()}
|
||||
disabled={!table.getCanNextPage()}
|
||||
>
|
||||
<span className='sr-only'>{t("common.go_to_next_page")}</span>
|
||||
<ChevronRightIcon className='w-4 h-4' />
|
||||
</Button>
|
||||
<Button
|
||||
type='button'
|
||||
variant='outline'
|
||||
className='hidden w-8 h-8 p-0 lg:flex'
|
||||
onClick={() => table.setPageIndex(table.getPageCount() + 1)}
|
||||
disabled={!table.getCanNextPage()}
|
||||
>
|
||||
<span className='sr-only'>{t("common.go_to_last_page")}</span>
|
||||
<ChevronsRightIcon className='w-4 h-4' />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -27,12 +27,20 @@ export const DataTableContext = createContext<IDataTableContextState | null>(nul
|
||||
export const DataTableProvider = ({
|
||||
syncWithLocation = true,
|
||||
initialGlobalFilter = "",
|
||||
initialPageIndex,
|
||||
initialPageSize,
|
||||
children,
|
||||
}: PropsWithChildren<{
|
||||
syncWithLocation?: boolean;
|
||||
initialGlobalFilter?: string;
|
||||
initialPageIndex?: number;
|
||||
initialPageSize?: number;
|
||||
}>) => {
|
||||
const [pagination, setPagination] = useSyncedPagination(syncWithLocation);
|
||||
const [pagination, setPagination] = useSyncedPagination({
|
||||
syncWithLocation,
|
||||
initialPageIndex,
|
||||
initialPageSize,
|
||||
});
|
||||
const [globalFilter, setGlobalFilter] = useState<string>(initialGlobalFilter);
|
||||
const [sorting, setSorting] = useState<SortingState>([]);
|
||||
|
||||
|
||||
@ -1,8 +1,21 @@
|
||||
import { usePagination, usePaginationSyncWithLocation } from "../usePagination";
|
||||
|
||||
export const useSyncedPagination = (syncWithLocation: boolean) => {
|
||||
type UseSyncedPaginationProps = {
|
||||
syncWithLocation?: boolean;
|
||||
initialPageIndex?: number;
|
||||
initialPageSize?: number;
|
||||
};
|
||||
|
||||
export const useSyncedPagination = ({
|
||||
syncWithLocation = true,
|
||||
initialPageIndex,
|
||||
initialPageSize,
|
||||
}: UseSyncedPaginationProps) => {
|
||||
const [paginationWithLocation, setPaginationWithLocation] = usePaginationSyncWithLocation();
|
||||
const [paginationWithoutLocation, setPaginationWithoutLocation] = usePagination();
|
||||
const [paginationWithoutLocation, setPaginationWithoutLocation] = usePagination(
|
||||
initialPageIndex,
|
||||
initialPageSize
|
||||
);
|
||||
|
||||
if (syncWithLocation) {
|
||||
return [paginationWithLocation, setPaginationWithLocation] as const;
|
||||
|
||||
@ -7,7 +7,7 @@ import {
|
||||
|
||||
import { useState } from "react";
|
||||
|
||||
export const DEFAULT_PAGE_SIZES = [15, 30, 50, 75, 100];
|
||||
export const DEFAULT_PAGE_SIZES = [5, 10, 15, 30, 50, 75, 100];
|
||||
|
||||
export interface PaginationState {
|
||||
pageIndex: number;
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import { CustomDialog } from "@/components";
|
||||
import { NullOr } from "@shared/utilities";
|
||||
import { PropsWithChildren, createContext, useCallback, useMemo, useState } from "react";
|
||||
import { createContext } from "react";
|
||||
import { UnsavedChangesNotifierProps } from "./useUnsavedChangesNotifier";
|
||||
|
||||
export interface IUnsavedWarnContextState {
|
||||
@ -8,48 +7,3 @@ export interface IUnsavedWarnContextState {
|
||||
}
|
||||
|
||||
export const UnsavedWarnContext = createContext<NullOr<IUnsavedWarnContextState>>(null);
|
||||
|
||||
export const UnsavedWarnProvider = ({ children }: PropsWithChildren) => {
|
||||
const [confirm, setConfirm] = useState<NullOr<UnsavedChangesNotifierProps>>(null);
|
||||
|
||||
const [open, toggle] = useState(false);
|
||||
|
||||
const show = useCallback(
|
||||
(confirmOptions: NullOr<UnsavedChangesNotifierProps>) => {
|
||||
setConfirm(confirmOptions);
|
||||
toggle(true);
|
||||
},
|
||||
[toggle, setConfirm]
|
||||
);
|
||||
|
||||
const onConfirm = () => {
|
||||
confirm?.onConfirm?.();
|
||||
toggle(false);
|
||||
};
|
||||
|
||||
const onCancel = () => {
|
||||
confirm?.onCancel?.();
|
||||
toggle(false);
|
||||
};
|
||||
|
||||
const value = useMemo(() => ({ show }), [show]);
|
||||
|
||||
return (
|
||||
<UnsavedWarnContext.Provider value={value}>
|
||||
{children}
|
||||
<CustomDialog
|
||||
//type='warning'
|
||||
onCancel={() => {
|
||||
console.log("onCancel");
|
||||
onCancel();
|
||||
}}
|
||||
onConfirm={() => onConfirm()}
|
||||
title={confirm?.title}
|
||||
description={confirm?.subtitle}
|
||||
confirmLabel={confirm?.confirmText}
|
||||
cancelLabel={confirm?.cancelText}
|
||||
isOpen={open}
|
||||
/>
|
||||
</UnsavedWarnContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
@ -0,0 +1,50 @@
|
||||
import { CustomDialog } from "@/components";
|
||||
import { NullOr } from "@shared/utilities";
|
||||
import { PropsWithChildren, useCallback, useMemo, useState } from "react";
|
||||
import { UnsavedChangesNotifierProps } from "./useUnsavedChangesNotifier";
|
||||
import { UnsavedWarnContext } from "./WarnAboutChangeContext";
|
||||
|
||||
export const UnsavedWarnProvider = ({ children }: PropsWithChildren) => {
|
||||
const [confirm, setConfirm] = useState<NullOr<UnsavedChangesNotifierProps>>(null);
|
||||
|
||||
const [open, toggle] = useState(false);
|
||||
|
||||
const show = useCallback(
|
||||
(confirmOptions: NullOr<UnsavedChangesNotifierProps>) => {
|
||||
setConfirm(confirmOptions);
|
||||
toggle(true);
|
||||
},
|
||||
[toggle, setConfirm]
|
||||
);
|
||||
|
||||
const onConfirm = () => {
|
||||
confirm?.onConfirm?.();
|
||||
toggle(false);
|
||||
};
|
||||
|
||||
const onCancel = () => {
|
||||
confirm?.onCancel?.();
|
||||
toggle(false);
|
||||
};
|
||||
|
||||
const value = useMemo(() => ({ show }), [show]);
|
||||
|
||||
return (
|
||||
<UnsavedWarnContext.Provider value={value}>
|
||||
{children}
|
||||
<CustomDialog
|
||||
//type='warning'
|
||||
onCancel={() => {
|
||||
console.log("onCancel");
|
||||
onCancel();
|
||||
}}
|
||||
onConfirm={() => onConfirm()}
|
||||
title={confirm?.title}
|
||||
description={confirm?.subtitle}
|
||||
confirmLabel={confirm?.confirmText}
|
||||
cancelLabel={confirm?.cancelText}
|
||||
isOpen={open}
|
||||
/>
|
||||
</UnsavedWarnContext.Provider>
|
||||
);
|
||||
};
|
||||
@ -1,3 +1,4 @@
|
||||
export * from "./WarnAboutChangeContext";
|
||||
export * from "./useUnsavedChangesNotifier";
|
||||
export * from "./useWarnAboutChange";
|
||||
export * from "./WarnAboutChangeContext";
|
||||
export * from "./WarnAboutChangeProvider";
|
||||
|
||||
@ -17,7 +17,8 @@
|
||||
"sort_desc": "Desc",
|
||||
"sort_desc_description": "In descending order. Click to sort in ascending order.",
|
||||
"sort_none_description": "No sorting order. Click to sort in ascending order.",
|
||||
"rows_selected": "{{count}} of {{total}} row(s) selected.",
|
||||
"rows_selected": "{{count}} row(s) selected.",
|
||||
"rows_selected_of_total": "{{count}} of {{total}} row(s) selected.",
|
||||
"rows_per_page": "Rows per page",
|
||||
"num_page_of_total": "Page {{count}} of {{total}}",
|
||||
"go_to_first_page": "Go to first page",
|
||||
@ -29,13 +30,17 @@
|
||||
"error": "Error",
|
||||
"actions": "Actions",
|
||||
"open_menu": "Open menu",
|
||||
"duplicate_rows": "Duplicate",
|
||||
"duplicate_rows_tooltip": "Duplicate selected row(s)",
|
||||
"duplicate_selected_rows": "Duplicate",
|
||||
"duplicate_selected_rows_tooltip": "Duplicate selected row(s)",
|
||||
"append_empty_row": "Append row",
|
||||
"append_empty_row_tooltip": "Append a empty row",
|
||||
"append_article": "Append article",
|
||||
"append_article_tooltip": "Select and add an item from the catalog",
|
||||
"remove_row": "Remove",
|
||||
"remove_selected_rows": "Remove",
|
||||
"remove_selected_rows_tooltip": "Remove selected row(s)",
|
||||
"reset_selected_rows": "Reset selection",
|
||||
"reset_selected_rows_tooltip": "Reset selected row(s)",
|
||||
"insert_row_above": "Insert row above",
|
||||
"insert_row_below": "Insert row below",
|
||||
"pick_date": "Select a date",
|
||||
|
||||
@ -17,7 +17,8 @@
|
||||
"sort_desc": "Desc",
|
||||
"sort_desc_description": "En orden descendente. Click para ordenar ascendentemente.",
|
||||
"sort_none_description": "Sin orden. Click para ordenar ascendentemente.",
|
||||
"rows_selected": "{{count}} de {{total}} fila(s) seleccionadas.",
|
||||
"rows_selected": "{{count}} fila(s) seleccionadas.",
|
||||
"rows_selected_of_total": "{{count}} de {{total}} fila(s) seleccionadas.",
|
||||
"rows_per_page": "Filas por página",
|
||||
"num_page_of_total": "Página {{count}} de {{total}}",
|
||||
"go_to_first_page": "Ir a la primera página",
|
||||
@ -29,13 +30,17 @@
|
||||
"error": "Error",
|
||||
"actions": "Acciones",
|
||||
"open_menu": "Abrir el menú",
|
||||
"duplicate_rows": "Duplicar",
|
||||
"duplicate_rows_tooltip": "Duplica las fila(s) seleccionadas(s)",
|
||||
"duplicate_selected_rows": "Duplicar",
|
||||
"duplicate_selected_rows_tooltip": "Duplica las fila(s) seleccionadas(s)",
|
||||
"append_empty_row": "Añadir fila",
|
||||
"append_empty_row_tooltip": "Añadir una fila vacía",
|
||||
"append_article": "Añadir artículo",
|
||||
"append_article_tooltip": "Elegir un artículo del catálogo y añadirlo",
|
||||
"remove_row": "Eliminar",
|
||||
"remove_selected_rows": "Eliminar",
|
||||
"remove_selected_rows_tooltip": "Elimina las fila(s) seleccionadas(s)",
|
||||
"reset_selected_rows": "Quitar selection",
|
||||
"reset_selected_rows_tooltip": "Dejar de seleccionar la(s) fila(s)",
|
||||
"insert_row_above": "Insertar fila encima",
|
||||
"insert_row_below": "Insertar fila debajo",
|
||||
"pick_date": "Elige una fecha",
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
export const INITIAL_PAGE_INDEX = 0;
|
||||
export const INITIAL_PAGE_SIZE = 15;
|
||||
export const INITIAL_PAGE_SIZE = 5;
|
||||
|
||||
export const MIN_PAGE_INDEX = 0;
|
||||
export const MIN_PAGE_SIZE = 1;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user