refactor(bookings): hand the notes and link to the shared lower part as form fields

The booking and transport dialogs each spelled out the notes and the link
as four value and change props, and with the saving flag the two calls
became one copied block. The shared lower part now takes the form and its
setter, as the dialogs hold them, and writes the two fields itself.
This commit is contained in:
Maurice
2026-10-29 15:50:09 +01:00
parent 97455da6ca
commit e8fbba236d
4 changed files with 17 additions and 26 deletions
@@ -28,10 +28,8 @@ function setup(overrides: Partial<Props> = {}) {
linkAndFiles.mockClear();
costs.mockClear();
const props: Props = {
notes: 'Gate B',
onNotesChange: vi.fn(),
url: 'https://example.com',
onUrlChange: vi.fn(),
form: { notes: 'Gate B', url: 'https://example.com' },
set: vi.fn(),
reservationId: 9,
tripFiles: [],
pendingFiles: [],
@@ -61,7 +59,7 @@ describe('BookingNotesFilesCosts', () => {
const area = screen.getByPlaceholderText('Additional notes...');
expect(area).toHaveValue('Gate B');
fireEvent.change(area, { target: { value: 'Gate C' } });
expect(props.onNotesChange).toHaveBeenCalledWith('Gate C');
expect(props.set).toHaveBeenCalledWith('notes', 'Gate C');
});
it('FE-PLANNER-NOTESFILESCOSTS-002: hands the link, the files and the costs their parts of the booking', () => {
@@ -70,7 +68,6 @@ describe('BookingNotesFilesCosts', () => {
expect(linkAndFiles).toHaveBeenCalledWith(
expect.objectContaining({
url: 'https://example.com',
onUrlChange: props.onUrlChange,
reservationId: 9,
canAttach: true,
uploading: false,
@@ -80,6 +77,8 @@ describe('BookingNotesFilesCosts', () => {
onDetached: props.attach.detachFile,
})
);
linkAndFiles.mock.calls[0][0].onUrlChange('https://example.org');
expect(props.set).toHaveBeenCalledWith('url', 'https://example.org');
expect(costs).toHaveBeenCalledWith(
expect.objectContaining({
reservationId: 9,
@@ -13,10 +13,8 @@ import type { useBookingFileAttach } from './useBookingFileAttach';
* files row, and the costs linked to this booking when the budget addon is on.
*/
export function BookingNotesFilesCosts({
notes,
onNotesChange,
url,
onUrlChange,
form,
set,
reservationId,
tripFiles,
pendingFiles,
@@ -28,10 +26,8 @@ export function BookingNotesFilesCosts({
pendingExpense,
expense,
}: {
notes: string;
onNotesChange: (notes: string) => void;
url: string;
onUrlChange: (url: string) => void;
form: { notes: string; url: string };
set: (field: 'notes' | 'url', value: string) => void;
/** The saved booking; unset while it is being created. */
reservationId: number | undefined;
tripFiles: TripFile[];
@@ -50,8 +46,8 @@ export function BookingNotesFilesCosts({
<>
<EditorField label={t('reservations.notes')}>
<textarea
value={notes}
onChange={(e) => onNotesChange(e.target.value)}
value={form.notes}
onChange={(e) => set('notes', e.target.value)}
rows={2}
placeholder={t('reservations.notesPlaceholder')}
className={TEXTAREA}
@@ -59,8 +55,8 @@ export function BookingNotesFilesCosts({
</EditorField>
<BookingLinkAndFiles
url={url}
onUrlChange={onUrlChange}
url={form.url}
onUrlChange={(url) => set('url', url)}
labelClass={LABEL}
inputClass={INPUT}
reservationId={reservationId}
@@ -386,10 +386,8 @@ export function ReservationModal({ isOpen, onClose, onSave, reservation, days, p
</div>
<BookingNotesFilesCosts
notes={form.notes}
onNotesChange={value => set('notes', value)}
url={form.url}
onUrlChange={value => set('url', value)}
form={form}
set={set}
reservationId={reservation?.id}
tripFiles={files}
pendingFiles={pendingFiles}
@@ -540,10 +540,8 @@ export function TransportModal({ isOpen, onClose, onSave, reservation, days, sel
</EditorField>
<BookingNotesFilesCosts
notes={form.notes}
onNotesChange={value => set('notes', value)}
url={form.url}
onUrlChange={value => set('url', value)}
form={form}
set={set}
reservationId={reservation?.id}
tripFiles={files}
pendingFiles={pendingFiles}