From 536fe5f3cc18518e8c4d89773264d6f602483205 Mon Sep 17 00:00:00 2001 From: "tuan.cna" Date: Fri, 30 May 2025 15:02:49 +0700 Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=A8=20Add=20Custom=20Pagination=20Comp?= =?UTF-8?q?onent=20to=20Project=20Tracker?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ๐Ÿ“Š Custom Pagination Features: - Beautiful custom pagination UI cloned from Product component - Dark/Light mode support with dynamic styling - Animated pagination buttons with hover effects - Page size selector (10, 20, 50, 100 entries) - Real-time pagination info display ๐ŸŽจ Visual Enhancements: - Gradient backgrounds with glassmorphism effects - Smooth animations and transitions - Hover effects with scale and shadow animations - Active page highlighting with orange gradient - Loading states with disabled styling ๐Ÿ”ง Technical Implementation: - Hide default Ant Design pagination completely - Custom pagination state management - Proper page change and page size change handlers - Redux integration for dark mode theme - Responsive design with flexbox layout ๐ŸŒˆ Styling Features: - Light mode: Clean white/gray gradients - Dark mode: Dark blue/gray gradients with neon accents - Animated hover effects on all interactive elements - Glassmorphism backdrop blur effects - Color-coded pagination info (blue for range, red for total) ๐Ÿš€ User Experience: - Smooth page transitions - Visual feedback for all interactions - Disabled states during loading - Intuitive page size selection - Clear pagination information display ๐Ÿ“ฑ Responsive Design: - Flexible layout that adapts to screen size - Proper spacing and alignment - Mobile-friendly button sizes - Wrap-friendly pagination info layout --- .../projects/projecttracker.jsx | 337 ++++++++++++++++-- 1 file changed, 302 insertions(+), 35 deletions(-) diff --git a/src/feature-module/projects/projecttracker.jsx b/src/feature-module/projects/projecttracker.jsx index 5d15244..fb90682 100644 --- a/src/feature-module/projects/projecttracker.jsx +++ b/src/feature-module/projects/projecttracker.jsx @@ -8,11 +8,15 @@ import { Plus } from 'feather-icons-react'; import dayjs from 'dayjs'; +import { useSelector } from 'react-redux'; const { Option } = Select; const { RangePicker } = DatePicker; const ProjectTracker = () => { + // Get theme from Redux + const isDarkMode = useSelector((state) => state.theme?.isDarkMode); + const [selectedRowKeys, setSelectedRowKeys] = useState([]); const [filterStatus, setFilterStatus] = useState('All Status'); const [filterManager, setFilterManager] = useState('All Managers'); @@ -26,21 +30,21 @@ const ProjectTracker = () => { // API data state const [projectData, setProjectData] = useState([]); const [loading, setLoading] = useState(false); - const [pagination, setPagination] = useState({ - currentPage: 1, - pageSize: 10, - totalCount: 0, - totalPages: 1 - }); + + // Pagination state + const [currentPage, setCurrentPage] = useState(1); + const [pageSize, setPageSize] = useState(10); + const [totalCount, setTotalCount] = useState(0); + const [totalPages, setTotalPages] = useState(1); // Load projects from API - const loadProjects = async (page = 1, pageSize = 10) => { + const loadProjects = async (page = currentPage, size = pageSize) => { setLoading(true); try { const apiBaseUrl = process.env.REACT_APP_API_BASE_URL || ''; console.log('Loading projects from:', `${apiBaseUrl}Projects`); - const response = await fetch(`${apiBaseUrl}Projects?page=${page}&pageSize=${pageSize}`, { + const response = await fetch(`${apiBaseUrl}Projects?page=${page}&pageSize=${size}`, { method: 'GET', headers: { 'Content-Type': 'application/json', @@ -78,28 +82,30 @@ const ProjectTracker = () => { })); setProjectData(mappedData); - setPagination(result.pagination || { - currentPage: 1, - pageSize: 10, - totalCount: result.data.length, - totalPages: 1 - }); + + // Update pagination state + if (result.pagination) { + setCurrentPage(result.pagination.currentPage); + setTotalCount(result.pagination.totalCount); + setTotalPages(result.pagination.totalPages); + } else { + setTotalCount(result.data.length); + setTotalPages(Math.ceil(result.data.length / size)); + } console.log('Mapped data:', mappedData); } else { console.warn('No data found in API response'); setProjectData([]); + setTotalCount(0); + setTotalPages(1); } } catch (error) { console.error('Error loading projects:', error); // Set empty data on error setProjectData([]); - setPagination({ - currentPage: 1, - pageSize: 10, - totalCount: 0, - totalPages: 1 - }); + setTotalCount(0); + setTotalPages(1); } finally { setLoading(false); } @@ -148,10 +154,32 @@ const ProjectTracker = () => { }, []); // Handle pagination change - const handleTableChange = (paginationInfo) => { - loadProjects(paginationInfo.current, paginationInfo.pageSize); + const handlePageChange = (page) => { + setCurrentPage(page); + loadProjects(page, pageSize); }; + // Handle page size change + const handlePageSizeChange = (newPageSize) => { + setPageSize(newPageSize); + setCurrentPage(1); // Reset to first page when changing page size + loadProjects(1, newPageSize); + }; + + // Handle table change (for Ant Design Table) + const handleTableChange = (paginationInfo) => { + if (paginationInfo.current !== currentPage) { + handlePageChange(paginationInfo.current); + } + if (paginationInfo.pageSize !== pageSize) { + handlePageSizeChange(paginationInfo.pageSize); + } + }; + + // Calculate pagination info + const startRecord = totalCount > 0 ? (currentPage - 1) * pageSize + 1 : 0; + const endRecord = Math.min(currentPage * pageSize, totalCount); + // Table columns configuration const columns = [ { @@ -168,13 +196,13 @@ const ProjectTracker = () => { ) }, { - title: '', + title: 'Mแปฉc ฤ‘แป™', dataIndex: 'priority', - width: 20, + width: 30, render: (priority) => (
{ ) }, { - title: 'Category', + title: 'Danh mแปฅc', dataIndex: 'category', + width: 100, key: 'category', render: (category, record) => ( @@ -408,6 +437,94 @@ const ProjectTracker = () => {
+ + { dataSource={projectData} loading={loading} onChange={handleTableChange} - pagination={{ - current: pagination.currentPage, - pageSize: pagination.pageSize, - total: pagination.totalCount, - showSizeChanger: true, - showQuickJumper: true, - showTotal: (total, range) => - `Row Per Page: ${range[1] - range[0] + 1} Entries | Showing ${range[0]} to ${range[1]} of ${total} entries` - }} + pagination={false} /> + + {/* Custom Pagination */} +
{ + e.currentTarget.style.transform = 'translateY(-2px)'; + e.currentTarget.style.boxShadow = '0 12px 40px rgba(0, 0, 0, 0.4), 0 4px 12px rgba(52, 152, 219, 0.2)'; + }} + onMouseLeave={(e) => { + e.currentTarget.style.transform = 'translateY(0)'; + e.currentTarget.style.boxShadow = '0 8px 32px rgba(0, 0, 0, 0.3), 0 2px 8px rgba(52, 152, 219, 0.1)'; + }} + > + {/* Pagination Info */} +
+
+ Row Per Page + + Entries +
+ +
+
+ ๐Ÿ“Š +
+ + Showing {startRecord} to {endRecord} of {totalCount} entries + +
+
+ + {/* Pagination Buttons */} +
+ {/* Numbered Pagination Buttons */} + {Array.from({ length: totalPages }, (_, i) => { + const pageNum = i + 1; + const isActive = currentPage === pageNum; + + return ( + + ); + })} +
+