mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-11-14 08:11:03 +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.
86 lines
3.7 KiB
TypeScript
86 lines
3.7 KiB
TypeScript
import { Fragment, type ReactNode } from 'react'
|
|
import EmptyState from '../shared/EmptyState'
|
|
import { MemoPlaceRow } from './PlacesSidebarRow'
|
|
import type { SidebarState } from './usePlacesSidebar'
|
|
import { usePluginViewContributions, PluginCardFooter } from '../Plugins/PluginContributions'
|
|
|
|
export function PlacesList({ header, ...S }: SidebarState & {
|
|
/**
|
|
* A block that sits above the places and scrolls WITH them.
|
|
*
|
|
* The Dawarich panel lives here rather than in a band of its own above the list. As its
|
|
* own band it could not grow: the list is the flex child that scrolls, so a panel with
|
|
* ten stays in it squeezed the list to nothing and took the rail's scrolling with it,
|
|
* and capping the panel left half its stays below a fold with no way to reach them.
|
|
* Inside the scroller it simply opens to its full height and the rail scrolls past it.
|
|
*/
|
|
header?: ReactNode
|
|
}) {
|
|
const {
|
|
filtered, scrollContainerRef, onScrollTopChange, filter, t, canEditPlaces, onAddPlace,
|
|
categories, selectedPlaceId, plannedIds, inDaySet, selectedIds, selectMode, selectedDayId,
|
|
isMobile, onPlaceClick, openContextMenu, placeMenuItems, onAssignToDay, toggleSelected, setDayPickerPlace, registerPlaceRow, tripId,
|
|
} = S
|
|
// Plugin-contributed columns/actions for the places view, keyed by place id (#plugins).
|
|
const contribFor = usePluginViewContributions('places', tripId)
|
|
return (
|
|
<div className="trek-stagger min-h-0 flex-1 overflow-y-auto px-2 pb-2" ref={scrollContainerRef} onScroll={(e) => onScrollTopChange?.((e.currentTarget as HTMLElement).scrollTop)}>
|
|
{header}
|
|
{filtered.length === 0 ? (
|
|
/* The mascot and one line, the shape every other empty state in TREK has.
|
|
The add link stays as the state's action: an empty list of places is
|
|
one of the few that has an obvious next step. */
|
|
<EmptyState
|
|
scene="search"
|
|
mood="sad"
|
|
size={92}
|
|
fill
|
|
surface="var(--bg-secondary)"
|
|
title={filter === 'unplanned' ? t('places.allPlanned') : t('places.noneFound')}
|
|
action={canEditPlaces ? (
|
|
<button
|
|
type="button"
|
|
onClick={onAddPlace}
|
|
className="text-caption text-content underline underline-offset-2 hover:text-accent"
|
|
>
|
|
{t('places.addPlace')}
|
|
</button>
|
|
) : undefined}
|
|
/>
|
|
) : (
|
|
filtered.map(place => {
|
|
const cat = categories.find(c => c.id === place.category_id)
|
|
const contributions = contribFor(place.id)
|
|
return (
|
|
<Fragment key={place.id}>
|
|
<MemoPlaceRow
|
|
place={place}
|
|
category={cat}
|
|
isSelected={place.id === selectedPlaceId}
|
|
isPlanned={plannedIds.has(place.id)}
|
|
inDay={inDaySet.has(place.id)}
|
|
isChecked={selectedIds.has(place.id)}
|
|
selectMode={selectMode}
|
|
selectedDayId={selectedDayId}
|
|
canEditPlaces={canEditPlaces}
|
|
isMobile={isMobile}
|
|
t={t}
|
|
onPlaceClick={onPlaceClick}
|
|
onContextMenu={openContextMenu}
|
|
menuItems={placeMenuItems}
|
|
onAssignToDay={onAssignToDay}
|
|
toggleSelected={toggleSelected}
|
|
setDayPickerPlace={setDayPickerPlace}
|
|
registerPlaceRow={registerPlaceRow}
|
|
/>
|
|
{contributions.length > 0 && (
|
|
<div className="px-2.5 pb-2"><PluginCardFooter items={contributions} tripId={tripId} /></div>
|
|
)}
|
|
</Fragment>
|
|
)
|
|
})
|
|
)}
|
|
</div>
|
|
)
|
|
}
|