seo check

This commit is contained in:
hamed
2026-06-05 21:28:12 +03:30
parent 3c9013ff0c
commit 35408b059a
10 changed files with 368 additions and 115 deletions
+13
View File
@@ -1,5 +1,18 @@
import AboutUsPage from "@/components/aboutUs";
import Layout from "@/components/layout/StLayout";
import { getStateInfo } from "@/lib/getStateInfo";
export async function generateMetadata() {
const { matchedCity } = await getStateInfo();
const siteName = matchedCity?.site_name || "نوبت 724";
const title = `درباره ما | ${siteName}`;
const description = `آشنایی با ${siteName}، سیستم آنلاین نوبت‌دهی پزشکی. هدف ما ارائه خدمات سریع و کارآمد رزرو نوبت پزشکی برای همه مردم است.`;
return {
title,
description,
openGraph: { title, description },
};
}
function AboutUs() {
return (
+68 -6
View File
@@ -1,24 +1,86 @@
import BlogPage from "@/components/blog";
import Layout from "@/components/layout/StLayout";
import { fetchReq } from "@/lib/req";
import { getStateInfo } from "@/lib/getStateInfo";
async function Blog({ params: { slug } }) {
export async function generateMetadata({ params }) {
const { slug } = await params;
const API_URL = process.env.NEXT_PUBLIC_API_URL;
const { matchedCity } = await getStateInfo();
const siteName = matchedCity?.site_name || "نوبت 724";
try {
const blog = await fetchReq(`${API_URL}/api/v1/blog/${slug}`);
if (!blog) return {};
const title = `${blog.title} | ${siteName}`;
const rawText = blog.body ? blog.body.replace(/<[^>]*>/g, "").trim() : "";
const description = rawText.slice(0, 160) || blog.title;
return {
title,
description,
openGraph: {
title,
description,
type: "article",
...(blog.created && { publishedTime: blog.created }),
images: blog.images?.[0]
? [blog.images[0]]
: ["https://www.nobat724.com/assets/images/logo.png"],
},
twitter: {
card: "summary_large_image",
title,
description,
images: blog.images?.[0]
? [blog.images[0]]
: ["https://www.nobat724.com/assets/images/logo.png"],
},
};
} catch {
return {};
}
}
async function Blog({ params }) {
const { slug } = await params;
const API_URL = process.env.NEXT_PUBLIC_API_URL;
const blog = await fetchReq(`${API_URL}/api/v1/blog/${slug}`);
// Get related blogs based on first tag
let relatedBlogs = [];
if (blog?.tag?.[0]?.id) {
const tagId = blog.tag[0].id;
const relatedResponse = await fetchReq(`${API_URL}/api/v1/blogs?page=1&limit=12&tag=${tagId}`);
const relatedResponse = await fetchReq(
`${API_URL}/api/v1/blogs?page=1&limit=12&tag=${tagId}`
);
relatedBlogs = relatedResponse?.blogs || [];
}
const jsonLd = blog
? {
"@context": "https://schema.org",
"@type": "Article",
headline: blog.title,
...(blog.images?.[0] && { image: blog.images[0] }),
...(blog.created && { datePublished: blog.created }),
...(blog.author && { author: { "@type": "Person", name: blog.author } }),
}
: null;
return (
<Layout>
<BlogPage blog={blog} blogs={relatedBlogs} />
</Layout>
<>
{jsonLd && (
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/>
)}
<Layout>
<BlogPage blog={blog} blogs={relatedBlogs} />
</Layout>
</>
);
}
+13
View File
@@ -1,5 +1,18 @@
import BlogsPage from "@/components/blogs";
import Layout from "@/components/layout/StLayout";
import { getStateInfo } from "@/lib/getStateInfo";
export async function generateMetadata() {
const { matchedCity } = await getStateInfo();
const siteName = matchedCity?.site_name || "نوبت 724";
const title = `مقالات و اخبار پزشکی | ${siteName}`;
const description = `جدیدترین مقالات، اخبار و راهنماهای پزشکی. اطلاعات تخصصی در حوزه سلامت و پزشکی از متخصصان ${siteName}.`;
return {
title,
description,
openGraph: { title, description },
};
}
export default function Blogs() {
return (
+55 -10
View File
@@ -1,28 +1,73 @@
import ClinicPage from "@/components/clinic";
import Layout from "@/components/layout/StLayout";
import { fetchReq } from "@/lib/req";
import { getStateInfo } from "@/lib/getStateInfo";
async function Clinic({ params: { slug },searchParams }) {
export async function generateMetadata({ params }) {
const { slug } = await params;
const API_URL = process.env.NEXT_PUBLIC_API_URL;
const { matchedCity } = await getStateInfo();
const siteName = matchedCity?.site_name || "نوبت 724";
try {
const clinic = await fetchReq(`${API_URL}/api/v1/clinic/${slug}`);
if (!clinic) return {};
const title = `${clinic.title} | ${siteName}`;
const description = `رزرو نوبت و اطلاعات ${clinic.title}. مشاهده لیست پزشکان و خدمات درمانی موجود.`;
return {
title,
description,
openGraph: {
title,
description,
images: clinic.images_clinic?.[0]
? [clinic.images_clinic[0]]
: ["https://www.nobat724.com/assets/images/logo.png"],
},
};
} catch {
return {};
}
}
async function Clinic({ params, searchParams }) {
const { slug } = await params;
const API_URL = process.env.NEXT_PUBLIC_API_URL;
const page = Number(searchParams?.page) || 1;
const reqClinics = await fetchReq(
`${API_URL}/api/v1/clinic/${slug}`
);
const limit=50
const limit = 50;
const reqClinics = await fetchReq(`${API_URL}/api/v1/clinic/${slug}`);
const reqDoctors = await fetchReq(
`${API_URL}/api/v1/clinic/doctor-list/${slug}?page=${page}&limit=${limit}`
);
const clinic = reqClinics;
const doctors = reqDoctors?.data || [];
const pagedoctors=reqDoctors?.page
const pagedoctors = reqDoctors?.page;
const jsonLd = clinic
? {
"@context": "https://schema.org",
"@type": "MedicalClinic",
name: clinic.title,
...(clinic.images_clinic?.[0] && { image: clinic.images_clinic[0] }),
}
: null;
return (
<Layout>
<ClinicPage data={clinic} doctors={doctors} slug={slug} pages={pagedoctors} />
</Layout>
<>
{jsonLd && (
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/>
)}
<Layout>
<ClinicPage data={clinic} doctors={doctors} slug={slug} pages={pagedoctors} />
</Layout>
</>
);
}
+17
View File
@@ -4,6 +4,23 @@ import Layout from "@/components/layout/StLayout";
import { getStateInfo } from "@/lib/getStateInfo";
import { buildClinicParams } from "@/helper";
export async function generateMetadata() {
const { matchedCity, matchedState } = await getStateInfo();
const siteName = matchedCity?.site_name || "نوبت 724";
const cityName = matchedCity?.name || matchedState?.name || "";
const title = cityName
? `کلینیک‌های ${cityName} | جستجو و رزرو نوبت | ${siteName}`
: `جستجوی کلینیک و مراکز درمانی | ${siteName}`;
const description = cityName
? `لیست کلینیک‌ها و مراکز درمانی در ${cityName}. رزرو آنلاین نوبت از بهترین مراکز درمانی ${cityName}.`
: `جستجوی کلینیک‌ها و مراکز درمانی در سراسر کشور. رزرو آنلاین نوبت سریع و آسان.`;
return {
title,
description,
openGraph: { title, description },
};
}
export default async function Clinics({ searchParams }) {
const awaitedSearchParams = await searchParams;
const { matchedCity, matchedState } = await getStateInfo();
+70 -9
View File
@@ -1,15 +1,54 @@
import DoctorPage from "@/components/doctor";
import Layout from "@/components/layout/StLayout";
import { getStateInfo } from "@/lib/getStateInfo";
import axios from "axios";
async function Doctor({ params: { slug } }) {
export async function generateMetadata({ params }) {
const { slug } = await params;
const API_URL = process.env.NEXT_PUBLIC_API_URL;
const { matchedCity } = await getStateInfo();
const siteName = matchedCity?.site_name || "نوبت 724";
try {
const res = await axios.get(`${API_URL}/api/v1/doctor/${slug}`);
const doctor = res.data;
if (!doctor) return {};
const specialtyNames = doctor.specialties?.map((s) => s.name).join(" و ") || "";
const title = `دکتر ${doctor.name}${specialtyNames ? " | " + specialtyNames : ""} | ${siteName}`;
const description =
doctor.detail ||
`رزرو نوبت آنلاین دکتر ${doctor.name}${specialtyNames ? " متخصص " + specialtyNames : ""}${doctor.address ? " | " + doctor.address : ""}`.trim();
return {
title,
description,
openGraph: {
title,
description,
type: "profile",
images: doctor.img ? [doctor.img] : ["https://www.nobat724.com/assets/images/logo.png"],
},
twitter: {
card: "summary",
title,
description,
images: doctor.img ? [doctor.img] : ["https://www.nobat724.com/assets/images/logo.png"],
},
};
} catch {
return {};
}
}
async function Doctor({ params }) {
const { slug } = await params;
let doctor = null;
let comments = null;
const API_URL = process.env.NEXT_PUBLIC_API_URL;
try {
const resDoctor = await axios.get(`${API_URL}/api/v1/doctor/${slug}`);
doctor = resDoctor.data;
if (doctor) {
const resComments = await axios.get(
@@ -17,15 +56,37 @@ async function Doctor({ params: { slug } }) {
);
comments = resComments?.data?.data;
}
} catch (error) {
// console.error("problem with req:", error.message);
// return redirect("/unauthorized");
}
} catch (error) {}
const specialtyNames = doctor?.specialties?.map((s) => s.name).join(" و ") || "";
const jsonLd = doctor
? {
"@context": "https://schema.org",
"@type": "Physician",
name: `دکتر ${doctor.name}`,
...(specialtyNames && { medicalSpecialty: specialtyNames }),
...(doctor.img && { image: doctor.img }),
...(doctor.address && {
address: {
"@type": "PostalAddress",
streetAddress: doctor.address,
},
}),
}
: null;
return (
<Layout>
<DoctorPage doctor={doctor} comments={comments} slug={slug} />
</Layout>
<>
{jsonLd && (
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/>
)}
<Layout>
<DoctorPage doctor={doctor} comments={comments} slug={slug} />
</Layout>
</>
);
}
+17
View File
@@ -4,6 +4,23 @@ import { buildDoctorParams } from "@/helper";
import { getStateInfo } from "@/lib/getStateInfo";
import axios from "axios";
export async function generateMetadata() {
const { matchedCity, matchedState } = await getStateInfo();
const siteName = matchedCity?.site_name || "نوبت 724";
const cityName = matchedCity?.name || matchedState?.name || "";
const title = cityName
? `پزشکان ${cityName} | جستجو و رزرو نوبت | ${siteName}`
: `جستجوی پزشک و رزرو نوبت آنلاین | ${siteName}`;
const description = cityName
? `لیست پزشکان متخصص در ${cityName}. جستجو بر اساس تخصص و منطقه. رزرو آنلاین نوبت پزشکی در ${cityName}.`
: `جستجوی پزشکان متخصص در سراسر کشور. رزرو آنلاین نوبت پزشکی سریع و آسان با نوبت 724.`;
return {
title,
description,
openGraph: { title, description },
};
}
async function Doctors({ searchParams }) {
const awaitedSearchParams = await searchParams;
const { matchedCity, matchedState } = await getStateInfo();
+16 -2
View File
@@ -14,13 +14,27 @@ export async function generateMetadata() {
const canonicalUrl = await getCanonicalUrl();
const title = matchedCity ? matchedCity.title : "نوبت724";
const description = matchedCity?.description || "نوبت 724 - سیستم آنلاین نوبت‌دهی برای پزشکان و کلینیک‌ها. با استفاده از نوبت 724، به راحتی نوبت پزشکی خود را به صورت آنلاین رزرو کنید و از خدمات سریع و کارآمد ما بهره‌مند شوید";
const baseMetadata = {
title: matchedCity ? matchedCity.title : "نوبت724",
description: matchedCity?.description || "نوبت 724 - سیستم آنلاین نوبت‌دهی برای پزشکان و کلینیک‌ها. با استفاده از نوبت 724، به راحتی نوبت پزشکی خود را به صورت آنلاین رزرو کنید و از خدمات سریع و کارآمد ما بهره‌مند شوید",
title,
description,
keywords: matchedCity
? matchedCity.keywords
: "نوبت 724, نوبت دهی, رزرو نوبت, پزشک, پزشکی, کلینیک, بیمارستان, نوبت آنلاین, سیستم نوبت دهی, سیستم نوبت دهی آنلاین, سیستم نوبت دهی پزشکی, سیستم نوبت دهی کلینیک, سیستم نوبت دهی بیمارستان, سیستم نوبت دهی آنلاین پزشکی, سیستم نوبت دهی آنلاین کلینیک, سیستم نوبت دهی آنلاین بیمارستان",
openGraph: {
title,
description,
type: "website",
locale: "fa_IR",
siteName: matchedCity?.site_name || "نوبت 724",
images: ["https://www.nobat724.com/assets/images/logo.png"],
},
twitter: {
card: "summary_large_image",
title,
description,
images: ["https://www.nobat724.com/assets/images/logo.png"],
},
};
+83 -88
View File
@@ -1,7 +1,6 @@
import { headers } from 'next/headers';
import cityData from '../data/city.json';
import specialtyData from '../data/specialties.json';
import stateData from '../data/state.json';
import {
DOMAIN_CONFIG,
isMainDomain,
@@ -11,93 +10,103 @@ import {
getBaseUrl
} from '../utils/sitemap';
// Get current domain from headers
function getCurrentDomain() {
try {
const headersList = headers();
const host = headersList.get('host');
return host || DOMAIN_CONFIG.MAIN_DOMAIN;
} catch (error) {
// Fallback for static generation
console.warn('Headers not available during static generation, using main domain');
return DOMAIN_CONFIG.MAIN_DOMAIN;
}
}
// Get city data based on domain
function getCityByDomain(domain) {
return cityData.find(city => city.domain === domain);
}
// Get state name by province_id
function getStateById(provinceId) {
return stateData.find(state => state.id === provinceId)?.name || '';
}
// Generate static pages URLs
function getStaticPages(baseUrl) {
return [
{
url: baseUrl,
lastModified: new Date(),
changeFrequency: 'daily',
priority: 1,
},
{
url: `${baseUrl}/about-us`,
lastModified: new Date(),
changeFrequency: 'monthly',
priority: 0.8,
},
{
url: `${baseUrl}/contact-us`,
lastModified: new Date(),
changeFrequency: 'monthly',
priority: 0.8,
},
{
url: `${baseUrl}/blogs`,
lastModified: new Date(),
changeFrequency: 'weekly',
priority: 0.9,
},
{
url: `${baseUrl}/doctors`,
lastModified: new Date(),
changeFrequency: 'daily',
priority: 0.9,
},
{
url: `${baseUrl}/clinics`,
lastModified: new Date(),
changeFrequency: 'daily',
priority: 0.9,
},
{
url: `${baseUrl}/specialties`,
lastModified: new Date(),
changeFrequency: 'weekly',
priority: 0.9,
},
{ url: baseUrl, lastModified: new Date(), changeFrequency: 'daily', priority: 1 },
{ url: `${baseUrl}/about-us`, lastModified: new Date(), changeFrequency: 'monthly', priority: 0.8 },
{ url: `${baseUrl}/contact-us`, lastModified: new Date(), changeFrequency: 'monthly', priority: 0.8 },
{ url: `${baseUrl}/blogs`, lastModified: new Date(), changeFrequency: 'weekly', priority: 0.9 },
{ url: `${baseUrl}/doctors`, lastModified: new Date(), changeFrequency: 'daily', priority: 0.9 },
{ url: `${baseUrl}/clinics`, lastModified: new Date(), changeFrequency: 'daily', priority: 0.9 },
{ url: `${baseUrl}/specialties`, lastModified: new Date(), changeFrequency: 'weekly', priority: 0.9 },
];
}
// Generate doctor search URLs based on domain
function getDoctorSearchUrls(domain, baseUrl) {
// Return empty array - no URLs with search parameters
return [];
async function getDoctorUrls(baseUrl) {
const API_URL = process.env.NEXT_PUBLIC_API_URL;
if (!API_URL) return [];
try {
const res = await fetch(`${API_URL}/api/v1/doctors?page=1&limit=500`, {
next: { revalidate: 3600 },
});
if (!res.ok) return [];
const data = await res.json();
const doctors = data?.data || data?.doctors || data || [];
return doctors
.filter((d) => d?.uuid)
.map((d) => ({
url: `${baseUrl}/doctor/${d.uuid}`,
lastModified: new Date(),
changeFrequency: 'weekly',
priority: 0.8,
}));
} catch {
return [];
}
}
// Generate clinic search URLs
function getClinicSearchUrls(domain, baseUrl) {
// Return empty array - no URLs with search parameters
return [];
async function getClinicUrls(baseUrl) {
const API_URL = process.env.NEXT_PUBLIC_API_URL;
if (!API_URL) return [];
try {
const res = await fetch(`${API_URL}/api/v1/clinics?page=1&limit=500`, {
next: { revalidate: 3600 },
});
if (!res.ok) return [];
const data = await res.json();
const clinics = data?.data || data?.clinics || data || [];
return clinics
.filter((c) => c?.uuid)
.map((c) => ({
url: `${baseUrl}/clinic/${c.uuid}`,
lastModified: new Date(),
changeFrequency: 'weekly',
priority: 0.7,
}));
} catch {
return [];
}
}
// Generate specialty pages URLs
function getSpecialtyUrls(domain, baseUrl) {
// Return empty array - no specialty URLs
return [];
async function getBlogUrls(baseUrl) {
const API_URL = process.env.NEXT_PUBLIC_API_URL;
if (!API_URL) return [];
try {
const res = await fetch(`${API_URL}/api/v1/blogs?page=1&limit=200`, {
next: { revalidate: 3600 },
});
if (!res.ok) return [];
const data = await res.json();
const blogs = data?.blogs || data?.data || data || [];
return blogs
.filter((b) => b?.slug || b?.uuid)
.map((b) => ({
url: `${baseUrl}/blog/${b.slug || b.uuid}`,
lastModified: b.created ? new Date(b.created) : new Date(),
changeFrequency: 'monthly',
priority: 0.6,
}));
} catch {
return [];
}
}
export default async function sitemap() {
@@ -105,34 +114,20 @@ export default async function sitemap() {
const domain = getCurrentDomain();
const baseUrl = getBaseUrl(domain);
console.log(`Generating sitemap for domain: ${domain}, baseUrl: ${baseUrl}`);
console.log(`City data length: ${cityData.length}, Specialty data length: ${specialtyData.length}`);
const [staticPages, doctorUrls, clinicUrls, blogUrls] = await Promise.all([
Promise.resolve(getStaticPages(baseUrl)),
getDoctorUrls(baseUrl),
getClinicUrls(baseUrl),
getBlogUrls(baseUrl),
]);
// Get all URLs
const staticPages = getStaticPages(baseUrl);
const doctorSearchUrls = getDoctorSearchUrls(domain, baseUrl);
const clinicSearchUrls = getClinicSearchUrls(domain, baseUrl);
const specialtyUrls = getSpecialtyUrls(domain, baseUrl);
const allUrls = [...staticPages, ...doctorUrls, ...clinicUrls, ...blogUrls];
console.log(`Generated URLs - Static: ${staticPages.length}, Doctors: ${doctorSearchUrls.length}, Clinics: ${clinicSearchUrls.length}, Specialties: ${specialtyUrls.length}`);
// Combine all URLs
const allUrls = [
...staticPages,
...doctorSearchUrls,
...clinicSearchUrls,
...specialtyUrls,
];
// Remove duplicates based on URL
const uniqueUrls = allUrls.filter((item, index, arr) =>
arr.findIndex(i => i.url === item.url) === index
return allUrls.filter(
(item, index, arr) => arr.findIndex((i) => i.url === item.url) === index
);
return uniqueUrls;
} catch (error) {
console.error('Error generating sitemap:', error);
return [];
}
}
}
+16
View File
@@ -1,5 +1,21 @@
import Layout from "@/components/layout/StLayout";
import SpecialtiesPage from "@/components/specialties";
import { getStateInfo } from "@/lib/getStateInfo";
export async function generateMetadata() {
const { matchedCity } = await getStateInfo();
const siteName = matchedCity?.site_name || "نوبت 724";
const cityName = matchedCity?.name || "";
const title = cityName
? `تخصص‌های پزشکی در ${cityName} | ${siteName}`
: `تخصص‌های پزشکی | ${siteName}`;
const description = `لیست کامل تخصص‌های پزشکی${cityName ? " در " + cityName : ""}. رزرو نوبت از متخصصین مختلف به صورت آنلاین.`;
return {
title,
description,
openGraph: { title, description },
};
}
function Specialties() {
return (