250 lines
7.1 KiB
TypeScript
250 lines
7.1 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 {
|
|
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]
|
|
|
|
|
|
// name
|
|
// title
|
|
// platform
|
|
// date
|
|
// archiveStatus
|
|
const columns = React.useMemo<ColumnDef<IStream>[]>(
|
|
() => [
|
|
{
|
|
header: 'VTuber',
|
|
accessorFn: d => d.attributes.vtuber.data?.attributes?.displayName,
|
|
},
|
|
{
|
|
header: 'Date',
|
|
accessorFn: d => new Date(d.attributes.date2).toISOString().split('T').at(0),
|
|
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>
|
|
|
|
)
|
|
}
|