'use client'; import { useState, useMemo, useEffect, ReactNode } from 'react'; import { useReactTable, getCoreRowModel, getSortedRowModel, getPaginationRowModel, getFilteredRowModel, ColumnDef, SortingState, ColumnFiltersState, } from '@tanstack/react-table'; import { IconImageFile, IconVideoFile, IconAudioFile, IconEye, IconDoubleArrowRight, IconArrowRight, IconDoubleArrowLeft, IconArrowLeft, IconArrowUp, IconArrowDown, IconFilter, IconFileOpen, IconShieldExclamation, IconDelete } from '@/app/ui/icons/icons'; import { useTranslations } from 'next-intl'; import DropDownList from '@/app/components/dropDownList'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { getUserFilesData, removeUserFile } from '@/app/actions/fileEntity'; import ModalWindow from '@/app/components/modalWindow'; import { toast } from 'sonner'; type FileType = 'image' | 'video' | 'audio'; type FileItem = { id: string; fileName: string; fileType: string; violations?: number | undefined; checks?: number | undefined; lastCheck?: number; _original?: ApiFile; }; type ApiFile = { id: string; originalFileName: string; mimeType: string; violations: number; checks: number; updatedAt: string; }; type ApiResponse = { files?: ApiFile[]; }; // Иконки для типов файлов const FileTypeIcon = ({ type }: { type: string }) => { switch (type) { case 'image': return ; case 'video': return ; case 'audio': return ; default: return 📄; } }; // Форматирование даты из timestamp const formatDate = (timestamp: number) => { const date = new Date(timestamp); const day = date.getDate().toString().padStart(2, '0'); const month = (date.getMonth() + 1).toString().padStart(2, '0'); const year = date.getFullYear(); return `${day}.${month}.${year}`; }; const formatDateTime = (timestamp: number) => { const date = new Date(timestamp); const hours = date.getHours().toString().padStart(2, '0'); const minutes = date.getMinutes().toString().padStart(2, '0'); return `${hours}:${minutes}`; }; export default function TanstakFilesTable({ fileType }: { fileType: string }) { const { data: tableData, isLoading, isError, error, } = useQuery({ queryKey: ['userFilesData'], queryFn: getUserFilesData, select: (data: ApiResponse): FileItem[] => { if (!data?.files) return []; return data.files.map((item: ApiFile) => { const [datePart, timePart] = item.updatedAt.split(' '); const [day, month, year] = datePart.split('-').map(Number); const [hours, minutes, seconds] = timePart.split(':').map(Number); const newDate = new Date(year, month - 1, day, hours, minutes, seconds).getTime(); return { id: item.id, fileName: item.originalFileName, fileType: item.mimeType.toLocaleLowerCase(), violations: item.violations, checks: item.checks, lastCheck: newDate, _original: item }; }); }, }); const queryClient = useQueryClient(); // Состояния const [sorting, setSorting] = useState([]); const [columnFilters, setColumnFilters] = useState([]); const [dateFilter, setDateFilter] = useState('all'); const [typeFilter, setTypeFilter] = useState(fileType); const [pagination, setPagination] = useState({ pageIndex: 0, pageSize: 10, }); const [openWindow, setOpenWindow] = useState(false); const [openWindowChildren, setOpenWindowChildren] = useState(null); const t = useTranslations("Global"); // Определение колонок const columns = useMemo[]>( () => [ { accessorKey: 'fileName', header: ({ column }) => (
{t('file')}
), cell: ({ row }) => (
{row.original.fileName}
), enableColumnFilter: false, }, { accessorKey: 'status', header: ({ column }) => (
status
), cell: ({ row }) => { return (
status
) }, }, { accessorKey: 'violations', header: ({ column }) => (
{t('violations')}
), cell: ({ row }) => { let classCollor = () => { let result = '' if (row.original.violations !== undefined) { result = row.original.violations > 0 ? 'text-red-600' : 'text-green-600' } return result; } return (
{row.original.violations !== undefined ? row.original.violations : '-'}
) }, }, { accessorKey: 'checks', header: ({ column }) => (
{t('checks')}
), cell: ({ row }) => (
{row.original.checks !== undefined ? row.original.checks : '-'}
), }, { accessorKey: 'lastCheck', header: ({ column }) => (
{t('last-check')}
), cell: ({ row }) => { return (
{row.original.lastCheck ? ( <> {formatDate(row.original.lastCheck)}
{formatDateTime(row.original.lastCheck)} ) : (
-
)}
) }, enableColumnFilter: false, }, { id: 'actions', header: () => { return (
{t('actions')}
) }, cell: ({ row }) => (
), enableSorting: false, enableColumnFilter: false, }, ], [] ); // Обработчики действий const handleView = (file: FileItem) => { console.log(`Просмотр файла: ${file.fileName}`); }; const handleDownload = (file: FileItem) => { console.log(`Скачать: ${file.fileName}`); }; const handleProtect = (file: FileItem) => { console.log(`Щиток: ${file.fileName}`); }; const handleOpenWindowForRemove = async (file: FileItem) => { setOpenWindowChildren(() => { return (

