feat(tools): add pagination to tools grid#31
Conversation
|
Someone is attempting to deploy a commit to the jhasourav07's projects Team on Vercel. A member of the Team first needs to authorize it. |
There was a problem hiding this comment.
Pull request overview
Adds client-side pagination to the Home “Tools” grid to keep the UI scalable as more tools are added.
Changes:
- Replaced the static tools grid markup with a paginated, data-driven render (6 tools per page).
- Added page navigation UI (Prev/Next + page numbers with ellipses) and animated page transitions.
- Expanded
.gitignoreto cover env files, caches, coverage, and common OS/editor artifacts.
Reviewed changes
Copilot reviewed 1 out of 5 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| src/pages/Home/components/ToolsGrid.jsx | Implements tools pagination, page-number generation, and animated page transitions. |
| src/components/PaginationControls.jsx | Present in PR but currently empty (0 lines). |
| src/components/ToolCard.jsx | Present in PR but currently empty (0 lines). |
| src/data/toolsData.js | Present in PR but currently empty (0 lines). |
| .gitignore | Adds ignores for env files, caches, coverage, and OS artifacts. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| <button | ||
| key={page} | ||
| onClick={() => handlePageChange(page)} | ||
| className={`px-4 py-2 rounded-lg transition-all duration-300 ${ | ||
| currentPage === page | ||
| ? 'bg-white text-black font-semibold shadow-lg scale-105' | ||
| : 'bg-zinc-800 text-zinc-300 hover:bg-zinc-700 hover:scale-105' | ||
| }`} | ||
| > | ||
| {page} | ||
| </button> |
There was a problem hiding this comment.
For screen readers, the active page isn’t currently indicated beyond styling. Consider adding aria-current="page" (or an equivalent accessible cue) to the active page button so assistive tech can announce the current page.
| const getPageNumbers = () => { | ||
| const pages = []; | ||
| const maxVisible = 5; | ||
|
|
||
| if (totalPages <= maxVisible) { | ||
| for (let i = 1; i <= totalPages; i++) { | ||
| pages.push(i); | ||
| } | ||
| } else { | ||
| if (currentPage <= 3) { | ||
| for (let i = 1; i <= 4; i++) pages.push(i); | ||
| pages.push('...'); | ||
| pages.push(totalPages); | ||
| } else if (currentPage >= totalPages - 2) { |
There was a problem hiding this comment.
maxVisible suggests an upper bound on the number of pagination items shown, but the branches below can render 6–7 items (including ellipses). Consider renaming it to reflect its actual role (threshold for switching to the ellipsis layout) or adjusting the logic to honor the limit.
| const [currentPage, setCurrentPage] = useState(1); | ||
| const toolsPerPage = 6; // Show 6 cards per page | ||
|
|
||
| // All tools data | ||
| const allTools = [ | ||
| { | ||
| id: 1, | ||
| name: "Merge PDF", | ||
| description: "Combine multiple PDFs into a single document in milliseconds. Drag, drop, and organize securely.", | ||
| icon: Layers, | ||
| path: "/merge", | ||
| buttonText: "Open Merge Tool", | ||
| iconSize: "w-6 h-6" | ||
| }, |
There was a problem hiding this comment.
allTools is defined inside the component, so the entire tools list object graph is recreated on every render and tightly couples the grid UI to the data. Consider moving the tools list to a module-level constant (or importing from src/data/toolsData.js if that’s meant to be the canonical source) so the pagination logic operates on shared data and avoids duplication across the app.
| <motion.div | ||
| key={tool.id} | ||
| variants={cardVariants} | ||
| custom={idx} |
There was a problem hiding this comment.
custom={idx} is passed to the card motion.div, but cardVariants is a static object and doesn’t use the custom value. Either remove custom or convert the variant to a function (e.g., to apply per-card delay) so the prop has an effect.
| custom={idx} |
| <div className="flex justify-center items-center gap-2"> | ||
| {/* Previous Button */} | ||
| <button | ||
| onClick={() => handlePageChange(currentPage - 1)} | ||
| disabled={currentPage === 1} | ||
| className={`flex items-center gap-2 px-4 py-2 rounded-lg transition-all duration-300 ${ | ||
| currentPage === 1 | ||
| ? 'bg-zinc-800 text-zinc-500 cursor-not-allowed opacity-50' | ||
| : 'bg-zinc-800 text-white hover:bg-zinc-700 hover:scale-105 active:scale-95' | ||
| }`} | ||
| > | ||
| ← Previous | ||
| </button> | ||
|
|
||
| {/* 11. Lock PDF Card */} | ||
| <motion.div | ||
| variants={cardVariants} | ||
| initial="hidden" | ||
| whileInView="visible" | ||
| viewport={{ once: true, margin: "-50px" }} | ||
| transition={{ delay: 0.4 }} | ||
| > | ||
| <Link | ||
| to="/lock-pdf" | ||
| className="group flex flex-col p-8 bg-[#0a0a0a] border border-white/10 rounded-3xl hover:border-white/30 hover:bg-white/[0.02] hover:shadow-[0_0_40px_rgba(255,255,255,0.03)] transition-all duration-500 text-left h-full relative overflow-hidden" | ||
| > | ||
| <div className="absolute inset-0 bg-gradient-to-br from-white/[0.02] to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-500" /> | ||
| <div className="relative z-10 w-14 h-14 border border-white/10 bg-zinc-900 text-white rounded-2xl flex items-center justify-center mb-8 group-hover:scale-110 group-hover:bg-white group-hover:text-black transition-all duration-500"> | ||
| <Lock className="w-6 h-6" /> | ||
| </div> | ||
| <h2 className="relative z-10 text-2xl font-semibold text-white mb-3 tracking-tight"> | ||
| Lock PDF | ||
| </h2> | ||
| <p className="relative z-10 text-zinc-400 mb-8 font-light flex-grow leading-relaxed"> | ||
| Password-protect your PDF with RC4 encryption. Processed entirely in your browser — nothing is uploaded. | ||
| </p> | ||
| <div className="relative z-10 flex items-center text-sm font-medium text-white group-hover:translate-x-2 transition-transform duration-300"> | ||
| Open Lock PDF <span className="ml-2">→</span> | ||
| </div> | ||
| </Link> | ||
| </motion.div> | ||
| {/* Page Numbers */} | ||
| <div className="flex gap-2 mx-2"> | ||
| {getPageNumbers().map((page, index) => ( |
There was a problem hiding this comment.
The pagination controls are a single non-wrapping flex row; with the current padding and labels ("← Previous", 5+ page buttons, "Next →"), this is likely to overflow on small screens. Consider adding flex-wrap (and/or responsive padding/shorter labels on mobile) or using horizontal scrolling to keep controls usable on narrow viewports.
|
Thanks for the contribution. |
Closes #7
✨ Feature: Tools Pagination
Implemented pagination for the tools grid.
✅ Changes
🎯 Result
Improves UI clarity and scalability.
📸 Screenshots