update package

This commit is contained in:
2025-11-19 15:13:00 -03:00
parent a180e269f2
commit d37db405c8
51 changed files with 5870 additions and 2490 deletions

View File

@@ -1,16 +0,0 @@
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

@@ -18,7 +18,8 @@ import {
Sidebar,
SidebarContent,
SidebarFooter,
SidebarHeader
SidebarHeader,
SidebarRail
} from '@repo/ui/components/ui/sidebar'
const data = {
@@ -75,13 +76,14 @@ const data = {
]
}
export function AppSidebar({ orgs = [] }) {
export function AppSidebar() {
return (
<Sidebar collapsible="icon">
<SidebarHeader>
<WorkspaceSwitcher />
</SidebarHeader>
<SidebarContent>
<SidebarRail />
<NavMain data={data} />
</SidebarContent>
<SidebarFooter />

View File

@@ -1,44 +0,0 @@
import { type Column } from '@tanstack/react-table'
import {
ChevronDownIcon,
ChevronsUpDownIcon,
ChevronUpIcon
} from 'lucide-react'
import { Button } from '@repo/ui/components/ui/button'
import { cn } from '@repo/ui/lib/utils'
interface DataTableColumnHeaderProps<TData, TValue>
extends React.HTMLAttributes<HTMLDivElement> {
column: Column<TData, TValue>
}
export function DataTableColumnHeader<TData, TValue>({
column,
className
}: DataTableColumnHeaderProps<TData, TValue>) {
// @ts-ignore
const title = column.columnDef?.meta?.title ?? column.id
if (!column.getCanSort()) {
return <div className={cn(className)}>{title}</div>
}
return (
<Button
variant="link"
size="sm"
className={cn('-ml-3 cursor-pointer text-inherit h-auto', className)}
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
>
<span>{title}</span>
{column.getIsSorted() === 'desc' ? (
<ChevronDownIcon />
) : column.getIsSorted() === 'asc' ? (
<ChevronUpIcon />
) : (
<ChevronsUpDownIcon />
)}
</Button>
)
}

View File

@@ -1,297 +0,0 @@
'use client'
import {
flexRender,
getCoreRowModel,
useReactTable,
type ColumnDef,
type ColumnSort,
type RowSelectionState,
type SortingState,
type Table,
type VisibilityState
} from '@tanstack/react-table'
import {
createContext,
useCallback,
useContext,
useEffect,
useMemo,
useRef,
useState,
type ReactNode
} from 'react'
import { useSearchParams } from 'react-router'
import { Card, CardContent } from '@repo/ui/components/ui/card'
import {
Table as Table_,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow
} from '@repo/ui/components/ui/table'
import { cn } from '@repo/ui/lib/utils'
import { DataTablePagination } from './pagination'
interface DataTableProps<TData, TValue> {
children?: ReactNode
columns: ColumnDef<TData, TValue>[]
data: TData[]
setSelectedRows?: (selectedRows: TData[]) => void
pageIndex: number
sort: SortingState
pageSize: number
rowCount: number
columnVisibilityInit?: VisibilityState
}
interface TableContextProps<TData> {
table: Table<TData>
}
const TableContext = createContext<TableContextProps<any> | null>(null)
export function useDataTable<TData>() {
const ctx = useContext(TableContext)
if (!ctx) {
throw new Error('TableContext is null')
}
return ctx as { table: Table<TData> }
}
export function DataTable<TData, TValue>({
data,
children,
columns,
sort,
pageIndex,
pageSize,
rowCount,
setSelectedRows,
columnVisibilityInit = {}
}: DataTableProps<TData, TValue>) {
const [dataTable, setDataTable] = useState<TData[]>(data)
const [searchParams, setSearchParams] = useSearchParams()
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 sortParam = searchParams.get('sort')
if (!sortParam) {
return sort
}
return sortParam.split(',').map((s) => {
const [id, dir] = s.split(':')
return { id, desc: dir === 'desc' }
})
}, [searchParams, sort])
const setPagination = useCallback(
(updater: any) => {
const newState =
typeof updater === 'function'
? updater({ pageIndex, pageSize })
: updater
setRowSelection({})
setSearchParams((prev) => {
prev.set('p', newState?.pageIndex.toString())
prev.set('perPage', newState?.pageSize.toString())
return prev
})
},
[pageIndex, pageSize, setSearchParams]
)
const setSorting = useCallback(
(updater: any) => {
const newSorting =
typeof updater === 'function' ? updater(sorting) : updater
setSearchParams((prev) => {
if (newSorting.length) {
const sort = newSorting
.map((s: ColumnSort) => `${s.id}:${s.desc ? 'desc' : 'asc'}`)
.join(',')
prev.set('sort', sort)
} else {
prev.delete('sort')
}
return prev
})
},
[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(() => {
setDataTable(data)
}, [data])
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,
columns,
rowCount,
state: {
sorting,
rowSelection,
columnVisibility,
pagination: { pageIndex, pageSize }
},
manualSorting: true,
manualPagination: true,
enableRowSelection: true,
getRowId,
getCoreRowModel: getCoreRowModel(),
onRowSelectionChange: setRowSelection,
onSortingChange: setSorting,
onColumnVisibilityChange: setColumnVisibility,
onPaginationChange: setPagination,
meta: { removeRow }
})
useEffect(() => {
if (!setSelectedRows) return
const selected = table.getSelectedRowModel().flatRows.map((r) => r.original)
setSelectedRows(selected)
}, [rowSelection, setSelectedRows, table])
return (
<TableContext value={{ table }}>
<div className="space-y-2.5 max-md:mb-2">
<Card className="relative w-full overflow-auto">
<CardContent>
{children}
<Table_ className="table-auto">
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow
key={headerGroup.id}
className="hover:bg-transparent"
>
{headerGroup.headers.map((header) => {
return (
<TableHead
key={header.id}
className={cn(
'p-2.5',
// @ts-ignore
header.column.columnDef.meta?.className
)}
>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext()
)}
</TableHead>
)
})}
</TableRow>
))}
</TableHeader>
<TableBody>
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row) => (
<TableRow
key={row.id}
data-state={row.getIsSelected() && 'selected'}
className="has-[[data-state=open]]:bg-muted/50"
>
{row.getVisibleCells().map((cell) => (
<TableCell
key={cell.id}
className={cn(
'p-2.5',
// @ts-ignore
cell.column.columnDef.meta?.className
)}
>
{flexRender(
cell.column.columnDef.cell,
cell.getContext()
)}
</TableCell>
))}
</TableRow>
))
) : (
<TableRow>
<TableCell
colSpan={columns.length}
className="h-24 text-center"
>
Nenhum resultado.
</TableCell>
</TableRow>
)}
</TableBody>
</Table_>
</CardContent>
</Card>
<DataTablePagination table={table} />
</div>
</TableContext>
)
}

