[feat] solution: 추천 일정 카드에 출발지(업소명·출발 시각) 한 줄을 얹는다

This commit is contained in:
김성경 2026-09-11 14:07:17 +09:00
parent 6760b38fec
commit 8a2964ebf7
2 changed files with 26 additions and 1 deletions

View File

@ -18,6 +18,7 @@ import {
type PlannedStop, type PlannedStop,
} from '@o2o/shared'; } from '@o2o/shared';
import {useLocalGuide} from '@/hooks/useLocalGuide'; import {useLocalGuide} from '@/hooks/useLocalGuide';
import {useBuilderStore} from '@/stores/builder';
import {SectionBody, SectionFrame, Rail} from '../../primitives'; import {SectionBody, SectionFrame, Rail} from '../../primitives';
import type {SectionRenderProps} from '../../types'; import type {SectionRenderProps} from '../../types';
import { import {
@ -36,6 +37,7 @@ export function ItineraryTickets(props: SectionRenderProps) {
const {section, isSelected, onSelect} = props; const {section, isSelected, onSelect} = props;
const parsed = parseSectionData<ItineraryItem>(section.type, section.data); const parsed = parseSectionData<ItineraryItem>(section.type, section.data);
const guide = useLocalGuide(); const guide = useLocalGuide();
const placeName = useBuilderStore((s) => s.storeName);
// 사장님이 적은 것이 언제나 이긴다 — 서버가 만든 건 비었을 때의 기본값이다. // 사장님이 적은 것이 언제나 이긴다 — 서버가 만든 건 비었을 때의 기본값이다.
const items = parsed.items.length > 0 ? parsed.items : guide.itineraries; const items = parsed.items.length > 0 ? parsed.items : guide.itineraries;
const live = currentSeasons(); const live = currentSeasons();
@ -105,6 +107,13 @@ export function ItineraryTickets(props: SectionRenderProps) {
{itineraryDays(item).map((entry, dayIndex) => ( {itineraryDays(item).map((entry, dayIndex) => (
<div key={`${entry.label}-${dayIndex}`} className="space-y-1.5"> <div key={`${entry.label}-${dayIndex}`} className="space-y-1.5">
{/* ★ 출발지 — 첫날에만. 일정 데이터에는 없다(프롬프트가 정거장으로
못 넣게 막는다) — 빌더가 이미 아는 상호명을 그 자리에서 얹는다. */}
{dayIndex === 0 && placeName && (
<p className="text-[11px] opacity-50">
{placeName}에서 출발 · {entry.day.from}
</p>
)}
{entry.label && ( {entry.label && (
<p className="text-[11px] font-bold" style={{color: ITEM_ACCENT}}> <p className="text-[11px] font-bold" style={{color: ITEM_ACCENT}}>
{entry.label} {entry.label}

View File

@ -51,12 +51,15 @@ function DayCard({
startTime, startTime,
stops, stops,
first, first,
placeName,
}: { }: {
item: ItineraryItem; item: ItineraryItem;
badge: string; badge: string;
startTime?: string; startTime?: string;
stops: PlannerStop[]; stops: PlannerStop[];
first: boolean; first: boolean;
/** 업소 상호명 — 출발지 표시용. 일정 데이터에는 없다(프롬프트가 정거장으로 못 넣게 막는다). */
placeName: string;
}) { }) {
const day = planDay({name: item.name, startTime, stops}); const day = planDay({name: item.name, startTime, stops});
@ -102,6 +105,9 @@ function DayCard({
{/* ★ why·audience 는 첫 장만 — 다 채우면 같은 문구가 이틀·사흘 반복된다. */} {/* ★ why·audience 는 첫 장만 — 다 채우면 같은 문구가 이틀·사흘 반복된다. */}
{first && item.audience && <p className="text-[12px] opacity-60">{item.audience}</p>} {first && item.audience && <p className="text-[12px] opacity-60">{item.audience}</p>}
{first && item.why && <p className="text-[13px] leading-relaxed opacity-80">{item.why}</p>} {first && item.why && <p className="text-[13px] leading-relaxed opacity-80">{item.why}</p>}
{/* ★ 출발지 — 첫 장만(위 audience·why 와 같은 규칙). day.from 은 planDay 가 계산한
출발 시각(item.startTime)이라 시간표의 첫 줄과 정확히 이어진다. */}
{first && <p className="text-[11px] opacity-50">{placeName}에서 출발 · {day.from}</p>}
</div> </div>
<RouteMap stops={day.stops.map((planned) => planned.stop)} /> <RouteMap stops={day.stops.map((planned) => planned.stop)} />
@ -231,6 +237,7 @@ export function ItinerarySection() {
key={active} key={active}
tab={tab} tab={tab}
items={items.filter((item) => (item.duration?.trim() || NO_DURATION) === tab)} items={items.filter((item) => (item.duration?.trim() || NO_DURATION) === tab)}
placeName={payload.place.name}
/> />
</div> </div>
))} ))}
@ -254,7 +261,15 @@ export function ItinerarySection() {
* ★ `audience`("차 없이 오는 손님")는 카드에만 둔다. 줄에도 적으면 같은 문구가 위아래로 * ★ `audience`("차 없이 오는 손님")는 카드에만 둔다. 줄에도 적으면 같은 문구가 위아래로
* 두 번 나오고, 코스가 하나일 때는 줄이 아예 없어서 그 문구만 사라진다. * 두 번 나오고, 코스가 하나일 때는 줄이 아예 없어서 그 문구만 사라진다.
*/ */
function DurationRail({tab, items}: {tab: string; items: ItineraryItem[]}) { function DurationRail({
tab,
items,
placeName,
}: {
tab: string;
items: ItineraryItem[];
placeName: string;
}) {
/** 코스별 시작 위치와 길이. 레일 인덱스 ↔ 코스를 잇는 유일한 표다. */ /** 코스별 시작 위치와 길이. 레일 인덱스 ↔ 코스를 잇는 유일한 표다. */
const courses = useMemo(() => { const courses = useMemo(() => {
let cursor = 0; let cursor = 0;
@ -311,6 +326,7 @@ function DurationRail({tab, items}: {tab: string; items: ItineraryItem[]}) {
startTime={day.startTime} startTime={day.startTime}
stops={day.stops} stops={day.stops}
first={dayIndex === 0} first={dayIndex === 0}
placeName={placeName}
/> />
)), )),
)} )}