mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-22 23:16:46 +00:00
0dd07befa7
The Plan tab was the last trip tab still in the old design. It now reads like Bookings, Costs, Lists, Files and Collab, without losing a function: - Day plan: each day is a card with a head band (day tile with weather, stay and rental pills, a "+" menu for place, stay, transport, transit and note), rows with "..." menus, white badges and a white route bar. - Places column: add, search and filters in three rows, a show dropdown, category and rating filters, and a selection bar at the foot. - Place inspector, day details and the stay editor on the shared dialog frame; the place form in the editor look with the name in the head band. - Clicking a booking or transport anywhere on the plan opens the booking detail popup first; Edit leads to the editor. The popup is one host shared with the Bookings and Transports tabs. - Files and Collab dialogs, notes, polls, links and chat moved to the same frame; shared DialogShell and dialogParts hold the building blocks. - The map hover card shows the place photo and follows the theme. Every new and reworded string is translated into all locales, and a few older English leftovers (help center, cost split, import hints) with them. Phone screens keep their own look.
95 lines
3.5 KiB
TypeScript
95 lines
3.5 KiB
TypeScript
import { useId, useState } from 'react'
|
|
import { CalendarPlus, Plus } from 'lucide-react'
|
|
import { DialogButton, FooterSpacer, fs } from '../shared/DialogShell'
|
|
import { dayDate } from '../../utils/dayLabel'
|
|
import type { DayAddControls } from '../../utils/dayAdd'
|
|
|
|
interface DayAddFooterProps {
|
|
/** Without it, or on a trip without dates, the footer keeps its single "Add day" button. */
|
|
dayAdd?: DayAddControls
|
|
onAddDay: () => void
|
|
onClose: () => void
|
|
t: (key: string, params?: Record<string, string | number>) => string
|
|
locale: string
|
|
}
|
|
|
|
/**
|
|
* The bar of a dialog with a line over its buttons. DialogFooter is one row;
|
|
* this is the same bar (border, inset) holding the line and the row under it.
|
|
*/
|
|
const FOOTER_COLUMN = 'flex flex-none flex-col gap-2.5 border-t border-edge-faint px-6 py-3.5'
|
|
const HINT = 'm-0 text-content-muted'
|
|
/** Should a language run long, the row wraps between buttons, never inside one. */
|
|
const ROW = 'flex flex-wrap items-center gap-2'
|
|
|
|
/**
|
|
* The foot of the reorder dialog: close it, or add a day.
|
|
*
|
|
* On a trip with dates there are two ways to add one. The next calendar day,
|
|
* named on its button, extends the trip by one; a day without a date goes to
|
|
* the end and leaves the trip dates alone. The line above them says what the
|
|
* button in view does, and switches while the pointer or the focus rests on
|
|
* the other one. When no day can be added it says why instead.
|
|
*/
|
|
export function DayAddFooter({ dayAdd, onAddDay, onClose, t, locale }: DayAddFooterProps) {
|
|
const [aboutUndated, setAboutUndated] = useState(false)
|
|
const hintId = useId()
|
|
const busy = dayAdd?.busy ?? false
|
|
const blocked = dayAdd?.blocked ?? null
|
|
const addOff = busy || !!blocked
|
|
|
|
const closeButton = <DialogButton onClick={onClose}>{t('common.close')}</DialogButton>
|
|
|
|
if (!dayAdd?.nextDate) {
|
|
return (
|
|
<footer className={FOOTER_COLUMN}>
|
|
{blocked && <p className={HINT} style={fs(12)}>{blocked}</p>}
|
|
<div className={ROW}>
|
|
<FooterSpacer />
|
|
{closeButton}
|
|
<DialogButton variant="primary" onClick={onAddDay} disabled={addOff} icon={<Plus size={14} strokeWidth={2} aria-hidden="true" />}>
|
|
{t('dayplan.addDay')}
|
|
</DialogButton>
|
|
</div>
|
|
</footer>
|
|
)
|
|
}
|
|
|
|
const date = dayDate(dayAdd.nextDate, locale) ?? dayAdd.nextDate
|
|
const hint = blocked
|
|
?? (aboutUndated ? t('dayplan.addUndatedDayHint') : dayAdd.datedBlocked ?? t('dayplan.addDatedDayHint', { date }))
|
|
|
|
return (
|
|
<footer className={FOOTER_COLUMN}>
|
|
<p id={hintId} aria-live="polite" className={HINT} style={fs(12)}>
|
|
{hint}
|
|
</p>
|
|
<div className={ROW}>
|
|
<DialogButton
|
|
onClick={onAddDay}
|
|
disabled={addOff}
|
|
aria-describedby={hintId}
|
|
icon={<Plus size={14} strokeWidth={2} aria-hidden="true" />}
|
|
onMouseEnter={() => setAboutUndated(true)}
|
|
onMouseLeave={() => setAboutUndated(false)}
|
|
onFocus={() => setAboutUndated(true)}
|
|
onBlur={() => setAboutUndated(false)}
|
|
>
|
|
{t('dayplan.addUndatedDay')}
|
|
</DialogButton>
|
|
<FooterSpacer />
|
|
{closeButton}
|
|
<DialogButton
|
|
variant="primary"
|
|
onClick={dayAdd.onAddDated}
|
|
disabled={addOff || !!dayAdd.datedBlocked}
|
|
aria-describedby={hintId}
|
|
icon={<CalendarPlus size={14} strokeWidth={2} aria-hidden="true" />}
|
|
>
|
|
{t('dayplan.addDatedDay', { date })}
|
|
</DialogButton>
|
|
</div>
|
|
</footer>
|
|
)
|
|
}
|