여러 섹션의 opacity-55~70 짜리 보조 텍스트가 밝은 배경에서 너무 흐렸다 — 85~100 으로 올림(Carousel 카운터·SectionHead 리드문·SNS 소식·주변 안내 거리·공식 채널 링크). StayBookingDemo: "날짜·시간 선택" 토글을 없애고 달력을 기본으로 펼쳐 보여준다 — 접어 두면 손님이 그 버튼을 눌러야만 날짜를 볼 수 있었다.
104 lines
4.3 KiB
TypeScript
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>
|
|
)}
|
|
</>
|
|
);
|
|
}
|