Commit Graph

468 Commits

Author SHA1 Message Date
Maurice 7c6b7622fc fix(planner): open the places filters across the whole sidebar row
Hung off an icon near the end of the row, the category and rating lists ran
out of the sidebar and were cut off.
2026-10-14 13:24:27 +02:00
Maurice 341520f98b fix(planner): give day plan rows their full width until the controls are wanted
The hover controls took their room at rest, so a place's line and a note's
text broke early. A place now gives way only while hovered, a note's controls
fade in over the end of its text in the note's own colour, and a picked place
shows none. All three controls of a place come out together: the add-booking
ticket waited for a React hover state and came a beat late. A click no longer
leaves them showing; keyboard focus and an open menu do.
2026-10-14 13:24:27 +02:00
Maurice 7795061630 refactor(tours): move the planner additions out of the map, sidebar and page files that grew past their size baseline 2026-10-14 10:00:08 +02:00
Maurice dbee1664af fix(tours): hold the client warning ratchet and the server format baseline flat after the rebase onto dev 2026-10-14 09:11:47 +02:00
elmocito 57ea219a43 feat(tours): complete planner follow-up scope
This commit completes the reviewed Tours follow-up scope after the Tours MVP was merged into dev.

Included feature blocks:

- RC-01: identify Tours in PLAN day rows and show compact Tour facts;
- RC-06: search for a city or address in TOUR-PLANNER without hidden writes;
- RC-07: reorder Tour waypoints in the left sidebar;
- RC-08: drag Tour waypoints on Leaflet, MapLibre and Mapbox with stable viewport, history and routing behavior;
- RS-01: persist Tour descriptions and HTTPS information links with local provider classification;
- RS-02: persist breaks/additional time and calculated or manually adjusted planned Tour duration without changing scheduling semantics.
The implementation preserves:

- ordinary Place and Legacy Track behavior;
- Tours-off fallback behavior;
- existing GPX/import behavior;
- Tour assignment and deletion behavior;
- calculated routing duration as a separate value;
- existing Tour provenance fields;
- keyboard/list alternatives for waypoint editing.
Not included:

- PR1-09 trip-wide Tour summary;
- Wanderer integration;
- GPX import changes;
- new Tour types;
- Tour-to-Day drag-and-drop;
- further roadmap or Post-Release features.
Validation:

- focused RC-01/RC-06/RC-07/RC-08/RS-01/RS-02 tests passed;
- affected shared/server/client typechecks passed;
- migration and generated DB type checks passed;
- scoped ESLint passed;
- staged diff checks passed;
- owner manual tests passed for all six feature blocks.
2026-10-14 08:39:31 +02:00
Maurice dd8d6a0b7d chore(knip): drop the exports nothing outside their module reads 2026-10-12 21:14:28 +00:00
Maurice 8832632b4e refactor(planner): each card over the map sets its own left side again, where the reading direction check counts it 2026-10-12 21:14:28 +00:00
Maurice 8f6dab30a2 refactor(planner): the weekday index of a plan day lives with the place opening state 2026-10-12 21:14:20 +00:00
Maurice 7b023c6b54 refactor(map): the road trip via helpers move to utils, so the route hook imports no component 2026-10-12 21:14:05 +00:00
Maurice 4a6ee6819c style(client): prettier on the files this branch touched 2026-10-12 20:01:22 +00:00
Maurice 5eb00d5878 refactor(planner): the day sidebar reads the stop days without a memo the compiler cannot keep 2026-10-12 19:57:48 +00:00
Maurice d3be55a88f fix(bookings): the transport dialog's submit always returns a promise, as the expense button expects 2026-10-12 19:57:48 +00:00
Maurice d4f4ab3891 fix(bookings): the delete question for a booking with a linked expense says the expense is deleted too
Deleting a booking also deletes the expenses linked to it, but the
question only said the booking would be deleted. A booking with a linked
expense now says so in every place that asks: the bookings list and detail,
the transport and transit dialogs, and the phone's booking cards. The MCP
delete_reservation description names the linked expenses and the stay a
hotel booking created as well.

