From ccb33cdaf544fcefc21c843b21ed6f080c50acf9 Mon Sep 17 00:00:00 2001 From: Adam Jackson Date: Thu, 25 Jun 2026 14:25:04 -0700 Subject: [PATCH 1/4] Record start and end date --- backend/models/Trip.ts | 1 + backend/routes/trips/[tripId]/index.ts | 26 +------- frontend/pages/[tripId]/itinerary.tsx | 52 +++------------ frontend/pages/[tripId]/settings.tsx | 57 ++++++++++++++--- frontend/pages/create.tsx | 88 +++++++++++++++++++++----- shared/types.ts | 5 ++ 6 files changed, 137 insertions(+), 92 deletions(-) diff --git a/backend/models/Trip.ts b/backend/models/Trip.ts index b6a474b7..a4f92ab8 100644 --- a/backend/models/Trip.ts +++ b/backend/models/Trip.ts @@ -87,6 +87,7 @@ const fields: Record< }, ], startDate: { type: String, default: null }, + endDate: { type: String, default: null }, startMonth: { type: Number, required: true }, endMonth: { type: Number, required: true }, imgUrl: { type: String, default: null }, diff --git a/backend/routes/trips/[tripId]/index.ts b/backend/routes/trips/[tripId]/index.ts index 864c2ecc..09c44a3a 100644 --- a/backend/routes/trips/[tripId]/index.ts +++ b/backend/routes/trips/[tripId]/index.ts @@ -97,6 +97,8 @@ trip.patch("/", async (c) => { const newData: Record = { name: data.name, region: data.region, + startDate: data.startDate ?? null, + endDate: data.endDate ?? null, startMonth: data.startMonth, endMonth: data.endMonth, }; @@ -264,28 +266,4 @@ trip.post("/share-code", async (c) => { throw new HTTPException(500, { message: "Failed to generate share code" }); }); -trip.patch("/set-start-date", async (c) => { - const session = await authenticate(c); - const tripId: string | undefined = c.req.param("tripId"); - - if (!tripId) { - throw new HTTPException(400, { message: "Trip ID is required" }); - } - - const { startDate } = await c.req.json<{ startDate: string }>(); - - await connect(); - const trip = await Trip.findById(tripId).lean(); - if (!trip) { - throw new HTTPException(404, { message: "Trip not found" }); - } - if (!(await isTripEditor(tripId, session.userId))) { - throw new HTTPException(403, { message: "Forbidden" }); - } - - await Trip.updateOne({ _id: tripId }, { startDate }); - - return c.json({}); -}); - export default trip; diff --git a/frontend/pages/[tripId]/itinerary.tsx b/frontend/pages/[tripId]/itinerary.tsx index 3edde419..b4db082b 100644 --- a/frontend/pages/[tripId]/itinerary.tsx +++ b/frontend/pages/[tripId]/itinerary.tsx @@ -3,10 +3,8 @@ import Header from "components/Header"; import TripNav from "components/TripNav"; import { useUser } from "hooks/useUser"; import ErrorBoundary from "components/ErrorBoundary"; -import Input from "components/Input"; import Button from "components/Button"; import { useTrip } from "hooks/useTrip"; -import toast from "react-hot-toast"; import { useModal } from "stores/modals"; import Icon from "components/Icon"; import NotFound from "components/NotFound"; @@ -19,7 +17,6 @@ export default function Itinerary() { const { is404, trip, canEdit } = useTrip(); const { close, modalId } = useModal(); const hasStartDate = !!trip?.startDate; - const [editingStartDate, setEditingStartDate] = React.useState(false); const shouldDefaultEdit = !!(trip && !trip?.startDate) || !!(trip && !trip?.itinerary?.length); const [editing, setEditing] = React.useState(shouldDefaultEdit); const [prevShouldDefaultEdit, setPrevShouldDefaultEdit] = React.useState(shouldDefaultEdit); @@ -30,15 +27,6 @@ export default function Itinerary() { if (shouldDefaultEdit) setEditing(true); } - const setStartDateMutation = useTripMutation<{ startDate: string }>({ - url: `/trips/${trip?._id}/set-start-date`, - method: "PATCH", - updateCache: (old, input) => ({ - ...old, - startDate: input.startDate, - }), - }); - const addDayMutation = useTripMutation<{ id: string; locations: any[] }>({ url: `/trips/${trip?._id}/itinerary`, method: "POST", @@ -48,16 +36,6 @@ export default function Itinerary() { }), }); - const submitStartDate = (e: React.FormEvent) => { - e.preventDefault(); - const form = e.currentTarget; - const date = form.date.value; - if (!date) return toast.error("Please choose a date"); - setStartDateMutation.mutate({ startDate: date }); - setEditingStartDate(false); - setEditing(true); - }; - const handleAddDay = () => { addDayMutation.mutate({ id: nanoId(6), locations: [] }); }; @@ -103,31 +81,17 @@ export default function Itinerary() { )} - {canEdit && !!trip?.startDate && !editingStartDate && ( - - )} - {canEdit && (!trip?.startDate || editingStartDate) && ( + {canEdit && !trip?.startDate && (
-

Choose start date

-
- - -
+

Set your trip dates

+

+ Add a start date in trip settings to build your day-by-day itinerary. +

+
)} {!canEdit && !trip?.startDate && ( diff --git a/frontend/pages/[tripId]/settings.tsx b/frontend/pages/[tripId]/settings.tsx index d0e292e3..4463f334 100644 --- a/frontend/pages/[tripId]/settings.tsx +++ b/frontend/pages/[tripId]/settings.tsx @@ -18,6 +18,12 @@ import Icon from "components/Icon"; import { useQueryClient } from "@tanstack/react-query"; import { getRegionCode, validateRegionFields, RegionFieldsValue } from "lib/region"; import { Trip } from "@birdplan/shared"; +import dayjs from "dayjs"; + +const monthOption = (month: number): Option => ({ + value: month.toString(), + label: months[month - 1], +}); export default function TripSettings() { const { trip, is404, isOwner } = useTrip(); @@ -48,16 +54,24 @@ type SettingsFormProps = { function SettingsForm({ trip, initialRegion, isOwner }: SettingsFormProps) { const navigate = useNavigate(); const queryClient = useQueryClient(); - const [startMonth, setStartMonth] = React.useState