rename
This commit is contained in:
@@ -6,6 +6,7 @@ import {
|
||||
useReactTable,
|
||||
type ColumnDef,
|
||||
type ColumnSort,
|
||||
type RowSelectionState,
|
||||
type SortingState,
|
||||
type Table,
|
||||
type VisibilityState
|
||||
@@ -34,7 +35,7 @@ interface DataTableProps<TData, TValue> {
|
||||
children?: ReactNode
|
||||
columns: ColumnDef<TData, TValue>[]
|
||||
data: TData[]
|
||||
onRowSelectionChange?: (rowSelection: TData[]) => void
|
||||
setSelectedRows?: (selectedRows: TData[]) => void
|
||||
pageIndex: number
|
||||
sort: SortingState
|
||||
pageSize: number
|
||||
@@ -62,16 +63,17 @@ export function DataTable<TData, TValue>({
|
||||
pageIndex,
|
||||
pageSize,
|
||||
rowCount,
|
||||
onRowSelectionChange,
|
||||
setSelectedRows,
|
||||
hiddenColumn = []
|
||||
}: DataTableProps<TData, TValue>) {
|
||||
const [searchParams, setSearchParams] = useSearchParams()
|
||||
const hiddenColumn_ = Object.fromEntries(
|
||||
const columnVisibilityInit = Object.fromEntries(
|
||||
hiddenColumn.map((column) => [column, false])
|
||||
)
|
||||
|
||||
const [searchParams, setSearchParams] = useSearchParams()
|
||||
const [columnVisibility, setColumnVisibility] =
|
||||
useState<VisibilityState>(hiddenColumn_)
|
||||
const [rowSelection, setRowSelection] = useState({})
|
||||
useState<VisibilityState>(columnVisibilityInit)
|
||||
const [rowSelection, setRowSelection] = useState<RowSelectionState>({})
|
||||
const sortParam = searchParams.get('sort')
|
||||
const sorting = sortParam
|
||||
? sortParam.split(',').map((s) => {
|
||||
@@ -84,9 +86,7 @@ export function DataTable<TData, TValue>({
|
||||
const newState =
|
||||
typeof updater === 'function' ? updater({ pageIndex, pageSize }) : updater
|
||||
|
||||
onRowSelectionChange?.([])
|
||||
setRowSelection({})
|
||||
|
||||
setSearchParams((searchParams) => {
|
||||
searchParams.set('p', newState?.pageIndex.toString())
|
||||
searchParams.set('perPage', newState?.pageSize.toString())
|
||||
@@ -135,8 +135,8 @@ export function DataTable<TData, TValue>({
|
||||
|
||||
useEffect(() => {
|
||||
const selected = table.getSelectedRowModel().flatRows.map((r) => r.original)
|
||||
onRowSelectionChange?.(selected)
|
||||
}, [rowSelection, table])
|
||||
setSelectedRows?.(selected)
|
||||
}, [rowSelection])
|
||||
|
||||
return (
|
||||
<TableContext value={{ table }}>
|
||||
|
||||
@@ -52,3 +52,18 @@ export const sortings: Record<string, string> = {
|
||||
failed_at: 'Reprovado em',
|
||||
canceled_at: 'Cancelado em'
|
||||
}
|
||||
|
||||
export const headers = {
|
||||
id: 'ID',
|
||||
'user.name': 'Nome',
|
||||
'user.email': 'Email',
|
||||
'user.cpf': 'CPF',
|
||||
'course.name': 'Curso',
|
||||
status: 'Status',
|
||||
progress: 'Progresso',
|
||||
created_at: 'Cadastrado em',
|
||||
started_at: 'Iniciado em',
|
||||
completed_at: 'Concluído em',
|
||||
failed_at: 'Reprovado em',
|
||||
canceled_at: 'Cancelado em'
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ import {
|
||||
} from '@repo/ui/components/ui/dropdown-menu'
|
||||
import { Kbd } from '@repo/ui/components/ui/kbd'
|
||||
import { columns, type Enrollment } from './columns'
|
||||
import { sortings, statuses } from './data'
|
||||
import { headers, sortings, statuses } from './data'
|
||||
|
||||
export function meta({}: Route.MetaArgs) {
|
||||
return [{ title: 'Matrículas' }]
|
||||
@@ -83,7 +83,7 @@ const formatted = new Intl.DateTimeFormat('en-CA', {
|
||||
|
||||
export default function Route({ loaderData: { data } }) {
|
||||
const [searchParams, setSearchParams] = useSearchParams()
|
||||
const [rowSelection, setRowSelection] = useState<Enrollment[]>([])
|
||||
const [selectedRows, setSelectedRows] = useState<Enrollment[]>([])
|
||||
const status = searchParams.get('status')
|
||||
const rangeParams = useRangeParams()
|
||||
|
||||
@@ -99,133 +99,134 @@ export default function Route({ loaderData: { data } }) {
|
||||
</div>
|
||||
|
||||
<Await resolve={data}>
|
||||
{({ hits, page, hitsPerPage, totalHits }) => {
|
||||
return (
|
||||
<DataTable
|
||||
sort={[{ id: 'created_at', desc: true }]}
|
||||
columns={columns}
|
||||
data={hits as Enrollment[]}
|
||||
pageIndex={page - 1}
|
||||
pageSize={hitsPerPage}
|
||||
onRowSelectionChange={setRowSelection}
|
||||
rowCount={totalHits}
|
||||
hiddenColumn={[
|
||||
'completed_at',
|
||||
'started_at',
|
||||
'failed_at',
|
||||
'canceled_at'
|
||||
]}
|
||||
>
|
||||
<div className="flex gap-2.5 mb-2.5">
|
||||
{rowSelection.length ? (
|
||||
<>
|
||||
<div className="flex gap-2.5 items-center">
|
||||
<Button variant="outline">
|
||||
<TagIcon /> Marcador
|
||||
</Button>
|
||||
<DropdownMenuExport rowSelection={rowSelection} />
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<div className="w-full 2xl:w-1/3">
|
||||
<SearchForm
|
||||
defaultValue={searchParams.get('q') || ''}
|
||||
placeholder={
|
||||
<>
|
||||
Digite <Kbd className="border font-mono">/</Kbd>{' '}
|
||||
para pesquisar
|
||||
</>
|
||||
}
|
||||
onChange={(value) =>
|
||||
{({ hits, page, hitsPerPage, totalHits }) => (
|
||||
<DataTable
|
||||
sort={[{ id: 'created_at', desc: true }]}
|
||||
columns={columns}
|
||||
data={hits as Enrollment[]}
|
||||
pageIndex={page - 1}
|
||||
pageSize={hitsPerPage}
|
||||
setSelectedRows={setSelectedRows}
|
||||
rowCount={totalHits}
|
||||
hiddenColumn={[
|
||||
'completed_at',
|
||||
'started_at',
|
||||
'failed_at',
|
||||
'canceled_at'
|
||||
]}
|
||||
>
|
||||
<div className="flex gap-2.5 mb-2.5">
|
||||
{selectedRows.length ? (
|
||||
<>
|
||||
<div className="flex gap-2.5 items-center">
|
||||
<Button variant="outline">
|
||||
<TagIcon /> Marcador
|
||||
</Button>
|
||||
<DropdownMenuExport
|
||||
headers={headers}
|
||||
selectedRows={selectedRows}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<div className="w-full 2xl:w-1/3">
|
||||
<SearchForm
|
||||
defaultValue={searchParams.get('q') || ''}
|
||||
placeholder={
|
||||
<>
|
||||
Digite <Kbd className="border font-mono">/</Kbd> para
|
||||
pesquisar
|
||||
</>
|
||||
}
|
||||
onChange={(value) =>
|
||||
setSearchParams((searchParams) => {
|
||||
searchParams.set('q', String(value))
|
||||
searchParams.delete('p')
|
||||
return searchParams
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-2.5 max-lg:flex-col w-full">
|
||||
<div className="flex gap-2.5 max-lg:flex-col">
|
||||
<FacetedFilter
|
||||
title="Status"
|
||||
icon={PlusCircleIcon}
|
||||
className="lg:flex-1"
|
||||
value={status ? status.split(',') : []}
|
||||
onChange={(statuses) => {
|
||||
setSearchParams((searchParams) => {
|
||||
searchParams.set('q', String(value))
|
||||
searchParams.delete('status')
|
||||
searchParams.delete('p')
|
||||
|
||||
if (statuses.length) {
|
||||
searchParams.set('status', statuses.join(','))
|
||||
}
|
||||
|
||||
return searchParams
|
||||
})
|
||||
}
|
||||
}}
|
||||
options={Object.entries(statuses).map(
|
||||
([key, value]) => ({
|
||||
value: key,
|
||||
...value
|
||||
})
|
||||
)}
|
||||
/>
|
||||
|
||||
<RangeCalendarFilter
|
||||
title="Período"
|
||||
icon={CalendarIcon}
|
||||
value={rangeParams}
|
||||
className="lg:flex-1"
|
||||
options={Object.entries(sortings).map(
|
||||
([value, label]) => ({
|
||||
value,
|
||||
label
|
||||
})
|
||||
)}
|
||||
onChange={(props) => {
|
||||
setSearchParams((searchParams) => {
|
||||
if (!props) {
|
||||
searchParams.delete('from')
|
||||
searchParams.delete('to')
|
||||
return searchParams
|
||||
}
|
||||
|
||||
const { rangeField, dateRange } = props
|
||||
|
||||
searchParams.set(
|
||||
'from',
|
||||
`${rangeField}:${formatted.format(dateRange?.from)}`
|
||||
)
|
||||
searchParams.set(
|
||||
'to',
|
||||
formatted.format(dateRange?.to)
|
||||
)
|
||||
|
||||
return searchParams
|
||||
})
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-2.5 max-lg:flex-col w-full">
|
||||
<div className="flex gap-2.5 max-lg:flex-col">
|
||||
<FacetedFilter
|
||||
title="Status"
|
||||
icon={PlusCircleIcon}
|
||||
className="lg:flex-1"
|
||||
value={status ? status.split(',') : []}
|
||||
onChange={(statuses) => {
|
||||
setSearchParams((searchParams) => {
|
||||
searchParams.delete('status')
|
||||
searchParams.delete('p')
|
||||
<div className="lg:ml-auto flex gap-2.5">
|
||||
<DataTableViewOptions className="flex-1" />
|
||||
|
||||
if (statuses.length) {
|
||||
searchParams.set('status', statuses.join(','))
|
||||
}
|
||||
|
||||
return searchParams
|
||||
})
|
||||
}}
|
||||
options={Object.entries(statuses).map(
|
||||
([key, value]) => ({
|
||||
value: key,
|
||||
...value
|
||||
})
|
||||
)}
|
||||
/>
|
||||
|
||||
<RangeCalendarFilter
|
||||
title="Período"
|
||||
icon={CalendarIcon}
|
||||
value={rangeParams}
|
||||
className="lg:flex-1"
|
||||
options={Object.entries(sortings).map(
|
||||
([value, label]) => ({
|
||||
value,
|
||||
label
|
||||
})
|
||||
)}
|
||||
onChange={(props) => {
|
||||
setSearchParams((searchParams) => {
|
||||
if (!props) {
|
||||
searchParams.delete('from')
|
||||
searchParams.delete('to')
|
||||
return searchParams
|
||||
}
|
||||
|
||||
const { rangeField, dateRange } = props
|
||||
|
||||
searchParams.set(
|
||||
'from',
|
||||
`${rangeField}:${formatted.format(dateRange?.from)}`
|
||||
)
|
||||
searchParams.set(
|
||||
'to',
|
||||
formatted.format(dateRange?.to)
|
||||
)
|
||||
|
||||
return searchParams
|
||||
})
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="lg:ml-auto flex gap-2.5">
|
||||
<DataTableViewOptions className="flex-1" />
|
||||
|
||||
<Button className="flex-1" asChild>
|
||||
<Link to="add">
|
||||
<PlusIcon /> Adicionar
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
<Button className="flex-1" asChild>
|
||||
<Link to="add">
|
||||
<PlusIcon /> Adicionar
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</DataTable>
|
||||
)
|
||||
}}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</DataTable>
|
||||
)}
|
||||
</Await>
|
||||
</Suspense>
|
||||
)
|
||||
@@ -251,32 +252,19 @@ function useRangeParams() {
|
||||
}
|
||||
|
||||
export function DropdownMenuExport({
|
||||
rowSelection = []
|
||||
headers,
|
||||
selectedRows = []
|
||||
}: {
|
||||
rowSelection: object[]
|
||||
headers: Record<string, string>
|
||||
selectedRows: object[]
|
||||
}) {
|
||||
const headers = {
|
||||
id: 'ID',
|
||||
'user.name': 'Nome',
|
||||
'user.email': 'Email',
|
||||
'user.cpf': 'CPF',
|
||||
'course.name': 'Curso',
|
||||
status: 'Status',
|
||||
progress: 'Progresso',
|
||||
created_at: 'Cadastrado em',
|
||||
started_at: 'Iniciado em',
|
||||
completed_at: 'Concluído em',
|
||||
failed_at: 'Reprovado em',
|
||||
canceled_at: 'Cancelado em'
|
||||
}
|
||||
|
||||
const handleExport = (bookType: BookType) => () => {
|
||||
if (!rowSelection.length) {
|
||||
if (!selectedRows.length) {
|
||||
return
|
||||
}
|
||||
|
||||
const header = Object.keys(headers)
|
||||
const data = rowSelection.map((data) => {
|
||||
const data = selectedRows.map((data) => {
|
||||
const obj: Record<string, string> = flatten(data)
|
||||
return Object.fromEntries(header.map((k) => [k, obj?.[k]]))
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user