.
This commit is contained in:
parent
2a1a42fd9c
commit
5bacdcc2fc
@ -1,57 +1,50 @@
|
||||
"use client"
|
||||
|
||||
import { Table } from "@tanstack/react-table"
|
||||
import { Settings2 } from "lucide-react"
|
||||
"use client";
|
||||
|
||||
import {
|
||||
Button, DropdownMenu,
|
||||
Button,
|
||||
DropdownMenu,
|
||||
DropdownMenuCheckboxItem,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger
|
||||
} from '@repo/shadcn-ui/components'
|
||||
DropdownMenuTrigger,
|
||||
} from "@repo/shadcn-ui/components";
|
||||
import type { Table } from "@tanstack/react-table";
|
||||
import { Settings2 } from "lucide-react";
|
||||
|
||||
export function DataTableViewOptions<TData>({
|
||||
table,
|
||||
}: {
|
||||
table: Table<TData>
|
||||
}) {
|
||||
import { useTranslation } from "../../locales/i18n.ts";
|
||||
|
||||
export function DataTableViewOptions<TData>({ table }: { table: Table<TData> }) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="ml-auto hidden h-8 lg:flex"
|
||||
>
|
||||
<Button className="ml-auto hidden h-8 lg:flex" size="sm" type="button" variant="outline">
|
||||
<Settings2 />
|
||||
View
|
||||
{t("components.datatable_view_options.view_button")}
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" className="w-[150px]">
|
||||
<DropdownMenuLabel>Toggle columns</DropdownMenuLabel>
|
||||
<DropdownMenuLabel>
|
||||
{t("components.datatable_view_options.toggle_columns")}
|
||||
</DropdownMenuLabel>
|
||||
<DropdownMenuSeparator />
|
||||
{table
|
||||
.getAllColumns()
|
||||
.filter(
|
||||
(column) =>
|
||||
typeof column.accessorFn !== "undefined" && column.getCanHide()
|
||||
)
|
||||
.filter((column) => typeof column.accessorFn !== "undefined" && column.getCanHide())
|
||||
.map((column) => {
|
||||
return (
|
||||
<DropdownMenuCheckboxItem
|
||||
key={column.id}
|
||||
className="capitalize"
|
||||
checked={column.getIsVisible()}
|
||||
className="capitalize"
|
||||
key={column.id}
|
||||
onCheckedChange={(value) => column.toggleVisibility(!!value)}
|
||||
>
|
||||
{column.id}
|
||||
</DropdownMenuCheckboxItem>
|
||||
)
|
||||
);
|
||||
})}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,24 +1,4 @@
|
||||
"use client"
|
||||
|
||||
import {
|
||||
ColumnDef,
|
||||
ColumnFiltersState,
|
||||
ColumnSizingState,
|
||||
Row,
|
||||
SortingState,
|
||||
Table,
|
||||
TableMeta,
|
||||
VisibilityState,
|
||||
flexRender,
|
||||
getCoreRowModel,
|
||||
getFacetedRowModel,
|
||||
getFacetedUniqueValues,
|
||||
getFilteredRowModel,
|
||||
getPaginationRowModel,
|
||||
getSortedRowModel,
|
||||
useReactTable
|
||||
} from "@tanstack/react-table"
|
||||
import * as React from "react"
|
||||
"use client";
|
||||
|
||||
import {
|
||||
Button,
|
||||
@ -34,16 +14,36 @@ import {
|
||||
TableFooter,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow
|
||||
} from '@repo/shadcn-ui/components'
|
||||
import { DataTablePagination } from './data-table-pagination.tsx'
|
||||
import { DataTableToolbar } from "./data-table-toolbar.tsx"
|
||||
TableRow,
|
||||
} from "@repo/shadcn-ui/components";
|
||||
import {
|
||||
type ColumnDef,
|
||||
type ColumnFiltersState,
|
||||
type ColumnSizingState,
|
||||
type Row,
|
||||
type SortingState,
|
||||
type Table,
|
||||
type TableMeta,
|
||||
type VisibilityState,
|
||||
flexRender,
|
||||
getCoreRowModel,
|
||||
getFacetedRowModel,
|
||||
getFacetedUniqueValues,
|
||||
getFilteredRowModel,
|
||||
getPaginationRowModel,
|
||||
getSortedRowModel,
|
||||
useReactTable,
|
||||
} from "@tanstack/react-table";
|
||||
import * as React from "react";
|
||||
|
||||
import { useTranslation } from "../../locales/i18n.ts"
|
||||
import { useTranslation } from "../../locales/i18n.ts";
|
||||
|
||||
import { DataTablePagination } from "./data-table-pagination.tsx";
|
||||
import { DataTableToolbar } from "./data-table-toolbar.tsx";
|
||||
|
||||
export type DataTableOps<TData> = {
|
||||
onAdd?: (table: Table<TData>) => void;
|
||||
}
|
||||
};
|
||||
|
||||
export type DataTableRowOps<TData> = {
|
||||
duplicate?(index: number, table: Table<TData>): void;
|
||||
@ -61,25 +61,25 @@ export type DataTableBulkRowOps<TData> = {
|
||||
};
|
||||
|
||||
export type DataTableMeta<TData> = TableMeta<TData> & {
|
||||
totalItems?: number; // para paginación server-side
|
||||
totalItems?: number; // para paginación server-side
|
||||
readOnly?: boolean;
|
||||
|
||||
tableOps?: DataTableOps<TData>
|
||||
rowOps?: DataTableRowOps<TData>
|
||||
bulkOps?: DataTableBulkRowOps<TData>
|
||||
}
|
||||
tableOps?: DataTableOps<TData>;
|
||||
rowOps?: DataTableRowOps<TData>;
|
||||
bulkOps?: DataTableBulkRowOps<TData>;
|
||||
};
|
||||
|
||||
export interface DataTableProps<TData, TValue> {
|
||||
columns: ColumnDef<TData, TValue>[]
|
||||
data: TData[]
|
||||
meta?: DataTableMeta<TData>
|
||||
columns: ColumnDef<TData, TValue>[];
|
||||
data: TData[];
|
||||
meta?: DataTableMeta<TData>;
|
||||
|
||||
// Configuración
|
||||
readOnly?: boolean
|
||||
enablePagination?: boolean
|
||||
pageSize?: number
|
||||
enableRowSelection?: boolean
|
||||
EditorComponent?: React.ComponentType<{ row: TData; index: number; onClose: () => void }>
|
||||
readOnly?: boolean;
|
||||
enablePagination?: boolean;
|
||||
pageSize?: number;
|
||||
enableRowSelection?: boolean;
|
||||
EditorComponent?: React.ComponentType<{ row: TData; index: number; onClose: () => void }>;
|
||||
|
||||
getRowId?: (originalRow: TData, index: number, parent?: Row<TData>) => string;
|
||||
|
||||
@ -155,9 +155,7 @@ export function DataTable<TData, TValue>({
|
||||
|
||||
// Propagar cambios al padre
|
||||
onPaginationChange: (updater) => {
|
||||
const next = typeof updater === "function"
|
||||
? updater({ pageIndex, pageSize })
|
||||
: updater;
|
||||
const next = typeof updater === "function" ? updater({ pageIndex, pageSize }) : updater;
|
||||
|
||||
if (typeof next.pageIndex === "number") onPageChange?.(next.pageIndex);
|
||||
if (typeof next.pageSize === "number") onPageSizeChange?.(next.pageSize);
|
||||
@ -175,17 +173,15 @@ export function DataTable<TData, TValue>({
|
||||
getSortedRowModel: getSortedRowModel(),
|
||||
getFacetedRowModel: getFacetedRowModel(),
|
||||
getFacetedUniqueValues: getFacetedUniqueValues(),
|
||||
})
|
||||
});
|
||||
|
||||
const handleCloseEditor = React.useCallback(() => setEditIndex(null), [])
|
||||
const handleCloseEditor = React.useCallback(() => setEditIndex(null), []);
|
||||
|
||||
// Render principal
|
||||
return (
|
||||
<div
|
||||
className="transition-[max-height] duration-300 ease-in-out"
|
||||
>
|
||||
<div className="transition-[max-height] duration-300 ease-in-out">
|
||||
<div className="flex flex-col gap-0">
|
||||
<DataTableToolbar table={table} showViewOptions={!readOnly} />
|
||||
<DataTableToolbar showViewOptions={!readOnly} table={table} />
|
||||
|
||||
<div className="overflow-hidden rounded-md border">
|
||||
<TableComp className="w-full text-sm">
|
||||
@ -199,8 +195,8 @@ export function DataTable<TData, TValue>({
|
||||
const maxW = h.column.columnDef.maxSize;
|
||||
return (
|
||||
<TableHead
|
||||
key={h.id}
|
||||
colSpan={h.colSpan}
|
||||
key={h.id}
|
||||
style={{
|
||||
width: w ? `${w}px` : undefined,
|
||||
minWidth: typeof minW === "number" ? `${minW}px` : undefined,
|
||||
@ -224,26 +220,28 @@ export function DataTable<TData, TValue>({
|
||||
{table.getRowModel().rows.length ? (
|
||||
table.getRowModel().rows.map((row, rowIndex) => (
|
||||
<TableRow
|
||||
key={row.id}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
data-state={row.getIsSelected() && "selected"}
|
||||
className={"group bg-background cursor-pointer"}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter" || e.key === " ") onRowClick?.(row.original, rowIndex, e as any);
|
||||
}}
|
||||
data-state={row.getIsSelected() && "selected"}
|
||||
key={row.id}
|
||||
onClick={(e) => onRowClick?.(row.original, rowIndex, e)}
|
||||
onDoubleClick={
|
||||
!readOnly && !onRowClick ? () => setEditIndex(rowIndex) : undefined
|
||||
} >
|
||||
readOnly || onRowClick ? undefined : () => setEditIndex(rowIndex)
|
||||
}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter" || e.key === " ")
|
||||
onRowClick?.(row.original, rowIndex, e as any);
|
||||
}}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
>
|
||||
{row.getVisibleCells().map((cell) => {
|
||||
const w = cell.column.getSize();
|
||||
const minW = cell.column.columnDef.minSize;
|
||||
const maxW = cell.column.columnDef.maxSize;
|
||||
return (
|
||||
<TableCell
|
||||
key={cell.id}
|
||||
className="align-top"
|
||||
key={cell.id}
|
||||
style={{
|
||||
width: w ? `${w}px` : undefined,
|
||||
minWidth: typeof minW === "number" ? `${minW}px` : undefined,
|
||||
@ -258,7 +256,10 @@ export function DataTable<TData, TValue>({
|
||||
))
|
||||
) : (
|
||||
<TableRow>
|
||||
<TableCell colSpan={columns.length} className="h-24 text-center text-muted-foreground">
|
||||
<TableCell
|
||||
className="h-24 text-center text-muted-foreground"
|
||||
colSpan={columns.length}
|
||||
>
|
||||
{t("components.datatable.empty")}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
@ -270,18 +271,21 @@ export function DataTable<TData, TValue>({
|
||||
<TableFooter>
|
||||
<TableRow>
|
||||
<TableCell colSpan={100}>
|
||||
<DataTablePagination table={table} onPageChange={onPageChange} onPageSizeChange={onPageSizeChange} />
|
||||
<DataTablePagination
|
||||
onPageChange={onPageChange}
|
||||
onPageSizeChange={onPageSizeChange}
|
||||
table={table}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableFooter>)
|
||||
}
|
||||
|
||||
</TableFooter>
|
||||
)}
|
||||
</TableComp>
|
||||
</div>
|
||||
|
||||
{/* Editor modal */}
|
||||
{EditorComponent && editIndex !== null && (
|
||||
<Dialog open onOpenChange={handleCloseEditor}>
|
||||
<Dialog onOpenChange={handleCloseEditor} open>
|
||||
<DialogContent className="max-w-3xl">
|
||||
<DialogHeader>
|
||||
<DialogTitle>{t("components.datatable.editor.title")}</DialogTitle>
|
||||
@ -290,14 +294,14 @@ export function DataTable<TData, TValue>({
|
||||
|
||||
<div className="mt-4">
|
||||
<EditorComponent
|
||||
row={data[editIndex]}
|
||||
index={editIndex}
|
||||
onClose={handleCloseEditor}
|
||||
row={data[editIndex]}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<DialogFooter>
|
||||
<Button type="button" variant="secondary" onClick={handleCloseEditor}>
|
||||
<Button onClick={handleCloseEditor} type="button" variant="secondary">
|
||||
{t("common.close")}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
@ -306,5 +310,5 @@ export function DataTable<TData, TValue>({
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
import { cn } from "@repo/shadcn-ui/lib/utils";
|
||||
import { PropsWithChildren } from "react";
|
||||
import type { PropsWithChildren } from "react";
|
||||
|
||||
export const AppContent = ({
|
||||
className,
|
||||
@ -8,10 +8,8 @@ export const AppContent = ({
|
||||
}: PropsWithChildren<{ className?: string }>) => {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"app-content flex flex-1 flex-col gap-4 p-4 pt-6 bg-primary/5 min-h-screen",
|
||||
className
|
||||
)}
|
||||
className={cn("app-content flex flex-1 flex-col gap-4 p-4 pt-6 min-h-screen", className)}
|
||||
style={{ backgroundColor: "#fdfdfd" }}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { SidebarInset, SidebarProvider } from "@repo/shadcn-ui/components";
|
||||
import { Outlet } from "react-router";
|
||||
|
||||
import { AppSidebar } from "./app-sidebar.tsx";
|
||||
|
||||
export const AppLayout = () => {
|
||||
@ -12,9 +13,9 @@ export const AppLayout = () => {
|
||||
} as React.CSSProperties
|
||||
}
|
||||
>
|
||||
<AppSidebar variant='inset' />
|
||||
<AppSidebar variant="inset" />
|
||||
{/* Aquí está el MAIN */}
|
||||
<SidebarInset className='app-main'>
|
||||
<SidebarInset className="app-main">
|
||||
<Outlet />
|
||||
</SidebarInset>
|
||||
</SidebarProvider>
|
||||
|
||||
@ -35,6 +35,10 @@
|
||||
"rows_selected": "{{count}} of {{total}} selected rows"
|
||||
}
|
||||
},
|
||||
"datatable_view_options": {
|
||||
"view_button": "View",
|
||||
"toggle_columns": "Toggle columns"
|
||||
},
|
||||
"loading_indicator": {
|
||||
"title": "Loading...",
|
||||
"subtitle": "This may take a few seconds. Please do not close this page."
|
||||
|
||||
@ -38,6 +38,10 @@
|
||||
"rows_selected": "{{count}} de {{total}} filas seleccionadas"
|
||||
}
|
||||
},
|
||||
"datatable_view_options": {
|
||||
"view_button": "Ver",
|
||||
"toggle_columns": "Alternar columnas"
|
||||
},
|
||||
"loading_indicator": {
|
||||
"title": "Cargando...",
|
||||
"subtitle": "Esto puede tardar unos segundos. Por favor, no cierre esta página."
|
||||
|
||||
Loading…
Reference in New Issue
Block a user