[feat] solution: 추천 일정 카드에 출발지(업소명·출발 시각) 한 줄을 얹는다
This commit is contained in:
parent
6760b38fec
commit
8a2964ebf7
@ -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}
|
||||||
|
|||||||
@ -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}
|
||||||
/>
|
/>
|
||||||
)),
|
)),
|
||||||
)}
|
)}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user