fp/packages/next/app/components/streams-table.tsx

280 lines
8.4 KiB
TypeScript

'use client'
import React from 'react'
import ReactDOM from 'react-dom/client'
import Link from 'next/link'
import {
keepPreviousData,
QueryClient,
useQuery,
} from '@tanstack/react-query'
import { format } from 'date-fns'
import Image from 'next/image'
import {
PaginationState,
useReactTable,
getCoreRowModel,
ColumnDef,
flexRender,
} from '@tanstack/react-table'
import { fetchStreamData, IStream } from '@/lib/streams'
const queryClient = new QueryClient()
function getStatusClass(value: string) {
switch (value) {
case 'issue':
return 'is-warning';
case 'missing':
return 'is-danger';
case 'good':
return 'is-success';
default:
return '';
}
}
export default function StreamsTable() {
const rerender = React.useReducer(() => ({}), {})[1]
// image & name
// title
// platform
// date & time
// archiveStatus
const columns = React.useMemo<ColumnDef<IStream>[]>(
() => [
{
header: 'VTuber',
accessorFn: d => ({
displayName: d.attributes.vtuber.data?.attributes?.displayName,
image: d.attributes.vtuber.data?.attributes.image,
imageBlur: d.attributes.vtuber.data?.attributes.imageBlur
}),
cell: info => {
const { displayName, image, imageBlur } = info.getValue<{ displayName: string, image: string, imageBlur: string }>();
return (
<>
<div className="columns is-mobile">
<div className="column mr-0 is-flex-grow-0">
<figure className="image is-24x24">
<Image
className="is-rounded"
src={image}
alt={displayName}
placeholder="blur"
objectFit='contain'
blurDataURL={imageBlur}
width={32}
height={32}
/>
</figure>
</div>
<div className="column ml-0">
<span>{displayName}</span>
</div>
</div>
</>
)
}
},
{
header: 'Date',
accessorFn: d => format(new Date(d.attributes.date2), 'yyyy-MM-dd HH:mm'),
cell: info => (<Link href={`/archive/${info.row.original.attributes.cuid}`}>{info.getValue() as string}</Link>)
},
{
header: 'Platform',
accessorFn: d => [
(d.attributes.isChaturbateStream && 'CB'),
(d.attributes.isFanslyStream && 'Fansly')
].filter(Boolean).join(' ') || '???'
},
{
header: 'Status',
accessorFn: d => {
if (!d.attributes.archiveStatus) return 'missing';
return d.attributes.archiveStatus
}
},
// {
// header: 'Name',
// footer: props => props.column.id,
// columns: [
// {
// accessorKey: 'firstName',
// cell: info => info.getValue(),
// footer: props => props.column.id,
// },
// {
// accessorFn: row => row.lastName,
// id: 'lastName',
// cell: info => info.getValue(),
// header: () => <span>Last Name</span>,
// footer: props => props.column.id,
// },
// ],
// },
],
[]
)
const [pagination, setPagination] = React.useState<PaginationState>({
pageIndex: 0,
pageSize: 50,
})
const dataQuery = useQuery({
queryKey: ['streams', pagination.pageIndex, pagination.pageSize],
queryFn: () => fetchStreamData(pagination),
placeholderData: keepPreviousData, // don't have 0 rows flash while changing pages/loading next page,
staleTime: 1000
}, queryClient)
const defaultData = React.useMemo(() => [], [])
const table = useReactTable({
data: dataQuery?.data?.rows ?? defaultData,
columns,
// pageCount: dataQuery.data?.pageCount ?? -1, //you can now pass in `rowCount` instead of pageCount and `pageCount` will be calculated internally (new in v8.13.0)
rowCount: dataQuery.data?.rowCount, // new in v8.13.0 - alternatively, just pass in `pageCount` directly
state: {
pagination,
},
onPaginationChange: setPagination,
getCoreRowModel: getCoreRowModel(),
manualPagination: true, //we're doing manual "server-side" pagination
// getPaginationRowModel: getPaginationRowModel(), // If only doing manual pagination, you don't need this
debugTable: true,
})
return (
<div className="p-2">
<div className="h-2" />
<table className='table is-hoverable is-fullwidth'>
<thead>
{table.getHeaderGroups().map(headerGroup => (
<tr key={headerGroup.id}>
{headerGroup.headers.map(header => {
return (
<th key={header.id} colSpan={header.colSpan}>
{header.isPlaceholder ? null : (
<div>
{flexRender(
header.column.columnDef.header,
header.getContext()
)}
</div>
)}
</th>
)
})}
</tr>
))}
</thead>
<tbody>
{table.getRowModel().rows.map(row => {
return (
<tr key={row.id}>
{row.getVisibleCells().map(cell => {
return (
<td
className={getStatusClass(cell.getValue() as string)}
key={cell.id}
>
{flexRender(
cell.column.columnDef.cell,
cell.getContext()
)}
</td>
)
})}
</tr>
)
})}
</tbody>
</table>
<div className="columns is-mobile is-vcentered">
<div className='column is-half'>
<button
className="button border rounded mx-1"
onClick={() => table.firstPage()}
disabled={!table.getCanPreviousPage()}
>
{'<<'}
</button>
<button
className="button border rounded mx-1"
onClick={() => table.previousPage()}
disabled={!table.getCanPreviousPage()}
>
{'<'}
</button>
<button
className="button border rounded mx-1"
onClick={() => table.nextPage()}
disabled={!table.getCanNextPage()}
>
{'>'}
</button>
<button
className="button border rounded mx-1"
onClick={() => table.lastPage()}
disabled={!table.getCanNextPage()}
>
{'>>'}
</button>
</div>
<div className='column is-half'>
<span>Page </span>
<strong>
{table.getState().pagination.pageIndex + 1} of{' '}
{table.getPageCount().toLocaleString()}
</strong>
</div>
</div>
{/* second row with page number input and pages-per-screen select */}
<div className='columns is-mobile is-vcentered'>
<div className='column is-2 '>
<span className='is-text-centered'>Go to page:</span>
</div>
<div className='column is-3'>
<input
type="number"
defaultValue={table.getState().pagination.pageIndex + 1}
onChange={e => {
const page = e.target.value ? Number(e.target.value) - 1 : 0
table.setPageIndex(page)
}}
className="input"
/>
</div>
<div className='column is-5'>
<div className="select">
<select
value={table.getState().pagination.pageSize}
onChange={e => {
table.setPageSize(Number(e.target.value))
}}
>
{[20, 50, 100].map(pageSize => (
<option key={pageSize} value={pageSize}>
Show {pageSize}
</option>
))}
</select>
</div>
</div>
</div>
</div>
)
}