diff --git a/src/app/ui/referral-page/invitations-table.tsx b/src/app/ui/referral-page/invitations-table.tsx index c051c40..6fc8d2b 100644 --- a/src/app/ui/referral-page/invitations-table.tsx +++ b/src/app/ui/referral-page/invitations-table.tsx @@ -1,77 +1,503 @@ -import { useTranslations } from 'next-intl'; +'use client' + +import { useState, useMemo, useEffect } from 'react'; +import { + useReactTable, + getCoreRowModel, + getSortedRowModel, + getPaginationRowModel, + getFilteredRowModel, + ColumnDef, + SortingState, + ColumnFiltersState, +} from '@tanstack/react-table'; +import { IconEye, IconDoubleArrowRight, IconArrowRight, IconDoubleArrowLeft, IconArrowLeft, IconArrowUp, IconArrowDown, IconFilter, IconFileDownload, IconShieldExclamation } from '@/app/ui/icons/icons'; +import { useTranslations, useLocale } from 'next-intl'; +import DropDownList from '@/app/components/DropDownList'; +import { useQuery, useQueryClient } from '@tanstack/react-query'; +import { toast } from 'sonner'; +import { pluralize } from '@/app/lib/pluralize'; + +type FileItem = { + status?: string; + email?: string; + registrationData: number; +}; + +type ApiFile = { + status?: string; + email?: string; + registrationData: string; +}; + +type ApiResponse = { + files?: ApiFile[]; +}; + + export default function InvitationsTable() { - const t = useTranslations('Global'); + const { + data: testData, + isLoading, + isError, + error, + } = useQuery({ + queryKey: ['testData'], + queryFn: () => { + return {} + }, + + select: (data: ApiResponse): FileItem[] => { + if (!data?.files) return [ + { + status: 'string1', + email: 'string1', + registrationData: 1 + }, + { + status: 'string2', + email: 'string2', + registrationData: 2 + }, + { + status: 'string3', + email: 'string3', + registrationData: 3 + } + ]; + + return data.files.map((item: ApiFile) => { + return { + status: 'string', + email: 'string', + registrationData: 40 + }; + }); + }, + }); + + const queryClient = useQueryClient(); + + // Состояния + const [sorting, setSorting] = useState([]); + const [columnFilters, setColumnFilters] = useState([]); + const [dateFilter, setDateFilter] = useState('all'); + const [statusFilter, setStatusFilter] = useState('all'); + const [pagination, setPagination] = useState({ + pageIndex: 0, + pageSize: 10, + }); + + const t = useTranslations("Global"); + const locale = useLocale(); + + // Определение колонок + const columns = useMemo[]>( + () => [ + { + accessorKey: 'email', + header: ({ column }) => ( +
+ + {t('email')} + + +
+ ), + cell: ({ row }) => ( +
+ {/* {row.original.size !== undefined ? convertBytes(row.original.size) : '-'} */} + {row.original.email} +
+ ), + }, + { + accessorKey: 'status', + header: ({ column }) => ( +
+ + {t('status')} + + +
+ ), + cell: ({ row }) => { + return ( +
+ + {row.original.status} + +
+ ) + }, + }, + { + accessorKey: 'registrationData', + header: ({ column }) => ( +
+ + {t('registration-date')} + + +
+ ), + cell: ({ row }) => { + return ( +
+ {row.original.registrationData} +
+ ) + }, + enableColumnFilter: false, + } + ], + [] + ); + + // Фильтрация по типу файла и дате + const filteredData = useMemo(() => { + let result = testData; + if (!result) { + return []; + } + + // Фильтр по типу файла + if (statusFilter !== 'all') { + result = result.filter(item => item.status === statusFilter); + } + + // Фильтр по дате + 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.registrationData) { + return item.registrationData >= todayStart + } else { + return 0 + } + }); + break; + case 'week': + const weekAgo = now - sevenDays; + result = result.filter(item => { + if (item.registrationData) { + return item.registrationData >= weekAgo; + } + }); + break; + case 'month': + const monthAgo = now - thirtyDays; + result = result.filter(item => { + if (item.registrationData) { + return item.registrationData >= monthAgo; + } + }); + break; + case 'older': + const monthAgo2 = now - thirtyDays; + result = result.filter(item => { + if (item.registrationData) { + return item.registrationData < monthAgo2; + } + }); + break; + } + } + + return result; + }, [testData, statusFilter, 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, + }, + }, + }); + + const pluralizeFiles = (number: number) => { + const translate = [t('file'), t('files-few'), t('files')]; + return pluralize(number, translate[0], translate[1], translate[2], locale); + }; + 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')} +
  • +
    +
    +
    -
    -
    - Email -
    -
    - Статус -
    -
    - Заработано -
    -
    - Дата приглашения -
    -
    - Регистрация +
    +
    {t('items-per-page')}:
    + + {[5, 10, 20, 50, 100].map(pageSize => ( +
  • + {t('show')} {pageSize} +
  • + ))} +
    +
    -
    - email@mail.com -
    -
    - {t('status')} -
    -
    - 0 -
    -
    - 0 -
    -
    - 0 + {/* Таблица */} +
    + + + {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')} +
    -
    - email@mail.com -
    -
    - {t('status')} -
    -
    - 0 -
    -
    - 0 -
    -
    - 0 -
    + {/* Пагинация */} +
    +
    + {/* + {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} {pluralizeFiles(filteredData.length || 0)} + */} +
    -
    - email@mail.com +
    + {table.getCanPreviousPage() && ( + + )} + {table.getCanPreviousPage() && ( + + )} + +
    + {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; + })} +
    + + {table.getCanNextPage() && ( + + )} + {table.getCanNextPage() && ( + + )} +
    -
    - {t('status')} -
    -
    - 0 -
    -
    - 0 -
    -
    - 0 -
    -
    +
    - ) + ); } \ No newline at end of file diff --git a/src/i18n/messages/en.json b/src/i18n/messages/en.json index 0342d9f..ec72fb4 100644 --- a/src/i18n/messages/en.json +++ b/src/i18n/messages/en.json @@ -206,7 +206,8 @@ "view": "View", "are-no-violations": "There are no violations", "error-reading-image": "Error reading image", - "error-user-not-have-tokens-for-protect": "User not have tokens for protect" + "error-user-not-have-tokens-for-protect": "User not have tokens for protect", + "registration-date": "Registration date" }, "Login-register-form": { "and": "and", diff --git a/src/i18n/messages/ru.json b/src/i18n/messages/ru.json index 2a74095..13f981c 100644 --- a/src/i18n/messages/ru.json +++ b/src/i18n/messages/ru.json @@ -206,7 +206,8 @@ "view": "Посмотреть", "are-no-violations": "Нарушений нет", "error-reading-image": "Ошибка чтения изображения", - "error-user-not-have-tokens-for-protect": "У пользователя нет токенов для защиты." + "error-user-not-have-tokens-for-protect": "У пользователя нет токенов для защиты.", + "registration-date": "Дата регистрации" }, "Login-register-form": { "and": "и",