Refs #2604
2026-10-12 20:42:55 +02:00
Maurice 62a80f420c fix(bookings): an end time before the start time on the same day shows a warning in the booking dialog
A booking saved with 18:00 to 09:00 and no end date was stored as ending
earlier on its start day without a word. The dialog now says that the end
time lies before the start time and that an end date is needed if it ends
the next day, on desktop and on the phone. It only warns and does not block
the save, since time zones can make such times correct.

Refs #2604
2026-10-12 20:42:48 +02:00
Maurice 3b933470d3 fix(planner): a booking pinned to a stop on another day is shown at that stop only, not on two days
A booking linked to a stop has two days to go by, its own date and the
stop's. They part when the stop is moved to another day or the trip is
shifted with "keep bookings on their dates", and the plan then listed the
booking at the stop and again in the timeline of its own day. The stop now
wins wherever both are drawn: the desktop day plan and day detail, the
phone's plan tab and day sheet, and the road trip rail on both. The
booking's date is left as it is; a stop on one of the booking's own days
changes nothing, so a rental picked up at a stop still runs across its
other days.

Refs #2604
2026-10-12 20:42:30 +02:00
Maurice fee41ab394 fix(collab): a note, poll, message or link that cannot be saved offline says it needs a connection
Collab, packing bags, packing templates and imports, the category a place
form creates and the place imports are not kept on the device, so offline
their writes cannot wait in the queue. They failed with a bare "Error" or the
generic save message. While TREK is offline the failure now reads "This needs
a connection. Try again once TREK is back online.", on desktop and on the
phone's Collab notes; online nothing changes.

Refs #2601
2026-10-12 20:18:04 +02:00
Maurice bdd8c08dd4 fix(planner): places put on a day, day names, day notes and trip details changed offline wait in the queue and sync once the connection is back
The day plan wrote straight to the API, so offline a place dropped on a day
snapped back with "Error assigning place", a renamed day or a new day note
failed, and a moved stop, a leg's travel mode or who joins a visit were lost.
Visits (put on a day, taken off, reordered, moved, travellers, leg mode), the
day's name, notes text and travel mode, and the day notes (add, edit, delete,
move) now go through assignmentRepo, dayRepo and dayNoteRepo and the mutation
queue: the cached day changes at once, a visit or note made offline gets a
temporary id that the edits after it wait for, and an online write held behind
an older queued one joins the queue, since none of these carry a version
token. A new order of a day that names a visit made offline is sent with its
real id, or without it when its create failed. Desktop, the phone timeline and
note sheet all write through the store, so both are covered.

The replay reads each answer for what it names: the day answer keeps the
cached notes and visits, the participants answer lands on its visit, and a
visit the server refuses with 409 (a Tour already on that day) fails instead
of waiting as a conflict no retry can solve.

The trip's name, description, reminder and the archive flag wait in the queue
too, from the planner, the phone and the dashboard. What only the server can
do says so instead of failing: new dates, a day count, a currency or a cover,
a new trip, a copy or a delete, members, guests, share and invite links, and
a new order of the days, which moves the dates of the bookings.

Refs #2601
2026-10-12 20:17:57 +02:00
Maurice 92c48fba22 fix(files): file notes, links, stars and the trash changed offline wait in the queue and sync once the connection is back
Every file write went straight to the API, so offline a star snapped back
without a word and moving a file to the trash failed with "Failed to delete
file". Notes, the place or booking of a file, a further link, the star, the
trash move, restore, delete for good and emptying the trash now go through
fileRepo and the mutation queue like the other trip data: the local copy
changes at once, and an online write to a file that still has an older one
queued joins the queue behind it, since file writes carry no version token.
The desktop file manager, its assign dialog and the phone's files tab,
menu, link and trash sheets all write through it.

The single file answers carry no ids of further links, so a replayed edit,
star or restore keeps the ones the local copy holds, and the link answer
({ success, links }) writes no row.

What only works online says so instead of failing: an upload shows that it
needs a connection, a place saved with newly picked files is stopped with a
hint, and removing a further link (its row id is known only to the server)
asks to try again once online.

