From 9e859169ecbdbdbaedbaa328e058d97d8f49848a Mon Sep 17 00:00:00 2001 From: Your Name Date: Sun, 19 Apr 2026 14:49:08 +0530 Subject: [PATCH 1/5] feat(tools): add pagination to tools grid --- .gitignore | 22 + src/components/PaginationControls.jsx | 0 src/components/ToolCard.jsx | 0 src/data/toolsData.js | 0 src/pages/Home/components/ToolsGrid.jsx | 608 +++++++++++------------- 5 files changed, 296 insertions(+), 334 deletions(-) create mode 100644 src/components/PaginationControls.jsx create mode 100644 src/components/ToolCard.jsx create mode 100644 src/data/toolsData.js diff --git a/.gitignore b/.gitignore index a547bf3..578bac1 100644 --- a/.gitignore +++ b/.gitignore @@ -7,16 +7,38 @@ yarn-error.log* pnpm-debug.log* lerna-debug.log* +# Dependencies node_modules + +# Build output dist dist-ssr + +# Environment files +.env +.env.* +!.env.example +!.env.sample +!.env.template + +# Local overrides *.local +# Cache and temp files +.cache +.temp +tmp +temp +.eslintcache +.vite +coverage + # Editor directories and files .vscode/* !.vscode/extensions.json .idea .DS_Store +Thumbs.db *.suo *.ntvs* *.njsproj diff --git a/src/components/PaginationControls.jsx b/src/components/PaginationControls.jsx new file mode 100644 index 0000000..e69de29 diff --git a/src/components/ToolCard.jsx b/src/components/ToolCard.jsx new file mode 100644 index 0000000..e69de29 diff --git a/src/data/toolsData.js b/src/data/toolsData.js new file mode 100644 index 0000000..e69de29 diff --git a/src/pages/Home/components/ToolsGrid.jsx b/src/pages/Home/components/ToolsGrid.jsx index be8a2c4..4981cfb 100644 --- a/src/pages/Home/components/ToolsGrid.jsx +++ b/src/pages/Home/components/ToolsGrid.jsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useState } from "react"; import { Link } from "react-router-dom"; import { Layers, @@ -14,9 +14,139 @@ import { Lock, FileEdit, } from "lucide-react"; -import { motion } from "framer-motion"; +import { motion, AnimatePresence } from "framer-motion"; export function ToolsGrid() { + 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" + }, + { + id: 2, + name: "Split PDF", + description: "Extract specific pages or break a massive document down into smaller files instantly.", + icon: SplitSquareHorizontal, + path: "/split", + buttonText: "Open Split Tool", + iconSize: "w-6 h-6" + }, + { + id: 3, + name: "Add Watermark", + description: "Stamp custom text diagonally across your documents. Perfect for sensitive drafts and contracts.", + icon: Stamp, + path: "/watermark", + buttonText: "Open Watermark Tool", + iconSize: "w-6 h-6" + }, + { + id: 4, + name: "Image to PDF", + description: "Convert JPG and PNG images into a high-quality PDF document. Drag to reorder your pages.", + icon: ImageIcon, + path: "/image-to-pdf", + buttonText: "Open Image to PDF", + iconSize: "w-6 h-6" + }, + { + id: 5, + name: "Compress PDF", + description: "Reduce file size while maintaining visual quality.", + icon: Minimize2, + path: "/compress", + buttonText: "Open Compress PDF", + iconSize: "w-6 h-6" + }, + { + id: 6, + name: "Rotate PDF", + description: "Rotate pages in your PDF document.", + icon: RefreshCw, + path: "/rotate", + buttonText: "Open Rotate PDF", + iconSize: "w-10 h-10" + }, + { + id: 7, + name: "Organize PDF", + description: "Organize pages in your PDF document.", + icon: LayoutGrid, + path: "/organize", + buttonText: "Open Organize PDF", + iconSize: "w-10 h-10" + }, + { + id: 8, + name: "PDF to Images", + description: "Extract images from your PDF document.", + icon: Images, + path: "/pdf-to-image", + buttonText: "Open PDF to Images", + iconSize: "w-6 h-6" + }, + { + id: 9, + name: "Grayscale PDF", + description: "Convert your PDF to grayscale.", + icon: Contrast, + path: "/grayscale", + buttonText: "Open Grayscale PDF", + iconSize: "w-6 h-6" + }, + { + id: 10, + name: "Page Numbers", + description: "Auto-stamp sequential numbers on every page footer. Choose position, prefix, and start number.", + icon: Hash, + path: "/page-numbers", + buttonText: "Open Page Numbers", + iconSize: "w-6 h-6" + }, + { + id: 11, + name: "Lock PDF", + description: "Password-protect your PDF with RC4 encryption. Processed entirely in your browser — nothing is uploaded.", + icon: Lock, + path: "/lock-pdf", + buttonText: "Open Lock PDF", + iconSize: "w-6 h-6" + }, + { + id: 12, + name: "Edit PDF", + description: "Draw, highlight, add text, and annotate your PDF pages directly in the browser.", + icon: FileEdit, + path: "/edit-pdf", + buttonText: "Open PDF Editor", + iconSize: "w-6 h-6" + } + ]; + + // Calculate pagination + const totalTools = allTools.length; + const totalPages = Math.ceil(totalTools / toolsPerPage); + const indexOfLastTool = currentPage * toolsPerPage; + const indexOfFirstTool = indexOfLastTool - toolsPerPage; + const currentTools = allTools.slice(indexOfFirstTool, indexOfLastTool); + + // Handle page change + const handlePageChange = (pageNumber) => { + setCurrentPage(pageNumber); + // Smooth scroll to top when page changes + window.scrollTo({ top: 0, behavior: 'smooth' }); + }; + + // Card variants for animation const cardVariants = { hidden: { opacity: 0, y: 40 }, visible: { @@ -26,346 +156,156 @@ export function ToolsGrid() { }, }; - return ( -
- {/* 1. Merge Card */} - - -
-
- -
-

