This commit is contained in:
2025-11-10 00:24:07 -03:00
parent 7f385bf175
commit 24dfefe395
6 changed files with 66 additions and 42 deletions

View File

@@ -0,0 +1,16 @@
type AbbrProps = {
children: string
maxLen?: number
}
export function Abbr({ children, maxLen = 30, ...props }: AbbrProps) {
if (children.length <= maxLen) {
return <abbr {...props}>{children}</abbr>
}
return (
<abbr title={children} {...props}>
{children.substring(0, maxLen).concat('...')}
</abbr>
)
}

View File

@@ -11,14 +11,15 @@ import { cn } from '@repo/ui/lib/utils'
interface DataTableColumnHeaderProps<TData, TValue> interface DataTableColumnHeaderProps<TData, TValue>
extends React.HTMLAttributes<HTMLDivElement> { extends React.HTMLAttributes<HTMLDivElement> {
column: Column<TData, TValue> column: Column<TData, TValue>
title: string
} }
export function DataTableColumnHeader<TData, TValue>({ export function DataTableColumnHeader<TData, TValue>({
column, column,
title,
className className
}: DataTableColumnHeaderProps<TData, TValue>) { }: DataTableColumnHeaderProps<TData, TValue>) {
// @ts-ignore
const title = column.columnDef?.meta?.title ?? column.id
if (!column.getCanSort()) { if (!column.getCanSort()) {
return <div className={cn(className)}>{title}</div> return <div className={cn(className)}>{title}</div>
} }

View File

@@ -10,7 +10,13 @@ import {
type Table, type Table,
type VisibilityState type VisibilityState
} from '@tanstack/react-table' } from '@tanstack/react-table'
import { createContext, useEffect, useState, type ReactNode } from 'react' import {
createContext,
useContext,
useEffect,
useState,
type ReactNode
} from 'react'
import { useSearchParams } from 'react-router' import { useSearchParams } from 'react-router'
import { Card, CardContent } from '@repo/ui/components/ui/card' import { Card, CardContent } from '@repo/ui/components/ui/card'
@@ -36,7 +42,17 @@ interface DataTableProps<TData, TValue> {
hiddenColumn?: string[] hiddenColumn?: string[]
} }
export const TableContext = createContext<{ table: Table<any> } | null>(null) const TableContext = createContext<{ table: Table<any> } | null>(null)
export function useDataTable<TData>() {
const ctx = useContext(TableContext) as { table: Table<TData> } | null
if (!ctx) {
throw new Error('TableContext is null')
}
return ctx
}
export function DataTable<TData, TValue>({ export function DataTable<TData, TValue>({
children, children,
@@ -68,6 +84,9 @@ export function DataTable<TData, TValue>({
const newState = const newState =
typeof updater === 'function' ? updater({ pageIndex, pageSize }) : updater typeof updater === 'function' ? updater({ pageIndex, pageSize }) : updater
onRowSelectionChange?.([])
setRowSelection({})
setSearchParams((searchParams) => { setSearchParams((searchParams) => {
searchParams.set('p', newState?.pageIndex.toString()) searchParams.set('p', newState?.pageIndex.toString())
searchParams.set('perPage', newState?.pageSize.toString()) searchParams.set('perPage', newState?.pageSize.toString())

View File

@@ -1,8 +1,5 @@
'use client' 'use client'
import { useContext } from 'react'
import { type Table } from '@tanstack/react-table'
import { Columns2Icon } from 'lucide-react' import { Columns2Icon } from 'lucide-react'
import { Button } from '@repo/ui/components/ui/button' import { Button } from '@repo/ui/components/ui/button'
@@ -13,20 +10,14 @@ import {
DropdownMenuTrigger DropdownMenuTrigger
} from '@repo/ui/components/ui/dropdown-menu' } from '@repo/ui/components/ui/dropdown-menu'
import { cn } from '@repo/ui/lib/utils' import { cn } from '@repo/ui/lib/utils'
import { TableContext } from './data-table' import { useDataTable } from './data-table'
export function DataTableViewOptions<TData>({ export function DataTableViewOptions<TData>({
className className
}: { }: {
className: string className: string
}) { }) {
const ctx = useContext(TableContext) as { table: Table<TData> } | null const { table } = useDataTable()
if (!ctx) {
throw new Error('TableContext is null')
}
const { table } = ctx
return ( return (
<DropdownMenu> <DropdownMenu>
@@ -43,13 +34,16 @@ export function DataTableViewOptions<TData>({
typeof column.accessorFn !== 'undefined' && column.getCanHide() typeof column.accessorFn !== 'undefined' && column.getCanHide()
) )
.map((column) => { .map((column) => {
// @ts-ignore
const title = column.columnDef?.meta?.title ?? column.id
return ( return (
<DropdownMenuCheckboxItem <DropdownMenuCheckboxItem
key={column.id} key={column.id}
checked={column.getIsVisible()} checked={column.getIsVisible()}
onCheckedChange={(value) => column.toggleVisibility(!!value)} onCheckedChange={(value) => column.toggleVisibility(!!value)}
> >
{column.columnDef?.meta?.title ?? column.id} {title}
</DropdownMenuCheckboxItem> </DropdownMenuCheckboxItem>
) )
})} })}

View File

@@ -9,6 +9,7 @@ import { Checkbox } from '@repo/ui/components/ui/checkbox'
import { Progress } from '@repo/ui/components/ui/progress' import { Progress } from '@repo/ui/components/ui/progress'
import { cn, initials } from '@repo/ui/lib/utils' import { cn, initials } from '@repo/ui/lib/utils'
import { Abbr } from '@/components/abbr'
import { DataTableColumnHeader } from '@/components/data-table/column-header' import { DataTableColumnHeader } from '@/components/data-table/column-header'
import { labels, statuses } from './data' import { labels, statuses } from './data'
@@ -46,6 +47,7 @@ export const columns: ColumnDef<Enrollment>[] = [
(table.getIsSomePageRowsSelected() && 'indeterminate') (table.getIsSomePageRowsSelected() && 'indeterminate')
} }
onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)} onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)}
className="cursor-pointer"
aria-label="Selecionar tudo" aria-label="Selecionar tudo"
/> />
), ),
@@ -54,6 +56,7 @@ export const columns: ColumnDef<Enrollment>[] = [
checked={row.getIsSelected()} checked={row.getIsSelected()}
disabled={!row.getCanSelect()} disabled={!row.getCanSelect()}
onCheckedChange={(value) => row.toggleSelected(!!value)} onCheckedChange={(value) => row.toggleSelected(!!value)}
className="cursor-pointer"
aria-label="Selecionar linha" aria-label="Selecionar linha"
/> />
) )
@@ -72,9 +75,11 @@ export const columns: ColumnDef<Enrollment>[] = [
</Avatar> </Avatar>
<ul> <ul>
<li className="font-bold truncate max-w-62">{user.name}</li> <li className="font-bold">
<li className="text-muted-foreground text-sm truncate max-w-62"> <Abbr>{user.name}</Abbr>
{user.email} </li>
<li className="text-muted-foreground text-sm">
<Abbr>{user.email}</Abbr>
</li> </li>
</ul> </ul>
</div> </div>
@@ -88,11 +93,7 @@ export const columns: ColumnDef<Enrollment>[] = [
cell: ({ row }) => { cell: ({ row }) => {
const { name } = row.getValue('course') as { name: string } const { name } = row.getValue('course') as { name: string }
return ( return <Abbr>{name}</Abbr>
<abbr className="truncate max-w-62 block" title={name}>
{name}
</abbr>
)
} }
}, },
{ {
@@ -129,9 +130,7 @@ export const columns: ColumnDef<Enrollment>[] = [
}, },
{ {
accessorKey: 'created_at', accessorKey: 'created_at',
header: ({ column }) => ( header: ({ column }) => <DataTableColumnHeader column={column} />,
<DataTableColumnHeader column={column} title="Cadastrado em" />
),
meta: { title: 'Cadastrado em' }, meta: { title: 'Cadastrado em' },
enableSorting: true, enableSorting: true,
enableHiding: true, enableHiding: true,
@@ -139,9 +138,7 @@ export const columns: ColumnDef<Enrollment>[] = [
}, },
{ {
accessorKey: 'started_at', accessorKey: 'started_at',
header: ({ column }) => ( header: ({ column }) => <DataTableColumnHeader column={column} />,
<DataTableColumnHeader column={column} title="Iniciado em" />
),
meta: { title: 'Iniciado em' }, meta: { title: 'Iniciado em' },
enableSorting: true, enableSorting: true,
enableHiding: true, enableHiding: true,
@@ -149,9 +146,7 @@ export const columns: ColumnDef<Enrollment>[] = [
}, },
{ {
accessorKey: 'completed_at', accessorKey: 'completed_at',
header: ({ column }) => ( header: ({ column }) => <DataTableColumnHeader column={column} />,
<DataTableColumnHeader column={column} title="Concluído em" />
),
meta: { title: 'Concluído em' }, meta: { title: 'Concluído em' },
enableSorting: true, enableSorting: true,
enableHiding: true, enableHiding: true,
@@ -159,9 +154,7 @@ export const columns: ColumnDef<Enrollment>[] = [
}, },
{ {
accessorKey: 'failed_at', accessorKey: 'failed_at',
header: ({ column }) => ( header: ({ column }) => <DataTableColumnHeader column={column} />,
<DataTableColumnHeader column={column} title="Reprovado em" />
),
meta: { title: 'Reprovado em' }, meta: { title: 'Reprovado em' },
enableSorting: true, enableSorting: true,
enableHiding: true, enableHiding: true,
@@ -169,9 +162,7 @@ export const columns: ColumnDef<Enrollment>[] = [
}, },
{ {
accessorKey: 'canceled_at', accessorKey: 'canceled_at',
header: ({ column }) => ( header: ({ column }) => <DataTableColumnHeader column={column} />,
<DataTableColumnHeader column={column} title="Cancelado em" />
),
meta: { title: 'Cancelado em' }, meta: { title: 'Cancelado em' },
enableSorting: true, enableSorting: true,
enableHiding: true, enableHiding: true,

View File

@@ -5,6 +5,7 @@ import { type ColumnDef } from '@tanstack/react-table'
import { ArrowRight } from 'lucide-react' import { ArrowRight } from 'lucide-react'
import { NavLink } from 'react-router' import { NavLink } from 'react-router'
import { Abbr } from '@/components/abbr'
import { Avatar, AvatarFallback } from '@repo/ui/components/ui/avatar' import { Avatar, AvatarFallback } from '@repo/ui/components/ui/avatar'
import { Button } from '@repo/ui/components/ui/button' import { Button } from '@repo/ui/components/ui/button'
import { Spinner } from '@repo/ui/components/ui/spinner' import { Spinner } from '@repo/ui/components/ui/spinner'
@@ -41,9 +42,11 @@ export const columns: ColumnDef<User>[] = [
</Avatar> </Avatar>
<ul> <ul>
<li className="font-bold truncate max-w-62">{name}</li> <li className="font-bold">
<li className="text-muted-foreground text-sm truncate max-w-92"> <Abbr>{name}</Abbr>
{email} </li>
<li className="text-muted-foreground text-sm">
<Abbr>{email}</Abbr>
</li> </li>
</ul> </ul>
</div> </div>