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.
This commit is contained in:
Maurice
2026-10-09 10:43:50 +02:00
parent 203e425e7e
commit fc3cc1bbbe
14 changed files with 1360 additions and 571 deletions
@@ -0,0 +1,52 @@
import { useRef } from 'react';
import type { BudgetItem } from '../../types';
import type { BookingExpenseIntent } from './bookingFormModel';
export interface BookingExpenseRemoval {
/** Deletes the expense linked to the booking. */
remove: (item: BudgetItem) => Promise<unknown>;
/** Called when the delete fails. */
onError: () => void;
}
/**
* The expense the user asked for from a booking form, held in a ref (not state) so
* the save started by the same click reads it. Behind the desktop booking and
* transport dialogs and the phone's booking and transport sheets: `create` (and on
* the desktop `edit`) note the wish and save; the form reads it with `take` at the
* point it always did and opens the editor for the saved booking.
*/
export function useBookingExpenseIntent(submit: () => Promise<unknown>, removal?: BookingExpenseRemoval) {
const intentRef = useRef<BookingExpenseIntent | null>(null);
return {
/** Forgets a wish left from an earlier opening. */
reset: () => {
intentRef.current = null;
},
/** Reads the wish and clears it. */
take: (): BookingExpenseIntent | null => {
const intent = intentRef.current;
intentRef.current = null;
return intent;
},
create: () => {
intentRef.current = { create: true };
void submit();
},
/** Saves the booking first, then opens the linked expense. */
edit: (item: BudgetItem) => {
intentRef.current = { editItem: item };
void submit();
},
remove: async (item: BudgetItem) => {
if (!removal) return;
try {
await removal.remove(item);
} catch {
removal.onError();
}
},
};
}