mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-20 22:16:26 +00:00
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
This commit is contained in:
@@ -129,7 +129,7 @@ function BookingCard({ res, planner, canEdit, compact }: {
|
||||
planner.setShowReservationModal(true)
|
||||
}
|
||||
|
||||
const { dayValue, timeValue } = cardWhen(res, startDay, endDay, t, locale, timeFormat)
|
||||
const { dayValue, dayDate, dayRange, timeValue } = cardWhen(res, startDay, endDay, t, locale, timeFormat)
|
||||
|
||||
return (
|
||||
<div className="mt-2 overflow-hidden rounded-2xl border border-[color:var(--m-rowbr)] bg-[color:var(--m-ic)]">
|
||||
@@ -146,6 +146,8 @@ function BookingCard({ res, planner, canEdit, compact }: {
|
||||
<div className="px-3 pb-3 pt-[9px]">
|
||||
<CardWhenAndCode
|
||||
dayValue={dayValue}
|
||||
dayDate={dayDate}
|
||||
dayRange={dayRange}
|
||||
timeValue={timeValue}
|
||||
code={res.confirmation_number}
|
||||
card={card}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// FE-COMP-MRESCARD-001 to FE-COMP-MRESCARD-007
|
||||
// FE-COMP-MRESCARD-001 to FE-COMP-MRESCARD-008
|
||||
import { fireEvent, render, screen } from '@testing-library/react';
|
||||
import { Hotel, Plane } from 'lucide-react';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
@@ -119,4 +119,26 @@ describe('MReservationCardParts', () => {
|
||||
fireEvent.click(screen.getByRole('button', { name: 'common.delete' }));
|
||||
expect(card.confirmDelete).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it('FE-COMP-MRESCARD-008: the date sits beside the day title and never shrinks, a range stacks the time under it (#2590)', () => {
|
||||
const { container } = render(
|
||||
<CardWhenAndCode
|
||||
dayValue="Germany Departure Flight – Korea Arrival"
|
||||
dayDate="Oct 21 – Oct 22"
|
||||
dayRange
|
||||
timeValue="15:35 – 09:55"
|
||||
code={null}
|
||||
card={cardState()}
|
||||
onOpen={vi.fn()}
|
||||
t={t}
|
||||
/>
|
||||
);
|
||||
|
||||
const title = screen.getByText('Germany Departure Flight – Korea Arrival');
|
||||
const date = screen.getByText('Oct 21 – Oct 22');
|
||||
expect(title).toHaveClass('truncate');
|
||||
expect(date).toHaveClass('flex-none');
|
||||
expect(title.contains(date)).toBe(false);
|
||||
expect(container.querySelector('button > div')).toHaveClass('flex-col');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -73,10 +73,14 @@ export function CardActions({ onEdit, onDelete, t }: { onEdit: () => void; onDel
|
||||
|
||||
/**
|
||||
* Date and time row, followed by the booking code. The code sits outside the
|
||||
* row button so its reveal can be its own control.
|
||||
* row button so its reveal can be its own control. The calendar date never
|
||||
* shrinks; a long day title is cut instead, and a day range takes the full
|
||||
* width with the time under it.
|
||||
*/
|
||||
export function CardWhenAndCode({
|
||||
dayValue,
|
||||
dayDate = null,
|
||||
dayRange = false,
|
||||
timeValue,
|
||||
code,
|
||||
card,
|
||||
@@ -84,6 +88,8 @@ export function CardWhenAndCode({
|
||||
t,
|
||||
}: {
|
||||
dayValue: string;
|
||||
dayDate?: string | null;
|
||||
dayRange?: boolean;
|
||||
timeValue: string;
|
||||
code: string | null | undefined;
|
||||
card: CardState;
|
||||
@@ -93,9 +99,14 @@ export function CardWhenAndCode({
|
||||
return (
|
||||
<>
|
||||
<button type="button" onClick={onOpen} className="block w-full text-start">
|
||||
<div className="flex gap-2">
|
||||
<div className={`flex gap-2 ${dayRange ? 'flex-col' : ''}`}>
|
||||
<Field label={t('reservations.date')} className="flex-[1.4]">
|
||||
{dayValue}
|
||||
{dayDate ? (
|
||||
<span className="flex min-w-0 items-center justify-center gap-1.5">
|
||||
<span className="min-w-0 truncate">{dayValue}</span>
|
||||
<span className="flex-none font-medium text-m-faint">{dayDate}</span>
|
||||
</span>
|
||||
) : dayValue}
|
||||
</Field>
|
||||
<Field label={t('reservations.time')} className="flex-1" tabular>
|
||||
{timeValue}
|
||||
|
||||
@@ -130,7 +130,7 @@ function TransportCard({ res, planner, shell, canEdit, compact }: {
|
||||
planner.setShowTransportModal(true)
|
||||
}
|
||||
|
||||
const { dayValue, timeValue } = cardWhen(res, startDay, endDay, t, locale, timeFormat)
|
||||
const { dayValue, dayDate, dayRange, timeValue } = cardWhen(res, startDay, endDay, t, locale, timeFormat)
|
||||
|
||||
return (
|
||||
<div className="mt-2 overflow-hidden rounded-2xl border border-[color:var(--m-rowbr)] bg-[color:var(--m-ic)]">
|
||||
@@ -158,6 +158,8 @@ function TransportCard({ res, planner, shell, canEdit, compact }: {
|
||||
<div className="px-3 pb-3 pt-[9px]">
|
||||
<CardWhenAndCode
|
||||
dayValue={dayValue}
|
||||
dayDate={dayDate}
|
||||
dayRange={dayRange}
|
||||
timeValue={timeValue}
|
||||
code={res.confirmation_number}
|
||||
card={card}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { formatTime, splitReservationDateTime } from '../../../../utils/formatters'
|
||||
import type { Day, Reservation, TranslationFn } from '../../../../types'
|
||||
import { linkedDayDate } from '../../../../components/Planner/bookings/bookingFacts'
|
||||
|
||||
/**
|
||||
* Transport view-model — the real-data counterpart to the demo's `trsSecs`
|
||||
@@ -45,8 +46,8 @@ export function parseTransportMeta(res: Reservation): TransportMeta {
|
||||
|
||||
/**
|
||||
* The date and time cells of a phone booking or transport card: the day (or day
|
||||
* range) the booking sits on, else its own date, and its time or time range. A
|
||||
* dash stands in for whichever is missing.
|
||||
* range) the booking sits on with its calendar date beside it, else its own
|
||||
* date, and its time or time range. A dash stands in for whichever is missing.
|
||||
*/
|
||||
export function cardWhen(
|
||||
res: Reservation,
|
||||
@@ -55,7 +56,7 @@ export function cardWhen(
|
||||
t: TranslationFn,
|
||||
locale: string,
|
||||
timeFormat: string,
|
||||
): { dayValue: string; timeValue: string } {
|
||||
): { dayValue: string; dayDate: string | null; dayRange: boolean; timeValue: string } {
|
||||
const startDt = splitReservationDateTime(res.reservation_time)
|
||||
const endDt = splitReservationDateTime(res.reservation_end_time)
|
||||
const fmtDate = (date: string) =>
|
||||
@@ -64,10 +65,14 @@ export function cardWhen(
|
||||
const timeValue = startDt.time
|
||||
? `${formatTime(startDt.time, locale, timeFormat)}${endDt.time ? ` – ${formatTime(endDt.time, locale, timeFormat)}` : ''}`
|
||||
: '—'
|
||||
const rangeEnd = startDay && endDay && endDay.id !== startDay.id ? endDay : undefined
|
||||
const dayValue = startDay
|
||||
? `${dayLabel(startDay)}${endDay && endDay.id !== startDay.id ? ` – ${dayLabel(endDay)}` : ''}`
|
||||
? `${dayLabel(startDay)}${rangeEnd ? ` – ${dayLabel(rangeEnd)}` : ''}`
|
||||
: startDt.date
|
||||
? fmtDate(startDt.date)
|
||||
: '—'
|
||||
return { dayValue, timeValue }
|
||||
// The same date the desktop card prints beside the day, so a long day title
|
||||
// can never hide when the booking actually is.
|
||||
const dayDate = startDay ? linkedDayDate(startDay, endDay, { start: startDt.date, end: endDt.date }, locale, ' – ') : null
|
||||
return { dayValue, dayDate, dayRange: !!rangeEnd, timeValue }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user