Refs #2601
2026-10-12 19:32:16 +02:00
Maurice 64ceecefb3 fix(bookings): bookings, transports and stays saved offline wait in the queue and sync once the connection is back
Every booking and stay write went straight to the API, so offline a new
booking failed with "Network Error" and was lost, and a status tick, the
travellers or a moved transport snapped back. Bookings (add, edit, status,
travellers, delete, day order) and stays (add, edit, delete) now go through
reservationRepo and accommodationRepo and the mutation queue like places and
costs: a temporary row offline, held behind an older queued write online,
since neither carries a version token. Desktop, the phone sheets, the day
plan, the road trip nights and the PDF all write and read through them.

The replay reads the booking from `reservation` only, so the travellers
answer ({ travelers, reservation }) lands on the booking it names. A booking
or its stay that names a place or stay created offline is sent with the real
id once that one has synced, inside create_accommodation too, so an imported
hotel reviewed offline can be linked to a place made for it.

What only works online says so instead of failing: an AirTrail import, a
booking file import, the AI retry and an attachment on a saved booking show
that they need a connection, and a save with newly picked files is stopped
with a hint, since an upload cannot wait in the queue.

Refs #2601
2026-10-12 19:07:33 +02:00
Maurice 125649ce1b fix(planner): cards over the map stay clear of the layer pill, and a wide tab bar shows full tab names
The base-layer pill sits in the map's bottom-left corner on purpose. The
place inspector, a tour's detail and the day panel are centred between the
sidebars and were assumed never to reach that corner, but with both
sidebars open in a 1280 window they do, and the pill covered "Remove from
Day" and Edit. These cards now keep their left edge clear of the pill: a
narrow map moves the card right of it, a wide map keeps it centred as
before. The pill itself does not move, and the phone sheets are unchanged.

The Bookings tab showed "Book" at every width, which read as cut off. The
short form now only shows where the bar is tight; from xl up the tab shows
its full name.

Refs #2604
2026-10-12 15:46:09 +02:00
Maurice 35eba21128 fix(costs): row buttons, settle-up rows and share toggles say what they are about to a screen reader
A screen reader lists a page's buttons together, and the costs views gave
it twenty "Edit" and "Delete" buttons with nothing to tell them apart.

- Edit and delete on an expense name the expense ("Edit: Dinner"), edit
  and undo on a payment name who paid whom, on desktop and phone.
- A settle-up row shows only initials in its avatars, its names lived in a
  tooltip; the row now carries them for assistive tech too, and its Settle
  button says who pays whom and how much.
- The search field of every searchable select (currency, time zone,
  category) was read out as "..."; it now says Search in the user's
  language.
- The public link toggles on the phone share sheet and the journey share
  toggles say whether they are on (aria-pressed), not only by colour.
- Japanese still said "you" in the settle-up lines.

Refs #2604
2026-10-12 15:45:56 +02:00
Maurice 9064dbb2f0 fix(costs): an expense made from a booking is dated on the booking day
Creating the expense from a booking opened the editor on today, and the
cost a booking import writes by itself had no date at all, so it sat under
"No date". Both now take the booking's own date, else a hotel's check-in
day, else the day the booking sits on. The server sets it for every
booking write that creates a cost, so REST, MCP, plugin RPC and the import
agree.

A start time typed on a booking with no day picked no longer borrows
today's date on desktop. Today was mostly outside the trip, the server
then moved the booking onto day 1 while the list kept today. Desktop now
does what the phone already did and keeps the time without a date.

Refs #2604
2026-10-12 15:04:22 +02:00
Maurice 12f08e3646 fix(planner): a timed booking dropped against its time goes back alone instead of taking the stops behind the drop along
The day list draws a row with a time by that time, and an untimed stop takes the
time of the timed row before it. A transit ride dropped behind a later stop was
stored there, drawn back at its time on the next render, and every untimed stop
behind the drop went with it; the next drag then stored that shuffle. A dropped
booking or timed note now lands in the slot nearest the drop the list draws it
in, so nothing else moves and what is stored is what is shown. Rides dropped
where their time allows stay on the drop, and the rule for untimed stops behind
a booking is unchanged.

The chronology check for a timed stop reads a booking's time for the day (the
arrival on the last day of a multi-day ride) instead of its departure, and the
three copies of the remove and insert step share one helper.

