mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-24 07:53:09 +00:00
fix(client): give the desktop halves the same fixes their mobile twins got
Several of the fixes in this branch landed on a mobile screen while the desktop component with the identical bug sat in another package and was left alone, which leaves the two disagreeing about the same handler. So: the appearance tab flushes its debounced write on unmount, the journey share copy guards navigator.clipboard (undefined on a plain-HTTP self-hosted install), the admin collab toggle rolls back from a scoped snapshot instead of a stale one, the reservation modal parses metadata through the guarded helper rather than a bare JSON.parse that takes the route down, and the notification toggle surfaces its failures and stops racing concurrent toggles. The Journey components reached the memories provider-status endpoint with a raw fetch(), past every apiClient interceptor; they go through a small memoriesApi now. And the server half of the OSM menu_url finding: the client renders it through safeHttpUrl, so place-enrichment should not be handing a javascript: value out in the first place. The share section hides itself when the server says the link is not this user's to manage, rather than offering a create button that 403s.
This commit is contained in:
@@ -1423,4 +1423,27 @@ describe('ReservationModal', () => {
|
||||
// Hotel place picker still shows its placeholder — no crash on the missing record.
|
||||
expect(screen.getByText('—')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// A legacy or corrupted metadata column used to throw straight out of the open
|
||||
// effect and take the whole planner route down.
|
||||
it('FE-PLANNER-RESMODAL-087: unparseable metadata opens the modal with empty check-in times', () => {
|
||||
const res = buildReservation({ id: 17, title: 'Grand Hotel', type: 'hotel' });
|
||||
(res as unknown as { metadata: string }).metadata = '{not json';
|
||||
render(<ReservationModal {...defaultProps} reservation={res} days={reviewDays()} />);
|
||||
|
||||
expect(screen.getByDisplayValue('Grand Hotel')).toBeInTheDocument();
|
||||
const times = screen.getAllByTestId('time-picker') as HTMLInputElement[];
|
||||
expect(times.map(i => i.value)).toEqual(['', '', '']);
|
||||
});
|
||||
|
||||
it('FE-PLANNER-RESMODAL-088: double-encoded metadata still fills the check-in times', () => {
|
||||
const res = buildReservation({ id: 18, title: 'Grand Hotel', type: 'hotel' });
|
||||
(res as unknown as { metadata: string }).metadata =
|
||||
JSON.stringify(JSON.stringify({ check_in_time: '16:00', check_out_time: '10:30' }));
|
||||
render(<ReservationModal {...defaultProps} reservation={res} days={reviewDays()} />);
|
||||
|
||||
const times = screen.getAllByTestId('time-picker') as HTMLInputElement[];
|
||||
expect(times[0].value).toBe('16:00');
|
||||
expect(times[2].value).toBe('10:30');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -12,6 +12,7 @@ import { useTranslation } from '../../i18n'
|
||||
import { CustomDatePicker } from '../shared/CustomDateTimePicker'
|
||||
import CustomTimePicker from '../shared/CustomTimePicker'
|
||||
import { openFile } from '../../utils/fileDownload'
|
||||
import { parseReservationMetadata } from '../../utils/flightLegs'
|
||||
import { resolveDayId } from '../../utils/formatters'
|
||||
import type { Day, Place, Reservation, TripFile, AssignmentsMap, Accommodation, BudgetItem } from '../../types'
|
||||
import { BookingCostsSection } from './BookingCostsSection'
|
||||
@@ -133,7 +134,7 @@ export function ReservationModal({ isOpen, onClose, onSave, reservation, days, p
|
||||
|
||||
setTravelerIds(new Set((reservation?.travelers || []).map(tv => tv.user_id)))
|
||||
if (reservation) {
|
||||
const meta = typeof reservation.metadata === 'string' ? JSON.parse(reservation.metadata || '{}') : (reservation.metadata || {})
|
||||
const meta = parseReservationMetadata(reservation)
|
||||
const rawEnd = reservation.reservation_end_time || ''
|
||||
let endDate = ''
|
||||
let endTime = rawEnd
|
||||
|
||||
Reference in New Issue
Block a user