fix: seo friendly

This commit is contained in:
reza karimi
2026-10-17 17:20:59 +03:30
parent f6c889e633
commit 3d7485bbfd
26 changed files with 417 additions and 192 deletions
+59 -105
View File
@@ -1,105 +1,59 @@
import React from 'react'
import {
MDBCard,
MDBCardImage,
MDBCardBody,
MDBCardTitle,
MDBCardText,
MDBRow,
MDBCol,
MDBContainer,
MDBCardHeader,
MDBBtn,
MDBIcon,
MDBInputGroupText,
MDBTypography
} from 'mdb-react-ui-kit'
import axios from 'axios'
import Link from 'next/link'
import AppContainer from './appContainer'
import { useRouter } from 'next/router'
import { AppUrl } from '../lib/api'
import { DownloadIcon } from '@chakra-ui/icons'
const News = ({ data, children }) => {
const router = useRouter()
const query = router.query.list
const dataPage = (
<div className='lg:mx-20 sm:mx-5'>
<h5 style={{ marginTop: '2rem' }} className={'right-align font-weight-bold'}>
اطلاعیه ها{' '}
</h5>
<hr />
<div>
<MDBTypography
style={{ direction: 'rtl', borderRadius: '6px solid', borderRight: '6px solid #1266f1', borderLeft: 'none' }}
note
noteColor='primary'
>
<DownloadIcon className={'ml-2'} />
<a
title={'دانلود دفترچه معرفی و فروش سهام عمومی بیمارستان تخصصی مهر ایرانیان پرند'}
download
href={'./take-stock.pdf'}
>
{' '}
دانلود دفترچه معرفی و فروش سهام عمومی بیمارستان تخصصی مهر پرند
</a>
</MDBTypography>
<MDBTypography
style={{ direction: 'rtl', borderRadius: '6px solid', borderRight: '6px solid #1266f1', borderLeft: 'none' }}
note
noteColor='primary'
>
<DownloadIcon className={'ml-2'} />
<a
title={'دانلود طرح توجیهی بیمارستان تخصصی مهر ایرانیان پرند'}
download
href={'./Explanatory_plan_of_Mehr_ranian_Parand_Hospital.pdf'}
>
{' '}
دانلود طرح توجیهی بیمارستان تخصصی مهر ایرانیان پرند
</a>
</MDBTypography>
</div>
<br />
{/* <MDBRow dir={'rtl'} className='row-cols-1 row-cols-md-3 g-4'>
{data && data.map(({ id, slug, title }) => (
<MDBCol key={id}>
<Link href={`page/news/${id}`} >
<MDBCard className='mb-3 h-3/12 new-card'>
<MDBCardHeader title={title} className={'right-align bk-med RTL font-weight-bold border-bottom-1 border-white '}>{title} </MDBCardHeader>
<MDBCardBody>
<MDBCardText className={'right-align RTL text-summery'}>
{slug}
</MDBCardText>
<div className={'flex justify-content-end'}>
<MDBBtn rounded className=' RTL text-primary-teal bg-white'>
مشاهده
</MDBBtn>
</div>
</MDBCardBody>
</MDBCard>
</Link>
</MDBCol>
))}
</MDBRow> */}
{children}
</div>
)
return <div>{/* {query ? <AppContainer children={dataPage} title={'اطلاعیه ها '} /> : dataPage} */}</div>
}
export default News
export const getServerSideProps = async context => {
// const allPostsData = await axios.get(AppUrl('news'))
// const data = await allPostsData.data
return {
props: {
data: null,
fallback: false
} // will be passed to the page component as props
}
}
import React from 'react'
import axios from 'axios'
import Link from 'next/link'
import { useRouter } from 'next/router'
import AppContainer from './appContainer'
import { AppUrl } from '../lib/api'
import { newsSeo } from '../lib/seo'
export default function News({ data = [], unavailable = false, children }) {
const router = useRouter()
const isHome = router.pathname === '/'
const posts = (Array.isArray(data) ? data : []).filter(post => post && post.title && /^\d+$/.test(String(post.id)))
if (isHome && posts.length === 0) return null
const content = (
<section className='lg:mx-20 sm:mx-5 my-5 RTL' aria-labelledby='news-title'>
{isHome ? <h2 id='news-title'>اخبار و اطلاعیه‌ها</h2> : <h1 id='news-title'>اخبار و اطلاعیه‌های بیمارستان مهر پرند</h1>}
<hr />
{unavailable && <p role='status'>دریافت اخبار موقتاً امکان‌پذیر نیست. لطفاً کمی بعد دوباره مراجعه کنید.</p>}
<div className='row row-cols-1 row-cols-md-3 g-4'>
{posts.map(post => (
<article className='col' key={post.id}>
<div className='card h-100 p-4'>
<h2 className='h5'>
<Link href={`/page/news/${post.id}`}><a>{post.title}</a></Link>
</h2>
<p>{newsSeo(post, post.id).description}</p>
<Link href={`/page/news/${post.id}`}><a>مشاهده خبر</a></Link>
</div>
</article>
))}
</div>
{!isHome && (
<div className='mt-4'>
<h2 className='h5'>فایل‌های معرفی بیمارستان</h2>
<p><a download href='/take-stock.pdf'>دانلود دفترچه معرفی و فروش سهام عمومی بیمارستان مهر پرند</a></p>
<p><a download href='/Explanatory_plan_of_Mehr_ranian_Parand_Hospital.pdf'>دانلود طرح توجیهی بیمارستان مهر پرند</a></p>
</div>
)}
{children}
</section>
)
return isHome ? content : <AppContainer>{content}</AppContainer>
}
export async function getServerSideProps({ res }) {
try {
const { data } = await axios.get(AppUrl('news'), { timeout: 10000 })
const posts = Array.isArray(data) ? data : data && data.data
if (!Array.isArray(posts)) throw new Error('Unexpected news response')
return { props: { data: posts } }
} catch (error) {
console.warn('News list: API unavailable:', error.message)
res.statusCode = 503
res.setHeader('Retry-After', '60')
return { props: { data: [], unavailable: true } }
}
}
+17 -5
View File
@@ -7,7 +7,7 @@ import "../styles/simple-grid.css"
import 'nprogress/nprogress.css';
import "react-lightbox-pack/dist/index.css";
import Router from "next/router";
import React, {useState} from "react";
import React, {useEffect, useState} from "react";
import Loader from "./components/Loader";
import NProgress from 'nprogress';
import {ChakraProvider} from "@chakra-ui/react";
@@ -15,6 +15,7 @@ import { extendTheme } from '@chakra-ui/react'
import { StepsStyleConfig as Steps } from 'chakra-ui-steps';
import {Provider} from "react-redux";
import {CoreStore} from "../src/redux/store/store";
import Seo from '../src/components/Seo';
const theme = extendTheme({
components: {
@@ -25,16 +26,27 @@ const theme = extendTheme({
export default function App({ Component, pageProps }) {
const [Loading,setLoading] = useState(false);
Router.events.on('routeChangeStart',(url) => {
useEffect(() => {
const start = () => {
NProgress.start()
setLoading(true)
})
Router.events.on('routeChangeComplete',(url) => {
}
const complete = () => {
NProgress.done()
setLoading(false)
})
}
Router.events.on('routeChangeStart', start)
Router.events.on('routeChangeComplete', complete)
Router.events.on('routeChangeError', complete)
return () => {
Router.events.off('routeChangeStart', start)
Router.events.off('routeChangeComplete', complete)
Router.events.off('routeChangeError', complete)
}
}, [])
return (
<Provider store={CoreStore}>
<Seo metadata={pageProps.seo || (pageProps.statusCode >= 400 ? { noindex: true } : {})} />
{Loading && <Loader />}
<ChakraProvider resetCSS={false} theme={theme}>
{/* <link href="https://use.fontawesome.com/releases/v5.15.1/css/all.css" rel="stylesheet" /> */}
+15
View File
@@ -0,0 +1,15 @@
import Document, { Html, Head, Main, NextScript } from 'next/document'
export default class MyDocument extends Document {
render() {
return (
<Html lang='fa' dir='rtl'>
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
}
+26 -25
View File
@@ -1,25 +1,26 @@
const { SitemapStream, streamToPromise } = require("sitemap");
const { Readable } = require("stream");
export default async (req, res) => {
// An array with your links
const links = [
{ url: "/page/news/36/", priority: 0.3 },
{ url: "/page/about", priority: 0.3 },
{ url: "/recruitment", priority: 0.3 },
{ url: "/contact-us", priority: 0.3 }
];
// Create a stream to write to
const stream = new SitemapStream({ hostname: `https://${req.headers.host}` });
res.writeHead(200, {
"Content-Type": "application/xml",
});
const xmlString = await streamToPromise(
Readable.from(links).pipe(stream)
).then((data) => data.toString());
res.end(xmlString);
};
import axios from 'axios'
import { SitemapStream, streamToPromise } from 'sitemap'
import { Readable } from 'stream'
import { AppUrl } from '../../lib/api'
import { SITE_URL, sitemapLinks } from '../../lib/seo'
export default async function sitemap(req, res) {
let news = []
let newsAvailable = false
try {
const { data } = await axios.get(AppUrl('news'), { timeout: 5000 })
const posts = Array.isArray(data) ? data : data && data.data
if (!Array.isArray(posts)) throw new Error('Unexpected news response')
news = posts
newsAvailable = true
} catch (error) {
// Keep core pages discoverable during a news API outage; retry soon.
console.warn('Sitemap: news API unavailable:', error.message)
}
const stream = new SitemapStream({ hostname: SITE_URL })
const xml = await streamToPromise(Readable.from(sitemapLinks(news).map(url => ({ url }))).pipe(stream))
res.setHeader('Content-Type', 'application/xml; charset=utf-8')
res.setHeader('Cache-Control', newsAvailable ? 'public, s-maxage=3600, stale-while-revalidate=86400' : 'public, s-maxage=60')
res.status(200).send(xml.toString())
}
+2 -9
View File
@@ -6,18 +6,11 @@ import dynamic from 'next/dynamic'
import { Router } from 'next/router'
const Chat = dynamic(() => import('./pack'), { ssr: false })
const AppContainer = ({ children, title }) => {
const AppContainer = ({ children }) => {
return (
<div>
<Head>
<title>{title ? title : 'بیمارستان مهر پرند '}</title>
<meta name='description' content='بیمارستان 96 تخت خوابی مهر ایرانیان پرند واقع در شهر پرند ' />
<meta
name='keywords'
content=' درمانگاه مهرپرند,بیمارستان مهرایرانیان پرند ,بیمارستان, بیمارستان مهر, سهام,پرند'
/>
<meta name="google-site-verification" content="hygRCp6fRa9qi8RXCaAosXK4mBcnCJVRH1ayHtAQy8o" />
<link rel='icon' type='image/x-icon' href='/favicon.ico' />
<script
type='text/javascript'
dangerouslySetInnerHTML={{
@@ -33,7 +26,7 @@ const AppContainer = ({ children, title }) => {
</Head>
<Chat />
<Header />
{children}
<main id='main-content'>{children}</main>
<Footer />
</div>
)
+2 -2
View File
@@ -248,7 +248,7 @@ const Service = () => {
<MDBCardBody style={{ display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
<Link href={'components/service'}>
<MDBCardText className={'bk'}>
<h1 className={'RTL dana'}>بیشتر ...</h1>{' '}
<span className={'RTL dana'}>بیشتر ...</span>{' '}
</MDBCardText>
</Link>
</MDBCardBody>
@@ -262,7 +262,7 @@ const Service = () => {
) : (
<AppContainer title={'کلینیک ها و بخش ها '}>
<div className='parallax-container'>
<h2 className={'center-align text-dark service-text dana font-weight-bold'}> کلینیک ها و بخش ها </h2>
<h1 className={'center-align text-dark service-text dana font-weight-bold'}> کلینیک ها و بخش ها </h1>
<Image
loader={Parallax}
src='Parallax.png'
+3 -3
View File
@@ -39,12 +39,12 @@ const Slider = () => {
</SwiperSlide> */}
<SwiperSlide key={3} style={{ cursor: 'pointer' }} className='pointer'>
<Link href='/landing'>
<img src={sliderMIpStart} />
<a><img src={sliderMIpStart} alt='معرفی بیمارستان مهر پرند و آخرین اخبار افتتاح' width='2071' height='759' style={{ width: '100%', height: 'auto' }} /></a>
</Link>
</SwiperSlide>
<SwiperSlide key={3} style={{ cursor: 'pointer' }} className='pointer'>
<SwiperSlide key={4} style={{ cursor: 'pointer' }} className='pointer'>
<Link href='/recruitment'>
<img src={sliderJob} />
<a><img src={sliderJob} alt='فرصت‌های شغلی و استخدام در بیمارستان مهر پرند' width='2877' height='1120' style={{ width: '100%', height: 'auto' }} loading='lazy' /></a>
</Link>
</SwiperSlide>
</Swiper>
+9 -5
View File
@@ -66,7 +66,7 @@ export default function Home({ data_NEWS }) {
font-size: 0.85rem;
font-weight: 700;
}
.opening-copy h2 {
.opening-copy h1 {
margin: 0;
color: #fff;
font-size: clamp(1.55rem, 3vw, 2.5rem);
@@ -123,7 +123,7 @@ export default function Home({ data_NEWS }) {
<section className='opening-announcement' aria-labelledby='opening-announcement-title'>
<div className='opening-copy'>
<span className='opening-label'>اطلاع‌رسانی افتتاح</span>
<h2 id='opening-announcement-title'>در چند قدمی افتتاح بیمارستان مهر پرند</h2>
<h1 id='opening-announcement-title'>در چند قدمی افتتاح بیمارستان مهر پرند</h1>
<p>اخبار و زمان دقیق افتتاح بیمارستان از طریق همین وب‌سایت اطلاع‌رسانی خواهد شد.</p>
</div>
<div className='opening-actions'>
@@ -136,9 +136,11 @@ export default function Home({ data_NEWS }) {
<div className='lg:mx-20 sm:mx-5'>
<SimpleGrid spacing={15} columns={[1, 2, 2]}>
<Link href={'/page/contact-us'}>
<a>
<MDBCard className={'bg-neutral-100 fst-box hover:bg-neutral-200 cursor-pointer hover:shadow-2xl'}>
<MDBCardBody className={'flex justify-content-center'}>ارتباط با ما</MDBCardBody>
</MDBCard>
</a>
</Link>
{/* <Link href={'/page/sales'}>
<MDBCard className={'bg-neutral-100 fst-box hover:bg-neutral-200 cursor-pointer hover:shadow-2xl'}>
@@ -146,9 +148,11 @@ export default function Home({ data_NEWS }) {
</MDBCard>
</Link> */}
<Link href={'/questions'}>
<a>
<MDBCard className={'bg-neutral-100 fst-box hover:bg-neutral-200 cursor-pointer hover:shadow-2xl'}>
<MDBCardBody className={'flex justify-content-center'}>سوالات متداول</MDBCardBody>
</MDBCard>
</a>
</Link>
{/* <Link href={'/stock-unit'}>
<MDBCard className={'bg-neutral-100 fst-box hover:bg-neutral-200 cursor-pointer hover:shadow-2xl'}>
@@ -169,15 +173,15 @@ export default function Home({ data_NEWS }) {
<Link
style={{ cursor: 'pointer' }}
className={'text-white'}
href={{ pathname: '/News', query: { list: 'list' } }}
href='/News'
>
<MDBBtn style={{ cursor: 'pointer' }} className='mx-2 RTL bg-dark shadow-1-strong' color=''>
<a className='btn mx-2 RTL bg-dark shadow-1-strong text-white'>
<MDBIcon className='ms-1 ' icon='plus' size='sm' />
<span style={{ cursor: 'pointer !important' }} className={'text-white '}>
{' '}
بیشتر
</span>
</MDBBtn>
</a>
</Link>
</div>
</News>
+2 -2
View File
@@ -7,11 +7,11 @@ const MedicalEquipment = () => {
return (
<AppContainer title={'واحد تجهیزات پزشکی'}>
<div className='parallax-container'>
<h2 className={'center-align text-white drop-shadow service-text dana font-weight-bold'}>واحد تجهیزات پزشکی</h2>
<h1 className={'center-align text-white drop-shadow service-text dana font-weight-bold'}>واحد تجهیزات پزشکی</h1>
<Image
src={MedicalStock}
className={'responsive-img shadow-5'}
alt='Picture of the author'
alt='تجهیزات پزشکی بیمارستان مهر پرند'
width={4090}
height={2000}
/>
+2 -2
View File
@@ -72,10 +72,10 @@ const Directors = () => {
return (
<AppContainer title={' تیم پروژه بیمارستان مهر پرند'}>
<div className='parallax-container'>
<h2 className={'center-align text-white header_back service-text dana font-weight-bold'}>
<h1 className={'center-align text-white header_back service-text dana font-weight-bold'}>
{' '}
تیم پروژه بیمارستان{' '}
</h2>
</h1>
<Image
loader={Parallax}
src='Parallax.png'
+4 -4
View File
@@ -19,14 +19,14 @@ const ContactUs = () => {
return (
<>
<AppContainer title={'درباره ما'}>
<AppContainer title={'تماس با ما'}>
<div className="parallax-container">
<h2 className={'center-align text-white header_back service-text dana font-weight-bold'}> تماس با ما </h2>
<h1 className={'center-align text-white header_back service-text dana font-weight-bold'}> تماس با ما </h1>
<Image
loader={Parallax}
src="Parallax.png"
className={'responsive-img shadow-5'}
alt="Picture of the author"
alt="بیمارستان مهر پرند؛ آدرس و راه‌های ارتباطی"
width={2050}
height={950}
/>
@@ -102,4 +102,4 @@ const ContactUs = () => {
);
};
export default ContactUs;
export default ContactUs;
+2 -2
View File
@@ -20,9 +20,9 @@ const CooperationSales = () => {
{/* <img src ={cartmo} alt="" /> */}
</div>
<h2 className="text-white text-lg font-bold lg:text-[28px] font-extrabold text-center pt-8 xl:pt-16">
<h1 className="text-white text-lg font-bold lg:text-[28px] font-extrabold text-center pt-8 xl:pt-16">
همکاری در فروش سهام
</h2>
</h1>
<p className="text-white w-[85%] md:w-[60%] text-sm lg:text-[22px] font-medium leading-[1.8rem] lg:w-[42%] xl:w-[40%] mx-auto text-center lg:leading-8 xl:leading-10 py-2 lg:py-4">
با ثبت نام در سامانه خرید سهام، کد معرف شما ایجاد و قابل اشتراک
گذاری می شود. کد معرف یکتای خود را با دوستان خود به اشتراک بگذارید
+2 -2
View File
@@ -18,7 +18,7 @@ const Docs = () => {
<AppContainer title={' اساسنامه شرکت'}>
<div className="parallax-container">
<div className={'d-flex'}>
<h2 className={'center-align text-white header_back service-text dana font-weight-bold'}> اسناد رسمی شرکت پزشکی مهر ایرانیان پرند </h2>
<h1 className={'center-align text-white header_back service-text dana font-weight-bold'}> اسناد رسمی شرکت پزشکی مهر ایرانیان پرند </h1>
</div>
<Image
loader={Parallax}
@@ -47,4 +47,4 @@ const Docs = () => {
);
};
export default Docs;
export default Docs;
+11 -7
View File
@@ -19,6 +19,7 @@ import {
} from 'mdb-react-ui-kit'
import Image from 'next/image'
import { AppFile, AppUrl } from '../../../lib/api'
import { newsSeo } from '../../../lib/seo'
import { Tag, TagLabel, HStack } from '@chakra-ui/react'
const moment = require('moment-jalaali')
@@ -40,7 +41,7 @@ const News = ({ post }) => {
loader={Parallax}
src='Parallax.png'
className={'responsive-img shadow-5'}
alt='Picture of the author'
alt={post.title}
width={2050}
height={1050}
priority
@@ -175,11 +176,14 @@ const News = ({ post }) => {
export default News
export const getServerSideProps = async context => {
const { query } = context
const posData = await axios.get(AppUrl(`news/${query.slug}`))
const post = await posData.data
return {
props: { post, fallback: false } // will be passed to the page component as props
const { slug } = context.params
try {
const { data: post } = await axios.get(AppUrl(`news/${encodeURIComponent(slug)}`), { timeout: 10000 })
if (!post || !post.title || typeof post.text !== 'string') return { notFound: true }
return { props: { post, seo: newsSeo(post, slug) } }
} catch (error) {
if (error.response && error.response.status === 404) return { notFound: true }
// Temporary upstream failures must stay 5xx rather than removing real articles from search.
throw error
}
}
+1 -1
View File
@@ -44,7 +44,7 @@ const Sales = () => {
return (
<div id='callstock' className=' text-center py-8 m '>
<h1 className='text-[#3c3c3c] text-[28px] font-bold mt-5 '>ارتباط با ما </h1>
<h2 className='text-[#3c3c3c] text-[28px] font-bold mt-5 '>ارتباط با ما </h2>
<span className='text-[#656565] text-base p-2 md:text-[22px] md:p-0 font-light'>
برای ارتباط با ما فرم زیر را تکمیل کنید
</span>
+5 -5
View File
@@ -31,9 +31,9 @@ const Recruitment = dataoccupational => {
<div className='parallax-container'>
<div className={'flex flex-column'}>
<div className={'mb-5'}>
<h2 className={'center-align bk-med text-white header_back service-text dana font-weight-bold'}>
<h1 className={'center-align bk-med text-white header_back service-text dana font-weight-bold'}>
فرصت های شغلی
</h2>
</h1>
</div>
<br />
<br />
@@ -82,11 +82,11 @@ const Recruitment = dataoccupational => {
<FiUsers size={35} />
</div>
<MDBCardTitle className={'bk-med mt-2 right'}>{item.title} </MDBCardTitle>
<Link href={`recruitment/item/${item.id}`}>
<MDBBtn rounded size={'sm'} className={'mt-2 flex align-items-center bk-light bg-primary-teal'}>
<Link href={`/recruitment/item/${item.id}`}>
<a className={'btn btn-rounded btn-sm mt-2 flex align-items-center bk-light bg-primary-teal text-white'}>
مشاهده
<FiArrowLeft className={'mr-2'} />
</MDBBtn>
</a>
</Link>
</MDBCardBody>
</MDBCard>
+7 -5
View File
@@ -19,7 +19,6 @@ import axios from 'axios'
import { AppProduction } from '../../../lib/api'
const Recruitment = jobs => {
console.log(jobs)
const Parallax = ({ quality }) => {
return `../../resource/parallax/33012321.jpg`
}
@@ -32,10 +31,10 @@ const Recruitment = jobs => {
<div className='parallax-container'>
<div className={'flex flex-column'}>
<div className={'mb-5'}>
<h2 className={'center-align bk-med text-white header_back service-text dana font-weight-bold'}>
<h1 className={'center-align bk-med text-white header_back service-text dana font-weight-bold'}>
فرصت های شغلی بیمارستان مهر پرند
</h2>
فرصت‌های شغلی {jobs.jobs.name.title} بیمارستان مهر پرند
</h1>
</div>
<br />
<br />
@@ -127,7 +126,10 @@ export async function getServerSideProps(ctx) {
const jobs = await res
// Pass data to the page via props
return { props: { jobs } }
return { props: { jobs, seo: {
title: `استخدام ${jobs.name.title}`,
description: `فرصت‌های شغلی گروه ${jobs.name.title} در بیمارستان مهر پرند؛ مشاهده عنوان‌های شغلی و ثبت درخواست استخدام.`
} } }
}
export default Recruitment
+1
View File
@@ -39,6 +39,7 @@ const StockUnit = () => {
/>
</div>
<div className={'mb-5 mt-5 mx-5'}>
<h1 className='text-center'>واحد امور سهام بیمارستان مهر پرند</h1>
<div className='my-4'>
<SimpleGrid spacing={15} columns={[1, 2]}>
<Link href={'/stock-unit/stock-check'}>