o2o-site-AEO/solution/site/src/sections/MobileTabBar.tsx
Mina Choi b7b8cb856c [fix] solution/site: 흐린 보조 글자 대비 올림 · 예약 달력 기본으로 펼침
여러 섹션의 opacity-55~70 짜리 보조 텍스트가 밝은 배경에서 너무 흐렸다 — 85~100 으로
올림(Carousel 카운터·SectionHead 리드문·SNS 소식·주변 안내 거리·공식 채널 링크).
StayBookingDemo: "날짜·시간 선택" 토글을 없애고 달력을 기본으로 펼쳐 보여준다 —
접어 두면 손님이 그 버튼을 눌러야만 날짜를 볼 수 있었다.
2026-09-23 13:18:02 +09:00

104 lines
4.3 KiB
TypeScript

import {useState} from 'react';
import {MapPin, MessageCircle, Phone} from 'lucide-react';
import {PlaceCategory} from '@o2o/shared';
import {useSite} from '@site/lib/site-context';
import {bookingActionLabel, bookingLinks, channelLabel, stayBookingView} from '@site/lib/derive';
import {Modal} from '@site/lib/ui';
import {StayBookingDemo} from './StayBookingDemo';
/**
* 좁은 화면 하단 고정 바.
*
* ★ 예전에는 '홈 · 객실 · 주변 · 위치' 탭이었다. 그런데 이 사이트는 한 장짜리라
* 탭이 하는 일은 앵커 이동뿐이고, 그건 상단 메뉴가 이미 한다 — 같은 일을 두 번 하면서
* **가장 많이 눌리는 것(전화·예약)이 갈 자리를 먹고 있었다.**
* 국내 숙박·공간 사이트가 하단에 고정으로 두는 건 예외 없이 연락·예약이다.
* ★ 근거 없는 버튼은 안 만든다. 전화번호가 없으면 전화 칸이 없고, 확정 채널이 없으면
* 예약 칸이 없다. 하나도 없으면 바 자체를 그리지 않는다 — 빈 띠가 화면만 먹는다.
* ★ 아이폰 홈 인디케이터를 피한다(.safe-b).
*/
export function MobileTabBar() {
const payload = useSite();
const phone = payload.place.phone;
const links = bookingLinks(payload);
const booking = links[0];
const kakao = payload.links.find((link) => link.confirmed && /kakao/i.test(link.url));
const stayBooking = payload.place.category === PlaceCategory.LODGING ? stayBookingView(payload) : null;
const [bookingOpen, setBookingOpen] = useState(false);
if (!phone && !booking && !kakao) return null;
return (
<>
<nav
aria-label="연락 · 예약"
className="border-line safe-b fixed inset-x-0 bottom-0 z-50 flex items-stretch gap-2 border-t px-3 pt-2 backdrop-blur-md lg:hidden"
style={{backgroundColor: 'color-mix(in srgb, var(--tpl-surface, #fff) 94%, transparent)'}}
>
<a
href="#location"
className="tap border-line flex w-12 shrink-0 flex-col items-center justify-center rounded-lg border text-[length:var(--fs-xs)] leading-none font-medium"
aria-label="오시는 길"
>
<MapPin className="size-4" />
<span className="mt-0.5"></span>
</a>
{kakao && (
<a
href={kakao.url}
target="_blank"
rel="noopener noreferrer"
className="tap border-line flex w-12 shrink-0 flex-col items-center justify-center rounded-lg border text-[length:var(--fs-xs)] leading-none font-medium"
aria-label={channelLabel(kakao)}
>
<MessageCircle className="size-4" />
<span className="mt-0.5"></span>
</a>
)}
{phone && (
<a
href={`tel:${phone}`}
className="tap border-line flex flex-1 items-center justify-center gap-1.5 rounded-lg border px-2 text-[length:var(--fs-sm)] font-bold"
>
<Phone className="size-4" />
<span></span>
</a>
)}
{booking && (
stayBooking ? (
<button
type="button"
onClick={() => setBookingOpen(true)}
className="tap flex min-w-0 flex-1 items-center justify-center truncate rounded-lg border border-transparent px-2 text-[length:var(--fs-sm)] font-bold"
style={{backgroundColor: 'var(--color-brand)', color: 'var(--tpl-bg, #fff)'}}
>
</button>
) : (
<a
href={booking.url}
target="_blank"
rel="noopener noreferrer"
className="tap flex min-w-0 flex-1 items-center justify-center truncate rounded-lg border border-transparent px-2 text-[length:var(--fs-sm)] font-bold"
style={{backgroundColor: 'var(--color-brand)', color: 'var(--tpl-bg, #fff)'}}
>
{/* ★ channelLabel() 은 수집된 제목("스테이,머뭄 네이버 플레이스")을 준다 —
390px 바에서 버튼 밖으로 넘쳤다. 버튼이 답할 건 '어디서 예약하나' 하나다. */}
{bookingActionLabel(booking)}
</a>
)
)}
</nav>
{stayBooking && (
<Modal open={bookingOpen} onClose={() => setBookingOpen(false)} label="예약 요청" title="예약 요청" wide>
<StayBookingDemo />
</Modal>
)}
</>
);
}