import {ArrowUpRight, BedDouble, CalendarCheck, Phone, ShieldCheck} from 'lucide-react'; import {useSite} from '@site/lib/site-context'; import {bookingCtaLabel, channelLabel, sectionName, stayBookingView} from '@site/lib/derive'; import {StayBookingDemo} from './StayBookingDemo'; /** * 숙박 예약 구성 — 손님이 **이 방을 · 이 값에 · 이 창구로** 예약할 수 있는 자리. * * ★ 왜 `BookingSection` 과 따로 있나 * 그쪽이 읽는 fact(`reservation_required`·`reservation_channel`)는 **숙박 스키마에 없다.** * 그래서 펜션·민박으로 발행하면 서버 기본표가 켜 두는 "실시간 예약" 섹션에 전화번호 * 한 줄만 남았다 — 요금도 인원도 취소 규정도 없는 예약 섹션이었다. 숙박은 예약이 * 곧 매출이고, "얼마예요 / 몇 명까지 / 어떻게 예약해요" 가 이 업종 질의의 대부분이다. * * ★ 예약을 **처리하지 않는다.** 우리는 빈 방 재고도 결제도 갖지 않는다(PRODUCT.md 6절 — * "사이트는 예약 채널로 보낸다. 거래를 품지 않는다"). 그래서 날짜 선택기·예약 폼을 * 그리지 않는다. 없는 기능을 화면으로 흉내내면 손님은 예약한 줄 알고 안 오고, * 그 클레임은 사장님이 받는다. 대신 **예약에 필요한 사실**과 **실제로 예약이 되는 창구**를 * 한자리에 모은다. * * ★ 지어낸 값이 없다. 요금·인원·취소 규정은 확인된 fact 뿐이고(`stayBookingView`), * 예약 버튼은 확정된 채널 URL 뿐이다. 근거가 하나도 없으면 섹션째 그리지 않는다 — * 그 판단도 `stayBookingView()` 한 곳이 한다(상단 내비·하단 탭이 같은 함수를 본다). * * ★ 기준 요금 숫자는 `unitBaseRate`(seo/jsonld.ts)가 고른 값이다. JSON-LD `makesOffer.price` * 와 같은 숫자여야 절대규칙 3(화면 = 구조화 데이터) 대조를 통과한다. */ export function StayBookingSection() { const payload = useSite(); const view = stayBookingView(payload); if (!view) return null; const {offers, notices, links, contacts, phone} = view; return (

Reservation

{sectionName(payload, 'booking', '예약 안내')}

{/* ★ "여기서 결제되지 않는다"를 먼저 말한다. 예약 버튼을 누른 뒤에 알게 되면 손님은 속은 것으로 느끼고, 그 인상은 업소가 가져간다. */}

빈 방 확인과 결제는 아래 예약 창구에서 진행됩니다. 이 페이지에서는 요금과 이용 조건만 안내합니다.

{/* ── 객실별 요금 · 인원 ───────────────────────────────── */} {offers.length > 0 && (

객실별 요금 · 인원

    {offers.map((offer) => (
  • {offer.name}

    {offer.baseRateText && ( {offer.baseRateText} )}
    {offer.capacityText && (

    {offer.capacityText}

    )} {offer.rateRows.length > 0 && (
    {offer.rateRows.map((row) => (
    {row.label}
    {row.value}
    ))}
    )} {/* 사진·전체 스펙은 객실 섹션이 갖고 있다. 한 장 사이트라 앵커다. */} {offer.name} 사진 · 상세 보기
  • ))}
)} {/* ── 예약 창구 ──────────────────────────────────────── */}

예약 창구

{phone && ( 전화 예약 {phone} )} {/* ★ 확정된 채널만 나간다 — 확정 전 URL 은 동명 업소의 예약 페이지일 수 있다. 눌렀는데 남의 숙소가 뜨면 그 예약은 영영 우리 것이 아니다. */} {links.map((link) => ( {bookingCtaLabel(link)} ))} {contacts.length > 0 && (

문의

)} {links.length === 0 && ( // 채널이 하나도 확정되지 않은 업소 — 전화가 유일한 창구다. 그 사실을 말해 준다.

온라인 예약 채널은 등록되지 않았습니다. 예약은 전화로 문의해 주세요.

)}
{/* ── 날짜 · 시간 선택 (화면 안에서만 도는 목업) ────────── */} {/* ── 예약 전 확인 ────────────────────────────────────── */} {notices.length > 0 && (

예약 전 확인

{/* 확인된 fact 만 줄이 된다 — "확인 중" 이라는 빈 줄을 그리면 손님은 그걸 규정으로 읽는다. */}
{notices.map((row) => (
{row.label}
{row.value}
))}
)}
); }