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
7 changes: 4 additions & 3 deletions src/components/rooms/RoomTripEditDialog.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,12 @@ it("uses the existing trimmed mutation payload and closes only after save succee
expect(dialog()).toBeNull();
});

it("retains the original date floor, date ordering and required destination validation", async () => {
it("allows an earlier start date and keeps date ordering and required destination validation", async () => {
await open();
// 저장된 시작일보다 이른 날짜로도 시작일을 앞당길 수 있다
await pickDates("start", "2026-10-09");
expect(dateField("start").getAttribute("aria-label")).toContain("2026년 10월 10일");
expect(button("적용하기", dialog()!).disabled).toBe(true);
expect(dateField("start").getAttribute("aria-label")).toContain("2026년 10월 9일");
expect(button("적용하기", dialog()!).disabled).toBe(false);
// 종료 칸에서 시작일보다 이른 날짜를 고르면 기간을 거꾸로 만들지 않고 새 시작일이 되며, 종료일은 그대로다
await pickDates("start", "2026-10-11", "2026-10-12");
await pickDates("end", "2026-10-10");
Expand Down
9 changes: 1 addition & 8 deletions src/components/rooms/RoomTripEditFields.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,10 @@ import {
} from "@/lib/plan/schedulePolicy";
import {
isTripDateRangeInvalid,
isTripStartBeforeMin,
ROOM_TRIP_TITLE_MAX_LENGTH,
TRIP_DATE_RANGE_INVALID_MESSAGE,
TRIP_DESTINATION_MAX_LENGTH,
TRIP_DESTINATIONS_MAX_COUNT,
TRIP_START_BEFORE_MIN_MESSAGE,
type TripFormValues,
} from "@/lib/rooms/trip-form";
import { cn } from "@/lib/utils";
Expand All @@ -29,7 +27,6 @@ type Props = {
idPrefix: string;
values: TripFormValues;
readOnly?: boolean;
startDateMin?: string;
onTitleChange: (value: string) => void;
onDestinationsChange: (next: string[]) => void;
onStartDateChange: (value: string) => void;
Expand All @@ -53,7 +50,6 @@ export function RoomTripEditFields({
idPrefix,
values,
readOnly = false,
startDateMin,
onTitleChange,
onDestinationsChange,
onStartDateChange,
Expand All @@ -64,11 +60,9 @@ export function RoomTripEditFields({
const [destinationWarning, setDestinationWarning] = useState<string | null>(null);
const destinationsFull = destinations.length >= TRIP_DESTINATIONS_MAX_COUNT;
const dateRangeInvalid = isTripDateRangeInvalid(startDate, endDate);
const startBeforeMin = startDateMin != null && isTripStartBeforeMin(startDate, startDateMin);
const scheduleDayLimitExceeded = isTripScheduleDayLimitExceeded(startDate, endDate);
let dateMessage = null;
if (startBeforeMin) dateMessage = TRIP_START_BEFORE_MIN_MESSAGE;
else if (dateRangeInvalid) dateMessage = TRIP_DATE_RANGE_INVALID_MESSAGE;
if (dateRangeInvalid) dateMessage = TRIP_DATE_RANGE_INVALID_MESSAGE;
else if (scheduleDayLimitExceeded) dateMessage = TRIP_SCHEDULE_DAY_LIMIT_MESSAGE;

function addDestination(place: { description: string; placeId: string } | null) {
Expand Down Expand Up @@ -168,7 +162,6 @@ export function RoomTripEditFields({
<DateRangeField
idPrefix={idPrefix}
value={{ start: startDate, end: endDate }}
min={startDateMin}
popupOnly
onChange={(next) => {
onStartDateChange(next.start);
Expand Down
23 changes: 19 additions & 4 deletions src/components/rooms/RoomTripEditForm.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ vi.mock("./TripDateShrinkConfirmModal", () => ({
TripDateShrinkConfirmModal: () => <div data-confirm />,
}));
Object.assign(globalThis, { IS_REACT_ACT_ENVIRONMENT: true });
it("preserves drafts and original date floor on same-room refresh; reopening and room switch reset correctly", async () => {
it("preserves drafts on same-room refresh; reopening and room switch reset correctly", async () => {
const container = document.createElement("div");
const root = createRoot(container);
const room = {
Expand Down Expand Up @@ -50,9 +50,7 @@ it("preserves drafts and original date floor on same-room refresh; reopening and
await render(room);
expect(state.fields!.values.startDate).toBe("2026-10-10");
expect(state.fields!.values.endDate).toBe("2026-10-12");
expect(state.fields!.startDateMin).toBe("2026-10-10");
await render({ ...room, startDate: "2026-10-11" });
expect(state.fields!.startDateMin).toBe("2026-10-10");
await act(async () => state.fields!.onEndDateChange("2026-10-11"));
await act(async () => container.querySelector<HTMLButtonElement>("button")!.click());
expect(container.querySelector("[data-confirm]")).not.toBeNull();
Expand All @@ -64,7 +62,6 @@ it("preserves drafts and original date floor on same-room refresh; reopening and
endDate: "2026-11-12",
});
expect(state.fields!.values.title).toBe("B");
expect(state.fields!.startDateMin).toBe("2026-11-10");
expect(container.querySelector("[data-confirm]")).toBeNull();
expect(state.update).not.toHaveBeenCalled();
await act(async () => root.unmount());
Expand Down Expand Up @@ -109,6 +106,24 @@ it("keeps the values from when it opened while the server changes and saves the
await act(async () => second.unmount());
});

it("allows moving the start date earlier than the saved start date", async () => {
state.update.mockClear();
const container = document.createElement("div");
const root = createRoot(container);
const room = { id: "r", title: "A", destinations: ["서울"], startDate: "2027-01-04", endDate: "2027-01-11" };
await act(async () => root.render(<RoomTripEditForm room={room} />));
expect("startDateMin" in state.fields!).toBe(false);
await act(async () => state.fields!.onStartDateChange("2027-01-02"));
await act(async () => container.querySelector<HTMLButtonElement>("button")!.click());
expect(state.update).toHaveBeenCalledWith(
expect.objectContaining({
data: expect.objectContaining({ startDate: "2027-01-02", endDate: "2027-01-11" }),
}),
expect.anything(),
);
await act(async () => root.unmount());
});

it("does not submit draft edits and submits once for rapid apply clicks", async () => {
state.update.mockClear();
const container = document.createElement("div");
Expand Down
6 changes: 0 additions & 6 deletions src/components/rooms/RoomTripEditForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import {
areDestinationsEqual,
isTripDateRangeInvalid,
isTripDestinationsValid,
isTripStartBeforeMin,
ROOM_TRIP_TITLE_MAX_LENGTH,
toTripFormValues,
type RoomTripFormSource,
Expand Down Expand Up @@ -50,9 +49,6 @@ function RoomTripEditSession({ room, readOnly = false, onSaved }: Props) {
const [endDate, setEndDate] = useState(opened.endDate);
const [shrinkConfirmOpen, setShrinkConfirmOpen] = useState(false);

// Keep the original date floor throughout this room's edit session.
const [baselineStartYmd] = useState(opened.startDate);

const { data: schedules } = useRoomSchedules(room.id);
const { mutate: updateRoom, isPending, error } = useUpdateRoom();

Expand All @@ -76,7 +72,6 @@ function RoomTripEditSession({ room, readOnly = false, onSaved }: Props) {
startDate &&
endDate &&
!dateRangeInvalid &&
!isTripStartBeforeMin(startDate, baselineStartYmd) &&
!scheduleDayLimitExceeded &&
!isPending &&
isDirty;
Expand Down Expand Up @@ -136,7 +131,6 @@ function RoomTripEditSession({ room, readOnly = false, onSaved }: Props) {
idPrefix={`trip-${room.id}`}
values={readOnly ? latest : { title, destinations, startDate, endDate }}
readOnly={readOnly}
startDateMin={baselineStartYmd || undefined}
onTitleChange={setTitle}
onDestinationsChange={setDestinations}
onStartDateChange={setStartDate}
Expand Down
Loading