feat: create reusable DataTable component
This commit is contained in:
parent
654f6f77ea
commit
8ab2aa77a6
4 changed files with 179 additions and 157 deletions
113
src/components/DataTable.tsx
Normal file
113
src/components/DataTable.tsx
Normal file
|
|
@ -0,0 +1,113 @@
|
||||||
|
import {
|
||||||
|
flexRender,
|
||||||
|
getCoreRowModel,
|
||||||
|
useReactTable,
|
||||||
|
getPaginationRowModel,
|
||||||
|
SortingState,
|
||||||
|
getSortedRowModel,
|
||||||
|
} from "@tanstack/react-table"
|
||||||
|
|
||||||
|
import { Button } from "./ui/button"
|
||||||
|
import {
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableHead,
|
||||||
|
TableHeader,
|
||||||
|
TableRow,
|
||||||
|
} from "@/components/ui/table"
|
||||||
|
import { useState } from "react"
|
||||||
|
|
||||||
|
export function DataTable({ columns, data, loading }) {
|
||||||
|
const [sorting, setSorting] = useState([])
|
||||||
|
const table = useReactTable({
|
||||||
|
data,
|
||||||
|
columns,
|
||||||
|
getCoreRowModel: getCoreRowModel(),
|
||||||
|
getPaginationRowModel: getPaginationRowModel(),
|
||||||
|
onSortingChange: setSorting,
|
||||||
|
getSortedRowModel: getSortedRowModel(),
|
||||||
|
state: {
|
||||||
|
sorting,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const pageCount = table.getPageCount()
|
||||||
|
const currentPage = table.getState().pagination?.pageIndex
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className="rounded-none border">
|
||||||
|
<Table>
|
||||||
|
<TableHeader>
|
||||||
|
{table.getHeaderGroups().map((headerGroup) => (
|
||||||
|
<TableRow key={headerGroup.id}>
|
||||||
|
{headerGroup.headers.map((header) => {
|
||||||
|
return (
|
||||||
|
<TableHead key={header.id}>
|
||||||
|
{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"}>
|
||||||
|
{row.getVisibleCells().map((cell) => (
|
||||||
|
<TableCell key={cell.id}>
|
||||||
|
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||||
|
</TableCell>
|
||||||
|
))}
|
||||||
|
</TableRow>
|
||||||
|
))
|
||||||
|
) : loading ? (
|
||||||
|
<TableRow>
|
||||||
|
<TableCell colSpan={columns.length} className="h-24 text-center">
|
||||||
|
Loading...
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
) : (
|
||||||
|
<TableRow>
|
||||||
|
<TableCell colSpan={columns.length} className="h-24 text-center">
|
||||||
|
No results.
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
)}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-end space-x-2 py-4 gap-2">
|
||||||
|
<div>
|
||||||
|
<span className="text-sm text-muted-foreground">{`${currentPage + 1} of ${pageCount}`}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Button
|
||||||
|
className="rounded-none"
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => table.previousPage()}
|
||||||
|
disabled={!table.getCanPreviousPage()}
|
||||||
|
>
|
||||||
|
Previous
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
className="rounded-none"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => table.nextPage()}
|
||||||
|
disabled={!table.getCanNextPage()}
|
||||||
|
>
|
||||||
|
Next
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -1,104 +0,0 @@
|
||||||
import {
|
|
||||||
flexRender,
|
|
||||||
getCoreRowModel,
|
|
||||||
useReactTable,
|
|
||||||
getPaginationRowModel,
|
|
||||||
} from "@tanstack/react-table"
|
|
||||||
|
|
||||||
import { Button } from "./ui/button"
|
|
||||||
import {
|
|
||||||
Table,
|
|
||||||
TableBody,
|
|
||||||
TableCell,
|
|
||||||
TableHead,
|
|
||||||
TableHeader,
|
|
||||||
TableRow,
|
|
||||||
} from "@/components/ui/table"
|
|
||||||
|
|
||||||
export function RecentEditsDataTable({ columns, data, loading }) {
|
|
||||||
const table = useReactTable({
|
|
||||||
data,
|
|
||||||
columns,
|
|
||||||
getCoreRowModel: getCoreRowModel(),
|
|
||||||
getPaginationRowModel: getPaginationRowModel(),
|
|
||||||
})
|
|
||||||
|
|
||||||
const pageCount = table.getPageCount()
|
|
||||||
const currentPage = table.getState().pagination?.pageIndex
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div className="rounded-none border">
|
|
||||||
<Table>
|
|
||||||
<TableHeader>
|
|
||||||
{table.getHeaderGroups().map((headerGroup) => (
|
|
||||||
<TableRow key={headerGroup.id}>
|
|
||||||
{headerGroup.headers.map((header) => {
|
|
||||||
return (
|
|
||||||
<TableHead key={header.id}>
|
|
||||||
{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"}>
|
|
||||||
{row.getVisibleCells().map((cell) => (
|
|
||||||
<TableCell key={cell.id}>
|
|
||||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
|
||||||
</TableCell>
|
|
||||||
))}
|
|
||||||
</TableRow>
|
|
||||||
))
|
|
||||||
) : loading ? (
|
|
||||||
<TableRow>
|
|
||||||
<TableCell colSpan={columns.length} className="h-24 text-center">
|
|
||||||
Loading...
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
) : (
|
|
||||||
<TableRow>
|
|
||||||
<TableCell colSpan={columns.length} className="h-24 text-center">
|
|
||||||
No results.
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
)}
|
|
||||||
</TableBody>
|
|
||||||
</Table>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center justify-end space-x-2 py-4 gap-2">
|
|
||||||
<div>
|
|
||||||
<span className="text-sm text-muted-foreground">{`${currentPage + 1} of ${pageCount}`}</span>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<Button
|
|
||||||
className="rounded-none"
|
|
||||||
variant="outline"
|
|
||||||
size="sm"
|
|
||||||
onClick={() => table.previousPage()}
|
|
||||||
disabled={!table.getCanPreviousPage()}
|
|
||||||
>
|
|
||||||
Previous
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
className="rounded-none"
|
|
||||||
size="sm"
|
|
||||||
onClick={() => table.nextPage()}
|
|
||||||
disabled={!table.getCanNextPage()}
|
|
||||||
>
|
|
||||||
Next
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
@ -2,63 +2,77 @@ import { RecentEditsDataTable } from "@/components/RecentEditsDataTable"
|
||||||
import { useQuery } from "@tanstack/react-query"
|
import { useQuery } from "@tanstack/react-query"
|
||||||
import api from "../api/eolas-api"
|
import api from "../api/eolas-api"
|
||||||
import { Link } from "react-router"
|
import { Link } from "react-router"
|
||||||
|
import { ArrowUpDown } from "lucide-react"
|
||||||
|
import { Button } from "@/components/ui/button"
|
||||||
|
import { DataTable } from "@/components/DataTable"
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
accessorKey: "title",
|
accessorKey: "title",
|
||||||
header: "Title",
|
header: ({ column }) => {
|
||||||
cell: ({ cell, row }) => {
|
return (
|
||||||
return (
|
<Button
|
||||||
<Link to={`entries/${row.original.title}`}>
|
variant="ghost"
|
||||||
<span className="text-foreground underline-offset-3 underline hover:text-gray-700 dark:hover:text-green-300">
|
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
|
||||||
{row.original.title}
|
>
|
||||||
</span>
|
Title
|
||||||
</Link>
|
<ArrowUpDown className="ml-2 h-4 w-4" />
|
||||||
)
|
</Button>
|
||||||
},
|
)
|
||||||
},
|
},
|
||||||
{
|
// header: "Title",
|
||||||
accessorKey: "date",
|
cell: ({ cell, row }) => {
|
||||||
header: "Date",
|
return (
|
||||||
},
|
<Link to={`entries/${row.original.title}`}>
|
||||||
{
|
<span className="text-foreground underline-offset-3 underline hover:text-gray-700 dark:hover:text-green-300">
|
||||||
accessorKey: "time",
|
{row.original.title}
|
||||||
header: "Time",
|
</span>
|
||||||
},
|
</Link>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "date",
|
||||||
|
header: "Date",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "time",
|
||||||
|
header: "Time",
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
export default function RecentEdits() {
|
export default function RecentEdits() {
|
||||||
const { data, isLoading } = useQuery({
|
const { data, isLoading } = useQuery({
|
||||||
queryKey: ["entries_recent"],
|
queryKey: ["entries_recent"],
|
||||||
queryFn: () => api.get("/entries?limit=20&sort=date").then((res) => res.data),
|
queryFn: () => api.get("/entries?limit=20&sort=date").then((res) => res.data),
|
||||||
})
|
})
|
||||||
|
|
||||||
console.log(data)
|
console.log(data)
|
||||||
const parsed = data?.data?.map((entry) => {
|
const parsed = data?.data?.map((entry) => {
|
||||||
const [date, time] = entry?.last_modified?.split(" ")
|
const [date, time] = entry?.last_modified?.split(" ")
|
||||||
return {
|
return {
|
||||||
title: entry.title.replace(/_/g, " "),
|
title: entry.title.replace(/_/g, " "),
|
||||||
date: new Date(date).toLocaleString("en-GB", {
|
date: new Date(date).toLocaleString("en-GB", {
|
||||||
day: "numeric",
|
day: "numeric",
|
||||||
month: "long",
|
month: "long",
|
||||||
year: "numeric",
|
year: "numeric",
|
||||||
}),
|
}),
|
||||||
time: time,
|
time: time,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="border w-full">
|
<div className="border w-full">
|
||||||
<div className="border-b py-2 px-4 lg:px-6 bg-sidebar">
|
<div className="border-b py-2 px-4 lg:px-6 bg-sidebar">
|
||||||
<h2 className="scroll-m-20 font-semibold">Recent edits</h2>
|
<h2 className="scroll-m-20 font-semibold">Recent edits</h2>
|
||||||
</div>
|
</div>
|
||||||
<div className="p-4 lg:p-6">
|
<div className="p-4 lg:p-6">
|
||||||
<div className="container mx-auto py-2">
|
<div className="container mx-auto py-2">
|
||||||
<RecentEditsDataTable columns={columns} data={parsed || []} loading={isLoading} />
|
<DataTable columns={columns} data={parsed || []} loading={isLoading} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import Main from "@/templates/Main"
|
|
||||||
import { useParams } from "react-router"
|
import { useParams } from "react-router"
|
||||||
import Page from "./Page"
|
import Page from "./Page"
|
||||||
export default function Tag() {
|
export default function Tag() {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue