diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 2a90430..e94cb4c 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,19 +1,25 @@ +import { lazy, Suspense } from "react"; import { Route, Routes } from "react-router"; import LoginPage from "@/pages/LoginPage"; import NotFoundPage from "@/pages/NotFoundPage"; import SessionsPage from "@/pages/SessionsPage"; import TeamsPage from "@/pages/TeamsPage"; -import UITestPage from "@/pages/UITestPage"; import UsersPage from "@/pages/UsersPage"; import WorkspacePage from "@/pages/WorkspacePage"; import LandingRedirect from "@/components/auth/LandingRedirect"; import RequireAuth from "@/components/auth/RequireAuth"; import NoticeModal from "@/components/elements/NoticeModal"; +import ToastContainer from "@/components/elements/ToastContainer"; import AppLayout from "@/components/layout/AppLayout"; -import ToastContainer from "@/components/toast/ToastContainer"; import { PATH_LIST } from "@/constants/commonConstants"; +/* Dev-only UI showcase — the import.meta.env.DEV guard is statically false in + production builds, so Rollup drops both the route and the chunk entirely. */ +const UITestPage = import.meta.env.DEV + ? lazy(() => import("@/pages/UITestPage")) + : null; + /** App defines the top-level route table for Rune Console. */ const App = () => { return ( @@ -27,7 +33,16 @@ const App = () => { } /> } /> } /> - } /> + {import.meta.env.DEV && UITestPage && ( + + + + } + /> + )} {/* 404 (SC-04) — reachable regardless of auth; outside RequireAuth so a diff --git a/frontend/src/components/elements/NoticeModal.tsx b/frontend/src/components/elements/NoticeModal.tsx index a789aee..3dc697f 100644 --- a/frontend/src/components/elements/NoticeModal.tsx +++ b/frontend/src/components/elements/NoticeModal.tsx @@ -1,8 +1,8 @@ import Button from "@/components/elements/Button"; import ModalLayout from "@/components/layout/ModalLayout"; +import { useNoticeStore } from "@/state/store/noticeStore"; import { cn } from "@/utils/cn"; import { BTN_TEXT } from "@/constants/commonConstants"; -import { useNoticeStore } from "@/stores/noticeStore"; /** * NoticeModal is the shared blocking result-notice modal. The title names diff --git a/frontend/src/components/elements/Pagination.tsx b/frontend/src/components/elements/Pagination.tsx index 2195a30..99eec5b 100644 --- a/frontend/src/components/elements/Pagination.tsx +++ b/frontend/src/components/elements/Pagination.tsx @@ -10,6 +10,15 @@ const styles = { numActive: "bg-mint text-on-mint font-semibold hover:text-on-mint", }; +/** Sliding 5-page window centered on the current page, clamped at both + ends so exactly min(5, totalPages) numbers always show — the button + count never jumps while paging (1→[1..5], 4→[2..6], 9/10→[6..10]). */ +const pageWindow = (page: number, totalPages: number): number[] => { + const size = Math.min(5, totalPages); + const start = Math.min(Math.max(1, page - 2), totalPages - size + 1); + return Array.from({ length: size }, (_, i) => start + i); +}; + interface PaginationProps { page: number; totalPages: number; @@ -19,8 +28,10 @@ interface PaginationProps { /** * Pagination is the numbered pager (wireframe spec form): ‹ 1 2 3 ›. - * The current page is filled mint; boundary arrows disable. Page-count - * ellipsis (…) is deferred until a screen needs it. + * The current page is filled mint; boundary arrows disable. Large page + * counts show a sliding 5-page window around the current page — the + * session-history table grows without bound, so an unwindowed row of + * hundreds of buttons is not an option. */ const Pagination = ({ page, @@ -39,7 +50,7 @@ const Pagination = ({ > ‹ - {Array.from({ length: totalPages }, (_, i) => i + 1).map((n) => ( + {pageWindow(page, totalPages).map((n) => (