164 lines
7.9 KiB
JavaScript
164 lines
7.9 KiB
JavaScript
import { useParams } from 'react-router-dom';
|
||
import ctaImage from '../assets/figma/checkup-cta-3d.webp';
|
||
import { api } from '../api.js';
|
||
import Breadcrumbs from '../components/Breadcrumbs.jsx';
|
||
import CtaBand from '../components/CtaBand.jsx';
|
||
import { useLeadModal } from '../context/LeadModalContext.jsx';
|
||
import { plural, priceLabel } from '../lib/format.js';
|
||
import { checkupMedia } from '../lib/media.js';
|
||
import { checklistItems, cleanText, introText } from '../lib/text.js';
|
||
import { useApi } from '../lib/useApi.js';
|
||
import { usePageTitle } from '../lib/usePageTitle.js';
|
||
import { pageMetadata } from '../lib/seo.js';
|
||
import { useInitialApiData } from '../context/ApiData.jsx';
|
||
import StubPage from './StubPage.jsx';
|
||
import './CheckupPage.css';
|
||
|
||
export default function CheckupPage() {
|
||
const { slug } = useParams();
|
||
const { open } = useLeadModal();
|
||
const checkup = useApi((signal) => api.checkup(slug, { signal }), [slug], `checkup:${slug}`);
|
||
const name = cleanText(checkup.data?.name);
|
||
const siteURL = useInitialApiData('siteURL') || (typeof window !== 'undefined' ? window.location.origin : '');
|
||
usePageTitle(name || undefined, checkup.data ? pageMetadata(`/checkup/${slug}/`, name, siteURL, checkup.data.seo, checkup.data.lead) : undefined);
|
||
|
||
if (checkup.error?.status === 404) return <StubPage notFound />;
|
||
if (checkup.error) return <p className="state">Не удалось загрузить программу. Обновите страницу.</p>;
|
||
if (!checkup.data || checkup.data.slug !== slug) return <p className="state checkup-loading">Загружаем…</p>;
|
||
|
||
const c = checkup.data;
|
||
const { hero, photo, cutout, heroBg, cutoutRatio } = checkupMedia(c);
|
||
const items = c.items?.length ? c.items.map(cleanText) : checklistItems(c.blocks);
|
||
const intro = introText(c.blocks);
|
||
const lead = cleanText(c.lead);
|
||
|
||
const book = () => open({ form: 'appointment', message: `Чек-ап: ${name}` });
|
||
const askPrice = () => open({ form: 'service_order', message: `Стоимость чек-апа: ${name}` });
|
||
|
||
// первый столбец списка заполняется сверху вниз, затем второй — как в макете
|
||
const rows = Math.ceil(items.length / 2);
|
||
|
||
return (
|
||
<div className="checkup-page">
|
||
<section
|
||
className={`checkup-hero${cutout ? ' checkup-hero--cutout' : ''}`}
|
||
style={heroBg ? { '--checkup-hero-bg': heroBg } : undefined}
|
||
>
|
||
<div className={`container checkup-hero__row${cutout ? ' checkup-hero__row--cutout' : ''}`}>
|
||
<div className="checkup-hero__copy">
|
||
<Breadcrumbs
|
||
items={[{ to: '/', label: 'Главная' }, { to: '/checkup/', label: 'Чек-апы' }, { label: name }]}
|
||
className="breadcrumbs--light checkup-hero__crumbs"
|
||
/>
|
||
<span className="checkup-hero__badge">Комплексная программа</span>
|
||
<h1 className="checkup-hero__title">{name}</h1>
|
||
{lead && <p className="checkup-hero__lead">{lead}</p>}
|
||
{intro && <p className="checkup-hero__text">{intro}</p>}
|
||
<div className="checkup-hero__actions">
|
||
<button type="button" className="btn checkup-hero__btn" onClick={book}>
|
||
Записаться на приём
|
||
</button>
|
||
<button type="button" className="btn checkup-hero__btn checkup-hero__btn--ghost" onClick={askPrice}>
|
||
Уточнить стоимость
|
||
</button>
|
||
</div>
|
||
</div>
|
||
{cutout ? (
|
||
<div
|
||
className="checkup-hero__image checkup-hero__image--cutout"
|
||
style={cutoutRatio ? { '--cutout-ratio': cutoutRatio } : undefined}
|
||
>
|
||
<img src={cutout} alt="" />
|
||
</div>
|
||
) : hero ? (
|
||
<div className="checkup-hero__image">
|
||
<img src={hero} alt="" />
|
||
</div>
|
||
) : (
|
||
photo && (
|
||
// крупного кадра нет — фото карточки в натуральном размере, без растягивания
|
||
<div className="checkup-hero__image checkup-hero__image--card">
|
||
<img src={photo} alt="" />
|
||
</div>
|
||
)
|
||
)}
|
||
</div>
|
||
</section>
|
||
|
||
<section className="checkup-facts">
|
||
<div className="container checkup-facts__row">
|
||
{items.length > 0 && (
|
||
<div className="checkup-fact">
|
||
<b>{items.length}</b>
|
||
<span>{plural(items.length, 'исследование и анализ', 'исследования и анализа', 'исследований и анализов')}</span>
|
||
</div>
|
||
)}
|
||
<div className="checkup-fact">
|
||
<b>{c.price ? priceLabel({ price: c.price }) : 'По запросу'}</b>
|
||
<span>
|
||
{c.price && c.old_price ? `вместо ${priceLabel({ price: c.old_price })}` : 'стоимость уточнит администратор'}
|
||
</span>
|
||
</div>
|
||
<div className="checkup-fact">
|
||
<b>Запись</b>
|
||
<span>в удобное для вас время</span>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section className="checkup-content">
|
||
<div className="container checkup-content__inner">
|
||
{items.length > 0 && (
|
||
<>
|
||
<div className="checkup-content__head">
|
||
<h2 className="checkup-content__title">Что входит в программу</h2>
|
||
<p className="checkup-content__text">
|
||
Все ключевые этапы собраны в одном маршруте — от приёма специалиста до лабораторной диагностики.
|
||
</p>
|
||
</div>
|
||
<ol className="checkup-items" style={{ '--rows': rows }}>
|
||
{items.map((it, i) => (
|
||
<li key={i} className="checkup-item">
|
||
<span className="checkup-item__num">{i + 1}</span>
|
||
<span className="checkup-item__text">{it}</span>
|
||
</li>
|
||
))}
|
||
</ol>
|
||
</>
|
||
)}
|
||
|
||
<div className="checkup-info">
|
||
<div className="checkup-info__card">
|
||
<h3 className="checkup-info__title">Стоимость</h3>
|
||
<p className="checkup-info__text">
|
||
{c.price
|
||
? `Стоимость программы — ${priceLabel({ price: c.price })}. Администратор подтвердит её при записи.`
|
||
: 'Актуальную стоимость программы администратор назовёт при записи или по телефону.'}
|
||
</p>
|
||
</div>
|
||
<div className="checkup-info__card">
|
||
<h3 className="checkup-info__title">Как подготовиться</h3>
|
||
<p className="checkup-info__text">
|
||
Администратор расскажет о подготовке к анализам и исследованиям при подтверждении записи.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
<p className="checkup-content__note">
|
||
Состав программы и актуальную стоимость уточняйте у администратора клиники.
|
||
</p>
|
||
</div>
|
||
</section>
|
||
|
||
<CtaBand
|
||
tone="lilac"
|
||
title="Позаботьтесь о здоровье вовремя"
|
||
text="Оставьте заявку — администратор поможет выбрать удобное время и уточнит детали программы."
|
||
action="Оставить заявку"
|
||
onAction={book}
|
||
image={ctaImage}
|
||
/>
|
||
</div>
|
||
);
|
||
}
|