/**
* 계절별 축제 — 지역 축제·행사만 따로 세운 섹션.
*
* ★ 왜 '주변 안내'에서 떼어냈나
* 맛집·명소는 아무 때나 가는 곳이고 축제는 **날짜가 지나면 못 가는 것**이다. 성격이 다른데
* 같은 목록에 섞여 있으면 손님은 기간을 안 읽고, 우리는 "이번 계절에 뭐 있어요?"라는
* 질문에 답할 자리를 잃는다. 계절을 축으로 세우면 그 질문이 곧 화면 구조가 된다.
*
* ★ 지금 계절만 열고 나머지 계절은 **HTML 에 남겨 둔 채 접는다**(`hidden`).
* 지우면 검색·AI 가 나머지 세 계절을 못 읽는다 — 이 레포의 규칙이다.
* ★ 첫 렌더에서는 계절이 비어 있다(`useLiveSeasons`). 그때는 아무것도 접지 않는다 —
* SSR 과 어긋나면 하이드레이션이 깨지고, 구운 HTML 에는 사계절이 다 남아야 한다.
* ★ 링크는 검색으로만 보낸다. 공공데이터가 준 공식 홈페이지가 있어도 우리가 붙이지 않는다 —
* 축제 사이트는 해마다 주소가 바뀌고, 죽은 링크의 책임은 이 홈페이지가 진다.
*/
import {useState} from 'react';
import {ArrowUpRight, MapPin} from 'lucide-react';
import type {FestivalEntry} from '@o2o/shared';
import {useSite} from '@/lib/site-context';
import {naverSearchUrl} from '@/lib/format';
import {useLiveSeasons} from '@/lib/use-live-seasons';
import {Section} from '@/lib/ui';
// shared 의 SEASON_ORDER 는 내보내지 않는다. 순서는 달력이 정한 것이라 갈릴 일이 없다.
const SEASONS = ['봄', '여름', '가을', '겨울'];
export function FestivalSection() {
const payload = useSite();
const festivals = payload.local.festivals;
const live = useLiveSeasons();
const [picked, setPicked] = useState(null);
if (festivals.length === 0) return null;
// 데이터에 실제로 있는 계절만 탭이 된다. 없는 계절 탭을 눌러 빈 화면을 보게 하지 않는다.
const tabs = SEASONS.filter((season) => festivals.some((f) => f.season === season));
// 계절을 안 적은 행사(연중·상시)는 어느 탭에서도 접지 않는다.
const evergreen = festivals.filter((f) => !f.season?.trim());
// 간절기에는 `live` 가 둘이고 뒤가 지금 계절이다 — 뒤에서부터 찾는다.
const auto = [...live].reverse().find((season) => tabs.includes(season));
/*
* ★ '전체' 를 넣는다 (2026-09-04, 사장님 지시)
* 지금 계절만 열어 두면 "가을 말고 다른 때 오면 뭐가 있나"를 보려고 탭을 네 번 눌러야 했다.
* 여행은 대개 몇 달 뒤를 잡는 일이라, 사계절을 한 번에 보는 길이 있어야 한다.
* 기본값은 그대로 지금 계절이다 — 처음 온 손님에게 열두 개를 늘어놓지 않는다.
*/
const ALL = '전체';
const active = picked ?? auto ?? null;
const showAll = active === ALL;
return (
지금은 {active} 축제입니다
) : undefined
}
footnote={
<>한국관광공사 TourAPI 기준. 일정은 주최 측 사정으로 바뀔 수 있습니다.>
}
>
{tabs.length > 1 && (
{[...tabs, ALL].map((season) => {
const on = active === season;
return (
);
})}
{/* 탭이 접혀 있어도 소제목은 남는다 — 펼쳐 놓고 읽는 기계에게는 이게 계절 표지다. */}
{season}
);
})}
{evergreen.length > 0 && (
계절 없이 열리는 행사
)}
);
}
/**
* ★ 좁은 화면에서는 **가로줄**, 넓어지면 세로 카드 (2026-09-04, 사장님: "모바일에서 너무 길다")
* 실측 390px 에서 이 섹션 하나가 1,274px — 화면 1.7개였다. 큰 사진을 세로로 쌓은 탓이다.
* 축제 카드에서 손님이 찾는 건 **언제·어디서** 셋 줄이지 사진이 아니다. 사진은 썸네일로 줄이고
* 글을 옆에 붙이면 한 장이 400px 에서 120px 이 된다.
* ★ 폭으로 컴포넌트를 갈아 끼우지 않고 CSS 로만 가른다 — 서버 렌더는 화면 폭을 모른다.
*/
function FestivalGrid({items}: {items: FestivalEntry[]}) {
return (