- Merge PDF -

-

- Combine multiple PDFs into a single document in milliseconds. Drag, - drop, and organize securely. -

-
- Open Merge Tool -
- - - - {/* 2. Split Card */} - - -
-
- -
-

- Split PDF -

-

- Extract specific pages or break a massive document down into smaller - files instantly. -

-
- Open Split Tool -
- - - - {/* 3. Watermark Card */} - - -
-
- -
-

- Add Watermark -

-

- Stamp custom text diagonally across your documents. Perfect for - sensitive drafts and contracts. -

-
- Open Watermark Tool -
- - - - {/* 4. Image to PDF Card */} - - -
-
- -
-

- Image to PDF -

-

- Convert JPG and PNG images into a high-quality PDF document. Drag to - reorder your pages. -

-
- Open Image to PDF -
- - - - {/* 5. Compress PDF Card */} - - -
-
- -
-

- Compress PDF -

-

- Reduce file size while maintaining visual quality. -

-
- Open Compress PDF -
- - - - {/* 6. Rotate PDF Card */} - - -
-
- -
-

- Rotate PDF -

-

- Rotate pages in your PDF document. -

-
- Open Rotate PDF -
- - + // Grid container variants for stagger animation + const containerVariants = { + hidden: { opacity: 0 }, + visible: { + opacity: 1, + transition: { + staggerChildren: 0.1, + delayChildren: 0.1 + } + }, + exit: { + opacity: 0, + transition: { duration: 0.2 } + } + }; - {/* 7. Organize PDF Card */} - - -
-
- -
-

- Organize PDF -

-

- Organize pages in your PDF document. -

-
- Open Organize PDF -
- - + // Generate page numbers to display + 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) { + pages.push(1); + pages.push('...'); + for (let i = totalPages - 3; i <= totalPages; i++) pages.push(i); + } else { + pages.push(1); + pages.push('...'); + for (let i = currentPage - 1; i <= currentPage + 1; i++) pages.push(i); + pages.push('...'); + pages.push(totalPages); + } + } + return pages; + }; - {/* 8. PDF to Images Card */} - - + {/* Tools Grid with Animation */} + + -
-
- , -
-