Refs #2631
2026-10-12 12:33:12 +02:00
Maurice c2dd66986c fix(maps): the day route leaves an imported gpx track to the track and carries on from its end
A GPX or KML import stores the track's first point as the place's position,
so the day route treated it as a single stop there and routed from the
track start to the next stop, a second road running alongside the track.
Any assigned place with a usable track now gets the gap a Tour already had:
the route runs up to the track start and continues from the track end, on
the desktop map and day list, the mobile timeline, the trip overview and the
PDF map, which now draws the track as a line. A place without a usable track
stays a plain stop and never shows as an invalid Tour.

Refs #2600
2026-10-12 11:43:50 +02:00
Maurice 3dbe76945d fix(roadtrip): the day view drives the road picked in the road trip
A chosen alternative or a hand-bent drive is stored as vias, and only the
road trip rail routed through them. The day route on the map, its
connectors in the day list and on the phone, and the overview of all days
asked the router for the bare stops, so they drew and measured the default
road again.

They now thread the same vias between two stops that follow each other in
the stored list, with the anchor counted the way the rail counts it, and
fold the answer back to one leg per stop pair. Only while the road trip
addon is on; avoid settings and the routing engine stay with the rail.

Refs #2580
2026-10-12 11:32:15 +02:00
Maurice 4ee5e3fa96 fix(bookings): booking cards always show the calendar date of their linked day
Phone booking and transport cards printed only the title of the linked day,
so a long title cut off in the field left no date at all. They now print the
date beside the title like the desktop card, as a span that never shrinks
while the title is truncated, and a day range takes the full width with the
time under it. The desktop card gets the same non-shrinking date.

A day without a date of its own (a trip planned without dates) now borrows
the booking's start or end date, on the desktop cards, the list, the detail
dialog, the shared page and the phone alike.

Refs #2590
2026-10-12 10:16:01 +02:00
Maurice c6f45b9be6 refactor(planner): a day head's pill row moves into a module of its own
The stays, hire cars and plugin label under a day's name now live in DayHeadPills, so DayPlanSidebar shrinks back under its size baseline. The stay fields are read through their types instead of an any cast, and the size, eslint and theme-disable baselines are lowered to the new counts.
2026-10-11 14:22:46 +02:00
Maurice 32606d20a6 fix(roadtrip): the rail passes the strict and lint gates, and its tests describe the new markup
The day head falls back to metric when no distance unit is set, VISIT_BOX is no longer exported, the day head is split into smaller parts and the booking icon is created once. The component tests now check the pill badges, the day tile and the ride cards instead of the old boxes, and both baselines are lowered to the new counts.
2026-10-11 14:01:18 +02:00
Maurice 0c554e319b fix(planner): a day head keeps its stays on one line, each name cut by the same share, under the name and the tools alike 2026-10-11 02:42:21 +02:00
Maurice 3ff88062f1 fix(planner): pills on a white row keep a hairline, share one height, and a transport reads its time before its phase 2026-10-11 02:36:58 +02:00
Maurice cc94bc6953 refactor(planner): the day header's stay order lives in its own module and its tests in their own file
The day badge fix grew DayPlanSidebar.tsx and its test past their size
baselines. The check-out, through, check-in order of the header's stays is
now dayHeadStays() with a test of its own, and the day header tests sit in
DayPlanSidebar.dayHead.test.tsx; both files drop below their old entries.
2026-10-10 22:18:36 +02:00
Maurice d496dd5d30 fix(planner): a day without a stay centres its name on the day badge 2026-10-10 21:33:38 +02:00
Maurice 836a845b61 fix(planner): moving a booking onto another day clears the leftover drag data
Dropping a booking on the body of another day moved it but left the
shared drag data (window.__dragData) of an earlier place or map drag in
place, while dropping it on the end zone of the day cleared both. A later
drop with nothing of its own could then read that leftover place id.

The cross day booking branch of the day body drop now clears
window.__dragData along with dragDataRef, the same as the end zone. The
two clears are one chained assignment, so DayPlanSidebar.tsx stays at
the line count lint:size holds it to.

