Skip to content

Commit

Permalink
feat(pci-databases-analytics): refactor data table and add filtering (#…
Browse files Browse the repository at this point in the history
…14524)

ref: DATATR-1763, DATATR-681

Signed-off-by: Jonathan Perchoc <[email protected]>
  • Loading branch information
jperchoc authored Jan 17, 2025
1 parent fdd8553 commit 18949ed
Show file tree
Hide file tree
Showing 61 changed files with 1,736 additions and 523 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@
"status-PENDING": "Ausstehend",
"status-UPDATING": "Update",
"status-ERROR": "Fehler",
"status-ERROR_INCONSISTENT_SPEC": "Fehler",
"status-ERROR_INCONSISTENT_SPEC": "SPEC-Fehler",
"status-READY": "Verfügbar"
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@
"status-PENDING": "Queued",
"status-UPDATING": "Updating",
"status-ERROR": "Error",
"status-ERROR_INCONSISTENT_SPEC": "Error",
"status-ERROR_INCONSISTENT_SPEC": "spec error",
"status-READY": "Available"
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@
"status-PENDING": "Pendiente",
"status-UPDATING": "Actualizada",
"status-ERROR": "En error",
"status-ERROR_INCONSISTENT_SPEC": "En error",
"status-ERROR_INCONSISTENT_SPEC": "Error spec",
"status-READY": "Disponible"
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@
"status-PENDING": "En attente",
"status-UPDATING": "Mise à jour",
"status-ERROR": "En erreur",
"status-ERROR_INCONSISTENT_SPEC": "En erreur",
"status-ERROR_INCONSISTENT_SPEC": "Erreur spec",
"status-READY": "Disponible"
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@
"status-PENDING": "En attente",
"status-UPDATING": "Mise à jour",
"status-ERROR": "En erreur",
"status-ERROR_INCONSISTENT_SPEC": "En erreur",
"status-ERROR_INCONSISTENT_SPEC": "Erreur spec",
"status-READY": "Disponible"
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@
"status-PENDING": "In attesa",
"status-UPDATING": "Aggiornamento",
"status-ERROR": "In errore",
"status-ERROR_INCONSISTENT_SPEC": "In errore",
"status-ERROR_INCONSISTENT_SPEC": "Errore specifico",
"status-READY": "Disponibile"
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@
"status-PENDING": "Oczekujące",
"status-UPDATING": "Aktualizacja",
"status-ERROR": "Błąd",
"status-ERROR_INCONSISTENT_SPEC": "Błąd",
"status-ERROR_INCONSISTENT_SPEC": "Błąd spec",
"status-READY": "Dostępny"
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@
"status-PENDING": "Em espera",
"status-UPDATING": "Atualização",
"status-ERROR": "Erro",
"status-ERROR_INCONSISTENT_SPEC": "Erro",
"status-ERROR_INCONSISTENT_SPEC": "Erro spec",
"status-READY": "Disponível"
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,13 @@ export const mockedQueries: database.service.currentqueries.Query = {

export const mockedQueryStatistics: database.mysql.querystatistics.Query = {
avgTimerWait: 3,
countStar: 3,
countStar: 11,
digest: 'digest',
digestText: 'digestText',
firstSeen: 'firstSeen',
lastSeen: 'lastSeen',
maxTimerWait: 3,
minTimerWait: 3,
maxTimerWait: 9,
minTimerWait: 2.01,
quantile95: 3,
quantile99: 1,
quantile999: 78987,
Expand All @@ -49,7 +49,7 @@ export const mockedQueryStatistics: database.mysql.querystatistics.Query = {
sumNoIndexUsed: 3,
sumRowsAffected: 3,
sumRowsExamined: 3,
sumRowsSent: 3,
sumRowsSent: 8,
sumSelectFullJoin: 3,
sumSelectFullRangeJoin: 3,
sumSelectRange: 3,
Expand All @@ -59,7 +59,7 @@ export const mockedQueryStatistics: database.mysql.querystatistics.Query = {
sumSortRange: 3,
sumSortRows: 3,
sumSortScan: 3,
sumTimerWait: 3,
sumTimerWait: 4,
sumWarnings: 3,
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,12 @@ import { PointerEvent } from './helpers/pointerEvent';
// it is requiered for DropdownMenus
// source: https://github.com/radix-ui/primitives/issues/856#issuecomment-928704064
window.PointerEvent = PointerEvent as any;

const originalConsoleError = console.error;

console.error = (...args) => {
if (typeof args[0] === 'string' && args[0].includes('connect ECONNREFUSED')) {
return;
}
originalConsoleError(...args);
};
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 './DataTable.context';
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from '@/components/ui/table';
import { Button } from '../ui/button';

export const MENU_COLUMN_ID = 'actions_menu_column';

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.component';
import { DataTable } from './DataTable.component';
import { DataTablePagination } from './DatatablePagination.component';

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,5 @@
import { ReactNode } from 'react';

export function DatatableAction({ children }: { children: ReactNode }) {
return <>{children || <></>}</>;
}
Loading

0 comments on commit 18949ed

Please sign in to comment.