fix: seo friendly
This commit is contained in:
@@ -1 +1,34 @@
|
|||||||
This is a starter template for [Learn Next.js](https://nextjs.org/learn).
|
# وبسایت بیمارستان مهر پرند
|
||||||
|
|
||||||
|
پروژه با Next.js 11 و Pages Router اجرا میشود.
|
||||||
|
|
||||||
|
## اجرا و بررسی
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm run dev
|
||||||
|
npm run build
|
||||||
|
npm run start
|
||||||
|
npm test
|
||||||
|
```
|
||||||
|
|
||||||
|
تستها با test runner داخلی Node.js اجرا میشوند و به Node.js 20 یا جدیدتر نیاز دارند. بیلد فعلی با Node.js 14.21.3 بررسی شده است؛ ارتقای نسخههای قدیمی Next.js و Node.js باید جداگانه انجام شود. تنظیمات و نسخه ESLint موجود با فرایند lint این نسخه Next.js سازگار نیست و بیلد هشدار میدهد؛ موفقیت بیلد به معنی موفقیت lint نیست.
|
||||||
|
|
||||||
|
## تنظیمات سئو
|
||||||
|
|
||||||
|
- عنوان و توضیحات صفحات عمومی و آدرس اصلی سایت در `lib/seo.js` تعریف شدهاند. هنگام افزودن صفحه عمومی، آن را به `PUBLIC_PAGES` اضافه کنید؛ این فهرست منبع صفحات ثابت sitemap نیز هست.
|
||||||
|
- `src/components/Seo.jsx` از طریق `_app.js` متادیتا، canonical، Open Graph، Twitter Card و JSON-LD را در HTML سرور تولید میکند. متادیتای محتوای پویا را با `props.seo` از `getServerSideProps` ارسال کنید.
|
||||||
|
- canonical از دامنه ثابت HTTPS ساخته میشود و query string و fragment را حذف میکند. عنوان سفارشی صفحات را در registry یا `props.seo` تنظیم کنید؛ prop قدیمی `AppContainer.title` دیگر منبع متادیتا نیست.
|
||||||
|
- صفحات فنی، کامپوننتهای مستقل، فرمهای درخواست و پیگیری و نمودارهایی که محتوایشان صرفاً سمت مرورگر است، بهصورت پیشفرض `noindex, follow` هستند. صفحه عمومی بخشها در `/components/service` استثنا است. `noindex` جایگزین کنترل دسترسی نیست.
|
||||||
|
- سند HTML دارای `lang="fa"` و `dir="rtl"` است. داده ساختاریافته بیمارستان از اطلاعات تماس موجود سایت ساخته شده؛ ساعت کاری یا تاریخ افتتاح فرضی درج نشده است.
|
||||||
|
|
||||||
|
## نقشه سایت و اخبار
|
||||||
|
|
||||||
|
آدرس عمومی نقشه سایت `https://miphospital.ir/sitemap.xml` است و به handler موجود `/api/my-sitemap` بازنویسی میشود. robots.txt همین آدرس HTTPS را معرفی میکند.
|
||||||
|
|
||||||
|
نقشه سایت صفحات عمومی و شناسههای معتبر اخبار برگشتی از `AppUrl('news')` را شامل میشود. اگر API اخبار در دسترس نباشد، صفحات ثابت همچنان برگردانده میشوند و زمان cache به ۶۰ ثانیه کاهش مییابد. API فعلی باید آرایه اخبار یا آبجکت دارای آرایه `data` برگرداند؛ در صورت اضافه شدن pagination، پیمایش صفحههای API نیز باید اضافه شود.
|
||||||
|
|
||||||
|
خبر ناموجود پاسخ ۴۰۴ دارد؛ خرابی موقت API جزئیات خبر به خطای سرور منجر میشود. فهرست اخبار هنگام خرابی API پاسخ ۵۰۳ با `Retry-After` میدهد تا خطا بهعنوان محتوای خالی موفق ثبت نشود.
|
||||||
|
|
||||||
|
در بررسی محلی، API فعلی اخبار (`https://mehrara-api.iran.liara.run/api/news`) پاسخ ۴۰۴ داد؛ در نتیجه مسیرهای واقعی اخبار و محتوای زنده آنها تا رفع API قابل بررسی نیستند. بعد از انتشار، sitemap را در Google Search Console ثبت و صفحات اصلی را با URL Inspection بررسی کنید.
|
||||||
|
|
||||||
|
مرجع: [راهنمای فنی سئو برای توسعهدهندگان در Google Search Central](https://developers.google.com/search/docs/fundamentals/get-started-developers).
|
||||||
|
|||||||
+116
@@ -0,0 +1,116 @@
|
|||||||
|
const SITE_URL = 'https://miphospital.ir'
|
||||||
|
const SITE_NAME = 'بیمارستان مهر پرند'
|
||||||
|
const DEFAULT_DESCRIPTION = 'وبسایت رسمی بیمارستان مهر ایرانیان پرند؛ معرفی بیمارستان، بخشهای تخصصی، اخبار افتتاح، فرصتهای شغلی و راههای ارتباط با ما.'
|
||||||
|
const DEFAULT_IMAGE = `${SITE_URL}/resource/logo/logo-green.png`
|
||||||
|
|
||||||
|
// This registry also defines the public, canonical pages included in the sitemap.
|
||||||
|
const PUBLIC_PAGES = {
|
||||||
|
'/': [SITE_NAME, DEFAULT_DESCRIPTION],
|
||||||
|
'/landing': ['معرفی بیمارستان مهر پرند', 'با بیمارستان مهر پرند، زیرساختهای درمانی، بخشهای تخصصی، امکانات و مسیرهای ارتباط با بیمارستان آشنا شوید.'],
|
||||||
|
'/page/about': ['درباره بیمارستان', 'معرفی بیمارستان مهر ایرانیان پرند، اهداف، امکانات و آخرین وضعیت پیشرفت پروژه بیمارستان در شهر پرند.'],
|
||||||
|
'/page/contact-us': ['تماس با ما', 'آدرس بیمارستان مهر پرند در فاز ۵، بلوار شهید قاسم سلیمانی؛ شماره تماس ۰۲۱-۵۳۴۵۷۰۰۰، ایمیل و موقعیت بیمارستان روی نقشه.'],
|
||||||
|
'/page/Directors': ['تیم پروژه بیمارستان', 'آشنایی با اعضای تیم و مدیران پروژه بیمارستان مهر ایرانیان پرند.'],
|
||||||
|
'/page/docs': ['اسناد رسمی شرکت', 'دسترسی به اساسنامه و اسناد رسمی شرکت پزشکی مهر ایرانیان پرند.'],
|
||||||
|
'/page/cooperationsales': ['همکاری در فروش', 'اطلاعات و شرایط همکاری در فروش با شرکت پزشکی مهر ایرانیان پرند.'],
|
||||||
|
'/Gallery': ['گالری تصاویر بیمارستان', 'تصاویر ساختمان، فضاهای داخلی و روند پیشرفت پروژه بیمارستان مهر پرند.'],
|
||||||
|
'/News': ['اخبار و اطلاعیهها', 'اخبار، اطلاعیهها و فایلهای معرفی بیمارستان مهر ایرانیان پرند.'],
|
||||||
|
'/questions': ['سوالات متداول', 'پاسخ به سوالات متداول درباره بیمارستان مهر پرند و امکانات باشگاه سهامداران.'],
|
||||||
|
'/recruitment': ['فرصتهای شغلی و استخدام', 'مشاهده فرصتهای شغلی و گروههای استخدامی بیمارستان مهر ایرانیان پرند و ارسال درخواست همکاری.'],
|
||||||
|
'/medical-equipment': ['واحد تجهیزات پزشکی', 'آشنایی با واحد تجهیزات پزشکی بیمارستان مهر پرند و راههای ارتباط با این واحد.'],
|
||||||
|
'/components/service': ['کلینیکها و بخشها', 'معرفی کلینیکها و بخشهای تخصصی بیمارستان مهر پرند و امکانات هر بخش.'],
|
||||||
|
'/stock-unit': ['واحد امور سهام', 'اطلاعات واحد امور سهام بیمارستان مهر پرند و دسترسی به خدمات و راههای ارتباط با این واحد.']
|
||||||
|
}
|
||||||
|
|
||||||
|
const UTILITY_TITLES = {
|
||||||
|
'/CooperationRequest': 'درخواست همکاری',
|
||||||
|
'/recruitment/inquiry': 'استعلام وضعیت درخواست استخدام',
|
||||||
|
'/stock-unit/stock-check': 'پیگیری درخواست سهام',
|
||||||
|
'/stock-unit/stock-sales': 'درخواست خرید سهام',
|
||||||
|
'/stock-unit/stock-transfer': 'درخواست انتقال سهام',
|
||||||
|
'/stock-price': 'قیمت سهام بیمارستان مهر پرند',
|
||||||
|
'/page/stock-price': 'نمودار قیمت سهام',
|
||||||
|
'/page/sales': 'ارتباط با واحد فروش',
|
||||||
|
'/smartadvice': 'مشاوره سرمایهگذاری'
|
||||||
|
}
|
||||||
|
|
||||||
|
function canonicalUrl(path = '/') {
|
||||||
|
const clean = String(path).split(/[?#]/)[0]
|
||||||
|
if (!clean.startsWith('/') || clean.startsWith('//') || clean.includes('\\')) return `${SITE_URL}/`
|
||||||
|
return SITE_URL + (clean.replace(/\/+$/, '') || '/')
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSeo(path = '/', overrides = {}) {
|
||||||
|
const canonical = canonicalUrl(path)
|
||||||
|
const pathname = canonical.slice(SITE_URL.length)
|
||||||
|
const page = PUBLIC_PAGES[pathname]
|
||||||
|
const dynamicPublic = /^\/page\/news\/[^/]+$/.test(pathname) || /^\/recruitment\/item\/[^/]+$/.test(pathname)
|
||||||
|
return {
|
||||||
|
title: page ? page[0] : UTILITY_TITLES[pathname] || (pathname.startsWith('/recruitment/item/request/') ? 'ثبت درخواست استخدام' : SITE_NAME),
|
||||||
|
description: page ? page[1] : DEFAULT_DESCRIPTION,
|
||||||
|
image: DEFAULT_IMAGE,
|
||||||
|
type: 'website',
|
||||||
|
noindex: !page && !dynamicPublic,
|
||||||
|
...overrides,
|
||||||
|
canonical
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function newsSeo(post, slug) {
|
||||||
|
const text = String(post.text || '')
|
||||||
|
.replace(/<(script|style)\b[^>]*>[\s\S]*?<\/\1>/gi, '')
|
||||||
|
.replace(/<[^>]*>/g, ' ')
|
||||||
|
.replace(/ | /g, ' ')
|
||||||
|
.replace(/&/g, '&')
|
||||||
|
.replace(/\s+/g, ' ').trim()
|
||||||
|
const description = text.slice(0, 160) || post.title
|
||||||
|
const image = post.cover ? `https://mehrara-api.iran.liara.run/panel-public/uploads/${post.cover}` : DEFAULT_IMAGE
|
||||||
|
const canonical = canonicalUrl(`/page/news/${encodeURIComponent(slug)}`)
|
||||||
|
return {
|
||||||
|
title: post.title,
|
||||||
|
description,
|
||||||
|
image,
|
||||||
|
type: 'article',
|
||||||
|
structuredData: {
|
||||||
|
'@context': 'https://schema.org',
|
||||||
|
'@type': 'NewsArticle',
|
||||||
|
headline: post.title,
|
||||||
|
description,
|
||||||
|
image: [image],
|
||||||
|
mainEntityOfPage: canonical,
|
||||||
|
publisher: { '@id': `${SITE_URL}/#hospital` }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const hospitalSchema = {
|
||||||
|
'@context': 'https://schema.org',
|
||||||
|
'@type': 'Hospital',
|
||||||
|
'@id': `${SITE_URL}/#hospital`,
|
||||||
|
name: SITE_NAME,
|
||||||
|
alternateName: 'بیمارستان مهر ایرانیان پرند',
|
||||||
|
url: `${SITE_URL}/`,
|
||||||
|
logo: DEFAULT_IMAGE,
|
||||||
|
telephone: '+98-21-53457000',
|
||||||
|
email: 'contact-us@miphospital.ir',
|
||||||
|
address: {
|
||||||
|
'@type': 'PostalAddress',
|
||||||
|
addressCountry: 'IR',
|
||||||
|
addressRegion: 'تهران',
|
||||||
|
addressLocality: 'پرند',
|
||||||
|
streetAddress: 'فاز ۵، بلوار شهید قاسم سلیمانی، نبش بلوار رسول اکرم (ص)'
|
||||||
|
},
|
||||||
|
sameAs: ['https://www.instagram.com/miphospital/']
|
||||||
|
}
|
||||||
|
|
||||||
|
function serializeJsonLd(value) {
|
||||||
|
return JSON.stringify(value).replace(/</g, '\\u003c')
|
||||||
|
}
|
||||||
|
|
||||||
|
function sitemapLinks(news = []) {
|
||||||
|
const articles = news
|
||||||
|
.filter(post => post && /^\d+$/.test(String(post.id)))
|
||||||
|
.map(post => `/page/news/${post.id}`)
|
||||||
|
return [...new Set([...Object.keys(PUBLIC_PAGES), ...articles])]
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { SITE_URL, SITE_NAME, canonicalUrl, getSeo, newsSeo, hospitalSchema, serializeJsonLd, sitemapLinks }
|
||||||
+3
-2
@@ -4,13 +4,14 @@ module.exports = {
|
|||||||
experimental: {
|
experimental: {
|
||||||
outputStandalone: true
|
outputStandalone: true
|
||||||
},
|
},
|
||||||
siteUrl: 'https://miphospital.ir',
|
|
||||||
generateRobotsTxt: true, // (optional)
|
|
||||||
...withImages(),
|
...withImages(),
|
||||||
// future: {
|
// future: {
|
||||||
// webpack5: true
|
// webpack5: true
|
||||||
// },
|
// },
|
||||||
images: {
|
images: {
|
||||||
disableStaticImages: true
|
disableStaticImages: true
|
||||||
|
},
|
||||||
|
async rewrites() {
|
||||||
|
return [{ source: '/sitemap.xml', destination: '/api/my-sitemap' }]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"test": "node --test tests/*.test.cjs",
|
||||||
"dev": "next dev -p 2000",
|
"dev": "next dev -p 2000",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start -p 2000"
|
"start": "next start -p 2000"
|
||||||
|
|||||||
+43
-89
@@ -1,105 +1,59 @@
|
|||||||
import React from 'react'
|
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 axios from 'axios'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import AppContainer from './appContainer'
|
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
|
import AppContainer from './appContainer'
|
||||||
import { AppUrl } from '../lib/api'
|
import { AppUrl } from '../lib/api'
|
||||||
import { DownloadIcon } from '@chakra-ui/icons'
|
import { newsSeo } from '../lib/seo'
|
||||||
|
|
||||||
const News = ({ data, children }) => {
|
export default function News({ data = [], unavailable = false, children }) {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const query = router.query.list
|
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 dataPage = (
|
const content = (
|
||||||
<div className='lg:mx-20 sm:mx-5'>
|
<section className='lg:mx-20 sm:mx-5 my-5 RTL' aria-labelledby='news-title'>
|
||||||
<h5 style={{ marginTop: '2rem' }} className={'right-align font-weight-bold'}>
|
{isHome ? <h2 id='news-title'>اخبار و اطلاعیهها</h2> : <h1 id='news-title'>اخبار و اطلاعیههای بیمارستان مهر پرند</h1>}
|
||||||
اطلاعیه ها{' '}
|
|
||||||
</h5>
|
|
||||||
<hr />
|
<hr />
|
||||||
<div>
|
{unavailable && <p role='status'>دریافت اخبار موقتاً امکانپذیر نیست. لطفاً کمی بعد دوباره مراجعه کنید.</p>}
|
||||||
<MDBTypography
|
<div className='row row-cols-1 row-cols-md-3 g-4'>
|
||||||
style={{ direction: 'rtl', borderRadius: '6px solid', borderRight: '6px solid #1266f1', borderLeft: 'none' }}
|
{posts.map(post => (
|
||||||
note
|
<article className='col' key={post.id}>
|
||||||
noteColor='primary'
|
<div className='card h-100 p-4'>
|
||||||
>
|
<h2 className='h5'>
|
||||||
<DownloadIcon className={'ml-2'} />
|
<Link href={`/page/news/${post.id}`}><a>{post.title}</a></Link>
|
||||||
<a
|
</h2>
|
||||||
title={'دانلود دفترچه معرفی و فروش سهام عمومی بیمارستان تخصصی مهر ایرانیان پرند'}
|
<p>{newsSeo(post, post.id).description}</p>
|
||||||
download
|
<Link href={`/page/news/${post.id}`}><a>مشاهده خبر</a></Link>
|
||||||
href={'./take-stock.pdf'}
|
</div>
|
||||||
>
|
</article>
|
||||||
{' '}
|
))}
|
||||||
دانلود دفترچه معرفی و فروش سهام عمومی بیمارستان تخصصی مهر پرند
|
|
||||||
</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>
|
</div>
|
||||||
<br />
|
{!isHome && (
|
||||||
{/* <MDBRow dir={'rtl'} className='row-cols-1 row-cols-md-3 g-4'>
|
<div className='mt-4'>
|
||||||
{data && data.map(({ id, slug, title }) => (
|
<h2 className='h5'>فایلهای معرفی بیمارستان</h2>
|
||||||
<MDBCol key={id}>
|
<p><a download href='/take-stock.pdf'>دانلود دفترچه معرفی و فروش سهام عمومی بیمارستان مهر پرند</a></p>
|
||||||
<Link href={`page/news/${id}`} >
|
<p><a download href='/Explanatory_plan_of_Mehr_ranian_Parand_Hospital.pdf'>دانلود طرح توجیهی بیمارستان مهر پرند</a></p>
|
||||||
<MDBCard className='mb-3 h-3/12 new-card'>
|
</div>
|
||||||
<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}
|
{children}
|
||||||
</div>
|
</section>
|
||||||
)
|
)
|
||||||
return <div>{/* {query ? <AppContainer children={dataPage} title={'اطلاعیه ها '} /> : dataPage} */}</div>
|
|
||||||
|
return isHome ? content : <AppContainer>{content}</AppContainer>
|
||||||
}
|
}
|
||||||
|
|
||||||
export default News
|
export async function getServerSideProps({ res }) {
|
||||||
|
try {
|
||||||
export const getServerSideProps = async context => {
|
const { data } = await axios.get(AppUrl('news'), { timeout: 10000 })
|
||||||
// const allPostsData = await axios.get(AppUrl('news'))
|
const posts = Array.isArray(data) ? data : data && data.data
|
||||||
// const data = await allPostsData.data
|
if (!Array.isArray(posts)) throw new Error('Unexpected news response')
|
||||||
return {
|
return { props: { data: posts } }
|
||||||
props: {
|
} catch (error) {
|
||||||
data: null,
|
console.warn('News list: API unavailable:', error.message)
|
||||||
fallback: false
|
res.statusCode = 503
|
||||||
} // will be passed to the page component as props
|
res.setHeader('Retry-After', '60')
|
||||||
|
return { props: { data: [], unavailable: true } }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+17
-5
@@ -7,7 +7,7 @@ import "../styles/simple-grid.css"
|
|||||||
import 'nprogress/nprogress.css';
|
import 'nprogress/nprogress.css';
|
||||||
import "react-lightbox-pack/dist/index.css";
|
import "react-lightbox-pack/dist/index.css";
|
||||||
import Router from "next/router";
|
import Router from "next/router";
|
||||||
import React, {useState} from "react";
|
import React, {useEffect, useState} from "react";
|
||||||
import Loader from "./components/Loader";
|
import Loader from "./components/Loader";
|
||||||
import NProgress from 'nprogress';
|
import NProgress from 'nprogress';
|
||||||
import {ChakraProvider} from "@chakra-ui/react";
|
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 { StepsStyleConfig as Steps } from 'chakra-ui-steps';
|
||||||
import {Provider} from "react-redux";
|
import {Provider} from "react-redux";
|
||||||
import {CoreStore} from "../src/redux/store/store";
|
import {CoreStore} from "../src/redux/store/store";
|
||||||
|
import Seo from '../src/components/Seo';
|
||||||
|
|
||||||
const theme = extendTheme({
|
const theme = extendTheme({
|
||||||
components: {
|
components: {
|
||||||
@@ -25,16 +26,27 @@ const theme = extendTheme({
|
|||||||
export default function App({ Component, pageProps }) {
|
export default function App({ Component, pageProps }) {
|
||||||
|
|
||||||
const [Loading,setLoading] = useState(false);
|
const [Loading,setLoading] = useState(false);
|
||||||
Router.events.on('routeChangeStart',(url) => {
|
useEffect(() => {
|
||||||
|
const start = () => {
|
||||||
NProgress.start()
|
NProgress.start()
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
})
|
}
|
||||||
Router.events.on('routeChangeComplete',(url) => {
|
const complete = () => {
|
||||||
NProgress.done()
|
NProgress.done()
|
||||||
setLoading(false)
|
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 (
|
return (
|
||||||
<Provider store={CoreStore}>
|
<Provider store={CoreStore}>
|
||||||
|
<Seo metadata={pageProps.seo || (pageProps.statusCode >= 400 ? { noindex: true } : {})} />
|
||||||
{Loading && <Loader />}
|
{Loading && <Loader />}
|
||||||
<ChakraProvider resetCSS={false} theme={theme}>
|
<ChakraProvider resetCSS={false} theme={theme}>
|
||||||
{/* <link href="https://use.fontawesome.com/releases/v5.15.1/css/all.css" rel="stylesheet" /> */}
|
{/* <link href="https://use.fontawesome.com/releases/v5.15.1/css/all.css" rel="stylesheet" /> */}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
+24
-23
@@ -1,25 +1,26 @@
|
|||||||
const { SitemapStream, streamToPromise } = require("sitemap");
|
import axios from 'axios'
|
||||||
const { Readable } = require("stream");
|
import { SitemapStream, streamToPromise } from 'sitemap'
|
||||||
|
import { Readable } from 'stream'
|
||||||
|
import { AppUrl } from '../../lib/api'
|
||||||
|
import { SITE_URL, sitemapLinks } from '../../lib/seo'
|
||||||
|
|
||||||
export default async (req, res) => {
|
export default async function sitemap(req, res) {
|
||||||
// An array with your links
|
let news = []
|
||||||
const links = [
|
let newsAvailable = false
|
||||||
{ url: "/page/news/36/", priority: 0.3 },
|
try {
|
||||||
{ url: "/page/about", priority: 0.3 },
|
const { data } = await axios.get(AppUrl('news'), { timeout: 5000 })
|
||||||
{ url: "/recruitment", priority: 0.3 },
|
const posts = Array.isArray(data) ? data : data && data.data
|
||||||
{ url: "/contact-us", priority: 0.3 }
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
// Create a stream to write to
|
const stream = new SitemapStream({ hostname: SITE_URL })
|
||||||
const stream = new SitemapStream({ hostname: `https://${req.headers.host}` });
|
const xml = await streamToPromise(Readable.from(sitemapLinks(news).map(url => ({ url }))).pipe(stream))
|
||||||
|
res.setHeader('Content-Type', 'application/xml; charset=utf-8')
|
||||||
res.writeHead(200, {
|
res.setHeader('Cache-Control', newsAvailable ? 'public, s-maxage=3600, stale-while-revalidate=86400' : 'public, s-maxage=60')
|
||||||
"Content-Type": "application/xml",
|
res.status(200).send(xml.toString())
|
||||||
});
|
}
|
||||||
|
|
||||||
const xmlString = await streamToPromise(
|
|
||||||
Readable.from(links).pipe(stream)
|
|
||||||
).then((data) => data.toString());
|
|
||||||
|
|
||||||
res.end(xmlString);
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -6,18 +6,11 @@ import dynamic from 'next/dynamic'
|
|||||||
import { Router } from 'next/router'
|
import { Router } from 'next/router'
|
||||||
|
|
||||||
const Chat = dynamic(() => import('./pack'), { ssr: false })
|
const Chat = dynamic(() => import('./pack'), { ssr: false })
|
||||||
const AppContainer = ({ children, title }) => {
|
const AppContainer = ({ children }) => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Head>
|
<Head>
|
||||||
<title>{title ? title : 'بیمارستان مهر پرند '}</title>
|
|
||||||
<meta name='description' content='بیمارستان 96 تخت خوابی مهر ایرانیان پرند واقع در شهر پرند ' />
|
|
||||||
<meta
|
|
||||||
name='keywords'
|
|
||||||
content=' درمانگاه مهرپرند,بیمارستان مهرایرانیان پرند ,بیمارستان, بیمارستان مهر, سهام,پرند'
|
|
||||||
/>
|
|
||||||
<meta name="google-site-verification" content="hygRCp6fRa9qi8RXCaAosXK4mBcnCJVRH1ayHtAQy8o" />
|
<meta name="google-site-verification" content="hygRCp6fRa9qi8RXCaAosXK4mBcnCJVRH1ayHtAQy8o" />
|
||||||
<link rel='icon' type='image/x-icon' href='/favicon.ico' />
|
|
||||||
<script
|
<script
|
||||||
type='text/javascript'
|
type='text/javascript'
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
@@ -33,7 +26,7 @@ const AppContainer = ({ children, title }) => {
|
|||||||
</Head>
|
</Head>
|
||||||
<Chat />
|
<Chat />
|
||||||
<Header />
|
<Header />
|
||||||
{children}
|
<main id='main-content'>{children}</main>
|
||||||
<Footer />
|
<Footer />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -248,7 +248,7 @@ const Service = () => {
|
|||||||
<MDBCardBody style={{ display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
<MDBCardBody style={{ display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
||||||
<Link href={'components/service'}>
|
<Link href={'components/service'}>
|
||||||
<MDBCardText className={'bk'}>
|
<MDBCardText className={'bk'}>
|
||||||
<h1 className={'RTL dana'}>بیشتر ...</h1>{' '}
|
<span className={'RTL dana'}>بیشتر ...</span>{' '}
|
||||||
</MDBCardText>
|
</MDBCardText>
|
||||||
</Link>
|
</Link>
|
||||||
</MDBCardBody>
|
</MDBCardBody>
|
||||||
@@ -262,7 +262,7 @@ const Service = () => {
|
|||||||
) : (
|
) : (
|
||||||
<AppContainer title={'کلینیک ها و بخش ها '}>
|
<AppContainer title={'کلینیک ها و بخش ها '}>
|
||||||
<div className='parallax-container'>
|
<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
|
<Image
|
||||||
loader={Parallax}
|
loader={Parallax}
|
||||||
src='Parallax.png'
|
src='Parallax.png'
|
||||||
|
|||||||
@@ -39,12 +39,12 @@ const Slider = () => {
|
|||||||
</SwiperSlide> */}
|
</SwiperSlide> */}
|
||||||
<SwiperSlide key={3} style={{ cursor: 'pointer' }} className='pointer'>
|
<SwiperSlide key={3} style={{ cursor: 'pointer' }} className='pointer'>
|
||||||
<Link href='/landing'>
|
<Link href='/landing'>
|
||||||
<img src={sliderMIpStart} />
|
<a><img src={sliderMIpStart} alt='معرفی بیمارستان مهر پرند و آخرین اخبار افتتاح' width='2071' height='759' style={{ width: '100%', height: 'auto' }} /></a>
|
||||||
</Link>
|
</Link>
|
||||||
</SwiperSlide>
|
</SwiperSlide>
|
||||||
<SwiperSlide key={3} style={{ cursor: 'pointer' }} className='pointer'>
|
<SwiperSlide key={4} style={{ cursor: 'pointer' }} className='pointer'>
|
||||||
<Link href='/recruitment'>
|
<Link href='/recruitment'>
|
||||||
<img src={sliderJob} />
|
<a><img src={sliderJob} alt='فرصتهای شغلی و استخدام در بیمارستان مهر پرند' width='2877' height='1120' style={{ width: '100%', height: 'auto' }} loading='lazy' /></a>
|
||||||
</Link>
|
</Link>
|
||||||
</SwiperSlide>
|
</SwiperSlide>
|
||||||
</Swiper>
|
</Swiper>
|
||||||
|
|||||||
+9
-5
@@ -66,7 +66,7 @@ export default function Home({ data_NEWS }) {
|
|||||||
font-size: 0.85rem;
|
font-size: 0.85rem;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
.opening-copy h2 {
|
.opening-copy h1 {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
font-size: clamp(1.55rem, 3vw, 2.5rem);
|
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'>
|
<section className='opening-announcement' aria-labelledby='opening-announcement-title'>
|
||||||
<div className='opening-copy'>
|
<div className='opening-copy'>
|
||||||
<span className='opening-label'>اطلاعرسانی افتتاح</span>
|
<span className='opening-label'>اطلاعرسانی افتتاح</span>
|
||||||
<h2 id='opening-announcement-title'>در چند قدمی افتتاح بیمارستان مهر پرند</h2>
|
<h1 id='opening-announcement-title'>در چند قدمی افتتاح بیمارستان مهر پرند</h1>
|
||||||
<p>اخبار و زمان دقیق افتتاح بیمارستان از طریق همین وبسایت اطلاعرسانی خواهد شد.</p>
|
<p>اخبار و زمان دقیق افتتاح بیمارستان از طریق همین وبسایت اطلاعرسانی خواهد شد.</p>
|
||||||
</div>
|
</div>
|
||||||
<div className='opening-actions'>
|
<div className='opening-actions'>
|
||||||
@@ -136,9 +136,11 @@ export default function Home({ data_NEWS }) {
|
|||||||
<div className='lg:mx-20 sm:mx-5'>
|
<div className='lg:mx-20 sm:mx-5'>
|
||||||
<SimpleGrid spacing={15} columns={[1, 2, 2]}>
|
<SimpleGrid spacing={15} columns={[1, 2, 2]}>
|
||||||
<Link href={'/page/contact-us'}>
|
<Link href={'/page/contact-us'}>
|
||||||
|
<a>
|
||||||
<MDBCard className={'bg-neutral-100 fst-box hover:bg-neutral-200 cursor-pointer hover:shadow-2xl'}>
|
<MDBCard className={'bg-neutral-100 fst-box hover:bg-neutral-200 cursor-pointer hover:shadow-2xl'}>
|
||||||
<MDBCardBody className={'flex justify-content-center'}>ارتباط با ما</MDBCardBody>
|
<MDBCardBody className={'flex justify-content-center'}>ارتباط با ما</MDBCardBody>
|
||||||
</MDBCard>
|
</MDBCard>
|
||||||
|
</a>
|
||||||
</Link>
|
</Link>
|
||||||
{/* <Link href={'/page/sales'}>
|
{/* <Link href={'/page/sales'}>
|
||||||
<MDBCard className={'bg-neutral-100 fst-box hover:bg-neutral-200 cursor-pointer hover:shadow-2xl'}>
|
<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>
|
</MDBCard>
|
||||||
</Link> */}
|
</Link> */}
|
||||||
<Link href={'/questions'}>
|
<Link href={'/questions'}>
|
||||||
|
<a>
|
||||||
<MDBCard className={'bg-neutral-100 fst-box hover:bg-neutral-200 cursor-pointer hover:shadow-2xl'}>
|
<MDBCard className={'bg-neutral-100 fst-box hover:bg-neutral-200 cursor-pointer hover:shadow-2xl'}>
|
||||||
<MDBCardBody className={'flex justify-content-center'}>سوالات متداول</MDBCardBody>
|
<MDBCardBody className={'flex justify-content-center'}>سوالات متداول</MDBCardBody>
|
||||||
</MDBCard>
|
</MDBCard>
|
||||||
|
</a>
|
||||||
</Link>
|
</Link>
|
||||||
{/* <Link href={'/stock-unit'}>
|
{/* <Link href={'/stock-unit'}>
|
||||||
<MDBCard className={'bg-neutral-100 fst-box hover:bg-neutral-200 cursor-pointer hover:shadow-2xl'}>
|
<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
|
<Link
|
||||||
style={{ cursor: 'pointer' }}
|
style={{ cursor: 'pointer' }}
|
||||||
className={'text-white'}
|
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' />
|
<MDBIcon className='ms-1 ' icon='plus' size='sm' />
|
||||||
<span style={{ cursor: 'pointer !important' }} className={'text-white '}>
|
<span style={{ cursor: 'pointer !important' }} className={'text-white '}>
|
||||||
{' '}
|
{' '}
|
||||||
بیشتر
|
بیشتر
|
||||||
</span>
|
</span>
|
||||||
</MDBBtn>
|
</a>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</News>
|
</News>
|
||||||
|
|||||||
@@ -7,11 +7,11 @@ const MedicalEquipment = () => {
|
|||||||
return (
|
return (
|
||||||
<AppContainer title={'واحد تجهیزات پزشکی'}>
|
<AppContainer title={'واحد تجهیزات پزشکی'}>
|
||||||
<div className='parallax-container'>
|
<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
|
<Image
|
||||||
src={MedicalStock}
|
src={MedicalStock}
|
||||||
className={'responsive-img shadow-5'}
|
className={'responsive-img shadow-5'}
|
||||||
alt='Picture of the author'
|
alt='تجهیزات پزشکی بیمارستان مهر پرند'
|
||||||
width={4090}
|
width={4090}
|
||||||
height={2000}
|
height={2000}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -72,10 +72,10 @@ const Directors = () => {
|
|||||||
return (
|
return (
|
||||||
<AppContainer title={' تیم پروژه بیمارستان مهر پرند'}>
|
<AppContainer title={' تیم پروژه بیمارستان مهر پرند'}>
|
||||||
<div className='parallax-container'>
|
<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
|
<Image
|
||||||
loader={Parallax}
|
loader={Parallax}
|
||||||
src='Parallax.png'
|
src='Parallax.png'
|
||||||
|
|||||||
@@ -19,14 +19,14 @@ const ContactUs = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<AppContainer title={'درباره ما'}>
|
<AppContainer title={'تماس با ما'}>
|
||||||
<div className="parallax-container">
|
<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
|
<Image
|
||||||
loader={Parallax}
|
loader={Parallax}
|
||||||
src="Parallax.png"
|
src="Parallax.png"
|
||||||
className={'responsive-img shadow-5'}
|
className={'responsive-img shadow-5'}
|
||||||
alt="Picture of the author"
|
alt="بیمارستان مهر پرند؛ آدرس و راههای ارتباطی"
|
||||||
width={2050}
|
width={2050}
|
||||||
height={950}
|
height={950}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -20,9 +20,9 @@ const CooperationSales = () => {
|
|||||||
{/* <img src ={cartmo} alt="" /> */}
|
{/* <img src ={cartmo} alt="" /> */}
|
||||||
</div>
|
</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">
|
<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">
|
||||||
با ثبت نام در سامانه خرید سهام، کد معرف شما ایجاد و قابل اشتراک
|
با ثبت نام در سامانه خرید سهام، کد معرف شما ایجاد و قابل اشتراک
|
||||||
گذاری می شود. کد معرف یکتای خود را با دوستان خود به اشتراک بگذارید
|
گذاری می شود. کد معرف یکتای خود را با دوستان خود به اشتراک بگذارید
|
||||||
|
|||||||
+1
-1
@@ -18,7 +18,7 @@ const Docs = () => {
|
|||||||
<AppContainer title={' اساسنامه شرکت'}>
|
<AppContainer title={' اساسنامه شرکت'}>
|
||||||
<div className="parallax-container">
|
<div className="parallax-container">
|
||||||
<div className={'d-flex'}>
|
<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>
|
</div>
|
||||||
<Image
|
<Image
|
||||||
loader={Parallax}
|
loader={Parallax}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import {
|
|||||||
} from 'mdb-react-ui-kit'
|
} from 'mdb-react-ui-kit'
|
||||||
import Image from 'next/image'
|
import Image from 'next/image'
|
||||||
import { AppFile, AppUrl } from '../../../lib/api'
|
import { AppFile, AppUrl } from '../../../lib/api'
|
||||||
|
import { newsSeo } from '../../../lib/seo'
|
||||||
import { Tag, TagLabel, HStack } from '@chakra-ui/react'
|
import { Tag, TagLabel, HStack } from '@chakra-ui/react'
|
||||||
|
|
||||||
const moment = require('moment-jalaali')
|
const moment = require('moment-jalaali')
|
||||||
@@ -40,7 +41,7 @@ const News = ({ post }) => {
|
|||||||
loader={Parallax}
|
loader={Parallax}
|
||||||
src='Parallax.png'
|
src='Parallax.png'
|
||||||
className={'responsive-img shadow-5'}
|
className={'responsive-img shadow-5'}
|
||||||
alt='Picture of the author'
|
alt={post.title}
|
||||||
width={2050}
|
width={2050}
|
||||||
height={1050}
|
height={1050}
|
||||||
priority
|
priority
|
||||||
@@ -175,11 +176,14 @@ const News = ({ post }) => {
|
|||||||
export default News
|
export default News
|
||||||
|
|
||||||
export const getServerSideProps = async context => {
|
export const getServerSideProps = async context => {
|
||||||
const { query } = context
|
const { slug } = context.params
|
||||||
const posData = await axios.get(AppUrl(`news/${query.slug}`))
|
try {
|
||||||
const post = await posData.data
|
const { data: post } = await axios.get(AppUrl(`news/${encodeURIComponent(slug)}`), { timeout: 10000 })
|
||||||
|
if (!post || !post.title || typeof post.text !== 'string') return { notFound: true }
|
||||||
return {
|
return { props: { post, seo: newsSeo(post, slug) } }
|
||||||
props: { post, fallback: false } // will be passed to the page component as props
|
} 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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ const Sales = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div id='callstock' className=' text-center py-8 m '>
|
<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 className='text-[#656565] text-base p-2 md:text-[22px] md:p-0 font-light'>
|
||||||
برای ارتباط با ما فرم زیر را تکمیل کنید
|
برای ارتباط با ما فرم زیر را تکمیل کنید
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -31,9 +31,9 @@ const Recruitment = dataoccupational => {
|
|||||||
<div className='parallax-container'>
|
<div className='parallax-container'>
|
||||||
<div className={'flex flex-column'}>
|
<div className={'flex flex-column'}>
|
||||||
<div className={'mb-5'}>
|
<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>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
@@ -82,11 +82,11 @@ const Recruitment = dataoccupational => {
|
|||||||
<FiUsers size={35} />
|
<FiUsers size={35} />
|
||||||
</div>
|
</div>
|
||||||
<MDBCardTitle className={'bk-med mt-2 right'}>{item.title} </MDBCardTitle>
|
<MDBCardTitle className={'bk-med mt-2 right'}>{item.title} </MDBCardTitle>
|
||||||
<Link href={`recruitment/item/${item.id}`}>
|
<Link href={`/recruitment/item/${item.id}`}>
|
||||||
<MDBBtn rounded size={'sm'} className={'mt-2 flex align-items-center bk-light bg-primary-teal'}>
|
<a className={'btn btn-rounded btn-sm mt-2 flex align-items-center bk-light bg-primary-teal text-white'}>
|
||||||
مشاهده
|
مشاهده
|
||||||
<FiArrowLeft className={'mr-2'} />
|
<FiArrowLeft className={'mr-2'} />
|
||||||
</MDBBtn>
|
</a>
|
||||||
</Link>
|
</Link>
|
||||||
</MDBCardBody>
|
</MDBCardBody>
|
||||||
</MDBCard>
|
</MDBCard>
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ import axios from 'axios'
|
|||||||
import { AppProduction } from '../../../lib/api'
|
import { AppProduction } from '../../../lib/api'
|
||||||
|
|
||||||
const Recruitment = jobs => {
|
const Recruitment = jobs => {
|
||||||
console.log(jobs)
|
|
||||||
const Parallax = ({ quality }) => {
|
const Parallax = ({ quality }) => {
|
||||||
return `../../resource/parallax/33012321.jpg`
|
return `../../resource/parallax/33012321.jpg`
|
||||||
}
|
}
|
||||||
@@ -32,10 +31,10 @@ const Recruitment = jobs => {
|
|||||||
<div className='parallax-container'>
|
<div className='parallax-container'>
|
||||||
<div className={'flex flex-column'}>
|
<div className={'flex flex-column'}>
|
||||||
<div className={'mb-5'}>
|
<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'}>
|
||||||
|
|
||||||
فرصت های شغلی بیمارستان مهر پرند
|
فرصتهای شغلی {jobs.jobs.name.title} بیمارستان مهر پرند
|
||||||
</h2>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
@@ -127,7 +126,10 @@ export async function getServerSideProps(ctx) {
|
|||||||
const jobs = await res
|
const jobs = await res
|
||||||
|
|
||||||
// Pass data to the page via props
|
// 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
|
export default Recruitment
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ const StockUnit = () => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className={'mb-5 mt-5 mx-5'}>
|
<div className={'mb-5 mt-5 mx-5'}>
|
||||||
|
<h1 className='text-center'>واحد امور سهام بیمارستان مهر پرند</h1>
|
||||||
<div className='my-4'>
|
<div className='my-4'>
|
||||||
<SimpleGrid spacing={15} columns={[1, 2]}>
|
<SimpleGrid spacing={15} columns={[1, 2]}>
|
||||||
<Link href={'/stock-unit/stock-check'}>
|
<Link href={'/stock-unit/stock-check'}>
|
||||||
|
|||||||
+3
-5
@@ -1,7 +1,5 @@
|
|||||||
User-agent: *
|
User-agent: *
|
||||||
Disallow: /includes/
|
Allow: /
|
||||||
|
Disallow: /api/
|
||||||
|
|
||||||
User-agent: Google
|
Sitemap: https://miphospital.ir/sitemap.xml
|
||||||
Allow: /includes/
|
|
||||||
|
|
||||||
Sitemap: http://miphospital.ir/api/my-sitemap
|
|
||||||
|
|||||||
@@ -1,29 +1,36 @@
|
|||||||
@font-face {
|
@font-face {
|
||||||
|
font-display: swap;
|
||||||
font-family: Vazir-import !important;
|
font-family: Vazir-import !important;
|
||||||
src: url("Vazir-Regular.ttf");
|
src: url("Vazir-Regular.ttf");
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
|
font-display: swap;
|
||||||
font-family: dana-fanum;
|
font-family: dana-fanum;
|
||||||
src: url("Dana-FaNum-Regular.ttf");
|
src: url("Dana-FaNum-Regular.ttf");
|
||||||
}
|
}
|
||||||
@font-face {
|
@font-face {
|
||||||
|
font-display: swap;
|
||||||
font-family: yekanBakh;
|
font-family: yekanBakh;
|
||||||
src: url("YekanLight.ttf");
|
src: url("YekanLight.ttf");
|
||||||
}
|
}
|
||||||
@font-face {
|
@font-face {
|
||||||
|
font-display: swap;
|
||||||
font-family: yekanBakhMed;
|
font-family: yekanBakhMed;
|
||||||
src: url("YekanMedium.ttf");
|
src: url("YekanMedium.ttf");
|
||||||
}
|
}
|
||||||
@font-face {
|
@font-face {
|
||||||
|
font-display: swap;
|
||||||
font-family: yekanBakhLight;
|
font-family: yekanBakhLight;
|
||||||
src: url("YekanLight.ttf");
|
src: url("YekanLight.ttf");
|
||||||
}
|
}
|
||||||
@font-face {
|
@font-face {
|
||||||
|
font-display: swap;
|
||||||
font-family: yekanBakhHeavy;
|
font-family: yekanBakhHeavy;
|
||||||
src: url("YekanBakhHeavy.ttf");
|
src: url("YekanBakhHeavy.ttf");
|
||||||
}
|
}
|
||||||
@font-face {
|
@font-face {
|
||||||
|
font-display: swap;
|
||||||
font-family: yekanBakhBold;
|
font-family: yekanBakhBold;
|
||||||
src: url("YekanBakhBold.ttf");
|
src: url("YekanBakhBold.ttf");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import Head from 'next/head'
|
||||||
|
import { useRouter } from 'next/router'
|
||||||
|
import { getSeo, SITE_NAME, hospitalSchema, serializeJsonLd } from '../../lib/seo'
|
||||||
|
|
||||||
|
export default function Seo({ metadata = {} }) {
|
||||||
|
const router = useRouter()
|
||||||
|
const seo = getSeo(router.asPath, metadata)
|
||||||
|
const title = seo.title.includes(SITE_NAME) ? seo.title : `${seo.title} | ${SITE_NAME}`
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Head>
|
||||||
|
<title>{title}</title>
|
||||||
|
<meta name='description' content={seo.description} key='description' />
|
||||||
|
<meta name='robots' content={seo.noindex ? 'noindex, follow' : 'index, follow, max-image-preview:large'} key='robots' />
|
||||||
|
<link rel='canonical' href={seo.canonical} key='canonical' />
|
||||||
|
<meta property='og:locale' content='fa_IR' />
|
||||||
|
<meta property='og:site_name' content={SITE_NAME} />
|
||||||
|
<meta property='og:type' content={seo.type} />
|
||||||
|
<meta property='og:title' content={title} />
|
||||||
|
<meta property='og:description' content={seo.description} />
|
||||||
|
<meta property='og:url' content={seo.canonical} />
|
||||||
|
<meta property='og:image' content={seo.image} />
|
||||||
|
<meta property='og:image:alt' content={seo.title} />
|
||||||
|
<meta name='twitter:card' content='summary_large_image' />
|
||||||
|
<meta name='twitter:title' content={title} />
|
||||||
|
<meta name='twitter:description' content={seo.description} />
|
||||||
|
<meta name='twitter:image' content={seo.image} />
|
||||||
|
<link rel='icon' href='/favicon.ico' />
|
||||||
|
{!seo.noindex && (
|
||||||
|
<script type='application/ld+json' key='structured-data' dangerouslySetInnerHTML={{
|
||||||
|
__html: serializeJsonLd(seo.structuredData ? [hospitalSchema, seo.structuredData] : hospitalSchema)
|
||||||
|
}} />
|
||||||
|
)}
|
||||||
|
</Head>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
const { test } = require('node:test')
|
||||||
|
const assert = require('node:assert/strict')
|
||||||
|
const { canonicalUrl, getSeo, newsSeo, serializeJsonLd, sitemapLinks } = require('../lib/seo')
|
||||||
|
|
||||||
|
test('canonical URLs remove tracking, fragments and trailing slashes without trusting an external host', () => {
|
||||||
|
assert.equal(canonicalUrl('/page/about/?utm_source=test#intro'), 'https://miphospital.ir/page/about')
|
||||||
|
assert.equal(canonicalUrl('/'), 'https://miphospital.ir/')
|
||||||
|
assert.equal(canonicalUrl('//evil.example/page'), 'https://miphospital.ir/')
|
||||||
|
assert.equal(canonicalUrl('https://evil.example/page'), 'https://miphospital.ir/')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('public pages have distinct descriptions and internal pages remain out of search', () => {
|
||||||
|
assert.notEqual(getSeo('/page/about').description, getSeo('/page/contact-us').description)
|
||||||
|
assert.equal(getSeo('/page/contact-us').noindex, false)
|
||||||
|
for (const route of ['/components/slider', '/appContainer', '/recruitment/inquiry', '/recruitment/item/request/23', '/404']) {
|
||||||
|
assert.equal(getSeo(route).noindex, true, route)
|
||||||
|
}
|
||||||
|
assert.equal(getSeo('/components/service').noindex, false)
|
||||||
|
assert.equal(getSeo('/page/news/36').noindex, false)
|
||||||
|
assert.match(getSeo('/recruitment/inquiry').title, /استعلام/)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('article metadata derives a plain text summary and absolute cover URL from the actual article', () => {
|
||||||
|
const seo = newsSeo({ title: 'خبر افتتاح', text: '<p>خبر <strong>بیمارستان</strong></p><script>bad()</script>', cover: 'news.jpg' }, '36')
|
||||||
|
assert.equal(seo.title, 'خبر افتتاح')
|
||||||
|
assert.equal(seo.description, 'خبر بیمارستان')
|
||||||
|
assert.equal(seo.image, 'https://mehrara-api.iran.liara.run/panel-public/uploads/news.jpg')
|
||||||
|
assert.equal(seo.structuredData['@type'], 'NewsArticle')
|
||||||
|
assert.equal(seo.structuredData.mainEntityOfPage, 'https://miphospital.ir/page/news/36')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('JSON-LD cannot terminate its HTML script element', () => {
|
||||||
|
const value = { name: '</script><script>alert(1)</script>' }
|
||||||
|
const serialized = serializeJsonLd(value)
|
||||||
|
assert.equal(serialized.includes('<'), false)
|
||||||
|
assert.deepEqual(JSON.parse(serialized), value)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('sitemap contains real public routes and unique valid article IDs only', () => {
|
||||||
|
const links = sitemapLinks([{ id: 36 }, { id: 36 }, { id: '../bad' }, { id: null }, { id: 82 }])
|
||||||
|
assert.ok(links.includes('/page/contact-us'))
|
||||||
|
assert.ok(links.includes('/page/news/36'))
|
||||||
|
assert.ok(links.includes('/page/news/82'))
|
||||||
|
assert.equal(links.filter(url => url === '/page/news/36').length, 1)
|
||||||
|
assert.equal(links.includes('/contact-us'), false)
|
||||||
|
assert.equal(links.some(url => url.includes('request') || url.includes('../')), false)
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user