Red test: FE-PLANNER-DAYPLAN-236 in
client/src/components/Planner/DayPlanSidebar.dragData.test.tsx failed
with "expected { placeId: '77' } to be null".

No existing assertions changed.
2026-10-09 18:58:57 +02:00
Maurice 8c9a31e5ef fix(planner): switching an edited flight or train to car no longer brings back stops from an earlier booking
The transport form stays mounted between openings. Editing a flight or a
train after a car kept the car's stops (desktop and phone) and, on the
desktop, the from and to picks of that earlier booking as well. Switching
the type to car in the same dialog showed those stale stops and places,
and saving wrote them onto the booking. The desktop also kept stale
flight and train rows when opening a car, bus or other booking.

seedTransportRoute only reset the rows a type does not show when the
phone asked for it (resetHiddenRoutes), and never reset the car stops for
a flight or a train. It now returns every row for every type, so a seed
always replaces the whole route. The option is removed; both views get
the same clean route.

Red test: FE-PLANNER-TRANSPORTFORM-008 in
client/src/components/Planner/useTransportForm.test.ts failed with
"expected [ { location: { ... }, time: '' } ] to deeply equal []".

Changed assertions (they asserted the stale rows were kept):
- useTransportForm.test.ts FE-PLANNER-TRANSPORTFORM-005: desktop
  fromPick toBe(picked) is now toEqual({}) (title updated), and the
  resetHiddenRoutes field is dropped from seedOptions and the phone call.
- transportEndpoints.test.ts FE-PLANNER-TRANSPORTEP-005: desktop route
  keys toEqual(['waypoints']) is now empty fromPick, toPick, carStops and
  trainWaypoints.
- transportEndpoints.test.ts FE-PLANNER-TRANSPORTEP-010: desktop car
  waypoints toBeUndefined() is now two empty rows (plus empty train rows),
  and the bus route now also carries the two empty row sets.
- transportEndpoints.test.ts: resetHiddenRoutes dropped from DESKTOP and
  PHONE.
2026-10-09 17:48:16 +02:00
Maurice 1e674f040a fix(planner): a create expense click that saves nothing no longer opens the editor on the next save
In the desktop booking and transport dialogs, clicking "Create expense"
(or editing a linked expense) before the booking had a title saved
nothing, which is right, but the wish was kept. The next ordinary save
then opened the expense editor out of nowhere. The same happened after a
save that threw.

useBookingExpenseIntent stored the wish in a ref and only cleared it when
the form read it, which the desktop forms do after a successful save. The
hook now clears the wish once the save its click started has settled,
unless a newer click replaced it in the meantime. The phone sheets read
it at the start of the save and are unaffected.

Red tests:
- FE-PLANNER-RESMODAL-107 in ReservationModal.test.tsx: "expected
  "vi.fn()" to not be called at all, but actually been called 1 times".
- FE-PLANNER-BOOKINGEXPENSE-005 in useBookingExpenseIntent.test.ts:
  "expected { create: true } to be null".
- FE-PLANNER-BOOKINGEXPENSE-006: "expected { editItem: { id: 9, ... } }
  to be null".

No existing assertions changed.
2026-10-09 17:43:24 +02:00
Maurice 70f4f7cef3 fix(planner): an imported booking with source files saves and opens its expense without file support
Saving an imported booking in the desktop booking dialog when the dialog
had no upload handler but the import carried source files rejected the
save: the files were handed to the missing handler, the promise rejected
with nothing to catch it, and the expense editor the user asked for never
opened. The transport dialog already skipped the upload in that case, the
booking dialog did not.

The shared uploadBookingFiles helper now takes an optional handler and
does nothing without one, so every form gets the same guard.

Red test: FE-PLANNER-RESMODAL-106 in
client/src/components/Planner/ReservationModal.test.tsx failed with
"expected spy to be called with arguments" for onOpenExpense, plus the
unhandled rejection "TypeError: upload is not a function". The helper
case is covered by FE-PLANNER-BOOKINGFORM-008.

