import { useState, useEffect } from "react"; // ============================================= // Mock API (DO NOT MODIFY) // ============================================= interface User { id: number; name: string; email: string; role: string; } interface PageResult { data: User[]; total: number; page: number; pageSize: number; totalPages: number; } const ALL_USERS: User[] = Array.from({ length: 87 }, (_, i) => ({ id: i + 1, name: `User ${i + 1}`, email: `user${i + 1}@example.com`, role: ["Admin", "Editor", "Viewer"][i % 3], })); async function fetchUsers(page: number, pageSize = 8): Promise<PageResult> { await new Promise((r) => setTimeout(r, 300)); const start = (page - 1) * pageSize; return { data: ALL_USERS.slice(start, start + pageSize), total: ALL_USERS.length, page, pageSize, totalPages: Math.ceil(ALL_USERS.length / pageSize), }; } // ============================================= // TODO: Build a Pagination Component // ============================================= // // Requirements: // 1. Fetch and display a paginated list of users // 2. Show page numbers with Prev / Next buttons // 3. Disable Prev on page 1, Next on last page // 4. Show ellipsis (…) for large ranges: 1 … 4 5 6 … 12 // 5. Always show first and last page numbers // 6. Highlight the current page // TODO: Implement getPageNumbers // e.g. page 6 of 12 → [1, "...", 4, 5, 6, 7, 8, "...", 12] function getPageNumbers(currentPage: number, totalPages: number): (number | "...")[] { // Your code here return Array.from({ length: totalPages }, (_, i) => i + 1); } export default function App() { const [result, setResult] = useState<PageResult | null>(null); const [currentPage, setCurrentPage] = useState(1); const [loading, setLoading] = useState(false); // TODO: Fetch users when currentPage changes // useEffect(() => { ... }, [currentPage]); // TODO: Implement goTo — validate range, then call setCurrentPage const goTo = (page: number) => { // Your code here }; const pages = result ? getPageNumbers(currentPage, result.totalPages) : []; return ( <div style={{ maxWidth: 640, margin: "0 auto", padding: 24, fontFamily: "system-ui" }}> <h2 style={{ fontSize: 20, fontWeight: 700, marginBottom: 4 }}>Pagination</h2> <p style={{ fontSize: 14, color: "#666", marginBottom: 24 }}> Implement <code>getPageNumbers</code> and the <code>useEffect</code> that fetches data. Then wire the Prev / Next / page buttons to <code>goTo</code>. </p> {/* Users table */} <div style={{ border: "1px solid #e5e7eb", borderRadius: 8, overflow: "hidden", marginBottom: 16, }}> {/* Table header */} <div style={{ display: "grid", gridTemplateColumns: "48px 1fr 1fr 80px", padding: "10px 16px", background: "#f9fafb", borderBottom: "1px solid #e5e7eb", fontSize: 12, fontWeight: 600, color: "#666", textTransform: "uppercase", letterSpacing: "0.05em", }}> <span>#</span><span>Name</span><span>Email</span><span>Role</span> </div> {/* Rows */} {loading ? ( <div style={{ padding: "40px 0", textAlign: "center", color: "#999", fontSize: 14 }}> Loading... </div> ) : result?.data.length ? ( result.data.map((user) => ( <div key={user.id} style={{ display: "grid", gridTemplateColumns: "48px 1fr 1fr 80px", padding: "12px 16px", borderBottom: "1px solid #f3f4f6", fontSize: 14, alignItems: "center", }} > <span style={{ color: "#999", fontSize: 12 }}>{user.id}</span> <span style={{ fontWeight: 500 }}>{user.name}</span> <span style={{ color: "#666", fontSize: 13 }}>{user.email}</span> <span style={{ display: "inline-flex", padding: "2px 8px", borderRadius: 4, fontSize: 11, fontWeight: 600, background: user.role === "Admin" ? "#fef3c7" : user.role === "Editor" ? "#dbeafe" : "#f3f4f6", color: user.role === "Admin" ? "#92400e" : user.role === "Editor" ? "#1e40af" : "#374151", }}> {user.role} </span> </div> )) ) : ( <div style={{ padding: "40px 0", textAlign: "center", color: "#999", fontSize: 14 }}> Implement the useEffect to load users </div> )} </div> {/* Footer: count + pagination */} <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", flexWrap: "wrap", gap: 12 }}> <span style={{ fontSize: 13, color: "#666" }}> {result ? `Showing ${(currentPage - 1) * result.pageSize + 1}–${Math.min(currentPage * result.pageSize, result.total)} of ${result.total} users` : "—"} </span> {/* TODO: Render page number buttons from `pages` array */} <div style={{ display: "flex", alignItems: "center", gap: 4 }}> <button onClick={() => goTo(currentPage - 1)} disabled={currentPage === 1} style={{ padding: "6px 12px", borderRadius: 6, border: "1px solid #e5e7eb", background: "#fff", fontSize: 13, cursor: currentPage === 1 ? "not-allowed" : "pointer", color: currentPage === 1 ? "#ccc" : "#111", }} > ← Prev </button> {/* TODO: replace with mapped page number buttons */} <span style={{ padding: "0 8px", fontSize: 13, color: "#666" }}> Page {currentPage} of {result?.totalPages ?? "?"} </span> <button onClick={() => goTo(currentPage + 1)} disabled={!result || currentPage === result.totalPages} style={{ padding: "6px 12px", borderRadius: 6, border: "1px solid #e5e7eb", background: "#fff", fontSize: 13, cursor: (!result || currentPage === result.totalPages) ? "not-allowed" : "pointer", color: (!result || currentPage === result.totalPages) ? "#ccc" : "#111", }} > Next → </button> </div> </div> {/* Hint */} <div style={{ marginTop: 32, padding: 16, background: "#f9fafb", borderLeft: "4px solid #111", borderRadius: "0 8px 8px 0", fontSize: 13, color: "#555", }}> <strong>Steps:</strong> (1) Fill the <code>useEffect</code> — call{" "} <code>fetchUsers(currentPage)</code> and set the result. (2) Implement{" "} <code>getPageNumbers</code> — always include 1 and totalPages, show a ±2 window around currentPage, insert <code>"..."</code> for gaps. (3) Replace the plain page label with mapped page number buttons. </div> </div> ); }