From 35408b059a02a8c32d6bbfd1cfa1960e35d32114 Mon Sep 17 00:00:00 2001
From: hamed <15238-genius.ha@users.noreply.drupalcode.org>
Date: Fri, 5 Jun 2026 21:28:12 +0330
Subject: [PATCH] seo check
---
app/about-us/page.js | 13 +++
app/blog/[slug]/page.js | 74 +++++++++++++++--
app/blogs/page.js | 13 +++
app/clinic/[slug]/page.js | 65 ++++++++++++---
app/clinics/page.js | 17 ++++
app/doctor/[slug]/page.js | 79 ++++++++++++++++--
app/doctors/page.js | 17 ++++
app/layout.js | 18 +++-
app/sitemap.js | 171 ++++++++++++++++++--------------------
app/specialties/page.js | 16 ++++
10 files changed, 368 insertions(+), 115 deletions(-)
diff --git a/app/about-us/page.js b/app/about-us/page.js
index 12cb522..89fa48a 100644
--- a/app/about-us/page.js
+++ b/app/about-us/page.js
@@ -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 (
diff --git a/app/blog/[slug]/page.js b/app/blog/[slug]/page.js
index fc27a13..12b84b3 100644
--- a/app/blog/[slug]/page.js
+++ b/app/blog/[slug]/page.js
@@ -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 (
-
-
-
+ <>
+ {jsonLd && (
+
+ )}
+
+
+
+ >
);
}
diff --git a/app/blogs/page.js b/app/blogs/page.js
index e0f39b0..2d29656 100644
--- a/app/blogs/page.js
+++ b/app/blogs/page.js
@@ -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 (
diff --git a/app/clinic/[slug]/page.js b/app/clinic/[slug]/page.js
index b434599..01155f3 100644
--- a/app/clinic/[slug]/page.js
+++ b/app/clinic/[slug]/page.js
@@ -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 (
-
-
-
+ <>
+ {jsonLd && (
+
+ )}
+
+
+
+ >
);
}
diff --git a/app/clinics/page.js b/app/clinics/page.js
index c830b80..1251526 100644
--- a/app/clinics/page.js
+++ b/app/clinics/page.js
@@ -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();
diff --git a/app/doctor/[slug]/page.js b/app/doctor/[slug]/page.js
index f5bfa19..32c14be 100644
--- a/app/doctor/[slug]/page.js
+++ b/app/doctor/[slug]/page.js
@@ -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 (
-
-
-
+ <>
+ {jsonLd && (
+
+ )}
+
+
+
+ >
);
}
diff --git a/app/doctors/page.js b/app/doctors/page.js
index ebeaab8..51692bd 100644
--- a/app/doctors/page.js
+++ b/app/doctors/page.js
@@ -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();
diff --git a/app/layout.js b/app/layout.js
index dbcaeb1..c8614f6 100644
--- a/app/layout.js
+++ b/app/layout.js
@@ -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"],
},
};
diff --git a/app/sitemap.js b/app/sitemap.js
index de37f08..2b10a50 100644
--- a/app/sitemap.js
+++ b/app/sitemap.js
@@ -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 [];
}
-}
\ No newline at end of file
+}
diff --git a/app/specialties/page.js b/app/specialties/page.js
index f8764e9..21c627f 100644
--- a/app/specialties/page.js
+++ b/app/specialties/page.js
@@ -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 (