Files
draletaha/DrSousan.Api/Pages/Blog/Index.cshtml
T
soroush.asadi 872e5c1818
CI/CD / CI · dotnet build (push) Successful in 35s
CI/CD / Deploy · drsousan (push) Successful in 28s
fix(blog): repair pagination on public and admin interfaces
Public /blog: the handler param was named `page`, which is a reserved
route token in Razor Pages and never binds — so every page silently
showed the same first 10 posts. Renamed the query param to `pg`
([FromQuery(Name="pg")]) and updated the pagination links to match.

Admin: the posts table had no pagination and dumped all rows at once.
Added client-side pagination (10/page) with a prev/next + numbered bar
over the already-loaded posts array.

Verified: public page1=10/page2=4 with zero overlap; admin shows
‹ 1 2 › with correct row counts and active state per page.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 00:23:22 +03:30

117 lines
7.8 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
@page "/blog"
@model DrSousan.Api.Pages.Blog.BlogIndexModel
@section Head {
<title>@ViewData["Title"]</title>
<meta name="description" content="مقالات تخصصی دکتر سوسن آل‌طه درباره زیبایی پوست، بوتاکس، فیلر، لیزر و مراقبت از پوست." />
<link rel="canonical" href="@((Environment.GetEnvironmentVariable("SITE_BASE_URL")?.TrimEnd('/') ?? (Request.Scheme + "://" + Request.Host)) + "/blog")" />
<style>
/* ─── Blog Hero ─────────────────────────────────────────────── */
.blog-hero{background:linear-gradient(135deg,var(--gold-pale) 0%,#EDE0CA 100%);padding:6rem 2rem 3rem;text-align:center}
.blog-hero h1{font-size:clamp(1.8rem,4vw,2.5rem);font-weight:700;color:var(--dark);margin-bottom:.6rem}
.blog-hero p{font-size:1rem;color:var(--mid);max-width:520px;margin:0 auto}
/* ─── Search ─────────────────────────────────────────────────── */
.search-wrap{max-width:480px;margin:1.5rem auto 0;position:relative}
.search-wrap input{width:100%;border:1.5px solid var(--border);border-radius:50px;padding:.65rem 1.2rem .65rem 3rem;font-family:'Vazirmatn',sans-serif;font-size:.9rem;direction:rtl;outline:none;background:var(--white);transition:border-color .2s}
.search-wrap input:focus{border-color:var(--gold)}
.search-wrap svg{position:absolute;left:1rem;top:50%;transform:translateY(-50%);width:18px;height:18px;color:var(--light)}
/* ─── Filter ─────────────────────────────────────────────────── */
.filter-bar{max-width:1100px;margin:2rem auto 0;padding:0 2rem;display:flex;gap:.6rem;flex-wrap:wrap}
.filter-btn{background:transparent;border:1.5px solid var(--border);color:var(--mid);padding:.4rem 1.1rem;border-radius:50px;font-family:'Vazirmatn',sans-serif;font-size:.85rem;cursor:pointer;transition:all .2s;text-decoration:none;display:inline-block}
.filter-btn.active,.filter-btn:hover{background:var(--gold);border-color:var(--gold);color:var(--white)}
/* ─── Blog Grid ──────────────────────────────────────────────── */
.blog-grid{max-width:1100px;margin:2rem auto;padding:0 2rem;display:grid;grid-template-columns:repeat(3,1fr);gap:1.5rem}
@@media(max-width:900px){.blog-grid{grid-template-columns:repeat(2,1fr)}}
@@media(max-width:600px){.blog-grid{grid-template-columns:1fr}}
.post-card{background:var(--white);border-radius:16px;border:1px solid var(--border);overflow:hidden;transition:transform .3s,box-shadow .3s;display:flex;flex-direction:column}
.post-card:hover{transform:translateY(-4px);box-shadow:0 12px 40px rgba(184,149,90,.15)}
.post-card-img{aspect-ratio:16/9;background:linear-gradient(135deg,var(--gold-pale),#EDE0CA);display:flex;align-items:center;justify-content:center;color:var(--gold);font-size:2rem;overflow:hidden}
.post-card-img img{width:100%;height:100%;object-fit:cover}
.post-card-body{padding:1.3rem;flex:1;display:flex;flex-direction:column;gap:.6rem}
.post-cat{font-size:.72rem;font-weight:600;color:var(--gold);background:var(--gold-pale);padding:.2rem .7rem;border-radius:50px;display:inline-block}
.post-title{font-size:1rem;font-weight:600;color:var(--dark);line-height:1.5}
.post-title:hover{color:var(--gold)}
.post-excerpt{font-size:.85rem;color:var(--mid);line-height:1.7;flex:1}
.post-meta{display:flex;align-items:center;justify-content:space-between;font-size:.75rem;color:var(--light);margin-top:auto;padding-top:.6rem;border-top:1px solid var(--border)}
.read-more{color:var(--gold);font-weight:500;font-size:.82rem}
/* ─── Empty ──────────────────────────────────────────────────── */
.empty{text-align:center;padding:4rem 2rem;color:var(--light);grid-column:1/-1}
/* ─── Pagination ─────────────────────────────────────────────── */
.pagination{display:flex;gap:.5rem;justify-content:center;padding:2rem;margin-top:1rem;max-width:1100px;margin-left:auto;margin-right:auto}
.page-btn{width:38px;height:38px;border-radius:8px;border:1.5px solid var(--border);background:transparent;font-family:'Vazirmatn',sans-serif;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .2s;text-decoration:none;color:var(--dark);font-size:.9rem}
.page-btn.active,.page-btn:hover{background:var(--gold);border-color:var(--gold);color:var(--white)}
</style>
}
<div class="blog-hero">
<h1>وبلاگ تخصصی پوست و زیبایی</h1>
<p>آخرین مقالات و راهنماهای تخصصی درباره مراقبت از پوست، زیبایی و درمان‌های تخصصی</p>
<div class="search-wrap">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
<input type="text" id="searchInput" placeholder="جستجو در مقالات..." />
</div>
</div>
<div class="filter-bar">
<a href="/blog" class="filter-btn @(string.IsNullOrEmpty(Model.ActiveCat) ? "active" : "")">همه</a>
@foreach (var cat in Model.Categories)
{
var active = Model.ActiveCat == cat.Slug;
var count = cat.Posts.Count(p => p.IsPublished);
<a href="/blog?category=@cat.Slug" class="filter-btn @(active ? "active" : "")">@cat.Name (@count)</a>
}
</div>
<div class="blog-grid" id="blogGrid">
@if (!Model.Posts.Any())
{
<div class="empty"><p>مقاله‌ای یافت نشد.</p></div>
}
else
{
@foreach (var post in Model.Posts)
{
<div class="post-card">
<div class="post-card-img">
@if (!string.IsNullOrEmpty(post.FeaturedImage))
{
<img src="@post.FeaturedImage" alt="@post.Title" loading="lazy" />
}
else { <span>📝</span> }
</div>
<div class="post-card-body">
@if (post.Category != null)
{
<span class="post-cat">@post.Category.Name</span>
}
<a href="/blog/@post.Slug" class="post-title">@post.Title</a>
<p class="post-excerpt">@(post.Excerpt.Length > 120 ? post.Excerpt.Substring(0, 120) + "..." : post.Excerpt)</p>
<div class="post-meta">
<span>🕐 @post.ReadingTimeMinutes دقیقه | 👁 @post.ViewCount</span>
<a href="/blog/@post.Slug" class="read-more">ادامه مطلب ←</a>
</div>
</div>
</div>
}
}
</div>
@if (Model.TotalPages > 1)
{
<div class="pagination">
@if (Model.CurrentPage > 1)
{
<a class="page-btn" href="/blog?pg=@(Model.CurrentPage - 1)@(!string.IsNullOrEmpty(Model.ActiveCat) ? "&category=" + Model.ActiveCat : "")"></a>
}
@for (int p = 1; p <= Model.TotalPages; p++)
{
<a class="page-btn @(p == Model.CurrentPage ? "active" : "")"
href="/blog?pg=@p@(!string.IsNullOrEmpty(Model.ActiveCat) ? "&category=" + Model.ActiveCat : "")">@p</a>
}
@if (Model.CurrentPage < Model.TotalPages)
{
<a class="page-btn" href="/blog?pg=@(Model.CurrentPage + 1)@(!string.IsNullOrEmpty(Model.ActiveCat) ? "&category=" + Model.ActiveCat : "")"></a>
}
</div>
}