add columns

This commit is contained in:
2025-11-19 00:46:57 -03:00
parent 786c5e1580
commit a180e269f2
6 changed files with 94 additions and 64 deletions

View File

@@ -26,9 +26,9 @@ export function DataTableColumnHeader<TData, TValue>({
return ( return (
<Button <Button
variant="ghost" variant="link"
size="sm" size="sm"
className={cn('-ml-3 cursor-pointer', className)} className={cn('-ml-3 cursor-pointer text-inherit h-auto', className)}
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')} onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
> >
<span>{title}</span> <span>{title}</span>

View File

@@ -17,6 +17,7 @@ import {
useContext, useContext,
useEffect, useEffect,
useMemo, useMemo,
useRef,
useState, useState,
type ReactNode type ReactNode
} from 'react' } from 'react'
@@ -43,7 +44,7 @@ interface DataTableProps<TData, TValue> {
sort: SortingState sort: SortingState
pageSize: number pageSize: number
rowCount: number rowCount: number
columnInvisibilityInit?: string[] columnVisibilityInit?: VisibilityState
} }
interface TableContextProps<TData> { interface TableContextProps<TData> {
@@ -71,21 +72,33 @@ export function DataTable<TData, TValue>({
pageSize, pageSize,
rowCount, rowCount,
setSelectedRows, setSelectedRows,
columnInvisibilityInit = [] columnVisibilityInit = {}
}: DataTableProps<TData, TValue>) { }: DataTableProps<TData, TValue>) {
const [dataTable, setDataTable] = useState<TData[]>(data) const [dataTable, setDataTable] = useState<TData[]>(data)
const columnInvisibility = useMemo<VisibilityState>(
() =>
Object.fromEntries(
columnInvisibilityInit.map((column) => [column, false])
),
[columnInvisibilityInit]
)
const [searchParams, setSearchParams] = useSearchParams() const [searchParams, setSearchParams] = useSearchParams()
const [columnVisibility, setColumnVisibility] =
useState<VisibilityState>(columnInvisibility)
const [rowSelection, setRowSelection] = useState<RowSelectionState>({}) const [rowSelection, setRowSelection] = useState<RowSelectionState>({})
const columnVisibilityInit_ = useMemo<VisibilityState>(() => {
const columns = searchParams.get('columns')
if (columns === null) {
return columnVisibilityInit
}
const columnVisibility = columns ? columns.split(',') : []
return Object.keys(columnVisibilityInit).reduce<VisibilityState>(
(acc, col) => ({
...acc,
[col]: columnVisibility.includes(col)
}),
{}
)
}, [])
const [columnVisibility, setColumnVisibility_] = useState<VisibilityState>(
columnVisibilityInit_
)
const sorting = useMemo(() => { const sorting = useMemo(() => {
const sortParam = searchParams.get('sort') const sortParam = searchParams.get('sort')
@@ -110,6 +123,7 @@ export function DataTable<TData, TValue>({
setSearchParams((prev) => { setSearchParams((prev) => {
prev.set('p', newState?.pageIndex.toString()) prev.set('p', newState?.pageIndex.toString())
prev.set('perPage', newState?.pageSize.toString()) prev.set('perPage', newState?.pageSize.toString())
return prev return prev
}) })
}, },
@@ -130,17 +144,47 @@ export function DataTable<TData, TValue>({
} else { } else {
prev.delete('sort') prev.delete('sort')
} }
return prev return prev
}) })
}, },
[sorting, setSearchParams] [sorting, setSearchParams]
) )
const setColumnVisibility = useCallback(
(updater: any) => {
const newColumnVisibility =
typeof updater === 'function' ? updater(columnVisibility) : updater
setColumnVisibility_(newColumnVisibility)
setSearchParams((prev) => {
const columns = Object.entries(newColumnVisibility)
.filter(([_, visible]) => visible)
.map(([col, _]) => col)
if (columns.length > 0) {
prev.set('columns', columns.join(','))
} else {
prev.set('columns', '')
}
return prev
})
},
[setSearchParams, setColumnVisibility_]
)
useEffect(() => { useEffect(() => {
setDataTable(data) setDataTable(data)
}, [data]) }, [data])
const tableConfig = useMemo( const getRowId = useCallback((row: any) => row.id, [])
() => ({ const removeRow = useCallback((rowId: string) => {
setDataTable((rows) => rows.filter((row: any) => row.id !== rowId))
}, [])
const table = useReactTable({
data: dataTable, data: dataTable,
columns, columns,
rowCount, rowCount,
@@ -148,42 +192,19 @@ export function DataTable<TData, TValue>({
sorting, sorting,
rowSelection, rowSelection,
columnVisibility, columnVisibility,
pagination: { pagination: { pageIndex, pageSize }
pageIndex,
pageSize
}
}, },
manualSorting: true, manualSorting: true,
manualPagination: true, manualPagination: true,
enableRowSelection: true, enableRowSelection: true,
getRowId: (row: any) => row.id, getRowId,
getCoreRowModel: getCoreRowModel(), getCoreRowModel: getCoreRowModel(),
onRowSelectionChange: setRowSelection, onRowSelectionChange: setRowSelection,
onSortingChange: setSorting, onSortingChange: setSorting,
onColumnVisibilityChange: setColumnVisibility, onColumnVisibilityChange: setColumnVisibility,
onPaginationChange: setPagination, onPaginationChange: setPagination,
meta: { meta: { removeRow }
removeRow: (rowId: string) => { })
setDataTable((rows) => rows.filter((row: any) => row?.id !== rowId))
}
}
}),
[
dataTable,
columns,
rowCount,
sorting,
rowSelection,
columnVisibility,
setColumnVisibility,
pageIndex,
pageSize,
setSorting,
setPagination
]
)
const table = useReactTable(tableConfig)
useEffect(() => { useEffect(() => {
if (!setSelectedRows) return if (!setSelectedRows) return

View File

@@ -41,6 +41,7 @@ export function DataTableViewOptions<TData>({
<DropdownMenuCheckboxItem <DropdownMenuCheckboxItem
key={column.id} key={column.id}
checked={column.getIsVisible()} checked={column.getIsVisible()}
onSelect={(e) => e.preventDefault()}
onCheckedChange={(value) => column.toggleVisibility(!!value)} onCheckedChange={(value) => column.toggleVisibility(!!value)}
> >
{title} {title}

View File

@@ -1,7 +1,7 @@
import { isbot } from 'isbot' import { isbot } from 'isbot'
import { renderToReadableStream } from 'react-dom/server' import { renderToReadableStream } from 'react-dom/server'
import type { AppLoadContext, EntryContext } from 'react-router' import type { AppLoadContext, EntryContext } from 'react-router'
import { ServerRouter } from 'react-router' import { ServerRouter, type HandleErrorFunction } from 'react-router'
export default async function handleRequest( export default async function handleRequest(
request: Request, request: Request,
@@ -44,3 +44,10 @@ export default async function handleRequest(
// https://reactrouter.com/how-to/suspense#timeouts // https://reactrouter.com/how-to/suspense#timeouts
export const streamTimeout = 7_000 export const streamTimeout = 7_000
// https://reactrouter.com/how-to/error-reporting
export const handleError: HandleErrorFunction = (error, { request }) => {
if (!request.signal.aborted) {
console.error(error)
}
}

View File

@@ -107,12 +107,13 @@ export default function Route({ loaderData: { data } }) {
pageSize={hitsPerPage} pageSize={hitsPerPage}
setSelectedRows={setSelectedRows} setSelectedRows={setSelectedRows}
rowCount={totalHits} rowCount={totalHits}
hiddenColumn={[ columnVisibilityInit={{
'completed_at', created_at: true,
'started_at', completed_at: false,
'failed_at', started_at: false,
'canceled_at' failed_at: false,
]} canceled_at: false
}}
> >
<div className="flex gap-2.5 mb-2.5"> <div className="flex gap-2.5 mb-2.5">
{selectedRows.length ? ( {selectedRows.length ? (

View File

@@ -117,7 +117,7 @@ export async function action({ params, request, context }: Route.ActionArgs) {
return { ok: true } return { ok: true }
} }
export default function Route() { export default function Route({}: Route.ComponentProps) {
const fetcher = useFetcher() const fetcher = useFetcher()
const { activeWorkspace } = useWorksapce() const { activeWorkspace } = useWorksapce()
const form = useForm({ const form = useForm({
@@ -143,7 +143,7 @@ export default function Route() {
case 'UserConflictError': case 'UserConflictError':
toast.error('O colaborador já foi vinculado anteriormente') toast.error('O colaborador já foi vinculado anteriormente')
} }
}, [fetcher.data]) }, [fetcher.data, reset])
useEffect(() => { useEffect(() => {
if (givenEmail) { if (givenEmail) {