View File

@@ -1,3 +0,0 @@
export { DataTableColumnHeader } from './column-header'
export { DataTable } from './data-table'
export { DataTableViewOptions } from './view-options'

View File

@@ -1,75 +0,0 @@
import { type Table } from '@tanstack/react-table'
import { ChevronLeftIcon, ChevronRightIcon } from 'lucide-react'
import { Button } from '@repo/ui/components/ui/button'
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue
} from '@repo/ui/components/ui/select'
interface DataTablePaginationProps<TData> {
table: Table<TData>
}
export function DataTablePagination<TData>({
table
}: DataTablePaginationProps<TData>) {
const { pageIndex, pageSize } = table.getState().pagination
const rowCount = table.getRowCount()
return (
<div className="flex items-center justify-end gap-3 lg:gap-6">
<div className="flex items-center gap-2">
<p className="text-sm font-medium hidden lg:block">Itens por página</p>
<Select
value={String(table.getState().pagination.pageSize)}
onValueChange={(value) => {
table.setPageSize(Number(value))
}}
>
<SelectTrigger>
<SelectValue placeholder={table.getState().pagination.pageSize} />
</SelectTrigger>
<SelectContent side="top">
{[12, 25, 50, 100].map((pageSize) => (
<SelectItem key={pageSize} value={String(pageSize)}>
{pageSize}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="text-sm font-medium">
{(pageIndex + 1) * pageSize - pageSize + 1}-
{Math.min((pageIndex + 1) * pageSize, rowCount)}
</div>
<div className="flex items-center gap-2 *:cursor-pointer">
<Button
variant="ghost"
size="icon"
className="size-8"
onClick={() => table.previousPage()}
disabled={!table.getCanPreviousPage()}
>
<span className="sr-only">Ir para a página anterior</span>
<ChevronLeftIcon />
</Button>
<Button
variant="ghost"
size="icon"
className="size-8"
onClick={() => table.nextPage()}
disabled={!table.getCanNextPage()}
>
<span className="sr-only">Ir para a próxima página</span>
<ChevronRightIcon />
</Button>
</div>
</div>
)
}

View File

@@ -1,54 +0,0 @@
'use client'
import { Columns2Icon } from 'lucide-react'
import { Button } from '@repo/ui/components/ui/button'
import {
DropdownMenu,
DropdownMenuCheckboxItem,
DropdownMenuContent,
DropdownMenuTrigger
} from '@repo/ui/components/ui/dropdown-menu'
import { cn } from '@repo/ui/lib/utils'
import { useDataTable } from './data-table'
export function DataTableViewOptions<TData>({
className
}: {
className: string
}) {
const { table } = useDataTable()
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" className={cn('cursor-pointer', className)}>
<Columns2Icon /> Colunas
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-44 *:cursor-pointer">
{table
.getAllColumns()
.filter(
(column) =>
typeof column.accessorFn !== 'undefined' && column.getCanHide()
)
.map((column) => {
// @ts-ignore
const title = column.columnDef?.meta?.title ?? column.id
return (
<DropdownMenuCheckboxItem
key={column.id}
checked={column.getIsVisible()}
onSelect={(e) => e.preventDefault()}
onCheckedChange={(value) => column.toggleVisibility(!!value)}
>
{title}
</DropdownMenuCheckboxItem>
)
})}
</DropdownMenuContent>
</DropdownMenu>
)
}

View File

@@ -44,9 +44,7 @@ export function NavMain({
<SidebarGroup>
<SidebarGroupContent>
<SidebarMenu>
<SidebarGroupLabel className="uppercase">
Colaboradores
</SidebarGroupLabel>
<SidebarGroupLabel>Colaboradores</SidebarGroupLabel>
{data.navUser.map((props, idx) => (
<SidebarMenuItemLink key={idx} {...props} />
))}
@@ -57,9 +55,7 @@ export function NavMain({
<SidebarGroup>
<SidebarGroupContent>
<SidebarMenu>
<SidebarGroupLabel className="uppercase">
Gestão de matrículas
</SidebarGroupLabel>
<SidebarGroupLabel>Gestão de matrículas</SidebarGroupLabel>
{data.navEnrollment.map((props, idx) => (
<SidebarMenuItemLink key={idx} {...props} />
))}
@@ -80,7 +76,7 @@ function SidebarMenuItemLink({ title, url, icon: Icon }: NavItem) {
return (
<SidebarMenuItem key={title} onClick={onToggle}>
<NavLink to={`/${orgid}${url}`}>
{({ isActive, isPending }) => (
{({ isActive }) => (
<SidebarMenuButton
asChild
className="data-[active=true]:text-lime-500"

View File

@@ -1,174 +0,0 @@
'use client'
import { EllipsisIcon } from 'lucide-react'
import { useState } from 'react'
import { type DateRange } from 'react-day-picker'
import { ptBR } from 'react-day-picker/locale'
import { Badge } from '@repo/ui/components/ui/badge'
import { Button } from '@repo/ui/components/ui/button'
import { Calendar } from '@repo/ui/components/ui/calendar'
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuRadioGroup,
DropdownMenuRadioItem,
DropdownMenuTrigger
} from '@repo/ui/components/ui/dropdown-menu'
import {
Popover,
PopoverContent,
PopoverTrigger
} from '@repo/ui/components/ui/popover'
import { Separator } from '@repo/ui/components/ui/separator'
import { cn } from '@repo/ui/lib/utils'
const formatted = new Intl.DateTimeFormat('pt-BR', {
day: '2-digit',
month: '2-digit',
year: '2-digit'
})
type FilterProps = {
rangeField?: string
dateRange?: DateRange
}
type RangeCalendarFilterProps = {
title: string
icon: React.ComponentType
value?: FilterProps
options: { value: string; label: string }[]
className?: string
onChange?: (values: FilterProps | undefined) => void
}
export function RangeCalendarFilter({
title,
icon: Icon,
value = undefined,
options = [],
className,
onChange
}: RangeCalendarFilterProps) {
const [rangeField, setRangeField] = useState<string | undefined>(
value?.rangeField || options?.[0]?.value
)
const [dateRange, setDateRange] = useState<DateRange | undefined>(
value?.dateRange
)
const handleReset = () => {
onChange?.(undefined)
setRangeField(options?.[0]?.value)
setDateRange(undefined)
}
return (
<Popover>
<div
className={cn(
'h-9 border rounded-md dark:bg-input/30 border-dashed flex items-center justify-center',
className
)}
>
<PopoverTrigger asChild>
<Button variant="ghost" size="sm" className="cursor-pointer h-full">
<Icon /> {title}
{dateRange && (
<>
<Separator orientation="vertical" className="mx-0.5 h-4" />
<div className="gap-1 flex">
<Badge
variant="outline"
className="rounded-sm px-1 font-mono"
>
{formatted.format(dateRange.from)}
</Badge>
<Badge
variant="outline"
className="rounded-sm px-1 font-mono"
>
{formatted.format(dateRange.to)}
</Badge>
</div>
</>
)}
</Button>
</PopoverTrigger>
{dateRange && (
<>
<Separator orientation="vertical" className="h-4" />
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" size="sm" className="cursor-pointer">
<EllipsisIcon />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="w-46" align="end">
<DropdownMenuRadioGroup
value={rangeField}
onValueChange={(rangeField) => {
setRangeField(rangeField)
onChange?.({ rangeField, dateRange })
}}
className="*:cursor-pointer"
>
{options.map(({ label, value }, idx) => (
<DropdownMenuRadioItem value={value} key={idx}>
{label}
</DropdownMenuRadioItem>
))}
</DropdownMenuRadioGroup>
</DropdownMenuContent>
</DropdownMenu>
</>
)}
</div>
<PopoverContent className="w-full p-0" align="start">
<Calendar
mode="range"
locale={ptBR}
captionLayout="dropdown"
defaultMonth={dateRange?.from}
selected={dateRange}
numberOfMonths={1}
startMonth={new Date(2012, 0)}
endMonth={new Date(new Date().getFullYear() + 1, 0)}
onSelect={(dateRange: DateRange | undefined) => {
if (!dateRange) {
onChange?.(undefined)
return setDateRange(undefined)
}
if (dateRange.from?.getTime() === dateRange.to?.getTime()) {
const nextDay = new Date(String(dateRange.from))
nextDay.setDate(nextDay.getDate() + 6)
dateRange.to = nextDay
}
onChange?.({ rangeField, dateRange })
setDateRange(dateRange)
}}
/>
{dateRange && (
<>
<Separator />
<div className="p-1">
<Button
variant="ghost"
className="w-full cursor-pointer font-normal"
onClick={handleReset}
>
Limpar
</Button>
</div>
</>
)}
</PopoverContent>
</Popover>
)
}

View File

@@ -18,7 +18,6 @@ import {
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
SidebarRail,
useSidebar
} from '@repo/ui/components/ui/sidebar'
import { initials } from '@repo/ui/lib/utils'
@@ -153,7 +152,6 @@ export function WorkspaceSwitcher() {
</DropdownMenuContent>
</DropdownMenu>
</SidebarMenuItem>
<SidebarRail />
</SidebarMenu>
)
}