Skip to content

Commit

Permalink
feat(pci.ai.notebook): udpate data table
Browse files Browse the repository at this point in the history
Signed-off-by: Arthur Bullet <[email protected]>
  • Loading branch information
abullet33 committed Jan 10, 2025
1 parent 1edc5d3 commit 090f6a8
Show file tree
Hide file tree
Showing 34 changed files with 1,322 additions and 320 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import React, { ReactElement, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { flexRender } from '@tanstack/react-table';
import { ChevronDown, ChevronUp } from 'lucide-react';
import { useDataTableContext } from './DataTableContext';
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from '@/components/ui/table';
import { Button } from '../ui/button';

export const MENU_COLUMN_ID = 'actions';

interface DatatableProps<TData> {
renderRowExpansion?: (row: TData) => ReactElement | null;
}

export function DataTable<TData>({
renderRowExpansion,
}: DatatableProps<TData>) {
const { table, rows } = useDataTableContext();
const { t } = useTranslation('pci-databases-analytics/components/data-table');
const [expandedRows, setExpandedRows] = useState<Record<string, boolean>>({});

const toggleRowExpansion = (rowId: string) => {
setExpandedRows((prev) => ({
...prev,
[rowId]: !prev[rowId],
}));
};

const headerGroups = table.getHeaderGroups();
return (
<Table>
<TableHeader className="border bg-gray-50">
{headerGroups.map((headerGroup) => (
<TableRow key={headerGroup.id}>
{renderRowExpansion && (
<TableHead className="border-r-0 w-6"></TableHead>
)}
{headerGroup.headers.map((header, index) => {
const isEmptyHeader = header.id === MENU_COLUMN_ID;
// Get a reference to the previous header
const isEmptyNextHeader =
headerGroup.headers[index + 1]?.id === MENU_COLUMN_ID;
return (
<TableHead
key={header.id}
className={`border font-semibold text-primary-800 ${
isEmptyHeader || renderRowExpansion
? 'border-l-0' // Remove left border for empty headers and row extend column
: ''
} ${
isEmptyNextHeader
? 'border-r-0' // Remove right border from current column if next header is empty
: ''
}`}
>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext(),
)}
</TableHead>
);
})}
</TableRow>
))}
</TableHeader>
<TableBody className="border">
{rows?.length ? (
rows.map((row) => (
<React.Fragment key={row.id}>
<TableRow data-state={row.getIsSelected() && 'selected'}>
{renderRowExpansion && (
<TableCell>
<Button
variant="ghost"
onClick={() => toggleRowExpansion(row.id)}
data-testid="table-row-expand-button"
>
{expandedRows[row.id] ? (
<ChevronUp className="h-4 w-4" />
) : (
<ChevronDown className="h-4 w-4" />
)}
</Button>
</TableCell>
)}
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</TableCell>
))}
</TableRow>
{expandedRows[row.id] && renderRowExpansion && (
<TableRow>
<TableCell colSpan={headerGroups[0].headers.length + 1}>
{renderRowExpansion(row.original as TData)}
</TableCell>
</TableRow>
)}
</React.Fragment>
))
) : (
<TableRow>
<TableCell
colSpan={headerGroups[0].headers.length}
className="h-24 text-center"
>
{t('noResult')}
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import {
ColumnDef,
Row,
SortingState,
Table,
getCoreRowModel,
getFilteredRowModel,
getPaginationRowModel,
getSortedRowModel,
useReactTable,
} from '@tanstack/react-table';
import { ReactNode, createContext, useContext, useMemo, useState } from 'react';
import { useColumnFilters } from './useColumnFilters.hook';
import { applyFilters } from '@/lib/filters';
import { ColumnFilter } from './DatatableDefaultFilterButton';
import { DataTable } from './DataTable';
import { DataTablePagination } from './DatatablePagination';

interface DataTableProviderProps<TData, TValue> {
columns: ColumnDef<TData, TValue>[];
data: TData[];
pageSize?: number;
itemNumber?: number;
filtersDefinition?: ColumnFilter[];
children?: ReactNode;
}

interface DataTableContextValue<TData> {
table: Table<TData>;
filtersDefinition?: ColumnFilter[];
columnFilters: ReturnType<typeof useColumnFilters>;
globalFilter: string;
data: TData[];
filteredData: TData[];
sorting: SortingState;
rows: Row<TData>[];
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const DataTableContext = createContext<DataTableContextValue<any> | null>(null);

export function DataTableProvider<TData, TValue>({
columns,
data,
pageSize,
filtersDefinition,
children,
}: DataTableProviderProps<TData, TValue>) {
const [sorting, setSorting] = useState<SortingState>([
{
id: columns[0]?.id as string,
desc: false,
},
]);
const [globalFilter, setGlobalFilter] = useState<string>('');
const columnFilters = useColumnFilters();

const filteredData = useMemo(
() => applyFilters(data || [], columnFilters.filters) as TData[],
[columnFilters.filters, data],
);
const table = useReactTable({
data: filteredData,
columns,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
onSortingChange: setSorting,
getSortedRowModel: getSortedRowModel(),
getFilteredRowModel: getFilteredRowModel(),
state: {
sorting,
globalFilter,
},
initialState: {
pagination: { pageSize: pageSize ?? 5 },
},
onGlobalFilterChange: (e) => {
setGlobalFilter(e);
},
globalFilterFn: 'auto',
});

const rows = useMemo(() => table.getRowModel()?.rows, [
table,
globalFilter,
columnFilters.filters,
data,
sorting,
]);

const contextValue: DataTableContextValue<TData> = {
table,
filtersDefinition,
columnFilters,
globalFilter,
data,
filteredData,
sorting,
rows,
};

return (
<DataTableContext.Provider value={contextValue}>
{children || (
<>
<DataTable />
<DataTablePagination />
</>
)}
</DataTableContext.Provider>
);
}

export function useDataTableContext<TData>() {
const context = useContext<DataTableContextValue<TData>>(DataTableContext);
if (!context) {
throw new Error(
'useDataTableContext must be used within a DataTableProvider',
);
}
return context as DataTableContextValue<TData>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { ReactNode } from 'react';

const DatatableAction = ({ children }: { children: ReactNode }) => {
return <>{children || <></>}</>;
};

export default DatatableAction;
Loading

0 comments on commit 090f6a8

Please sign in to comment.