Split the trip page into tabs and move the header into the navbar
Three deep-linkable tabs (#/trip/:id/:tab): Trip (countdown, calendar, map, summary), Money (costs + expenses side by side), Checklist. Panels stay mounted; switching toggles visibility and updates the URL via history.replaceState so an open day editor and the live tickers are undisturbed. The Leaflet map revalidates its size on every trip-tab activation but only auto-fits once, when its container first gains real size - later visits keep the user's pan/zoom. The full-width trip-header card is gone: the navbar now carries a compact identity (back link, truncated name, date/days/currency) plus a single overflow menu holding members, join code, edit (now a centered modal) and delete. Dead header CSS removed; new styles live in tabs.css.
This commit is contained in:
+20
-1
@@ -400,7 +400,26 @@ Proxies `https://nominatim.openstreetmap.org/search?format=jsonv2&limit=5&accept
|
||||
|
||||
## Frontend contract notes
|
||||
|
||||
- SPA served from `public/`; all non-`/api` GETs fall back to `public/index.html` is NOT required — a single `index.html` with hash-based routing (`#/login`, `#/trips`, `#/trip/:id`) is the expected design, so no server-side fallback is needed.
|
||||
- SPA served from `public/`; all non-`/api` GETs fall back to `public/index.html` is NOT required — a single `index.html` with hash-based routing (`#/login`, `#/trips`, `#/trip/:id[/:tab]`) is the expected design, so no server-side fallback is needed.
|
||||
|
||||
### Trip page tabs & compact nav header (frontend-only)
|
||||
|
||||
The trip page is split into three tabs, and the old full-width trip-header card is REMOVED — its contents move into the top navbar so the page starts with actual content.
|
||||
|
||||
**Navbar trip context** — `renderNav()` (app.js) always includes an empty `.nav-trip-slot` element between the brand and the user area; non-trip views leave it empty (it costs no space). On a trip page, `tripDetail` fills it after the trip loads with a compact identity block:
|
||||
|
||||
- `←` back icon-link to `#/trips`, the trip name (truncated with ellipsis when long, `title` attr carries the full name), and a muted one-line subtitle `29 Sept – 6 Oct · 8 days · EUR` (hidden on narrow screens).
|
||||
- A single `⋯` trip-menu button on the nav's right (before the user area) opening a small dropdown panel containing everything the old header card held: the members list (with the owner's `×` remove buttons), the join-code chip + 📋 Copy + ↻ Regenerate (owner only), `✎ Edit trip`, and `Delete trip` (owner only, danger-styled). The dropdown closes on outside click and Escape.
|
||||
- `✎ Edit trip` opens the existing edit form (name/dates/currency) in a centered modal (`.overlay-center`/`.modal` pattern like the checklist-suggestions dialog) instead of expanding inside a header card.
|
||||
- The slot is naturally discarded when the router re-renders the nav on navigation; `refreshTrip()` must re-fill it (member/join-code/name changes show immediately).
|
||||
|
||||
- **Tabs**: `trip` (countdown, calendar, then map + summary in the existing `detail-grid`), `money` (Costs and Expenses cards side by side in a two-column `money-grid`, stacked on narrow screens), `checklist` (the checklist card, centered at a comfortable reading width). Tab bar order: Trip, Money, Checklist — icons + labels (🗓️ Trip / 💰 Money / 🧳 Checklist), rendered as the first element of the page (the navbar now carries the trip identity; there is no header card).
|
||||
- **URL**: `#/trip/:id/:tab` with `tab ∈ trip|money|checklist`; missing or unknown tab segment → `trip` (so old `#/trip/5` links keep working). `parseHash` passes the extra segment through as `params.tab`; unknown segments must not 404.
|
||||
- **Switching is NOT navigation**: clicking a tab toggles panel visibility and updates the address bar via `history.replaceState` — it must NOT set `location.hash` (a `hashchange` would close the day-editor overlay, stop the calendar's now-ticker, and trigger a full route/re-fetch). Deep-links work because the router reads the tab on load/route; the back button traverses pages, not tabs.
|
||||
- **All three panels stay mounted** — switching toggles a class/`hidden` attr, never re-renders. `refreshTrip()` re-draws all panels but must preserve the active tab. `refreshCosts()` keeps its swap-in-place behaviour regardless of which tab is active.
|
||||
- **Map sizing**: Leaflet initialised in a hidden panel has zero size; on every activation of the `trip` tab the map must be size-revalidated (an exposed `invalidateSize` hook on the map view, or an equivalent explicit mechanism — not a reliance on browser resize happening to occur).
|
||||
- **Keyboard/a11y**: the tab bar uses `role="tablist"`/`role="tab"`/`aria-selected` and the panels `role="tabpanel"`; Left/Right arrows move between tabs.
|
||||
- Tab, nav-slot, trip-menu-dropdown, and edit-modal styles live in `public/css/tabs.css` (new; styles.css is at its 500-line cap and must not grow).
|
||||
- Leaflet 1.9.x via unpkg CDN in `index.html`.
|
||||
- Session cookie is httpOnly; frontend detects auth state via `GET /api/auth/me` on load.
|
||||
- Expenses UI lives in `public/js/views/expenses.js` (+ `public/css/expenses.css` — styles.css is at its 500-line cap), rendered as a card in the trip detail side column directly below Costs (above Checklist). Self-fetching from `GET .../expenses` (never triggers a whole-trip refresh; after add/edit/delete it re-fetches itself AND tells the Costs panel to refresh). Shows: trip total + per-day grouped rows (day heading with day total; each row = category icon, description, payer, amount) by default; a **sort control** (Date ↑/↓, Amount ↑/↓, Category, Payer — non-date sorts flatten to a single list, pure client-side); a quick-add row (date defaulting to today clamped into the trip range, description, amount, category select, payer, split — same split modes/participants UI pattern as `costForm.js`); edit + delete per row; and an **Export CSV** button that simply navigates to `GET .../expenses/export.csv` (cookie auth makes a plain link work).
|
||||
|
||||
Reference in New Issue
Block a user