Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 39 additions & 13 deletions src/app/(app)/trips/new/date/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,13 @@ function parseDate(value?: string) {
}

function LocationField({
label,
placeholder,
value,
onOpen,
}: {
label: string;
placeholder: string;
value: LocationInput | null;
onOpen: () => void;
}) {
Expand All @@ -49,13 +53,13 @@ function LocationField({
<MapPin size={17} strokeWidth={2.1} aria-hidden />
</span>
<span className="min-w-0 flex-1">
<span className="block text-[0.7rem] font-medium text-[#8695aa]">출발지</span>
<span className="block text-[0.7rem] font-medium text-[#8695aa]">{label}</span>
<span
className={`block truncate text-sm font-bold tracking-[-0.02em] ${
value ? "text-[#10254e]" : "text-[#71819a]"
}`}
>
{value?.name ?? "역, 터미널 또는 장소를 검색하세요"}
{value?.name ?? placeholder}
</span>
</span>
<span className="shrink-0 pr-1 text-xl text-[#a7b5c7]" aria-hidden>
Expand All @@ -73,12 +77,15 @@ export default function TripDatePage() {
const [startLocation, setStartLocation] = useState<LocationInput | null>(
draft.startLocation ?? null,
);
const [pickerOpen, setPickerOpen] = useState(false);
const [endLocation, setEndLocation] = useState<LocationInput | null>(
draft.lodgingPlan.mode === "FIXED_BASE" ? draft.lodgingPlan.baseLocation : null,
);
const [pickerTarget, setPickerTarget] = useState<"START" | "END" | null>(null);
const [error, setError] = useState<string | null>(null);

const nights = start && end ? differenceInDays(start, end) : 0;
const tooLong = nights > 3;
const ready = Boolean(start && end && !tooLong && startLocation);
const ready = Boolean(start && end && !tooLong && startLocation && endLocation);

function handleSelect(date: Date) {
setError(null);
Expand All @@ -100,13 +107,13 @@ export default function TripDatePage() {
}

function continueFlow() {
if (!ready || !start || !end || !startLocation) return;
if (!ready || !start || !end || !startLocation || !endLocation) return;
updateDraft({
startDate: toDateInput(start),
endDate: toDateInput(end),
startLocation,
startTime: undefined,
lodgingPlan: { mode: "UNDECIDED" },
lodgingPlan: { mode: "FIXED_BASE", baseLocation: endLocation },
endConstraint: undefined,
fixedEvents: [],
dayOverrides: [],
Expand Down Expand Up @@ -196,8 +203,21 @@ export default function TripDatePage() {

{start && end && !tooLong && (
<section className="mt-6 border-t border-[#dce8f5] pt-5">
<h2 className="mb-3 text-lg font-extrabold tracking-[-0.04em] text-[#0b2146]">출발지</h2>
<LocationField value={startLocation} onOpen={() => setPickerOpen(true)} />
<h2 className="mb-3 text-lg font-extrabold tracking-[-0.04em] text-[#0b2146]">이동 기준 위치</h2>
<div className="flex flex-col gap-3">
<LocationField
label="출발지"
placeholder="출발할 역, 터미널 또는 장소를 검색하세요"
value={startLocation}
onOpen={() => setPickerTarget("START")}
/>
<LocationField
label="숙소·도착지"
placeholder="마지막에 도착할 숙소 또는 장소를 검색하세요"
value={endLocation}
onOpen={() => setPickerTarget("END")}
/>
</div>
</section>
)}

Expand All @@ -211,12 +231,18 @@ export default function TripDatePage() {
</button>
</div>

{pickerOpen && (
{pickerTarget && (
<LocationPickerSheet
title="출발지 선택"
initial={startLocation}
onClose={() => setPickerOpen(false)}
onSelect={setStartLocation}
title={pickerTarget === "START" ? "출발지 선택" : "숙소·도착지 선택"}
initial={pickerTarget === "START" ? startLocation : endLocation}
onClose={() => setPickerTarget(null)}
onSelect={(point) => {
if (pickerTarget === "START") {
setStartLocation(point);
} else {
setEndLocation(point);
}
}}
/>
)}
</PageFade>
Expand Down
3 changes: 0 additions & 3 deletions src/app/(app)/trips/new/places/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -184,9 +184,6 @@ export default function TripPlacesIntroPage() {
<h2 className="text-[1.55rem] font-extrabold tracking-[-0.05em] text-[#071b3f]">
꼭 가고 싶은 곳이 있나요?
</h2>
<p className="mt-2 text-sm leading-relaxed font-medium text-[#6e7f99]">
먼저 담아두면 그 장소를 중심으로 동선까지 맞춰 일정을 짜드려요
</p>
</section>

<div className="mt-auto flex flex-col gap-3 pt-7">
Expand Down
37 changes: 26 additions & 11 deletions src/app/(app)/trips/new/preview/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import { useCallback, useEffect, useRef, useState } from "react";
import { useRouter } from "next/navigation";
import { ChevronDown } from "lucide-react";
import AppHeader from "@/components/layout/AppHeader";
import { ApiError } from "@/lib/api/axios";
import { getTripQuestions } from "@/lib/api/questions";
Expand Down Expand Up @@ -167,6 +168,12 @@ export default function TripPreviewPage() {
<dt className="text-zinc-400">시작 위치</dt>
<dd className="text-right font-semibold">{draft.startLocation?.name}</dd>
</div>
{draft.lodgingPlan.mode === "FIXED_BASE" && (
<div className="flex items-start justify-between gap-4">
<dt className="text-zinc-400">숙소·도착지</dt>
<dd className="text-right font-semibold">{draft.lodgingPlan.baseLocation.name}</dd>
</div>
)}
<div>
<dt className="text-zinc-400">여행 취향</dt>
<dd className="mt-2 flex flex-wrap gap-2">
Expand All @@ -192,18 +199,26 @@ export default function TripPreviewPage() {
<button
type="button"
onClick={() => openTimeEditor(day)}
className="flex w-full gap-3 text-left"
className="flex w-full gap-3 rounded-2xl p-2 text-left transition-colors hover:bg-[#f6f9ff] active:bg-[#eef5ff]"
>
<span className="grid size-8 shrink-0 place-items-center rounded-full bg-zinc-900 text-xs font-bold text-white">{index + 1}</span>
<div className="min-w-0 flex-1">
<div className="flex items-center justify-between gap-3">
<p className="text-sm font-semibold">{day.date}</p>
<p className="text-sm font-bold text-[#2E7DF2]">{day.availableFrom} - {day.availableUntil}</p>
<span className="grid size-8 shrink-0 place-items-center rounded-full bg-zinc-900 text-xs font-bold text-white">{index + 1}</span>
<div className="min-w-0 flex-1">
<div className="flex items-start justify-between gap-3">
<div className="min-w-0">
<p className="text-sm font-semibold">{day.date}</p>
<p className="mt-1 truncate text-xs text-zinc-400">
{locationLabel(day.startLocation?.name, day.startLocationSource)} → {locationLabel(day.endLocation?.name, day.endLocationSource)}
</p>
</div>
<div className="shrink-0 text-right">
<p className="text-sm font-bold text-[#2E7DF2]">{day.availableFrom} - {day.availableUntil}</p>
<span className="mt-1 inline-flex items-center gap-0.5 rounded-full bg-[#EAF2FE] px-2 py-0.5 text-[0.65rem] font-bold text-[#2E7DF2]">
시간 수정
<ChevronDown size={12} className={editingDate === day.date ? "rotate-180 transition-transform" : "transition-transform"} />
</span>
</div>
</div>
</div>
<p className="mt-1 truncate text-xs text-zinc-400">
{locationLabel(day.startLocation?.name, day.startLocationSource)} → {locationLabel(day.endLocation?.name, day.endLocationSource)}
</p>
</div>
</button>
{editingDate === day.date && (
<div className="mt-3 rounded-2xl bg-[#f6f9ff] p-3">
Expand Down Expand Up @@ -260,7 +275,7 @@ export default function TripPreviewPage() {

{preview.routeCoverage === "ATTRACTION_ROUTES_ONLY" && (
<p className="rounded-xl bg-amber-50 p-4 text-sm leading-relaxed text-amber-800">
도착지와 숙소를 별도로 입력하지 않아 출발지를 기준으로 하루를 마무리합니다. 방문지 사이의 이동시간을 우선 고려해 일정을 만듭니다.
숙소·도착지를 별도로 입력하지 않아 출발지를 기준으로 하루를 마무리합니다. 방문지 사이의 이동시간을 우선 고려해 일정을 만듭니다.
</p>
)}

Expand Down
Loading