No existing assertions changed.
2026-10-09 17:36:59 +02:00
Maurice 07117949ef fix(mobile): editing a bus, car, taxi or ferry on the phone keeps its price (#2233)
Editing a transport without metadata of its own (bus, car, taxi, ferry
without a transit itinerary) in the phone sheet wiped the expense price
shown on the booking card. The phone built the payload with
emptyMetadataAsNull and sent metadata: null, which the reservations
service reads as "clear the column", so keepMirroredPrice had nothing to
carry price and priceCurrency into. The desktop and both booking forms
send an empty object, which keeps the price.

The option only existed to keep the phone's null, so it is gone and both
views now send the rebuilt metadata object as is.

Red test: FE-MOB-TRFRM-060 in
client/tests/unit/mobile/trip/MTransportFormSheet.test.tsx failed with
"AssertionError: expected null to deeply equal {}".
RESV-SVC-043 in server/tests/unit/nest/reservations.service.test.ts shows
the server side: {} keeps the stored price, null drops it.

Changed assertions (they asserted the null):
- MTransportFormSheet.test.tsx FE-MOB-TRFRM-006: payload metadata null
  is now {}.
- MTransportFormSheet.test.tsx FE-MOB-TRFRM-011: metadata toBeNull is now
  toEqual({}).
- MTransportFormSheet.test.tsx FE-MOB-TRFRM-027: metadata toBeNull is now
  toEqual({}).
- transportEndpoints.test.ts FE-PLANNER-TRANSPORTEP-014: phone metadata
  toBeNull is now toEqual({}) (title updated to match), and the
  emptyMetadataAsNull field is dropped from DESKTOP_SAVE and PHONE_SAVE.
- useTransportForm.test.ts FE-PLANNER-TRANSPORTFORM-007: metadata: null
  is now metadata: {}.
2026-10-09 17:29:22 +02:00
Maurice a843b66c33 refactor(bookings): draw the notes, files and costs of the booking dialogs with one component
The desktop booking and transport dialogs ended on the same notes field,
link and files row and linked costs block. Both now render
BookingNotesFilesCosts next to them, with the same markup as before.
2026-10-09 16:29:40 +02:00
Maurice e86cb6a897 refactor(bookings): let the phone plugin slots use the reservation detail plugin hook
ReservationPluginSlots in tabChrome repeated the plugin store selector
that useReservationDetailPlugins already holds for the desktop panels.
It now calls the hook, which gets its own test.
2026-10-09 16:15:49 +02:00
Maurice c1a9e98ec4 refactor(planner): share the cross-day move and end-of-day drop between the day body and the end zone
The day body and the drop zone at the end of the list both carried their
own copy of two steps: moving a stop or note dragged in from another day,
and putting a same-day row after the day's last row. Both now live in the
plan hook as moveInFromOtherDay and dropAfterLastItem.

The differences stay where they were: only the end zone passes a
reservation id, so only it moves a booking to the end, and only it clears
the drag state after the move, which it still skips on an empty day.
2026-10-09 16:15:48 +02:00
Maurice 188d867aee refactor(planner): stop echoing the day plan's props back through its hook
useDayPlanSidebar returned about 145 names, and DayPlanSidebar destructured
the same list again, the largest copied block in the client. Most of it
was the component's own props passed straight back, plus values nothing
read any more.

The optional props' fallbacks move into withDayPlanDefaults, which the
hook and the view both read the props through, so they are written once.
The hook now returns only what it computes. State that only the view
reads (the context menu, the permission flags, the hover and expansion
states, the pending note deletion) is declared in the view, and the day
note handlers come back as one notes object. The values no one read are
gone, which also drops their unused variable warnings.

No behaviour changes: every value keeps its source and its fallback.
2026-10-09 16:15:48 +02:00
Maurice eb271f0d45 refactor(planner): share the suggestion dropdown between the airport, location and address fields
AirportSelect, LocationSelect and AddressInput each carried their own copy
of the open state, the keyboard highlight, the outside click that closes the
list, the floating list with its loading line and the suggestion row.

useSuggestionDropdown now holds the open state, the highlight, the outside
click and the arrow, Enter and Escape handling. SuggestionDropdown.tsx holds
the boxed input with its icon and clear button (airport and location), the
floating list (all three), the row button and the place row content
(location and address).

What still differs between the fields stays with them or is a parameter:
the icon, the placeholder, what typing does (LocationSelect also resets the
highlight), the rows the keys walk (LocationSelect walks the trip's places
below three characters), whether the loading line may show (not while
LocationSelect offers the trip's places) and how a row lines up its content
(centred for airports). Each field keeps its own query, search and pick.
2026-10-09 16:15:48 +02:00
Maurice 97d3512aa7 refactor(planner): share the trip export between desktop and phone
The export dialog and the phone export sheet keep their rows and run the PDF, the calendar file and the GPX download through useTripExport, which also reads the print settings and whether my plan is offered. The desktop still runs one export at a time and closes after each one; the phone still lets a calendar or GPX file run beside a PDF and stays open after the PDF.
2026-10-09 15:19:43 +02:00
Maurice a4e3e83d3f refactor(planner): share the transport detail day and segment labels between desktop and phone
The transport detail dialog and the phone transport sheet name the booking's day and each segment's own booking code through transportDetailModel, next to the files filter they already share. The rest of the two views reads the booking differently on purpose and stays as it is.
2026-10-09 15:19:42 +02:00
Maurice fc3cc1bbbe refactor(planner): share the booking form and its hotel stay between desktop and phone
The booking dialog and the phone booking sheet keep their markup and run on useReservationForm: how a saved booking or an import fills the form, the end before start check, the start date and time, the place and hotel picks, the stay range and the request body with the stay a hotel booking creates come from reservationFormModel. The desktop's check-in window, its link to a stop inside the form and its imported price stay its own, behind options where the code is shared.
2026-10-09 15:19:42 +02:00
Maurice 203e425e7e refactor(planner): share the transport booking form between desktop and phone
The transport dialog and the phone transport sheet keep their markup and run on useTransportForm: the route rows, how a saved booking or an import fills them, the day options and the request body come from transportEndpoints. Where the two shells differ today (unknown type fallback, stopover days, which rows are reset, train anchors, empty metadata, the link field) the shared code takes an option. The phone's upload helper moves to bookingFormModel next to the traveller checks both booking forms use.
2026-10-09 15:19:42 +02:00
Maurice 71289ba43e refactor(places): share the place form and the maps search between desktop and phone
usePlaceForm and usePlaceSearch take the form state, the prefill, the
opening hours selection, the duplicate name check, the payload and the
maps search out of PlaceFormModal. MPlaceEditSheet, PlPlaceSearch and
PlCategoryPicker run on the same hooks, and PlTimeFields uses the shared
timeCollisions.

Where the two shells behaved differently the hooks take a dialog or a
sheet variant: the coordinate pair rule, abort and console handling,
when the billing session ends, whether a pick clears the suggestions,
the empty search cue, whether a map link clears the results, and how a
new category is trimmed and guarded. The open effect and the submit flow
stay per shell, because service stops, expense intents and delete arming
differ too much to share.
2026-10-09 15:19:41 +02:00
Maurice bbe3da7722 refactor(places): share the places pool filters and selection between desktop and phone
usePlacesPool holds the category filter, the search, the multi select
and the bulk mark visited of the places pool, for usePlacesSidebar and
for MPlacesBrowser with its filter controls and sheet. When selected
places are deleted, the desktop leaves select mode once none of them is
left and the phone drops only the missing ids. The hook keeps both.
2026-10-09 15:19:41 +02:00
Maurice 4dc446424f refactor(places): share the GPX, KML and KMZ file import between desktop and phone
usePlacesFileImport holds the GPX, KML and KMZ import that
FileImportModal and ImpFileStep each carried. The desktop keeps one
toast and one undo label and counts only the summary's skips. The phone
keeps a toast per format, the importFiles undo label for a mix and the
top level skipped fallback.
2026-10-09 15:19:41 +02:00
Maurice f2b6a14e9f refactor(places): share the Google and Naver list import between desktop and phone
useListImport holds the Google and Naver shared list import that
usePlacesSidebar and ImpListStep each carried. The desktop closes the
dialog and clears the link before the undo step, the phone calls onDone
after it, and the hook keeps both orders.
2026-10-09 15:19:41 +02:00