- PDF to Images -

-

- Extract images from your PDF document. -

-
- Open PDF to Images -
- - + {currentTools.map((tool, idx) => { + const IconComponent = tool.icon; + return ( + + +
+
+ +
+

+ {tool.name} +

+

+ {tool.description} +

+
+ {tool.buttonText} +
+ + + ); + })} + + - {/* 9. Grayscale Card */} - - -
-
- + {/* Pagination Controls - Only show if more than 1 page */} + {totalPages > 1 && ( + <> + {/* Info text */} +
+ Showing {indexOfFirstTool + 1}-{Math.min(indexOfLastTool, totalTools)} of {totalTools} tools
-

- Grayscale PDF -

-

- Convert your PDF to grayscale. -

-
- Open Grayscale PDF -
- - - {/* 10. Page Numbers Card */} - - -
-
- -
-

- Page Numbers -

-

- Auto-stamp sequential numbers on every page footer. Choose position, prefix, and start number. -

-
- Open Page Numbers -
- - + {/* Pagination Buttons */} +
+ {/* Previous Button */} + - {/* 11. Lock PDF Card */} - - -
-
- -
-

- Lock PDF -

-

- Password-protect your PDF with RC4 encryption. Processed entirely in your browser — nothing is uploaded. -

-
- Open Lock PDF -
- - + {/* Page Numbers */} +
+ {getPageNumbers().map((page, index) => ( + page === '...' ? ( + + ... + + ) : ( + + ) + ))} +
- {/* 12. Edit PDF Card */} - - -
-
- -
-

- Edit PDF -

-

- Draw, highlight, add text, and annotate your PDF pages directly in the browser. -

-
- Open PDF Editor + {/* Next Button */} +
- - + + )}
); -} +} \ No newline at end of file From 291b4e82c2eea7b02fe5ffee3ad0b2e92bdcf5b2 Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 20 Apr 2026 15:27:18 +0530 Subject: [PATCH 2/5] fix(pagination): improve accessibility and structure --- src/data/toolsData.js | 132 ++++++++++ src/pages/Home/components/ToolsGrid.jsx | 310 ++++++++---------------- 2 files changed, 232 insertions(+), 210 deletions(-) diff --git a/src/data/toolsData.js b/src/data/toolsData.js index e69de29..d3a1608 100644 --- a/src/data/toolsData.js +++ b/src/data/toolsData.js @@ -0,0 +1,132 @@ +import { + Contrast, + FileEdit, + Hash, + Image as ImageIcon, + Images, + Layers, + LayoutGrid, + Lock, + Minimize2, + RefreshCw, + SplitSquareHorizontal, + Stamp, +} from "lucide-react"; + +export const ALL_TOOLS = [ + { + 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", + }, + { + id: 2, + name: "Split PDF", + description: + "Extract specific pages or break a massive document down into smaller files instantly.", + icon: SplitSquareHorizontal, + path: "/split", + buttonText: "Open Split Tool", + iconSize: "w-6 h-6", + }, + { + id: 3, + name: "Add Watermark", + description: + "Stamp custom text diagonally across your documents. Perfect for sensitive drafts and contracts.", + icon: Stamp, + path: "/watermark", + buttonText: "Open Watermark Tool", + iconSize: "w-6 h-6", + }, + { + id: 4, + name: "Image to PDF", + description: + "Convert JPG and PNG images into a high-quality PDF document. Drag to reorder your pages.", + icon: ImageIcon, + path: "/image-to-pdf", + buttonText: "Open Image to PDF", + iconSize: "w-6 h-6", + }, + { + id: 5, + name: "Compress PDF", + description: "Reduce file size while maintaining visual quality.", + icon: Minimize2, + path: "/compress", + buttonText: "Open Compress PDF", + iconSize: "w-6 h-6", + }, + { + id: 6, + name: "Rotate PDF", + description: "Rotate pages in your PDF document.", + icon: RefreshCw, + path: "/rotate", + buttonText: "Open Rotate PDF", + iconSize: "w-10 h-10", + }, + { + id: 7, + name: "Organize PDF", + description: "Organize pages in your PDF document.", + icon: LayoutGrid, + path: "/organize", + buttonText: "Open Organize PDF", + iconSize: "w-10 h-10", + }, + { + id: 8, + name: "PDF to Images", + description: "Extract images from your PDF document.", + icon: Images, + path: "/pdf-to-image", + buttonText: "Open PDF to Images", + iconSize: "w-6 h-6", + }, + { + id: 9, + name: "Grayscale PDF", + description: "Convert your PDF to grayscale.", + icon: Contrast, + path: "/grayscale", + buttonText: "Open Grayscale PDF", + iconSize: "w-6 h-6", + }, + { + id: 10, + name: "Page Numbers", + description: + "Auto-stamp sequential numbers on every page footer. Choose position, prefix, and start number.", + icon: Hash, + path: "/page-numbers", + buttonText: "Open Page Numbers", + iconSize: "w-6 h-6", + }, + { + id: 11, + name: "Lock PDF", + description: + "Password-protect your PDF with RC4 encryption. Processed entirely in your browser - nothing is uploaded.", + icon: Lock, + path: "/lock-pdf", + buttonText: "Open Lock PDF", + iconSize: "w-6 h-6", + }, + { + id: 12, + name: "Edit PDF", + description: + "Draw, highlight, add text, and annotate your PDF pages directly in the browser.", + icon: FileEdit, + path: "/edit-pdf", + buttonText: "Open PDF Editor", + iconSize: "w-6 h-6", + }, +]; diff --git a/src/pages/Home/components/ToolsGrid.jsx b/src/pages/Home/components/ToolsGrid.jsx index 4981cfb..134dcad 100644 --- a/src/pages/Home/components/ToolsGrid.jsx +++ b/src/pages/Home/components/ToolsGrid.jsx @@ -1,277 +1,164 @@ import React, { useState } from "react"; import { Link } from "react-router-dom"; -import { - Layers, - SplitSquareHorizontal, - Stamp, - Image as ImageIcon, - Minimize2, - RefreshCw, - LayoutGrid, - Images, - Contrast, - Hash, - Lock, - FileEdit, -} from "lucide-react"; -import { motion, AnimatePresence } from "framer-motion"; +import { motion as Motion, AnimatePresence } from "framer-motion"; +import { ALL_TOOLS } from "../../../data/toolsData"; export function ToolsGrid() { const [currentPage, setCurrentPage] = useState(1); - const toolsPerPage = 6; // Show 6 cards per page + const toolsPerPage = 6; - // 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" - }, - { - id: 2, - name: "Split PDF", - description: "Extract specific pages or break a massive document down into smaller files instantly.", - icon: SplitSquareHorizontal, - path: "/split", - buttonText: "Open Split Tool", - iconSize: "w-6 h-6" - }, - { - id: 3, - name: "Add Watermark", - description: "Stamp custom text diagonally across your documents. Perfect for sensitive drafts and contracts.", - icon: Stamp, - path: "/watermark", - buttonText: "Open Watermark Tool", - iconSize: "w-6 h-6" - }, - { - id: 4, - name: "Image to PDF", - description: "Convert JPG and PNG images into a high-quality PDF document. Drag to reorder your pages.", - icon: ImageIcon, - path: "/image-to-pdf", - buttonText: "Open Image to PDF", - iconSize: "w-6 h-6" - }, - { - id: 5, - name: "Compress PDF", - description: "Reduce file size while maintaining visual quality.", - icon: Minimize2, - path: "/compress", - buttonText: "Open Compress PDF", - iconSize: "w-6 h-6" - }, - { - id: 6, - name: "Rotate PDF", - description: "Rotate pages in your PDF document.", - icon: RefreshCw, - path: "/rotate", - buttonText: "Open Rotate PDF", - iconSize: "w-10 h-10" - }, - { - id: 7, - name: "Organize PDF", - description: "Organize pages in your PDF document.", - icon: LayoutGrid, - path: "/organize", - buttonText: "Open Organize PDF", - iconSize: "w-10 h-10" - }, - { - id: 8, - name: "PDF to Images", - description: "Extract images from your PDF document.", - icon: Images, - path: "/pdf-to-image", - buttonText: "Open PDF to Images", - iconSize: "w-6 h-6" - }, - { - id: 9, - name: "Grayscale PDF", - description: "Convert your PDF to grayscale.", - icon: Contrast, - path: "/grayscale", - buttonText: "Open Grayscale PDF", - iconSize: "w-6 h-6" - }, - { - id: 10, - name: "Page Numbers", - description: "Auto-stamp sequential numbers on every page footer. Choose position, prefix, and start number.", - icon: Hash, - path: "/page-numbers", - buttonText: "Open Page Numbers", - iconSize: "w-6 h-6" - }, - { - id: 11, - name: "Lock PDF", - description: "Password-protect your PDF with RC4 encryption. Processed entirely in your browser — nothing is uploaded.", - icon: Lock, - path: "/lock-pdf", - buttonText: "Open Lock PDF", - iconSize: "w-6 h-6" - }, - { - id: 12, - name: "Edit PDF", - description: "Draw, highlight, add text, and annotate your PDF pages directly in the browser.", - icon: FileEdit, - path: "/edit-pdf", - buttonText: "Open PDF Editor", - iconSize: "w-6 h-6" - } - ]; - - // Calculate pagination - const totalTools = allTools.length; + const totalTools = ALL_TOOLS.length; const totalPages = Math.ceil(totalTools / toolsPerPage); const indexOfLastTool = currentPage * toolsPerPage; const indexOfFirstTool = indexOfLastTool - toolsPerPage; - const currentTools = allTools.slice(indexOfFirstTool, indexOfLastTool); + const currentTools = ALL_TOOLS.slice(indexOfFirstTool, indexOfLastTool); - // Handle page change const handlePageChange = (pageNumber) => { - setCurrentPage(pageNumber); - // Smooth scroll to top when page changes - window.scrollTo({ top: 0, behavior: 'smooth' }); + const nextPage = Math.min(Math.max(pageNumber, 1), totalPages); + + if (nextPage === currentPage) { + return; + } + + setCurrentPage(nextPage); + window.scrollTo({ top: 0, behavior: "smooth" }); }; - // Card variants for animation const cardVariants = { hidden: { opacity: 0, y: 40 }, - visible: { + visible: (idx) => ({ opacity: 1, y: 0, - transition: { type: "spring", stiffness: 200, damping: 20 }, - }, + transition: { + type: "spring", + stiffness: 200, + damping: 20, + delay: idx * 0.06, + }, + }), }; - // Grid container variants for stagger animation const containerVariants = { hidden: { opacity: 0 }, visible: { opacity: 1, - transition: { - staggerChildren: 0.1, - delayChildren: 0.1 - } + transition: { duration: 0.2 }, }, exit: { opacity: 0, - transition: { duration: 0.2 } - } + transition: { duration: 0.2 }, + }, }; - // Generate page numbers to display const getPageNumbers = () => { const pages = []; - const maxVisible = 5; - - if (totalPages <= maxVisible) { - for (let i = 1; i <= totalPages; i++) { + const ellipsisThreshold = 5; + + if (totalPages <= ellipsisThreshold) { + for (let i = 1; i <= totalPages; i += 1) { + pages.push(i); + } + + return pages; + } + + if (currentPage <= 3) { + for (let i = 1; i <= 4; i += 1) { 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) { - pages.push(1); - pages.push('...'); - for (let i = totalPages - 3; i <= totalPages; i++) pages.push(i); - } else { - pages.push(1); - pages.push('...'); - for (let i = currentPage - 1; i <= currentPage + 1; i++) pages.push(i); - pages.push('...'); - pages.push(totalPages); + pages.push("..."); + pages.push(totalPages); + return pages; + } + + if (currentPage >= totalPages - 2) { + pages.push(1); + pages.push("..."); + for (let i = totalPages - 3; i <= totalPages; i += 1) { + pages.push(i); } + return pages; } + + pages.push(1); + pages.push("..."); + for (let i = currentPage - 1; i <= currentPage + 1; i += 1) { + pages.push(i); + } + pages.push("..."); + pages.push(totalPages); + return pages; }; return (
- {/* Tools Grid with Animation */} - {currentTools.map((tool, idx) => { const IconComponent = tool.icon; + return ( - + -
-
+
+
-

+

{tool.name}

-

+

{tool.description}

-
- {tool.buttonText} +
+ {tool.buttonText} {"->"}
- + ); })} - + - {/* Pagination Controls - Only show if more than 1 page */} {totalPages > 1 && ( <> - {/* Info text */} -
- Showing {indexOfFirstTool + 1}-{Math.min(indexOfLastTool, totalTools)} of {totalTools} tools +
+ Showing {indexOfFirstTool + 1}-{Math.min(indexOfLastTool, totalTools)} of{" "} + {totalTools} tools
- {/* Pagination Buttons */} -
- {/* Previous Button */} + )}
); -} \ No newline at end of file +} From 7f449860275a0369952cbf77f3f5dea93d6235c3 Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 21 Apr 2026 16:16:58 +0530 Subject: [PATCH 3/5] fix(tools): update tools pagination --- src/pages/Home/components/ToolsGrid.jsx | 384 +++++++++++++++--------- 1 file changed, 240 insertions(+), 144 deletions(-) diff --git a/src/pages/Home/components/ToolsGrid.jsx b/src/pages/Home/components/ToolsGrid.jsx index 134dcad..bedb763 100644 --- a/src/pages/Home/components/ToolsGrid.jsx +++ b/src/pages/Home/components/ToolsGrid.jsx @@ -1,201 +1,297 @@ import React, { useState } from "react"; import { Link } from "react-router-dom"; -import { motion as Motion, AnimatePresence } from "framer-motion"; -import { ALL_TOOLS } from "../../../data/toolsData"; +import { motion as Motion } from 'framer-motion'; + +import { + Layers, + SplitSquareHorizontal, + Stamp, + Image as ImageIcon, + Minimize2, + RefreshCw, + LayoutGrid, + Images, + Contrast, + Hash, + Lock, + FileEdit, +} from "lucide-react"; + +// FIX #1: Move tools data outside component to avoid recreation on every render +const TOOLS_DATA = [ + { + 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", + delay: 0, + }, + { + id: 2, + name: "Split PDF", + description: "Extract specific pages or break a massive document down into smaller files instantly.", + icon: SplitSquareHorizontal, + path: "/split", + buttonText: "Open Split Tool", + iconSize: "w-6 h-6", + delay: 0.1, + }, + { + id: 3, + name: "Add Watermark", + description: "Stamp custom text diagonally across your documents. Perfect for sensitive drafts and contracts.", + icon: Stamp, + path: "/watermark", + buttonText: "Open Watermark Tool", + iconSize: "w-6 h-6", + delay: 0.2, + }, + { + id: 4, + name: "Image to PDF", + description: "Convert JPG and PNG images into a high-quality PDF document. Drag to reorder your pages.", + icon: ImageIcon, + path: "/image-to-pdf", + buttonText: "Open Image to PDF", + iconSize: "w-6 h-6", + delay: 0.3, + }, + { + id: 5, + name: "Compress PDF", + description: "Reduce file size while maintaining visual quality.", + icon: Minimize2, + path: "/compress", + buttonText: "Open Compress PDF", + iconSize: "w-6 h-6", + delay: 0.3, + }, + { + id: 6, + name: "Rotate PDF", + description: "Rotate pages in your PDF document.", + icon: RefreshCw, + path: "/rotate", + buttonText: "Open Rotate PDF", + iconSize: "w-10 h-10", + delay: 0.3, + }, + { + id: 7, + name: "Organize PDF", + description: "Organize pages in your PDF document.", + icon: LayoutGrid, + path: "/organize", + buttonText: "Open Organize PDF", + iconSize: "w-10 h-10", + delay: 0.3, + }, + { + id: 8, + name: "PDF to Images", + description: "Extract images from your PDF document.", + icon: Images, + path: "/pdf-to-image", + buttonText: "Open PDF to Images", + iconSize: "w-6 h-6", + delay: 0.3, + }, + { + id: 9, + name: "Grayscale PDF", + description: "Convert your PDF to grayscale.", + icon: Contrast, + path: "/grayscale", + buttonText: "Open Grayscale PDF", + iconSize: "w-6 h-6", + delay: 0.3, + }, + { + id: 10, + name: "Page Numbers", + description: "Auto-stamp sequential numbers on every page footer. Choose position, prefix, and start number.", + icon: Hash, + path: "/page-numbers", + buttonText: "Open Page Numbers", + iconSize: "w-6 h-6", + delay: 0.4, + }, + { + id: 11, + name: "Lock PDF", + description: "Password-protect your PDF with RC4 encryption. Processed entirely in your browser — nothing is uploaded.", + icon: Lock, + path: "/lock-pdf", + buttonText: "Open Lock PDF", + iconSize: "w-6 h-6", + delay: 0.4, + }, + { + id: 12, + name: "Edit PDF", + description: "Draw, highlight, add text, and annotate your PDF pages directly in the browser.", + icon: FileEdit, + path: "/edit-pdf", + buttonText: "Open PDF Editor", + iconSize: "w-6 h-6", + delay: 0.4, + }, +]; export function ToolsGrid() { const [currentPage, setCurrentPage] = useState(1); const toolsPerPage = 6; + const totalPages = Math.ceil(TOOLS_DATA.length / toolsPerPage); - const totalTools = ALL_TOOLS.length; - const totalPages = Math.ceil(totalTools / toolsPerPage); - const indexOfLastTool = currentPage * toolsPerPage; - const indexOfFirstTool = indexOfLastTool - toolsPerPage; - const currentTools = ALL_TOOLS.slice(indexOfFirstTool, indexOfLastTool); - - const handlePageChange = (pageNumber) => { - const nextPage = Math.min(Math.max(pageNumber, 1), totalPages); - - if (nextPage === currentPage) { - return; - } - - setCurrentPage(nextPage); - window.scrollTo({ top: 0, behavior: "smooth" }); - }; - - const cardVariants = { + // FIX #4: Convert cardVariants to a function to use the custom prop for per-card delays + const cardVariants = (delay) => ({ hidden: { opacity: 0, y: 40 }, - visible: (idx) => ({ - opacity: 1, - y: 0, - transition: { - type: "spring", - stiffness: 200, - damping: 20, - delay: idx * 0.06, - }, - }), - }; - - const containerVariants = { - hidden: { opacity: 0 }, visible: { opacity: 1, - transition: { duration: 0.2 }, - }, - exit: { - opacity: 0, - transition: { duration: 0.2 }, + y: 0, + transition: { type: "spring", stiffness: 200, damping: 20, delay }, }, - }; + }); + // FIX #3: Rename maxVisible to switchThreshold to clarify its purpose const getPageNumbers = () => { const pages = []; - const ellipsisThreshold = 5; - - if (totalPages <= ellipsisThreshold) { - for (let i = 1; i <= totalPages; i += 1) { - pages.push(i); - } + const switchThreshold = 5; // Threshold for switching to ellipsis layout - return pages; - } - - if (currentPage <= 3) { - for (let i = 1; i <= 4; i += 1) { + if (totalPages <= switchThreshold) { + for (let i = 1; i <= totalPages; i++) { pages.push(i); } - pages.push("..."); - pages.push(totalPages); - return pages; - } - - if (currentPage >= totalPages - 2) { - pages.push(1); - pages.push("..."); - for (let i = totalPages - 3; i <= totalPages; i += 1) { - 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) { + pages.push(1); + pages.push('...'); + for (let i = totalPages - 3; i <= totalPages; i++) pages.push(i); + } else { + pages.push(1); + pages.push('...'); + for (let i = currentPage - 1; i <= currentPage + 1; i++) pages.push(i); + pages.push('...'); + pages.push(totalPages); } - return pages; } - - pages.push(1); - pages.push("..."); - for (let i = currentPage - 1; i <= currentPage + 1; i += 1) { - pages.push(i); - } - pages.push("..."); - pages.push(totalPages); - return pages; }; - return ( -
- - - {currentTools.map((tool, idx) => { - const IconComponent = tool.icon; + const handlePageChange = (page) => { + setCurrentPage(page); + window.scrollTo({ top: 0, behavior: 'smooth' }); + }; - return ( - - -
-
- -
-

- {tool.name} -

-

- {tool.description} -

-
- {tool.buttonText} {"->"} -
- - - ); - })} - - + const startIdx = (currentPage - 1) * toolsPerPage; + const endIdx = startIdx + toolsPerPage; + const currentTools = TOOLS_DATA.slice(startIdx, endIdx); - {totalPages > 1 && ( - <> -
- Showing {indexOfFirstTool + 1}-{Math.min(indexOfLastTool, totalTools)} of{" "} - {totalTools} tools -
+ return ( +
+ {/* Tools Grid */} +
+ {currentTools.map((tool, idx) => { + const IconComponent = tool.icon; + return ( + + +
+
+ +
+

+ {tool.name} +

+

+ {tool.description} +

+
+ {tool.buttonText} +
+ + + ); + })} +
- - +
+ + {/* Page Info */} +
+ Page {currentPage} of {totalPages} +
+
)}
); -} +} \ No newline at end of file From cf485a2d458c053d1ddad711813eae70f4ac437e Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 21 Apr 2026 16:35:18 +0530 Subject: [PATCH 4/5] fix(tools): resolve pagination review comments --- src/pages/Home/components/ToolsGrid.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/Home/components/ToolsGrid.jsx b/src/pages/Home/components/ToolsGrid.jsx index bedb763..43172e6 100644 --- a/src/pages/Home/components/ToolsGrid.jsx +++ b/src/pages/Home/components/ToolsGrid.jsx @@ -198,7 +198,7 @@ export function ToolsGrid() {
{/* Tools Grid */}
- {currentTools.map((tool, idx) => { + {currentTools.map((tool) => { const IconComponent = tool.icon; return ( ); -} \ No newline at end of file +} From 4c76bacffc5f5485f63dafe158b5edd8d851d8b7 Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 21 Apr 2026 20:33:52 +0530 Subject: [PATCH 5/5] fix: resolve merge conflicts --- src/pages/Rotate/Rotate.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/Rotate/Rotate.jsx b/src/pages/Rotate/Rotate.jsx index 14932e6..f7caf62 100644 --- a/src/pages/Rotate/Rotate.jsx +++ b/src/pages/Rotate/Rotate.jsx @@ -43,7 +43,7 @@ function usePdfPages(file) { })(); return () => { cancelled = true; }; - }, [file]); // eslint-disable-line + }, [file]); async function loadBatch(pdf, from, to, cancelled) { setIsLoading(true); @@ -76,7 +76,7 @@ function usePdfPages(file) { if (from > pdf.numPages) return; const to = Math.min(from + PAGE_BATCH - 1, pdf.numPages); loadBatch(pdf, from, to, false); - }, [isLoading]); // eslint-disable-line + }, [isLoading]); const hasMore = loadedRef.current < totalCount;