add violation tanstak-table
This commit is contained in:
@@ -120,7 +120,7 @@ export async function viewFileInfo(fileId: string) {
|
||||
}
|
||||
|
||||
export async function downloadFile(fileId: string, fileName: string) {
|
||||
const token = await getSessionData('token')
|
||||
const token = await getSessionData('token');
|
||||
|
||||
const response = await fetch(`${API_BASE_URL}/api/v1/files/download/${fileId}`, {
|
||||
headers: {
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
'use server'
|
||||
|
||||
import { getSessionData } from '@/app/actions/session';
|
||||
import { API_BASE_URL } from '@/app/actions/definitions';
|
||||
|
||||
export async function getViolationSearchStatus() {
|
||||
const token = await getSessionData('token');
|
||||
console.log('getViolationSearchStatus');
|
||||
|
||||
try {
|
||||
const response = await fetch(`${API_BASE_URL}/api/v1/global-search/actual-task`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'Authorization': `Bearer ${token}`,
|
||||
}
|
||||
});
|
||||
/* console.log(response); */
|
||||
|
||||
if (response.ok) {
|
||||
console.log('parsed');
|
||||
const parsed = await response.json();
|
||||
console.log(parsed);
|
||||
|
||||
return parsed
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
} catch (error) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
export async function startGlobalMonitoring(monitoringType: 'monitoring' | 'all_files') {
|
||||
const token = await getSessionData('token');
|
||||
console.log('startGlobalMonitoring');
|
||||
|
||||
try {
|
||||
const response = await fetch(`${API_BASE_URL}/api/v1/global-search/start`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
searchType: monitoringType
|
||||
}),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${token}`,
|
||||
}
|
||||
});
|
||||
console.log(response);
|
||||
|
||||
if (response.ok) {
|
||||
const parsed = await response.json();
|
||||
console.log(parsed);
|
||||
if (parsed?.message_code === 0) {
|
||||
return parsed
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
} catch (error) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
@@ -4074,6 +4074,10 @@
|
||||
background: transparent;
|
||||
border: 2px solid #6366f1;
|
||||
color: #6366f1;
|
||||
|
||||
&:disabled {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
&.btn-confirm:disabled,
|
||||
|
||||
@@ -1,18 +1,112 @@
|
||||
'use client'
|
||||
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { getViolationSearchStatus, startGlobalMonitoring } from '@/app/actions/violationActions';
|
||||
import { useState } from 'react';
|
||||
import ModalWindow from '@/app/components/ModalWindow';
|
||||
import { fa } from 'zod/v4/locales';
|
||||
|
||||
export default function ViolationsCheckAllSection() {
|
||||
const {
|
||||
data: violationSearchStatus,
|
||||
isLoading,
|
||||
isError,
|
||||
error
|
||||
} = useQuery({
|
||||
queryKey: ['violationSearchStatus'],
|
||||
queryFn: () => {
|
||||
return getViolationSearchStatus();
|
||||
},
|
||||
select: (data) => {
|
||||
/* console.log(data); */
|
||||
if (data) {
|
||||
return data;
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
});
|
||||
const t = useTranslations('Global');
|
||||
const [isProcessing, setIsProcessing] = useState<boolean>(false);
|
||||
const [openWindow, setOpenWindow] = useState<boolean>(false);
|
||||
|
||||
function openModalWindow() {
|
||||
setOpenWindow(true);
|
||||
}
|
||||
|
||||
async function handlerStartMonitoring(type: 'monitoring' | 'all_files') {
|
||||
setIsProcessing(true);
|
||||
|
||||
try {
|
||||
const response = await startGlobalMonitoring(type);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
} finally {
|
||||
setIsProcessing(false);
|
||||
setOpenWindow(false);
|
||||
}
|
||||
}
|
||||
|
||||
function ConfirmationModal() {
|
||||
return (
|
||||
<div className="modal-window-confirm-payment">
|
||||
<h3 className="modal-window-confirm-payment-title">
|
||||
Поиск нарушений во всех ваших изображениях
|
||||
</h3>
|
||||
<div className="modal-window-confirm-payment-price">
|
||||
<div>Варианты проверки.</div>
|
||||
</div>
|
||||
<div className="flex justify-center gap-4">
|
||||
<button
|
||||
className="btn-primary btn-cancel"
|
||||
disabled={isProcessing}
|
||||
onClick={() => {
|
||||
handlerStartMonitoring('all_files');
|
||||
}}
|
||||
>
|
||||
Проверить все
|
||||
</button>
|
||||
<button
|
||||
className="btn-primary btn-cancel"
|
||||
disabled={isProcessing}
|
||||
onClick={() => {
|
||||
handlerStartMonitoring('monitoring');
|
||||
}}
|
||||
>
|
||||
Проверить файлы с мониторингом
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<ModalWindow state={openWindow} callBack={setOpenWindow}>
|
||||
<ConfirmationModal />
|
||||
</ModalWindow>
|
||||
<div className="violation-check-all-section">
|
||||
|
||||
<div className="check-all-left">
|
||||
<div className="check-all-header">
|
||||
<div className="check-all-icon">🔍</div>
|
||||
{/* <div className="check-all-icon">🔍</div> */}
|
||||
<div className="check-all-title">Глобальная проверка контента</div>
|
||||
</div>
|
||||
<div className="check-all-description">
|
||||
Поиск нарушений во всех ваших изображениях. Проверка только точных совпадений (95%+).
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" className="check-all-btn">
|
||||
🌐 Проверить все
|
||||
<button
|
||||
type="button"
|
||||
className="check-all-btn"
|
||||
onClick={() => {
|
||||
openModalWindow()
|
||||
}}
|
||||
>
|
||||
Проверить все
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -1,80 +1,496 @@
|
||||
'use client'
|
||||
|
||||
import { useMemo, useState } from 'react';
|
||||
import { useReactTable, getCoreRowModel, getSortedRowModel, getPaginationRowModel, getFilteredRowModel, ColumnDef, SortingState, ColumnFiltersState } from '@tanstack/react-table';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { IconArrowUp, IconArrowDown, IconFilter, IconDoubleArrowRight, IconArrowRight, IconDoubleArrowLeft, IconArrowLeft } from '@/app/ui/icons/icons';
|
||||
import DropDownList from '@/app/components/DropDownList';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { getUserFilesData } from '@/app/actions/fileEntity';
|
||||
|
||||
|
||||
export default function ViolationsTable() {
|
||||
const {
|
||||
data: violationData,
|
||||
isLoading,
|
||||
isError,
|
||||
error,
|
||||
} = useQuery({
|
||||
queryKey: ['violationData', 1, 10000],
|
||||
queryFn: () => getUserFilesData(1, 10000),
|
||||
|
||||
select: (data) => {
|
||||
if (!data?.files) return [];
|
||||
|
||||
return data.files.map((item: any) => {
|
||||
/* const newDate = new Date(item.updatedAt).getTime(); */
|
||||
|
||||
return {
|
||||
id: item.id,
|
||||
};
|
||||
});
|
||||
},
|
||||
refetchInterval: 30000
|
||||
});
|
||||
const t = useTranslations('Global');
|
||||
|
||||
// Состояния
|
||||
const [sorting, setSorting] = useState<SortingState>([]);
|
||||
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]);
|
||||
const [dateFilter, setDateFilter] = useState<string>('all');
|
||||
const [pagination, setPagination] = useState({
|
||||
pageIndex: 0,
|
||||
pageSize: 10,
|
||||
});
|
||||
|
||||
const columns = useMemo<ColumnDef<any>[]>(
|
||||
() => [
|
||||
{
|
||||
accessorKey: 'file',
|
||||
header: ({ column }) => (
|
||||
<div className="column">
|
||||
<span>
|
||||
Файл
|
||||
</span>
|
||||
<button
|
||||
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
|
||||
className="sort-button"
|
||||
>
|
||||
{
|
||||
column.getIsSorted() === 'asc' ?
|
||||
<span>
|
||||
<IconArrowUp />
|
||||
</span>
|
||||
: column.getIsSorted() === 'desc' ?
|
||||
<span>
|
||||
<IconArrowDown />
|
||||
</span>
|
||||
: <span>
|
||||
<IconFilter />
|
||||
</span>
|
||||
}
|
||||
</button>
|
||||
</div>
|
||||
),
|
||||
cell: ({ row }) => {
|
||||
return (
|
||||
<div className="violations-table-section">
|
||||
<div className="section-header">
|
||||
<div className="section-title">Обнаруженные нарушения (17)</div>
|
||||
<div>
|
||||
<div className="text-center table-item table-item-id">
|
||||
{/* {row.original} */}
|
||||
Файл
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
},
|
||||
{
|
||||
accessorKey: 'violationUrl',
|
||||
header: ({ column }) => (
|
||||
<div className="column">
|
||||
<span>
|
||||
violationUrl
|
||||
</span>
|
||||
<button
|
||||
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
|
||||
className="sort-button"
|
||||
>
|
||||
{
|
||||
column.getIsSorted() === 'asc' ?
|
||||
<span>
|
||||
<IconArrowUp />
|
||||
</span>
|
||||
: column.getIsSorted() === 'desc' ?
|
||||
<span>
|
||||
<IconArrowDown />
|
||||
</span>
|
||||
: <span>
|
||||
<IconFilter />
|
||||
</span>
|
||||
}
|
||||
</button>
|
||||
</div>
|
||||
),
|
||||
cell: ({ row }) => (
|
||||
<div className="text-center table-item">
|
||||
{/* {row.original.amount} */}
|
||||
violationUrl
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
accessorKey: 'statistic',
|
||||
header: ({ column }) => (
|
||||
<div className="column">
|
||||
<span>
|
||||
Статистика
|
||||
</span>
|
||||
<button
|
||||
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
|
||||
className="sort-button"
|
||||
>
|
||||
{
|
||||
column.getIsSorted() === 'asc' ?
|
||||
<span>
|
||||
<IconArrowUp />
|
||||
</span>
|
||||
: column.getIsSorted() === 'desc' ?
|
||||
<span>
|
||||
<IconArrowDown />
|
||||
</span>
|
||||
: <span>
|
||||
<IconFilter />
|
||||
</span>
|
||||
}
|
||||
</button>
|
||||
</div>
|
||||
),
|
||||
cell: ({ row }) => (
|
||||
<div className="text-center table-item">
|
||||
Статистика
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
accessorKey: 'DMCA',
|
||||
header: ({ column }) => (
|
||||
<div className="column">
|
||||
<span>
|
||||
DMCA жалоба
|
||||
</span>
|
||||
<button
|
||||
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
|
||||
className="sort-button"
|
||||
>
|
||||
{
|
||||
column.getIsSorted() === 'asc' ?
|
||||
<span>
|
||||
<IconArrowUp />
|
||||
</span>
|
||||
: column.getIsSorted() === 'desc' ?
|
||||
<span>
|
||||
<IconArrowDown />
|
||||
</span>
|
||||
: <span>
|
||||
<IconFilter />
|
||||
</span>
|
||||
}
|
||||
</button>
|
||||
</div>
|
||||
),
|
||||
cell: ({ row }) => {
|
||||
return (
|
||||
<div className="text-center font-semibold table-item">
|
||||
<p className={`table-item-status`}>
|
||||
DMCA жалоба
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: 'actions',
|
||||
header: ({ column }) => (
|
||||
<div className="column">
|
||||
<span>
|
||||
actions
|
||||
</span>
|
||||
</div>
|
||||
),
|
||||
cell: ({ row }) => {
|
||||
return (
|
||||
<div className="actions-group">
|
||||
actions
|
||||
</div>
|
||||
)
|
||||
}
|
||||
},
|
||||
],
|
||||
[]
|
||||
)
|
||||
|
||||
const filteredData = useMemo(() => {
|
||||
let result = violationData?.payments;
|
||||
if (!result) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Фильтр по дате
|
||||
if (dateFilter !== 'all') {
|
||||
const now = Date.now();
|
||||
const oneDay = 24 * 60 * 60 * 1000;
|
||||
const sevenDays = 7 * oneDay;
|
||||
const thirtyDays = 30 * oneDay;
|
||||
|
||||
switch (dateFilter) {
|
||||
case 'today':
|
||||
const todayStart = new Date().setHours(0, 0, 0, 0);
|
||||
result = result.filter((item: any) => {
|
||||
if (item.createdAt) {
|
||||
return new Date(item.createdAt).getTime() >= todayStart
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
});
|
||||
break;
|
||||
case 'week':
|
||||
const weekAgo = now - sevenDays;
|
||||
result = result.filter((item: any) => {
|
||||
if (item.createdAt) {
|
||||
return new Date(item.createdAt).getTime() >= weekAgo;
|
||||
}
|
||||
});
|
||||
break;
|
||||
case 'month':
|
||||
const monthAgo = now - thirtyDays;
|
||||
result = result.filter((item: any) => {
|
||||
if (item.createdAt) {
|
||||
return new Date(item.createdAt).getTime() >= monthAgo;
|
||||
}
|
||||
});
|
||||
break;
|
||||
case 'older':
|
||||
const monthAgo2 = now - thirtyDays;
|
||||
result = result.filter((item: any) => {
|
||||
if (item.createdAt) {
|
||||
return new Date(item.createdAt).getTime() < monthAgo2;
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}, [violationData?.payments, dateFilter])
|
||||
|
||||
const table = useReactTable({
|
||||
data: filteredData,
|
||||
columns,
|
||||
state: {
|
||||
sorting,
|
||||
columnFilters,
|
||||
pagination
|
||||
},
|
||||
autoResetPageIndex: false,
|
||||
onPaginationChange: setPagination,
|
||||
onSortingChange: setSorting,
|
||||
onColumnFiltersChange: setColumnFilters,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getSortedRowModel: getSortedRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
getFilteredRowModel: getFilteredRowModel(),
|
||||
initialState: {
|
||||
pagination: {
|
||||
pageSize: 10,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<div
|
||||
className="block-wrapper"
|
||||
>
|
||||
<div className="tab-content">
|
||||
<div
|
||||
className="tanstak-table-wrapper"
|
||||
>
|
||||
<h3
|
||||
className="tanstak-table-title"
|
||||
>
|
||||
Обнаруженные нарушения
|
||||
</h3>
|
||||
|
||||
{/* Фильтры */}
|
||||
{/* <div className="tanstak-table-filtres">
|
||||
<div className="table-filtres-wrapper">
|
||||
<div className="table-filtres-item">
|
||||
<div className="table-filtres-label">{t('date-filter')}:</div>
|
||||
<DropDownList
|
||||
value={dateFilter}
|
||||
translatedValue={(() => {
|
||||
switch (dateFilter) {
|
||||
case 'all':
|
||||
return t('all-dates')
|
||||
case 'week':
|
||||
return t('for-a-week')
|
||||
case 'month':
|
||||
return t('for-a-month')
|
||||
case 'older':
|
||||
return t('older-than-a-month')
|
||||
default:
|
||||
return t('today')
|
||||
}
|
||||
})()}
|
||||
callBack={setDateFilter}
|
||||
>
|
||||
<li value="all">
|
||||
{t('all-dates')}
|
||||
</li>
|
||||
<li value="today">
|
||||
{t('today')}
|
||||
</li>
|
||||
<li value="week">
|
||||
{t('for-a-week')}
|
||||
</li>
|
||||
<li value="month">
|
||||
{t('for-a-month')}
|
||||
</li>
|
||||
<li value="older">
|
||||
{t('older-than-a-month')}
|
||||
</li>
|
||||
</DropDownList>
|
||||
</div>
|
||||
|
||||
<div className="violations-table-container">
|
||||
<table className="violations-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Файл</th>
|
||||
<th>URL нарушения</th>
|
||||
<th>Статистика</th>
|
||||
<th>DMCA Жалоба</th>
|
||||
<th>Действия</th>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className="table-filtres-wrapper"
|
||||
>
|
||||
<div className="table-filtres-item">
|
||||
<div className="table-filtres-label">{t('items-per-page')}:</div>
|
||||
<DropDownList
|
||||
value={table.getState().pagination.pageSize}
|
||||
//@ts-ignore сделал так потому что для table.setPageSize нужна цифра
|
||||
callBack={table.setPageSize}
|
||||
>
|
||||
{[5, 10, 20, 50, 100].map(pageSize => (
|
||||
<li key={pageSize} value={pageSize}>
|
||||
{t('show')} {pageSize}
|
||||
</li>
|
||||
))}
|
||||
</DropDownList>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div> */}
|
||||
|
||||
{/* Таблица */}
|
||||
<div className="tanstak-table-block">
|
||||
<table className="tanstak-table">
|
||||
<thead className="tanstak-table-head">
|
||||
{table.getHeaderGroups().map(headerGroup => (
|
||||
<tr key={headerGroup.id}>
|
||||
{headerGroup.headers.map(header => (
|
||||
<th
|
||||
key={header.id}
|
||||
>
|
||||
{header.isPlaceholder
|
||||
? null
|
||||
: typeof header.column.columnDef.header === 'function'
|
||||
? header.column.columnDef.header(header.getContext())
|
||||
: header.column.columnDef.header as string}
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</thead>
|
||||
<tbody>
|
||||
<tbody className="tanstak-table-body">
|
||||
{table.getRowModel().rows.length > 0 ? (
|
||||
table.getRowModel().rows.map(row => (
|
||||
<tr key={row.id}>
|
||||
{row.getVisibleCells().map(cell => (
|
||||
<td key={cell.id}>
|
||||
{typeof cell.column.columnDef.cell === 'function'
|
||||
? cell.column.columnDef.cell(cell.getContext())
|
||||
: cell.getValue() as string}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
))
|
||||
) : (
|
||||
<tr>
|
||||
|
||||
<td className="violation-preview-cell">
|
||||
<div className="violation-preview">
|
||||
<img src="image-preview.php?id=303" alt="Preview" loading="lazy" />
|
||||
</div>
|
||||
</td>
|
||||
|
||||
|
||||
<td className="violation-filename-cell">
|
||||
<div className="violation-filename" title="banner111.png">
|
||||
banner111.png</div>
|
||||
<div className="violation-date">
|
||||
📅 17.10.2025</div>
|
||||
</td>
|
||||
|
||||
|
||||
<td className="violation-url-cell">
|
||||
<a href="https://m.apkpure.com/th/4k-wallpapers/com.starwalls.BACKGROUND.wallpapers4K.starwall" target="_blank" className="violation-link" title="https://m.apkpure.com/th/4k-wallpapers/com.starwalls.BACKGROUND.wallpapers4K.starwall">
|
||||
https://m.apkpure.com/th/4k-wallpapers/com.starwalls.BACKGROUND.wallpapers4K.starwall</a>
|
||||
</td>
|
||||
|
||||
|
||||
<td className="violation-stats-cell">
|
||||
<div className="confidence-badge">
|
||||
📊 85%
|
||||
</div>
|
||||
<div className="sources-count">
|
||||
🔗 2 источников
|
||||
</div>
|
||||
</td>
|
||||
|
||||
|
||||
<td className="violation-dmca-cell">
|
||||
<button className="btn-dmca-complaint" title="Подать DMCA жалобу">
|
||||
🚨 ПОЖАЛОВАТЬСЯ
|
||||
</button>
|
||||
</td>
|
||||
|
||||
|
||||
<td className="violation-actions-cell">
|
||||
<div className="violation-actions">
|
||||
<button className="action-icon-btn btn-view" title="Детали">
|
||||
👁️
|
||||
</button>
|
||||
<button className="action-icon-btn btn-ok" title="Одобрить">
|
||||
✅
|
||||
</button>
|
||||
<button className="action-icon-btn btn-block" title="Жалоба">
|
||||
❌
|
||||
</button>
|
||||
</div>
|
||||
<td colSpan={columns.length} className="text-center">
|
||||
{t('no-data-for-selected-filters')}
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{/* Пагинация */}
|
||||
<div className="tanstak-table-pagination">
|
||||
<div className="pagination-info">
|
||||
<span className="pagination-info-pages">
|
||||
{t('page')}{' '}
|
||||
<strong>
|
||||
{table.getState().pagination.pageIndex + 1} {t('out-of')} {table.getPageCount() ? table.getPageCount() : 1}
|
||||
</strong>
|
||||
</span>
|
||||
<span className="pagination-info-files">
|
||||
| {t('shown')} {table.getRowModel().rows.length} {t('out-of')} {filteredData.length}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="pagination-controls">
|
||||
{table.getCanPreviousPage() && (
|
||||
<button
|
||||
className="arrow"
|
||||
onClick={() => table.firstPage()}
|
||||
disabled={!table.getCanPreviousPage()}
|
||||
>
|
||||
<IconDoubleArrowLeft />
|
||||
</button>
|
||||
)}
|
||||
{table.getCanPreviousPage() && (
|
||||
<button
|
||||
className="arrow"
|
||||
onClick={() => table.previousPage()}
|
||||
disabled={!table.getCanPreviousPage()}
|
||||
>
|
||||
<IconArrowLeft />
|
||||
</button>
|
||||
)}
|
||||
|
||||
<div className="pagination-controls-pages">
|
||||
{Array.from({ length: Math.min(5, table.getPageCount()) }, (_, i) => {
|
||||
const pageIndex = Math.max(
|
||||
0,
|
||||
Math.min(
|
||||
table.getPageCount() - 5,
|
||||
table.getState().pagination.pageIndex - 2
|
||||
)
|
||||
) + i;
|
||||
|
||||
if (pageIndex < table.getPageCount()) {
|
||||
return (
|
||||
<button
|
||||
key={pageIndex}
|
||||
className={`${table.getState().pagination.pageIndex === pageIndex
|
||||
? 'current'
|
||||
: 'other'
|
||||
}`}
|
||||
onClick={() => table.setPageIndex(pageIndex)}
|
||||
>
|
||||
{pageIndex + 1}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
})}
|
||||
</div>
|
||||
|
||||
{table.getCanNextPage() && (
|
||||
<button
|
||||
className="arrow"
|
||||
onClick={() => table.nextPage()}
|
||||
disabled={!table.getCanNextPage()}
|
||||
>
|
||||
<IconArrowRight />
|
||||
</button>
|
||||
)}
|
||||
{table.getCanNextPage() && (
|
||||
<button
|
||||
className="arrow"
|
||||
onClick={() => table.lastPage()}
|
||||
disabled={!table.getCanNextPage()}
|
||||
>
|
||||
<IconDoubleArrowRight />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user