import { renderToString } from 'react-dom/server';
import { StaticRouter } from 'react-router-dom/server.js';
import App from './App.jsx';
import { ApiDataProvider } from './context/ApiData.jsx';
import { parseBlocks } from './lib/serviceContent.js';
import { serviceMetadata, doctorMetadata, priceMetadata, pageMetadata, jsonForHTML } from './lib/seo.js';
import { DIRECTION_MEDIA, directionOf } from './lib/media.js';
export { parseBlocks };
export function siteFromCommon(common) {
const branches = common.branches || [];
const settings = common.settings || {};
const first = branches[0];
const coords = branches.find((b) => b.lat && b.lon);
return {
phone: first?.phones?.[0] || '+7 928 962-00-94', hours: first?.work_hours?.[0] || 'Пн–Вс: 08:00–20:00',
addresses: branches.map((b) => b.address.replace(/^г\.\s*/, '').replace(/(\d)\(/g, '$1 (')),
branches, coords: coords ? { lat: coords.lat, lon: coords.lon } : null,
maxUrl: settings.max_url || '', email: settings.email || '', legal: settings.legal || null,
};
}
export function renderPage(url, data, siteURL) {
const pathname = url.split('?')[0];
const initial = { ...data, prerendered: true, siteURL };
const servicePath = pathname.startsWith('/services/') ? pathname.slice(10).replace(/\/$/, '') : '';
const service = initial[`service:${servicePath}`]?.data;
if (service) {
service.content_sections = parseBlocks(service.blocks);
const specialty = DIRECTION_MEDIA[directionOf(service.path)]?.specialty;
initial[`doctors:${specialty}`] = { data: (initial.doctors?.data || []).filter((d) => d.specialties?.some((s) => s.slug === specialty)), meta: {} };
}
const parts = pathname.split('/').filter(Boolean);
const doctor = parts.length === 4 && parts[0] === 'company' ? initial[`doctor:${parts[3]}`]?.data : null;
const legal = parts[0] === 'legaldocs' ? initial[`page:${parts[1] || 'legaldocs'}`]?.data : null;
const checkup = parts.length === 2 && parts[0] === 'checkup' ? initial[`checkup:${parts[1]}`]?.data : null;
const specialty = parts.length === 3 && parts[0] === 'company' ? initial.specialties?.data.find((s) => s.slug === parts[2]) : null;
const titles = { '/services/': 'Услуги', '/company/staff/': 'Врачи', '/price/': 'Цены на медицинские услуги',
'/contacts/': 'Контакты', '/checkup/': 'Чек-апы', '/sales/': 'Акции и специальные предложения',
'/company/': 'О клинике', '/news/': 'Новости', '/articles/': 'Статьи', '/reviews/': 'Отзывы',
'/company/partners/': 'Партнёры', '/company/nadzor/': 'Надзорные органы', '/company/vacancy/': 'Вакансии',
'/info/faq/': 'Вопросы и ответы', '/company/licenses/': 'Лицензии и награды', '/patients/': 'Пациенту' };
// раздел «Компании» → запись pages с заголовком и SEO
const sectionSlugs = { '/company/': 'about', '/news/': 'news', '/articles/': 'articles', '/reviews/': 'reviews',
'/company/partners/': 'partners', '/company/nadzor/': 'nadzor', '/company/vacancy/': 'vacancy', '/info/faq/': 'faq', '/company/licenses/': 'licenses', '/patients/': 'patients' };
const section = sectionSlugs[pathname] ? initial[`page:${sectionSlugs[pathname]}`]?.data : null;
const post = parts.length === 2 && ['news', 'articles'].includes(parts[0]) ? initial[`${parts[0]}:${parts[1]}`]?.data : null;
let metadata = pageMetadata(pathname, specialty?.name || (pathname.startsWith('/price/') ? 'Цены на медицинские услуги' : titles[pathname]) || 'Клиника семейного здоровья', siteURL);
if (service) metadata = serviceMetadata(service, siteURL);
if (doctor) metadata = doctorMetadata(doctor, siteURL);
if (legal) metadata = pageMetadata(pathname, legal.title, siteURL, legal.seo);
if (checkup) metadata = pageMetadata(pathname, checkup.name, siteURL, checkup.seo, checkup.lead);
if (section) metadata = pageMetadata(pathname, section.title, siteURL, section.seo);
if (post) metadata = pageMetadata(pathname, post.title, siteURL, post.seo, post.lead);
if (parts[0] === 'price') metadata = priceMetadata(parts[1], initial.prices?.data, siteURL);
const html = renderToString(
);
const name = doctor?.full_name || legal?.title || checkup?.name || post?.title || section?.title || specialty?.name || titles[pathname] || metadata.title;
const crumbs = [{ name: 'Главная', item: `${siteURL}/` }];
if (doctor) crumbs.push({ name: 'Врачи', item: `${siteURL}/company/staff/` });
if (post) crumbs.push({ name: titles[`/${parts[0]}/`], item: `${siteURL}/${parts[0]}/` });
if (pathname !== '/') crumbs.push({ name, item: metadata.canonical });
const clinics = (initial.site?.branches || []).map((b) => ({ '@type': 'MedicalClinic',
'@id': `${siteURL}/contacts/#${b.slug}`, name: `DANA Medical — ${b.name}`, url: `${siteURL}/contacts/`,
address: { '@type': 'PostalAddress', streetAddress: b.address, addressCountry: 'RU' }, telephone: b.phones?.[0],
}));
const schema = service ? null : { '@context': 'https://schema.org', '@graph': [
{ '@type': doctor || checkup ? 'MedicalWebPage' : post ? (parts[0] === 'news' ? 'WebPage' : 'MedicalWebPage') : 'WebPage', '@id': metadata.canonical,
name, url: metadata.canonical, description: metadata.description, inLanguage: 'ru-RU',
breadcrumb: { '@id': `${metadata.canonical}#breadcrumbs` },
...(doctor ? { mainEntity: { '@id': `${metadata.canonical}#physician` } } : {}),
},
{ '@type': 'BreadcrumbList', '@id': `${metadata.canonical}#breadcrumbs`, itemListElement: crumbs.map((c, i) => ({ '@type': 'ListItem', position: i + 1, ...c })) },
...clinics,
...(doctor ? [{ '@type': 'Physician', '@id': `${metadata.canonical}#physician`, name: doctor.full_name,
url: metadata.canonical, description: doctor.position || undefined,
image: doctor.photo ? new URL(doctor.photo, siteURL).href : undefined }] : []),
] };
return { html, initial: jsonForHTML(initial), metadata, schema };
}
export function renderService(service, parent, common, siteURL) {
service.content_sections = parseBlocks(service.blocks);
const specialty = DIRECTION_MEDIA[directionOf(service.path)]?.specialty;
const initial = {
prerendered: true, siteURL, site: common.site,
[`service:${service.path}`]: { data: service, meta: {} },
specialties: { data: common.specialties, meta: {} },
prices: { data: common.prices || [], meta: {} },
[`doctors:${specialty}`]: { data: common.doctors.filter((d) =>
d.specialties?.some((s) => s.slug === specialty)), meta: {} },
};
if (parent) initial[`service:${parent.path}`] = { data: parent, meta: {} };
const html = renderToString(
,
);
return { html, initial: jsonForHTML(initial), metadata: serviceMetadata(service, siteURL) };
}