diff --git a/src/widgets/transactions-table.tsx b/src/widgets/transactions-table.tsx deleted file mode 100644 index 3f03147..0000000 --- a/src/widgets/transactions-table.tsx +++ /dev/null @@ -1,273 +0,0 @@ -'use client'; - -import { format } from 'date-fns'; -import { ru } from 'date-fns/locale'; -import { CalendarIcon, X } from 'lucide-react'; -import { useEffect, useState } from 'react'; - -import TableLoadingOverlay from '@/shared/components/table-loading-overlay'; -import { useTextController } from '@/shared/language/hooks/use-text-controller'; -import { Button } from '@/shared/shadcn-ui/button'; -import { Calendar } from '@/shared/shadcn-ui/calendar'; -import { Label } from '@/shared/shadcn-ui/label'; -import { - Popover, - PopoverContent, - PopoverTrigger, -} from '@/shared/shadcn-ui/popover'; -import { - Table, - TableBody, - TableCell, - TableHeader, - TableRow, -} from '@/shared/shadcn-ui/table'; - -import { - Pagination, - PaginationContent, - PaginationItem, - PaginationLink, - PaginationNext, - PaginationPrevious, -} from './pagination'; - -export interface TransactionRequest { - start_date?: string; - end_date?: string; - page: number; - limit: number; -} - -export interface TransactionsTableProps { - data: { - transactions: T[]; - total_pages: number; - total_records: number; - }; - isLoading: boolean; - onChange: (request: TransactionRequest) => void; - renderHeaders: () => React.ReactNode; - renderRow: (transaction: T, index: number) => React.ReactNode; - itemsPerPageOptions?: number[]; -} - -export const TransactionsTable = ({ - data, - isLoading, - onChange, - renderHeaders, - renderRow, - itemsPerPageOptions = [5, 10, 20, 50], -}: TransactionsTableProps) => { - const [startDate, setStartDate] = useState( - new Date(new Date().setMonth(new Date().getMonth() - 1)), - ); - const [endDate, setEndDate] = useState(new Date()); - const [currentPage, setCurrentPage] = useState(1); - const [itemsPerPage, setItemsPerPage] = useState(itemsPerPageOptions[0]); - - const handlePageChange = (page: number) => { - if (page < 1 || page > data.total_pages) return; - setCurrentPage(page); - }; - - const getPageNumbers = () => { - const pages = []; - const maxVisiblePages = 5; // Maximum number of visible pages - const halfVisible = Math.floor(maxVisiblePages / 2); - - let startPage = Math.max(1, currentPage - halfVisible); - let endPage = Math.min(data.total_pages, currentPage + halfVisible); - - if (currentPage <= halfVisible) { - endPage = Math.min(data.total_pages, maxVisiblePages); - } else if (currentPage + halfVisible >= data.total_pages) { - startPage = Math.max(1, data.total_pages - maxVisiblePages + 1); - } - - for (let i = startPage; i <= endPage; i++) { - pages.push(i); - } - - return pages; - }; - - const filterTransactions = () => { - if (!startDate || !endDate) return; - setCurrentPage(1); // Reset to the first page when applying filters - }; - - const { t } = useTextController(); - - useEffect(() => { - onChange({ - limit: itemsPerPage, - page: currentPage, - ...(startDate ? { start_date: format(startDate, 'yyyy-MM-dd') } : {}), - ...(endDate ? { end_date: format(endDate, 'yyyy-MM-dd') } : {}), - }); - }, [startDate, endDate, itemsPerPage, currentPage]); - - if (!data) return null; - - return ( -
- -
-

- {t('corporate.transactions.title')} -

- -
-
-
- - - - - - - - - - -
- -
- - - - - - - - - - -
-
-
-
- -
- - {renderHeaders()} - - {data.transactions.length > 0 ? ( - data.transactions.map((transaction, index) => - renderRow(transaction, index), - ) - ) : ( - - - {t('corporate.transactions.no-data')} - - - )} - -
-
- - {data.transactions.length > 0 && ( -
-
- Показано {data.transactions.length} из {data.total_records} операций -
- - - - - handlePageChange(currentPage - 1)} - className={ - currentPage === 1 ? 'pointer-events-none opacity-50' : '' - } - /> - - - {getPageNumbers().map((page, index) => ( - - handlePageChange(page as number)} - > - {page} - - - ))} - - - handlePageChange(currentPage + 1)} - className={ - currentPage === data.total_pages - ? 'pointer-events-none opacity-50' - : '' - } - /> - - - - -
- - {t('transactions.entries')} - - -
-
- )} -
- ); -}; diff --git a/src/widgets/transactions-table/index.ts b/src/widgets/transactions-table/index.ts new file mode 100644 index 0000000..8bb7323 --- /dev/null +++ b/src/widgets/transactions-table/index.ts @@ -0,0 +1 @@ +export { TransactionsTable } from "./ui/transactions-table"; diff --git a/src/widgets/transactions-table/ui/table-pagination.tsx b/src/widgets/transactions-table/ui/table-pagination.tsx new file mode 100644 index 0000000..0bf8d46 --- /dev/null +++ b/src/widgets/transactions-table/ui/table-pagination.tsx @@ -0,0 +1,121 @@ +import { ChangeEvent } from 'react'; + +import { useTextController } from '@/shared/language/hooks/use-text-controller'; + +import { + Pagination, + PaginationContent, + PaginationItem, + PaginationLink, + PaginationNext, + PaginationPrevious, +} from '../../pagination'; + +interface TablePaginationProps { + currentPage: number; + itemsPerPage: number; + totalPages: number; + totalOperations?: number; + transactionsQuantity: number; + itemsPerPageOptions: number[]; + onPageChange: (page: number) => void; + onItemsPerPageChange: (e: ChangeEvent) => void; +} + +export const TransactionsTablePagination = ({ + currentPage, + itemsPerPage, + totalPages, + totalOperations = 0, + itemsPerPageOptions, + transactionsQuantity, + onPageChange, + onItemsPerPageChange, +}: TablePaginationProps) => { + const { t } = useTextController(); + + const getPageNumbers = () => { + const pages = []; + const maxVisiblePages = 5; // Maximum number of visible pages + const halfVisible = Math.floor(maxVisiblePages / 2); + + let startPage = Math.max(1, currentPage - halfVisible); + let endPage = Math.min(totalPages, currentPage + halfVisible); + + if (currentPage <= halfVisible) { + endPage = Math.min(totalPages, maxVisiblePages); + } else if (currentPage + halfVisible >= totalPages) { + startPage = Math.max(1, totalPages - maxVisiblePages + 1); + } + + for (let i = startPage; i <= endPage; i++) { + pages.push(i); + } + + return pages; + }; + + return ( + <> + {transactionsQuantity > 0 && ( +
+
+ Показано {transactionsQuantity} из {totalOperations} операций +
+ + + + + onPageChange(currentPage - 1)} + className={ + currentPage === 1 ? 'pointer-events-none opacity-50' : '' + } + /> + + + {getPageNumbers().map((page, index) => ( + + onPageChange(page as number)} + > + {page} + + + ))} + + + onPageChange(currentPage + 1)} + className={ + currentPage === totalPages + ? 'pointer-events-none opacity-50' + : '' + } + /> + + + + +
+ + {t('transactions.entries')} + + +
+
+ )} + + ); +}; diff --git a/src/widgets/transactions-table/ui/transactions-table-header.tsx b/src/widgets/transactions-table/ui/transactions-table-header.tsx new file mode 100644 index 0000000..e2d61d9 --- /dev/null +++ b/src/widgets/transactions-table/ui/transactions-table-header.tsx @@ -0,0 +1,102 @@ +import { format } from 'date-fns'; +import { ru } from 'date-fns/locale'; +import { CalendarIcon, X } from 'lucide-react'; +import { Dispatch, SetStateAction } from 'react'; + +import { useTextController } from '@/shared/language/hooks/use-text-controller'; +import { Button } from '@/shared/shadcn-ui/button'; +import { Calendar } from '@/shared/shadcn-ui/calendar'; +import { Label } from '@/shared/shadcn-ui/label'; +import { + Popover, + PopoverContent, + PopoverTrigger, +} from '@/shared/shadcn-ui/popover'; + +interface TransactionsTableHeaderProps { + startDate: Date | undefined; + setStartDate: Dispatch>; + endDate: Date | undefined; + setEndDate: Dispatch>; +} + +export const TransactionsTableHeader = ({ + startDate, + setStartDate, + endDate, + setEndDate, +}: TransactionsTableHeaderProps) => { + const { t } = useTextController(); + + return ( +
+

+ {t('corporate.transactions.title')} +

+ +
+
+
+ + + + + + + + + + +
+ +
+ + + + + + + + + + +
+
+
+
+ ); +}; diff --git a/src/widgets/transactions-table/ui/transactions-table.tsx b/src/widgets/transactions-table/ui/transactions-table.tsx new file mode 100644 index 0000000..0f4690c --- /dev/null +++ b/src/widgets/transactions-table/ui/transactions-table.tsx @@ -0,0 +1,123 @@ +import { format } from 'date-fns'; +import { ChangeEvent, useEffect, useState } from 'react'; + +import TableLoadingOverlay from '@/shared/components/table-loading-overlay'; +import { useTextController } from '@/shared/language/hooks/use-text-controller'; +import { + Table, + TableBody, + TableCell, + TableHeader, + TableRow, +} from '@/shared/shadcn-ui/table'; + +import { TransactionsTablePagination } from './table-pagination'; +import { TransactionsTableHeader } from './transactions-table-header'; + +export interface TransactionRequest { + start_date?: string; + end_date?: string; + page: number; + limit: number; +} + +export interface TransactionsTableProps { + data: { + transactions: T[]; + total_pages: number; + total_records: number; + }; + isLoading: boolean; + onChange: (request: TransactionRequest) => void; + renderHeaders: () => React.ReactNode; + renderRow: (transaction: T, index: number) => React.ReactNode; + itemsPerPageOptions?: number[]; +} + +export const TransactionsTable = ({ + data, + isLoading, + onChange, + renderHeaders, + renderRow, + itemsPerPageOptions = [5, 10, 20, 50], +}: TransactionsTableProps) => { + const [startDate, setStartDate] = useState( + new Date(new Date().setMonth(new Date().getMonth() - 1)), + ); + const [endDate, setEndDate] = useState(new Date()); + const [currentPage, setCurrentPage] = useState(1); + const [itemsPerPage, setItemsPerPage] = useState(itemsPerPageOptions[0]); + + const handlePageChange = (page: number) => { + if (page < 1 || page > data.total_pages) return; + setCurrentPage(page); + }; + + const handleItemsPerPageChange = (e: ChangeEvent) => { + setItemsPerPage(Number(e.target.value)); + setCurrentPage(1); // Reset to first page when changing items per page + }; + + const filterTransactions = () => { + if (!startDate || !endDate) return; + setCurrentPage(1); // Reset to the first page when applying filters + }; + + const { t } = useTextController(); + + useEffect(() => { + onChange({ + limit: itemsPerPage, + page: currentPage, + ...(startDate ? { start_date: format(startDate, 'yyyy-MM-dd') } : {}), + ...(endDate ? { end_date: format(endDate, 'yyyy-MM-dd') } : {}), + }); + }, [startDate, endDate, itemsPerPage, currentPage]); + + if (!data) return null; + + return ( +
+ + + +
+ + {renderHeaders()} + + {data.transactions.length > 0 ? ( + data.transactions.map((transaction, index) => + renderRow(transaction, index), + ) + ) : ( + + + {t('corporate.transactions.no-data')} + + + )} + +
+
+ +
+ ); +};