Add CMS website and Qom landing pages with responsive layout fixes.
Ship blog/portfolio routes, shared content components, and mobile-safe typography so the marketing site can deploy cleanly. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
47bee1ce66
commit
112e5eb46d
@@ -0,0 +1,127 @@
|
||||
"use client";
|
||||
|
||||
import Link from "next/link";
|
||||
|
||||
type PaginationProps = {
|
||||
page: number;
|
||||
pageSize: number;
|
||||
total: number;
|
||||
basePath: string;
|
||||
};
|
||||
|
||||
function pageHref(basePath: string, page: number) {
|
||||
if (page <= 1) return basePath;
|
||||
return `${basePath}?page=${page}`;
|
||||
}
|
||||
|
||||
export function Pagination({
|
||||
page,
|
||||
pageSize,
|
||||
total,
|
||||
basePath,
|
||||
}: PaginationProps) {
|
||||
const totalPages = Math.max(1, Math.ceil(total / pageSize));
|
||||
|
||||
if (totalPages <= 1) return null;
|
||||
|
||||
const windowSize = 5;
|
||||
let start = Math.max(1, page - Math.floor(windowSize / 2));
|
||||
const end = Math.min(totalPages, start + windowSize - 1);
|
||||
start = Math.max(1, end - windowSize + 1);
|
||||
|
||||
const pages = Array.from({ length: end - start + 1 }, (_, i) => start + i);
|
||||
|
||||
const scrollTop = () => {
|
||||
window.scrollTo({ top: 0, behavior: "smooth" });
|
||||
};
|
||||
|
||||
return (
|
||||
<nav
|
||||
className="mt-14 flex flex-wrap items-center justify-center gap-2"
|
||||
aria-label="صفحهبندی"
|
||||
>
|
||||
{page > 1 ? (
|
||||
<Link
|
||||
href={pageHref(basePath, page - 1)}
|
||||
onClick={scrollTop}
|
||||
scroll
|
||||
className="rounded-full border border-line/70 px-4 py-2 text-sm text-ink-soft transition hover:border-ink/30 hover:text-ink"
|
||||
>
|
||||
قبلی
|
||||
</Link>
|
||||
) : (
|
||||
<span className="rounded-full border border-line/40 px-4 py-2 text-sm text-muted/40">
|
||||
قبلی
|
||||
</span>
|
||||
)}
|
||||
|
||||
{start > 1 ? (
|
||||
<>
|
||||
<Link
|
||||
href={pageHref(basePath, 1)}
|
||||
onClick={scrollTop}
|
||||
scroll
|
||||
className="rounded-full border border-line/70 px-3.5 py-2 text-sm text-ink-soft transition hover:border-ink/30 hover:text-ink"
|
||||
>
|
||||
۱
|
||||
</Link>
|
||||
{start > 2 ? (
|
||||
<span className="px-1 text-muted" aria-hidden>
|
||||
…
|
||||
</span>
|
||||
) : null}
|
||||
</>
|
||||
) : null}
|
||||
|
||||
{pages.map((n) => (
|
||||
<Link
|
||||
key={n}
|
||||
href={pageHref(basePath, n)}
|
||||
onClick={scrollTop}
|
||||
scroll
|
||||
aria-current={n === page ? "page" : undefined}
|
||||
className={`rounded-full border px-3.5 py-2 text-sm transition ${
|
||||
n === page
|
||||
? "border-ink bg-ink text-surface"
|
||||
: "border-line/70 text-ink-soft hover:border-ink/30 hover:text-ink"
|
||||
}`}
|
||||
>
|
||||
{n.toLocaleString("fa-IR")}
|
||||
</Link>
|
||||
))}
|
||||
|
||||
{end < totalPages ? (
|
||||
<>
|
||||
{end < totalPages - 1 ? (
|
||||
<span className="px-1 text-muted" aria-hidden>
|
||||
…
|
||||
</span>
|
||||
) : null}
|
||||
<Link
|
||||
href={pageHref(basePath, totalPages)}
|
||||
onClick={scrollTop}
|
||||
scroll
|
||||
className="rounded-full border border-line/70 px-3.5 py-2 text-sm text-ink-soft transition hover:border-ink/30 hover:text-ink"
|
||||
>
|
||||
{totalPages.toLocaleString("fa-IR")}
|
||||
</Link>
|
||||
</>
|
||||
) : null}
|
||||
|
||||
{page < totalPages ? (
|
||||
<Link
|
||||
href={pageHref(basePath, page + 1)}
|
||||
onClick={scrollTop}
|
||||
scroll
|
||||
className="rounded-full border border-line/70 px-4 py-2 text-sm text-ink-soft transition hover:border-ink/30 hover:text-ink"
|
||||
>
|
||||
بعدی
|
||||
</Link>
|
||||
) : (
|
||||
<span className="rounded-full border border-line/40 px-4 py-2 text-sm text-muted/40">
|
||||
بعدی
|
||||
</span>
|
||||
)}
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user