feat: Revert homepage changes, fix Persian slug 404, and make various article and about us adjustments
- Removed the newly added city-focused section from the homepage. - Fixed 404 error for Persian slugs in specialties and blog pages by implementing a decode helper. - Removed the "مخصوص شهر" label from article headers. - Linked article tags to their respective filter pages. - Adjusted related content images to prevent distortion. - Added a section in the "About Us" page for physician registration guidance. - Updated the logo in the "About Us" header to the correct version. - Added tests for the new functionality and ensured existing tests are updated accordingly.
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import Head from "@/components/blog/head";
|
||||
|
||||
const BLOG = {
|
||||
title: "آلرژی دارویی",
|
||||
author: "دکتر رضایی",
|
||||
created: 1750000000,
|
||||
reading_time: 5,
|
||||
tag: [{ name: "آلرژی" }, { name: "قلب و عروق" }],
|
||||
};
|
||||
|
||||
describe("سربرگ مقاله", () => {
|
||||
it("برچسب «مخصوص شهر» را نشان نمیدهد", () => {
|
||||
render(<Head data={BLOG} />);
|
||||
|
||||
expect(screen.queryByText("مخصوص شهر:")).toBeNull();
|
||||
});
|
||||
|
||||
it("نویسنده و تاریخ انتشار سر جایشان میمانند", () => {
|
||||
render(<Head data={BLOG} />);
|
||||
|
||||
expect(screen.getByText("نویسنده:")).toBeInTheDocument();
|
||||
expect(screen.getByText("تاریخ انتشار:")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("هر تگ به لیست مقالههای همان تگ لینک میشود", () => {
|
||||
render(<Head data={BLOG} />);
|
||||
|
||||
const links = screen.getAllByRole("link", { name: "قلب و عروق" });
|
||||
const url = new URL(links[0].getAttribute("href"), "https://yasuj-nobat.ir");
|
||||
|
||||
expect(links).toHaveLength(1);
|
||||
expect(url.pathname).toBe("/blogs");
|
||||
// قرارداد واقعی همان چیزی است که `searchParams.get("tag")` میخواند،
|
||||
// نه شکل دقیق encode — شکل شیئیِ href فاصله را `+` میکند و بردکرامب `%20`.
|
||||
expect(url.searchParams.get("tag")).toBe("قلب و عروق");
|
||||
});
|
||||
|
||||
it("تگ حاوی & با پارامتر دوم اشتباه گرفته نمیشود", () => {
|
||||
render(<Head data={{ ...BLOG, tag: [{ name: "قلب & عروق" }] }} />);
|
||||
|
||||
const links = screen.getAllByRole("link", { name: "قلب & عروق" });
|
||||
const url = new URL(links[0].getAttribute("href"), "https://yasuj-nobat.ir");
|
||||
|
||||
expect(url.searchParams.get("tag")).toBe("قلب & عروق");
|
||||
expect(Array.from(url.searchParams.keys())).toEqual(["tag"]);
|
||||
});
|
||||
|
||||
it("مقالهٔ بدون تگ هیچ فهرست تگی رندر نمیکند", () => {
|
||||
const { container } = render(<Head data={{ ...BLOG, tag: [] }} />);
|
||||
|
||||
expect(container.querySelector("ul")).toBeNull();
|
||||
});
|
||||
});
|
||||
@@ -4,7 +4,7 @@ import { convertTimestampToJalali } from "@/helper";
|
||||
const crumbClass =
|
||||
"text-[#9B9B9B] text-[11px] sm:text-[13px] md:text-[15px] lg:text-[16px] font-normal";
|
||||
|
||||
function Head({ data, cityName }) {
|
||||
function Head({ data }) {
|
||||
const firstTag = data?.tag?.[0];
|
||||
const createdDate = data?.created ? convertTimestampToJalali(data.created) : "";
|
||||
// normalizeBlog تگها را به [{name}] تبدیل میکند؛ نامِ خالی حذف میشود.
|
||||
@@ -44,18 +44,6 @@ function Head({ data, cityName }) {
|
||||
</h1>
|
||||
</div>
|
||||
<div className="flex mt-[16px] sm:mt-[18px] md:mt-[21px] lg:mt-[24px] items-center justify-start gap-[35px]">
|
||||
{/* شهر پست در HTML قابلمشاهده — نه فقط در متادیتا. برچسب با همان
|
||||
تایپوگرافی «نویسنده/تاریخ» تا با بقیهٔ سربرگ همخانواده بماند. */}
|
||||
{cityName && (
|
||||
<div className="flex items-center justify-start gap-1">
|
||||
<p className="text-[#9B9B9B] text-[11px] md:text-[13px] lg:text-[14px] font-normal">
|
||||
مخصوص شهر:
|
||||
</p>
|
||||
<p className="text-[#9B9B9B] text-[11px] md:text-[14px] lg:text-[16px] font-medium">
|
||||
{cityName}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
{data?.author && (
|
||||
<div className="flex items-center justify-start gap-1">
|
||||
<p className="text-[#9B9B9B] text-[11px] md:text-[13px] lg:text-[14px] font-normal">
|
||||
@@ -88,15 +76,18 @@ function Head({ data, cityName }) {
|
||||
)}
|
||||
</div>
|
||||
{/* فهرست کامل تگها — تا این تغییر فقط تگ اول در بردکرامب دیده میشد.
|
||||
صفحهٔ فیلتر تگ وجود ندارد، پس چیپها بدون لینک رندر میشوند. */}
|
||||
مقصد هر چیپ همان لیست مقالههاست که `?tag=` را میخواند؛ شکل شیئیِ href
|
||||
تگِ حاوی فاصله یا & را هم درست encode میکند. */}
|
||||
{tags.length > 0 && (
|
||||
<ul className="flex flex-wrap items-center justify-start gap-2 mt-[12px] md:mt-[16px]">
|
||||
{tags.map((tag, idx) => (
|
||||
<li
|
||||
key={`${tag}-${idx}`}
|
||||
className="bg-[#F5F5F5] text-[#525252] rounded-full px-[12px] py-[4px] text-[11px] md:text-[13px] font-medium"
|
||||
>
|
||||
{tag}
|
||||
<li key={`${tag}-${idx}`}>
|
||||
<Link
|
||||
href={{ pathname: "/blogs", query: { tag } }}
|
||||
className="block bg-[#F5F5F5] text-[#525252] rounded-full px-[12px] py-[4px] text-[11px] md:text-[13px] font-medium hover:bg-[#E8E8E8] hover:text-[#3B3B3B] transition-colors"
|
||||
>
|
||||
{tag}
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
@@ -2,10 +2,10 @@ import Detail from "./detail";
|
||||
import Head from "./head";
|
||||
import RelatedContent from "./relatedContent";
|
||||
|
||||
function BlogPage({ blog, blogs, cityName }) {
|
||||
function BlogPage({ blog, blogs }) {
|
||||
return (
|
||||
<div className="padding-responsive pt-[84px] sm:pt-[110px] md:pt-[140px] lg:pt-[168px]">
|
||||
<Head data={blog} cityName={cityName} />
|
||||
<Head data={blog} />
|
||||
<div className="flex flex-col gap-y-[32px] lg:flex-row items-start justify-center gap-[24px] mt-[16px] sm:mt-[19px] md:mt-[21px] lg:mt-[24px]">
|
||||
<Detail data={blog} />
|
||||
<RelatedContent data={blogs} />
|
||||
|
||||
@@ -10,8 +10,10 @@ function Item({ data, idx, loading }) {
|
||||
|
||||
return (
|
||||
<li className="flex w-full relative items-start justify-start gap-[8px] py-[12px]">
|
||||
{/* CustomLoading ظرف نمیسازد، پس فرزند مستقیمِ فلکس همین لینک است؛
|
||||
بدون shrink-0 تیتر بلند عرض عکس را میبلعد و ارتفاعش ثابت میماند. */}
|
||||
<CustomLoading width={80} height={72} loading={loading}>
|
||||
<Link href={`/blog/${data.uuid}`}>
|
||||
<Link href={`/blog/${data.uuid}`} className="block shrink-0">
|
||||
<img
|
||||
className=" w-[64px] sm:w-[73px] md:w-[85px] lg:w-[97px] h-[56px] sm:h-[64px] md:h-[75px] lg:h-[88px] rounded-[8px] overflow-hidden object-cover "
|
||||
src={cover}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import Item from "@/components/blog/relatedContent/Item";
|
||||
|
||||
const DATA = {
|
||||
uuid: "8f1c0f2e-0000-4000-8000-000000000001",
|
||||
title: "تیتری که بسیار بلند است و باید ستون متن را پر کند بدون آنکه عکس را باریک کند",
|
||||
created: 1750000000,
|
||||
};
|
||||
|
||||
describe("آیتم مطالب مرتبط", () => {
|
||||
it("ظرف عکس در فلکس فشرده نمیشود", () => {
|
||||
render(<Item data={DATA} idx={0} loading={false} />);
|
||||
|
||||
const image = screen.getByRole("img", { name: DATA.title });
|
||||
|
||||
// jsdom چیدمان را اندازه نمیگیرد؛ آنچه قابل تست است همین قرارداد کلاس است.
|
||||
expect(image.closest("a")).toHaveClass("shrink-0");
|
||||
});
|
||||
|
||||
it("ستون متن اجازهٔ کوچک شدن دارد تا سرریز نکند", () => {
|
||||
const { container } = render(<Item data={DATA} idx={0} loading={false} />);
|
||||
|
||||
expect(container.querySelector("li > div")).toHaveClass("min-w-0");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user