feat: create Recent Edits component
This commit is contained in:
parent
5507e9aa02
commit
5b77317b5f
8 changed files with 372 additions and 82 deletions
34
package-lock.json
generated
34
package-lock.json
generated
|
|
@ -16,6 +16,7 @@
|
||||||
"@tailwindcss/vite": "^4.1.4",
|
"@tailwindcss/vite": "^4.1.4",
|
||||||
"@tanstack/react-query": "^5.83.0",
|
"@tanstack/react-query": "^5.83.0",
|
||||||
"@tanstack/react-query-devtools": "^5.83.0",
|
"@tanstack/react-query-devtools": "^5.83.0",
|
||||||
|
"@tanstack/react-table": "^8.21.3",
|
||||||
"axios": "^1.10.0",
|
"axios": "^1.10.0",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
|
|
@ -2112,6 +2113,39 @@
|
||||||
"react": "^18 || ^19"
|
"react": "^18 || ^19"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@tanstack/react-table": {
|
||||||
|
"version": "8.21.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.21.3.tgz",
|
||||||
|
"integrity": "sha512-5nNMTSETP4ykGegmVkhjcS8tTLW6Vl4axfEGQN3v0zdHYbK4UfoqfPChclTrJ4EoK9QynqAu9oUf8VEmrpZ5Ww==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@tanstack/table-core": "8.21.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/tannerlinsley"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=16.8",
|
||||||
|
"react-dom": ">=16.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@tanstack/table-core": {
|
||||||
|
"version": "8.21.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.21.3.tgz",
|
||||||
|
"integrity": "sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/tannerlinsley"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/babel__core": {
|
"node_modules/@types/babel__core": {
|
||||||
"version": "7.20.5",
|
"version": "7.20.5",
|
||||||
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
|
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@
|
||||||
"@tailwindcss/vite": "^4.1.4",
|
"@tailwindcss/vite": "^4.1.4",
|
||||||
"@tanstack/react-query": "^5.83.0",
|
"@tanstack/react-query": "^5.83.0",
|
||||||
"@tanstack/react-query-devtools": "^5.83.0",
|
"@tanstack/react-query-devtools": "^5.83.0",
|
||||||
|
"@tanstack/react-table": "^8.21.3",
|
||||||
"axios": "^1.10.0",
|
"axios": "^1.10.0",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@ export default function EntriesListSidebar() {
|
||||||
queryFn: () => api.get("/entries").then((res) => res.data),
|
queryFn: () => api.get("/entries").then((res) => res.data),
|
||||||
})
|
})
|
||||||
|
|
||||||
console.log(entries)
|
|
||||||
return (
|
return (
|
||||||
<Collapsible className="group/collapsible">
|
<Collapsible className="group/collapsible">
|
||||||
<SidebarMenuItem key="entries">
|
<SidebarMenuItem key="entries">
|
||||||
|
|
@ -30,7 +29,7 @@ export default function EntriesListSidebar() {
|
||||||
</CollapsibleTrigger>
|
</CollapsibleTrigger>
|
||||||
<CollapsibleContent>
|
<CollapsibleContent>
|
||||||
<SidebarMenuSub>
|
<SidebarMenuSub>
|
||||||
{entries?.entries.map((item, i) => (
|
{entries?.data.map((item, i) => (
|
||||||
<SidebarMenuItem key={i}>
|
<SidebarMenuItem key={i}>
|
||||||
<a>
|
<a>
|
||||||
<span className="text-xs">{item.title.replace(/_/g, " ")}</span>
|
<span className="text-xs">{item.title.replace(/_/g, " ")}</span>
|
||||||
|
|
|
||||||
104
src/components/RecentEditsDataTable.tsx
Normal file
104
src/components/RecentEditsDataTable.tsx
Normal file
|
|
@ -0,0 +1,104 @@
|
||||||
|
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-between space-x-2 py-4">
|
||||||
|
<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>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -33,7 +33,7 @@ export default function TagListSidebar() {
|
||||||
</CollapsibleTrigger>
|
</CollapsibleTrigger>
|
||||||
<CollapsibleContent>
|
<CollapsibleContent>
|
||||||
<SidebarMenuSub>
|
<SidebarMenuSub>
|
||||||
{tags?.tags.map((item, i) => (
|
{tags?.data.map((item, i) => (
|
||||||
<SidebarMenuItem key={i}>
|
<SidebarMenuItem key={i}>
|
||||||
<a>
|
<a>
|
||||||
<span className="text-xs">{item}</span>
|
<span className="text-xs">{item}</span>
|
||||||
|
|
|
||||||
114
src/components/ui/table.tsx
Normal file
114
src/components/ui/table.tsx
Normal file
|
|
@ -0,0 +1,114 @@
|
||||||
|
import * as React from "react"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
function Table({ className, ...props }: React.ComponentProps<"table">) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
data-slot="table-container"
|
||||||
|
className="relative w-full overflow-x-auto"
|
||||||
|
>
|
||||||
|
<table
|
||||||
|
data-slot="table"
|
||||||
|
className={cn("w-full caption-bottom text-sm", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function TableHeader({ className, ...props }: React.ComponentProps<"thead">) {
|
||||||
|
return (
|
||||||
|
<thead
|
||||||
|
data-slot="table-header"
|
||||||
|
className={cn("[&_tr]:border-b", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function TableBody({ className, ...props }: React.ComponentProps<"tbody">) {
|
||||||
|
return (
|
||||||
|
<tbody
|
||||||
|
data-slot="table-body"
|
||||||
|
className={cn("[&_tr:last-child]:border-0", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function TableFooter({ className, ...props }: React.ComponentProps<"tfoot">) {
|
||||||
|
return (
|
||||||
|
<tfoot
|
||||||
|
data-slot="table-footer"
|
||||||
|
className={cn(
|
||||||
|
"bg-muted/50 border-t font-medium [&>tr]:last:border-b-0",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function TableRow({ className, ...props }: React.ComponentProps<"tr">) {
|
||||||
|
return (
|
||||||
|
<tr
|
||||||
|
data-slot="table-row"
|
||||||
|
className={cn(
|
||||||
|
"hover:bg-muted/50 data-[state=selected]:bg-muted border-b transition-colors",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function TableHead({ className, ...props }: React.ComponentProps<"th">) {
|
||||||
|
return (
|
||||||
|
<th
|
||||||
|
data-slot="table-head"
|
||||||
|
className={cn(
|
||||||
|
"text-foreground h-10 px-2 text-left align-middle font-medium whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function TableCell({ className, ...props }: React.ComponentProps<"td">) {
|
||||||
|
return (
|
||||||
|
<td
|
||||||
|
data-slot="table-cell"
|
||||||
|
className={cn(
|
||||||
|
"p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function TableCaption({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<"caption">) {
|
||||||
|
return (
|
||||||
|
<caption
|
||||||
|
data-slot="table-caption"
|
||||||
|
className={cn("text-muted-foreground mt-4 text-sm", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
Table,
|
||||||
|
TableHeader,
|
||||||
|
TableBody,
|
||||||
|
TableFooter,
|
||||||
|
TableHead,
|
||||||
|
TableRow,
|
||||||
|
TableCell,
|
||||||
|
TableCaption,
|
||||||
|
}
|
||||||
54
src/containers/RecentEdits.tsx
Normal file
54
src/containers/RecentEdits.tsx
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
import { RecentEditsDataTable } from "@/components/RecentEditsDataTable"
|
||||||
|
import { useQuery } from "@tanstack/react-query"
|
||||||
|
import api from "../api/eolas-api"
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
accessorKey: "title",
|
||||||
|
header: "Title",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "date",
|
||||||
|
header: "Date",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "time",
|
||||||
|
header: "Time",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
export default function RecentEdits() {
|
||||||
|
const { data, isLoading } = useQuery({
|
||||||
|
queryKey: ["entries_recent"],
|
||||||
|
queryFn: () => api.get("/entries?limit=20&sort=date").then((res) => res.data),
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log(data)
|
||||||
|
const parsed = data?.data?.map((entry) => {
|
||||||
|
const [date, time] = entry?.last_modified?.split(" ")
|
||||||
|
return {
|
||||||
|
title: entry.title.replace(/_/g, " "),
|
||||||
|
date: new Date(date).toLocaleString("en-GB", {
|
||||||
|
day: "numeric",
|
||||||
|
month: "long",
|
||||||
|
year: "numeric",
|
||||||
|
}),
|
||||||
|
time: time,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="border w-full">
|
||||||
|
<div className="border-b py-2 px-4 lg:px-6 bg-sidebar">
|
||||||
|
<h2 className="scroll-m-20 font-semibold">Recent edits</h2>
|
||||||
|
</div>
|
||||||
|
<div className="p-4 lg:p-6">
|
||||||
|
<div className="container mx-auto py-2">
|
||||||
|
<RecentEditsDataTable columns={columns} data={parsed || []} loading={isLoading} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -1,35 +1,28 @@
|
||||||
import { Card, CardContent } from "@/components/ui/card"
|
|
||||||
import Main from "@/templates/Main"
|
import Main from "@/templates/Main"
|
||||||
|
import RecentEdits from "@/containers/RecentEdits"
|
||||||
// const scoreData = [
|
|
||||||
// { label: "Total Posts", count: "548" },
|
|
||||||
// { label: "Tags", count: "68" },
|
|
||||||
// { label: "Backlinks", count: "766" },
|
|
||||||
// ]
|
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Main pageTitle="Home">
|
<Main pageTitle="Home">
|
||||||
{/*
|
|
||||||
<div className="grid grid-cols-3 gap-4 p-6 pb-0">
|
|
||||||
{scoreData.map((item, index) => (
|
|
||||||
<ScoreCard key={index} label={item.label} count={item.count} />
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
*/}
|
|
||||||
|
|
||||||
<div className="flex-1 flex flex-col overflow-auto">
|
<div className="flex-1 flex flex-col overflow-auto">
|
||||||
<div className="@container/main flex flex-col">
|
<div className="@container/main flex flex-col">
|
||||||
<div className="p-4 lg:p-6 flex-1 flex">
|
<div className="p-4 lg:p-6 flex flex-1">
|
||||||
<Card className="border rounded-none shadow-none w-full p-2">
|
<div className="border w-full">
|
||||||
<CardContent className="p-4 pt-6 overflow-auto">
|
<div className="border-b py-2 px-4 lg:px-6 bg-sidebar">
|
||||||
<h2 className="scroll-m-20 text-xl font-semibold">Welcome</h2>
|
<h2 className="scroll-m-20 font-semibold">Welcome</h2>
|
||||||
<p className="leading-7 [&:not(:first-child)]:mt-6 font-normal">
|
</div>
|
||||||
|
<div className="p-4 lg:p-6">
|
||||||
|
<p className="leading-6 [&:not(:first-child)]:mt-6 font-normal">
|
||||||
I'm Thomas. Eólas is my technical knowledge management system, or
|
I'm Thomas. Eólas is my technical knowledge management system, or
|
||||||
"second-brain", comprising notes from my study of software engineering
|
"second-brain", comprising notes from my study of software engineering
|
||||||
and computer science.
|
and computer science.
|
||||||
</p>
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/*
|
||||||
|
|
||||||
<div className="bg-muted rounded-none p-3 font-medium text-sm text-muted-foreground my-4 mb-6">
|
<div className="bg-muted rounded-none p-3 font-medium text-sm text-muted-foreground my-4 mb-6">
|
||||||
<p className="">
|
<p className="">
|
||||||
|
|
@ -39,9 +32,9 @@ export default function Home() {
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h2 className="scroll-m-20 text-lg font-semibold">Recent edits</h2>
|
*/}
|
||||||
</CardContent>
|
<div className="px-4 lg:px-6 flex-1 flex">
|
||||||
</Card>
|
<RecentEdits />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -49,12 +42,3 @@ export default function Home() {
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// const ScoreCard = ({ label, count }) => (
|
|
||||||
// <div className="rounded-sm border bg-card text-card-foreground p-6">
|
|
||||||
// <div className="flex flex-col space-y-1.5">
|
|
||||||
// <h3 className="text-2xl font-semibold leading-none tracking-tight">{count}</h3>
|
|
||||||
// <p className="text-sm text-muted-foreground">{label}</p>
|
|
||||||
// </div>
|
|
||||||
// </div>
|
|
||||||
// )
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue