Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
implemented pagination
  • Loading branch information
Swoyam authored and Swoyam committed Oct 11, 2025
commit 2898cfa7fd40553d20fa7e9657149b35c90d2a66
18 changes: 16 additions & 2 deletions src/client/src/components/layout/Body.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
import React from "react";
import ProblemGrid from "../problems/ProblemGrid";
import Pagination from "./Pagination"; // Add this import
import "../../styles/layout/Body.css";

const Body = ({ problems, loading, error, filters }) => {
const Body = ({
problems,
loading,
error,
filters,
currentPage,
totalPages,
onPageChange
}) => {
if (loading) {
return (
<div className="body loading">
Expand All @@ -26,9 +35,14 @@ const Body = ({ problems, loading, error, filters }) => {
<main className="body">
<div className="body-content">
<ProblemGrid problems={problems} filters={filters} />
<Pagination
currentPage={currentPage}
totalPages={totalPages}
onPageChange={onPageChange}
/>
</div>
</main>
);
};

export default Body;
export default Body;
27 changes: 24 additions & 3 deletions src/client/src/components/layout/Main.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,25 @@ const Main = () => {
timePeriod: "",
difficulty: "",
});
const [currentPage, setCurrentPage] = useState(1);
const [totalPages, setTotalPages] = useState(1);

const PROBLEMS_PER_PAGE = 50;

useEffect(() => {
const loadProblems = async () => {
setLoading(true);
try {
const data = await fetchProblems();
const data = await fetchProblems(currentPage, PROBLEMS_PER_PAGE, filters);
setProblems(data);
setError(null);

// Estimate total pages based on response
if (data.length < PROBLEMS_PER_PAGE) {
setTotalPages(currentPage);
} else {
setTotalPages(currentPage + 1);
}
} catch (err) {
setError("Failed to fetch problems. Please try again later.");
setProblems([]);
Expand All @@ -30,7 +41,7 @@ const Main = () => {
};

loadProblems();
}, []);
}, [currentPage, filters]);

// Extract unique company names from problems
const companies = useMemo(() => {
Expand All @@ -47,6 +58,13 @@ const Main = () => {

const handleFilterChange = (newFilters) => {
setFilters(newFilters);
setCurrentPage(1); // Reset to first page when filters change
};

const handlePageChange = (page) => {
setCurrentPage(page);
// Scroll to top when page changes
window.scrollTo({ top: 0, behavior: 'smooth' });
};

return (
Expand All @@ -61,9 +79,12 @@ const Main = () => {
loading={loading}
error={error}
filters={filters}
currentPage={currentPage}
totalPages={totalPages}
onPageChange={handlePageChange}
/>
</div>
);
};

export default Main;
export default Main;
75 changes: 75 additions & 0 deletions src/client/src/components/layout/Pagination.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// components/Pagination.js
import React from "react";
import "../../styles/layout/Pagination.css";

const Pagination = ({ currentPage, totalPages, onPageChange }) => {
const getPageNumbers = () => {
const pages = [];
const maxVisiblePages = 5;

let startPage = Math.max(1, currentPage - Math.floor(maxVisiblePages / 2));
let endPage = Math.min(totalPages, startPage + maxVisiblePages - 1);

// Adjust start page if we're near the end
if (endPage - startPage + 1 < maxVisiblePages) {
startPage = Math.max(1, endPage - maxVisiblePages + 1);
}

for (let i = startPage; i <= endPage; i++) {
pages.push(i);
}

return pages;
};

if (totalPages <= 1) return null;

return (
<div className="pagination">
{/* First Page Button */}
<button
className="pagination-btn pagination-first"
disabled={currentPage === 1}
onClick={() => onPageChange(1)}
title="First Page"
>
&laquo;
</button>

{/* Previous Page Button */}
<button
className="pagination-btn pagination-prev"
disabled={currentPage === 1}
onClick={() => onPageChange(currentPage - 1)}
title="Previous Page"
>
&lsaquo;
</button>

{/* Page Numbers */}
<div className="pagination-pages">
{getPageNumbers().map(page => (
<button
key={page}
className={`pagination-btn ${currentPage === page ? 'active' : ''}`}
onClick={() => onPageChange(page)}
>
{page}
</button>
))}
</div>

{/* Next Page Button */}
<button
className="pagination-btn pagination-next"
disabled={currentPage === totalPages}
onClick={() => onPageChange(currentPage + 1)}
title="Next Page"
>
&rsaquo;
</button>
</div>
);
};

export default Pagination;
24 changes: 21 additions & 3 deletions src/client/src/services/api.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,25 @@
// services/api.js
const API_BASE_URL = "http://localhost:5164/api";

export const fetchProblems = async () => {
const response = await fetch(`${API_BASE_URL}/problems`);
export const fetchProblems = async (page = 1, limit = 50, filters = {}) => {
const skip = (page - 1) * limit;
const params = new URLSearchParams({
skip: skip.toString(),
limit: limit.toString()
});

// Add filter parameters if they exist
if (filters.company) {
params.append('companies', filters.company);
}
if (filters.difficulty) {
params.append('difficulties', filters.difficulty);
}
if (filters.timePeriod) {
params.append('tags', filters.timePeriod);
}

const response = await fetch(`${API_BASE_URL}/problems?${params}`);
if (!response.ok) {
throw new Error(`Network response was not ok: ${response.status}`);
}
Expand All @@ -14,4 +32,4 @@ export const getProblemById = async (id) => {
throw new Error(`Failed to fetch problem #${id}`);
}
return await response.json();
};
};
7 changes: 6 additions & 1 deletion src/client/src/styles/layout/Body.css
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,9 @@

.error-message button:hover {
background-color: #c82333;
}
}

/* Add to styles/layout/Body.css */
.body-content {
min-height: 60vh;
}
94 changes: 94 additions & 0 deletions src/client/src/styles/layout/Pagination.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/* styles/components/Pagination.css */
.pagination {
display: flex;
justify-content: center;
align-items: center;
gap: 0.5rem;
margin: 2rem 0;
padding: 1rem;
}

.pagination-pages {
display: flex;
gap: 0.25rem;
}

.pagination-btn {
padding: 0.5rem 0.75rem;
border: 1px solid #e2e8f0;
background: white;
color: #4a5568;
border-radius: 6px;
cursor: pointer;
transition: all 0.2s ease;
font-weight: 500;
min-width: 2.5rem;
display: flex;
align-items: center;
justify-content: center;
}

.pagination-btn:hover:not(:disabled) {
background: #f7fafc;
border-color: #cbd5e0;
transform: translateY(-1px);
}

.pagination-btn.active {
background: #0066cc;
color: white;
border-color: #0066cc;
}

.pagination-btn:disabled {
opacity: 0.4;
cursor: not-allowed;
transform: none;
}

/* Specific styles for navigation buttons */
.pagination-first,
.pagination-last {
font-weight: bold;
font-size: 1.1rem;
}

.pagination-prev,
.pagination-next {
font-weight: bold;
}

.pagination-info {
text-align: center;
color: #666;
margin-bottom: 1rem;
font-size: 0.9rem;
}

/* Responsive */
@media (max-width: 768px) {
.pagination {
gap: 0.25rem;
}

.pagination-btn {
padding: 0.4rem 0.6rem;
font-size: 0.9rem;
min-width: 2.25rem;
}

.pagination-pages {
gap: 0.125rem;
}

/* Hide page numbers on very small screens, keep only navigation */
@media (max-width: 480px) {
.pagination-pages {
display: none;
}

.pagination {
gap: 0.5rem;
}
}
}