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:
Alireza Hassani
2026-07-29 21:57:44 +03:30
co-authored by Cursor
parent 47bee1ce66
commit 112e5eb46d
29 changed files with 1584 additions and 147 deletions
+127
View File
@@ -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>
);
}