'use client' import { useMemo, useState, useEffect } 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, IconEye } from '@/app/ui/icons/icons'; import { useQuery } from '@tanstack/react-query'; import { getViolationFilesArray } from '@/app/actions/violationActions'; import { useViewport } from '@/app/hooks/useViewport'; import { formatDate, formatDateTime } from '@/app/lib/formatDate'; import Link from 'next/link'; import DropDownList from '@/app/components/DropDownList'; import { useDebounce } from 'use-debounce'; interface FileViolation { createdAt: string; fileId: string; fileName: string; fileSize: number; latestViolationDate: string; mimeType: string; status: string; supportId: number; violationCount: number; countComplaint: number; countLawCase: number; } interface ViolationData { content: FileViolation[]; totalPages: number; totalElements: number; size: number; number: number; } export default function ViolationsTable() { const t = useTranslations('Global'); const viewport = useViewport(); const [fileNameFilter, setFileNameFilter] = useState(''); const [dateFilter, setDateFilter] = useState('all'); const [pagination, setPagination] = useState({ pageIndex: 0, pageSize: 10, }); const [sorting, setSorting] = useState([]); const [debouncedFileName] = useDebounce(fileNameFilter, 500); const getDateRange = (filter: string): { start_date?: string; end_date?: string } => { const now = new Date(); const end_date = formatDateTimeForAPI(now); switch (filter) { case 'today': { const start_date = formatDateTimeForAPI(new Date(now.setHours(0, 0, 0, 0))); return { start_date, end_date }; } case 'week': { const weekAgo = new Date(now); weekAgo.setDate(weekAgo.getDate() - 7); weekAgo.setHours(0, 0, 0, 0); return { start_date: formatDateTimeForAPI(weekAgo), end_date }; } case 'month': { const monthAgo = new Date(now); monthAgo.setMonth(monthAgo.getMonth() - 1); monthAgo.setHours(0, 0, 0, 0); return { start_date: formatDateTimeForAPI(monthAgo), end_date }; } default: return {}; } }; const formatDateTimeForAPI = (date: Date): string => { const year = date.getFullYear(); const month = String(date.getMonth() + 1).padStart(2, '0'); const day = String(date.getDate()).padStart(2, '0'); const hours = String(date.getHours()).padStart(2, '0'); const minutes = String(date.getMinutes()).padStart(2, '0'); const seconds = String(date.getSeconds()).padStart(2, '0'); return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; }; const dateRange = getDateRange(dateFilter); const { data: violationData, isLoading, isFetching, isError, error, refetch, } = useQuery({ queryKey: ['violationData', pagination.pageIndex, pagination.pageSize, debouncedFileName, dateFilter], queryFn: () => getViolationFilesArray({ page: pagination.pageIndex, size: pagination.pageSize, file_name: debouncedFileName || undefined, start_date: dateRange.start_date, end_date: dateRange.end_date, }), select: (data) => { return data; }, placeholderData: (previousData) => previousData, }); useEffect(() => { if (violationData) { setPagination(prev => ({ ...prev, pageIndex: violationData.number || 0, })); } }, [violationData]); const getMaxLengthByWidth = (): number => { if (viewport.device === 'MOBILE') return 26; if (viewport.device === 'TABLET') return 32; if (viewport.device === 'DESKTOP') return 84; return 100; }; const cutFileName = (fileName: string) => { const MAX_FILE_NAME_LENGTH = getMaxLengthByWidth(); const lastDotIndex = fileName.lastIndexOf('.'); if (lastDotIndex <= 0) { return fileName.length > MAX_FILE_NAME_LENGTH ? fileName.substring(0, MAX_FILE_NAME_LENGTH - 3) + '...' : fileName; } const nameWithoutExtension = fileName.substring(0, lastDotIndex); const extension = fileName.substring(lastDotIndex); if (fileName.length <= MAX_FILE_NAME_LENGTH) { return fileName; } const maxNameLength = MAX_FILE_NAME_LENGTH - extension.length - 3; if (maxNameLength <= 0) { return '...'; } return nameWithoutExtension.substring(0, maxNameLength) + '...'; }; const columns = useMemo[]>( () => [ { accessorKey: 'supportId', header: ({ column }) => (
ID
), cell: ({ row }) => (
{row.original.supportId}
), }, { accessorKey: 'fileName', header: ({ column }) => (
{t('file')}
), cell: ({ row }) => (
{cutFileName(row.original.fileName)}
), }, { accessorKey: 'violationCount', header: ({ column }) => (
{t('matches')}
), cell: ({ row }) => (
{row.original.violationCount}
), }, { accessorKey: 'countComplaint', header: ({ column }) => (
{t('complaint')}
), cell: ({ row }) => (
{row.original.countComplaint}
), }, { accessorKey: 'countLawCase', header: ({ column }) => (
{t('claims')}
), cell: ({ row }) => (
{row.original.countLawCase}
), }, { accessorKey: 'actions', header: () => (
{t('actions')}
), cell: ({ row }) => (
), }, ], [t, viewport] ); const table = useReactTable({ data: violationData?.content || [], columns, state: { sorting, pagination, }, pageCount: violationData?.totalPages || -1, autoResetPageIndex: false, manualPagination: true, // Пагинация с бека manualSorting: false, // сортировка локально так как эндпоинт не поддерживает сортировку. manualFiltering: true, // Фильтрация с бека onPaginationChange: setPagination, onSortingChange: setSorting, getCoreRowModel: getCoreRowModel(), getSortedRowModel: getSortedRowModel(), getPaginationRowModel: getPaginationRowModel(), }); const handleFileNameChange = (e: React.ChangeEvent) => { setFileNameFilter(e.target.value); setPagination(prev => ({ ...prev, pageIndex: 0 })); }; const handleDateFilterChange = (value: string) => { setDateFilter(value); setPagination(prev => ({ ...prev, pageIndex: 0 })); }; if (isError) { return
Error: {error?.message || t('error')}
; } return (

{t('matches-detected')}

{/* Фильтры */}
{/* Фильтр по дате */}
{t('date-filter')}:
{ switch (dateFilter) { case 'all': return t('all-dates'); case 'week': return t('for-a-week'); case 'month': return t('for-a-month'); default: return t('today'); } })()} callBack={handleDateFilterChange} >
  • {t('all-dates')}
  • {t('today')}
  • {t('for-a-week')}
  • {t('for-a-month')}
  • {/* Фильтр по имени файла */}
    {t('file-name')}:
    {t('items-per-page')}:
    { table.setPageSize(value); setPagination(prev => ({ ...prev, pageSize: value, pageIndex: 0 })); }} > {[5, 10, 20, 50, 100].map(pageSize => (
  • {t('show')} {pageSize}
  • ))}
    {/* Таблица */}
    {table.getHeaderGroups().map(headerGroup => ( {headerGroup.headers.map(header => ( ))} ))} {isLoading && ( )} {table.getRowModel().rows.length > 0 ? ( table.getRowModel().rows.map(row => ( {row.getVisibleCells().map(cell => ( ))} )) ) : ( {!isLoading && ( )} )}
    {header.isPlaceholder ? null : typeof header.column.columnDef.header === 'function' ? header.column.columnDef.header(header.getContext()) : (header.column.columnDef.header as string)}
    {typeof cell.column.columnDef.cell === 'function' ? cell.column.columnDef.cell(cell.getContext()) : (cell.getValue() as string)}
    {t('no-data-for-selected-filters')}
    {/* Пагинация */}
    {t('page')}{' '} {table.getState().pagination.pageIndex + 1} {t('out-of')} {violationData?.totalPages || 1}
    {Array.from({ length: Math.min(5, violationData?.totalPages || 0) }, (_, i) => { const totalPages = violationData?.totalPages || 0; const currentPage = table.getState().pagination.pageIndex; let pageIndex = 0; if (totalPages <= 5) { pageIndex = i; } else if (currentPage <= 2) { pageIndex = i; } else if (currentPage >= totalPages - 3) { pageIndex = totalPages - 5 + i; } else { pageIndex = currentPage - 2 + i; } if (pageIndex < totalPages) { return ( ); } return null; })}
    ); }