{t('you-sure-you-want-to-delete')}

{file.fileName}
) }) setOpenWindow(true); }; const deleteMutation = useMutation({ mutationFn: removeUserFile, onMutate: async (fileId: string) => { await queryClient.cancelQueries({ queryKey: ['userFilesData'] }); queryClient.setQueryData(['userFilesData'], (old) => { if (!old?.files) return old; return { ...old, files: old.files.filter(file => file.id !== fileId) }; }); }, onError: () => { queryClient.invalidateQueries({ queryKey: ['userFilesData'] }); toast.error(t('error')); }, onSuccess: (response, fileId) => { if (response === fileId) { queryClient.invalidateQueries({ queryKey: ['userFilesData'], refetchType: 'active' }); setOpenWindow(false); setOpenWindowChildren(null); toast.success(t('file-has-been-deleted')); } }, }); // Фильтрация по типу файла и дате const filteredData = useMemo(() => { let result = tableData; if (!result) { return []; } // Фильтр по типу файла if (typeFilter !== 'all') { result = result.filter(item => item.fileType === typeFilter); } // Фильтр по дате 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 => { if (item.lastCheck) { return item.lastCheck >= todayStart } else { return 0 } }); break; case 'week': const weekAgo = now - sevenDays; result = result.filter(item => { if (item.lastCheck) { return item.lastCheck >= weekAgo; } }); break; case 'month': const monthAgo = now - thirtyDays; result = result.filter(item => { if (item.lastCheck) { return item.lastCheck >= monthAgo; } }); break; case 'older': const monthAgo2 = now - thirtyDays; result = result.filter(item => { if (item.lastCheck) { return item.lastCheck < monthAgo2; } }); break; } } return result; }, [tableData, typeFilter, dateFilter]); useEffect(() => { const currentPageRows = table.getRowModel().rows; const pageCount = table.getPageCount(); if (currentPageRows.length === 0 && pagination.pageIndex > 0 && pageCount > 0) { table.setPageIndex(pagination.pageIndex - 1); } }, [filteredData, pagination.pageIndex]); // Создание таблицы 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 (
{/* Фильтры */}
{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') case 'older': return t('older-than-a-month') default: return t('today') } })()} callBack={setDateFilter} >
  • {t('all-dates')}
  • {t('today')}
  • {t('for-a-week')}
  • {t('for-a-month')}
  • {t('older-than-a-month')}
  • {fileType === "all" && (
    {t('type-filter')}:
    { switch (typeFilter) { case 'image': return t('images') case 'video': return t('videos') case 'audio': return t('audios') default: return t('all-types') } })() } callBack={setTypeFilter} >
  • {t('all-types')}
  • {t('images')}
  • {t('videos')}
  • {t('audios')}
  • )}
    {t('items-per-page')}:
    {[5, 10, 20, 50, 100].map(pageSize => (
  • {t('show')} {pageSize}
  • ))}
    {/* Таблица */}
    {table.getHeaderGroups().map(headerGroup => ( {headerGroup.headers.map(header => ( ))} ))} {table.getRowModel().rows.length > 0 ? ( table.getRowModel().rows.map(row => ( {row.getVisibleCells().map(cell => ( ))} )) ) : ( )}
    {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')} {table.getPageCount() ? table.getPageCount() : 1} | {t('shown')} {table.getRowModel().rows.length} {t('out-of')} {filteredData.length} {t('files')}
    {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 ( ); } return null; })}
    ); }