docs(uat): SHIPPED annotations for PR17 (layout polish)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -424,7 +424,7 @@ _Component refactors, multi-file edits, single-service tweaks, new validators._
|
||||
> - **Sidebar collapsed (64px):** wire the transform to use the collapsed-aware width. Cleanest: expose a single `--current-sidebar-width` CSS variable on the sidebar root that flips between `var(--width-sidebar)` and `var(--width-sidebar-collapsed)` based on collapse state. Topbar's search wrapper reads `--current-sidebar-width` so the shift adjusts automatically with no React state plumbing. ~10 min to add the variable + ~5 min to wire the transform.
|
||||
> - **Mobile (< sm):** the sidebar is hidden and the layout is different (`MobileLayoutProvider` with bottom-tabs); the transform should only apply on `sm:` and up. Use `sm:-translate-x-[calc(var(--current-sidebar-width)/2)]`.
|
||||
> - **Left column doesn't get visually overlapped:** since the search shifts via transform (paint-only, doesn't affect layout flow), the breadcrumbs in the left grid slot retain their declared width — but the search will visually overlap them. Solution: reduce the breadcrumbs slot's effective width (e.g. `minmax(0,0.6fr)` instead of `1fr`) OR add `pointer-events: none` to the breadcrumbs when the search is focused. Easier: hide breadcrumbs on narrower laptop widths and rely on the back-chevron + page-h1 for context (also addresses the breadcrumb-wrap finding above).
|
||||
> - **Effort:** ~30-45 min total — the `--current-sidebar-width` variable + the transform + the grid bump + verifying behaviour at collapsed/expanded/mobile. Captured 2026-05-18 from UAT.
|
||||
> - **Effort:** ~30-45 min total — the `--current-sidebar-width` variable + the transform + the grid bump + verifying behaviour at collapsed/expanded/mobile. Captured 2026-05-18 from UAT. **SHIPPED in 8fcbe45:** grid middle slot bumped from `minmax(360,640)` → `minmax(420,800)`; search wrapper `max-w-md` → `max-w-2xl`; `sm:-translate-x-[calc(var(--width-sidebar)/2)]` centers against the full viewport. Collapsed-sidebar-aware `--current-sidebar-width` variable parked.
|
||||
> - **Pageviews chart: X-axis date ticks too cramped — drop the time component** — _src/components/website-analytics/pageviews-chart.tsx_ (recharts `XAxis`) — current bucket labels render in `YYYY-MM-DD HH:MM:SS` format from Umami's `x` field, which the chart's X-axis prints verbatim. On a 30-day range the labels overlap into an unreadable strip. Fix: pass a `tickFormatter` to `XAxis` that parses `row.x` and renders just the date portion (`MMM d` or `M/d`), keeping the timestamp available via Tooltip's full-precision render. ~10 min. Captured 2026-05-18 from UAT.
|
||||
> - **Pageviews chart: inline note explaining Pageviews vs Sessions** — _src/components/website-analytics/pageviews-chart.tsx_ + the Card's CardHeader subtitle slot — add a small `?` info popover (matching the pattern on the Pipeline Value tile) next to the chart title that explains: "Pageviews = total page hits including refreshes. Sessions = distinct visitor sessions (a single visitor browsing multiple pages = 1 session, many pageviews)." Helpful because the chart shows both series and the distinction is non-obvious. ~10 min. Captured 2026-05-18 from UAT.
|
||||
> - **Inbox page: swap section order — Reminders above Alerts** — _src/components/inbox/inbox-page-shell.tsx:84-111_ — current order is `Alerts` (line 84) then `Reminders` (line 99). User wants the order reversed so Reminders is the top section. Swap the two `<section>` blocks; ids (`inbox-section-alerts`, `inbox-section-reminders`), URL-hash deep-link logic, and the localStorage open-state keys all remain untouched (they're keyed on section id, not order). PageHeader copy "Alerts & Reminders" should also flip to "Reminders & Alerts" to mirror the new visual order. ~3 min. Captured 2026-05-18 from UAT. **SHIPPED in 203f543.**
|
||||
@@ -435,7 +435,7 @@ _Component refactors, multi-file edits, single-service tweaks, new validators._
|
||||
> - **(b) Better: ellipsis-collapse middle crumbs on overflow** — industry-standard pattern. When crumb count > 3 OR available width can't fit all crumbs single-line (detect via `ResizeObserver` on the `<nav>` or a CSS `:has(+ overflow)` trick), collapse middle crumbs to a `<BreadcrumbEllipsis>` button that opens a dropdown listing the hidden crumbs. First (root) + last (current page) always visible. Primitive already exports `BreadcrumbEllipsis` — just wire it. ~45 min. Result: breadcrumb stays single-line at every width, no wrap at all.
|
||||
> - **(c) Layout polish: top-align the back-chevron** — _topbar.tsx:59_ — change the wrapping `<div className="min-w-0 flex items-center gap-1.5">` to `items-start` so even if the breadcrumb does wrap, the back-button stays top-aligned with the first crumb line instead of vertical-centering across the wrapped block. Also worth considering: hide the back-button when meaningful breadcrumbs are visible (the breadcrumb's parent link already does "go back"; two affordances is one too many). ~10 min.
|
||||
> - **Topbar grid sizing observation:** topbar columns are `[minmax(0,1fr)_minmax(360px,640px)_minmax(0,1fr)]` — left slot competes for space with the centered search bar's `minmax(360px,640px)`. When search hits its max width, left slot is squeezed → breadcrumb wraps sooner. Consider bumping to `minmax(0,1.5fr)` OR letting the search shrink below 360px when needed. Optional, evaluate after (a)+(b) land.
|
||||
> - **Effort:** ~15 min for (a), ~45 min for (b), ~10 min for (c). Bundle ~1h. Captured 2026-05-18 from UAT.
|
||||
> - **Effort:** ~15 min for (a), ~45 min for (b), ~10 min for (c). Bundle ~1h. Captured 2026-05-18 from UAT. **SHIPPED (a) in 8fcbe45:** each crumb + its trailing ChevronRight now share a single `<BreadcrumbItem>`; flex-wrap can no longer strand a separator. Ellipsis-collapse (b) + back-chevron alignment (c) parked.
|
||||
> - **BulkAddBerthsWizard: currency field should use `<CurrencySelect>` (already exists, used elsewhere)** — _src/components/admin/bulk-add-berths-wizard.tsx_ (the `priceCurrency` `<Input>` in the apply-to-all row at ~lines 282-290, and the per-row instance below it) — currently a free-text `<Input>` that uppercases on blur, defaulting to `USD`. Reps can type any string (including invalid codes); no auto-complete; no consistency with other forms. The `<CurrencySelect>` component already exists at _src/components/shared/currency-select.tsx_, backed by the curated `SUPPORTED_CURRENCIES` list in _src/lib/utils/currency.ts_, and is used by the single-berth edit form (_berth-form.tsx:414_) + the expense form dialog (_expense-form-dialog.tsx:238_). Quick fix: import `CurrencySelect`, replace both the apply-to-all and per-row currency inputs with the dropdown bound to the same handlers (`applyToAll('priceCurrency', v)` / `setRowField(idx, 'priceCurrency', v)`). ~10 min. Captured 2026-05-18 from UAT. **SHIPPED in 2bcf544.**
|
||||
> - **BulkAddBerthsWizard + single-berth editor: toggleable input units (ft/m) for dimension fields** — _src/components/admin/bulk-add-berths-wizard.tsx_ (the "Width (ft)" / "Length (ft)" / "Draft (ft)" inline-table headers + input parsing), _src/components/berths/berth-form.tsx_ (or equivalent single-edit) — the wizard's column headers and input parsing are hard-coded to feet. The schema supports per-dimension entry-unit discriminators (`lengthUnit`, `widthUnit`, `draftUnit` on `berths`, all defaulting to `'ft'`) plus separate `_M` numeric columns where metres-original values live — but neither the bulk wizard nor the single editor lets the rep pick which unit they're typing in. Reps who think in metres convert manually and the entry-unit discriminator never gets set.
|
||||
> - **Fix:** (a) add a small `ft | m` toggle in the wizard header (and on the single-berth edit form) that flips the column header labels (e.g. "Width (ft)" → "Width (m)") and the parser. The toggle should default to whichever unit the user's `dimensionUnit` preference is set to (see the Dimensions-column-toggle finding earlier — same preference). (b) On submit, if entered unit is `'m'`, convert to ft for the stored numeric (`berths.lengthM` is the canonical metres column; `lengths.lengthFt` would be the feet column — verify the actual column names) AND set `lengthUnit='m'` so downstream document generation honours the rep's original input. Same for width / draft / nominalBoatSize / waterDepth. (c) Reuse the `src/lib/utils/dimensions.ts` helper from the Dimensions-column finding so conversion is centralized.
|
||||
@@ -446,7 +446,7 @@ _Component refactors, multi-file edits, single-service tweaks, new validators._
|
||||
> - **(b) Docks are a first-class entity** (separate `docks` table with `port_id` + `letter` + metadata like `position`, `pontoon_type`, `power_capacity`): then the wizard needs a "+ New dock" affordance opening a small dock-create dialog (letter + name + optional metadata), then returning to the wizard with the new dock pre-selected. Permission: `berths.manage_docks` (or whichever owns dock metadata). The user's question — "_or is this an admin setting?_" — suggests they're not sure either; if it IS an admin-only concern (docks are infrastructure not data the rep should touch), then keep it admin-side and just surface a contextual link in the wizard ("New dock? Add it in Admin → Docks first → [link]"). ~1-2h depending on the model.
|
||||
> - **Action item:** check whether `docks` / `pontoons` / `marina_sections` table exists in the schema (`grep -r "docks\|pontoons" src/lib/db/schema/`); shape the fix accordingly. If no dedicated table, the wizard fix is trivial; if there is one, decide admin-only vs in-wizard-create with the team. Captured 2026-05-18 from UAT.
|
||||
> - **DropdownMenu content stretches to fill viewport — cap it** — _src/components/ui/dropdown-menu.tsx:66_ — the shadcn `DropdownMenuContent` primitive uses `max-h-(--radix-dropdown-menu-content-available-height)` (Radix's CSS variable that exposes the room between the trigger and the viewport edge). On long lists the menu visually stretches all the way to the viewport bottom even though the items don't need that height; reads as a wall of options. Internal `overflow-y-auto` is already on so scrolling works. Fix: replace the Radix `max-h-(...)` token with a fixed `max-h-96` (384px) or `max-h-[28rem]` (448px) so the menu caps at a comfortable height regardless of available space, scrolling internally for longer lists. Global change in the base primitive — affects every dropdown in the app, which is the right call (no consumer currently relies on the "fill the viewport" behaviour). ~2 min. If a specific dropdown needs the old behaviour, it can pass `className="max-h-[var(--radix-dropdown-menu-content-available-height)]"` to opt back in. Captured 2026-05-18 from UAT. **SHIPPED in c6dcf49.**
|
||||
> - **DocumentsHub aside column: flush-left with the app sidebar (kill the AppShell padding for this page)** — _src/components/documents/documents-hub.tsx:246_ + _src/components/layout/app-shell.tsx:113-121_ — the desktop `<main>` wrapper applies `px-6 pt-3 pb-6` to all dashboard pages, so the DocumentsHub two-pane (`ResizablePanelGroup` with the `<aside>` folder column on the left) gets 24px of whitespace between the global app sidebar and its own border. The folder column should sit flush against the app sidebar — it reads as "an extension of the navigation," not "a card inside the page." Fix (surgical): change DocumentsHub's root `<div className="h-full">` at line 246 to `<div className="h-full -mx-6 -mt-3 -mb-6">` (mirror the AppShell desktop padding so the hub renders full-bleed inside the main viewport). Add a comment explaining the intentional escape. The right-pane content keeps its own internal `p-4` so it doesn't run flush with the viewport edge. **Alternative (cleaner long-term):** make the AppShell padding route-aware via a prop on `<main>` (or a layout-level opt-out for hub-style pages); but (a) is the right call until a second page needs the same treatment. ~5 min for the negative-margin fix. Captured 2026-05-18 from UAT.
|
||||
> - **DocumentsHub aside column: flush-left with the app sidebar (kill the AppShell padding for this page)** — _src/components/documents/documents-hub.tsx:246_ + _src/components/layout/app-shell.tsx:113-121_ — the desktop `<main>` wrapper applies `px-6 pt-3 pb-6` to all dashboard pages, so the DocumentsHub two-pane (`ResizablePanelGroup` with the `<aside>` folder column on the left) gets 24px of whitespace between the global app sidebar and its own border. The folder column should sit flush against the app sidebar — it reads as "an extension of the navigation," not "a card inside the page." Fix (surgical): change DocumentsHub's root `<div className="h-full">` at line 246 to `<div className="h-full -mx-6 -mt-3 -mb-6">` (mirror the AppShell desktop padding so the hub renders full-bleed inside the main viewport). Add a comment explaining the intentional escape. The right-pane content keeps its own internal `p-4` so it doesn't run flush with the viewport edge. **Alternative (cleaner long-term):** make the AppShell padding route-aware via a prop on `<main>` (or a layout-level opt-out for hub-style pages); but (a) is the right call until a second page needs the same treatment. ~5 min for the negative-margin fix. Captured 2026-05-18 from UAT. **SHIPPED in 8fcbe45:** `sm:-mx-6 sm:-mt-3 sm:-mb-6` on the wrapper (mobile layout unchanged).
|
||||
> - **DocumentsHub: hide breadcrumb on root "All documents" view, move PageHeader up to fill the space** — _src/components/documents/documents-hub.tsx:196-209_ — the top row currently always renders the `FolderBreadcrumb` (and conditionally the `NewDocumentMenu` when a folder is selected); on the root view (`selectedFolderId === undefined`) the breadcrumb shows only a "Home / All documents" label with no useful navigation, eating vertical space above the `PageHeader` that already says "Documents" + description. Fix: wrap the entire breadcrumb row at line 196-209 in `{selectedFolderId !== undefined && ( … )}` so the row is gone on the root; the PageHeader becomes the top element. When the rep navigates into a folder, the row reappears with both breadcrumb + NewDocumentMenu (the existing folder views don't render PageHeader, so the breadcrumb is the wayfinding cue). ~5 min. Captured 2026-05-18 from UAT. **SHIPPED in 2bcf544.**
|
||||
> - **Residential InterestsTab: whole row should navigate to the interest, not just the "View" link** — _src/components/residential/residential-client-tabs.tsx:273-289_ — current `<li>` lays out `[stage chip] [preferences/notes truncated text] [View → link]` and only the "View" text on the right is clickable. The whole row should be a target, matching the idiom used in the main client's `InterestRowItem` (`src/components/clients/client-interests-tab.tsx:53`) — the entire card is a `<button>`/`<Link>` so reps can tap anywhere. Fix: wrap the `<li>`'s flex container in `<Link href={…}>` (`className="block w-full"` to preserve layout), drop the trailing "View" link, add `hover:bg-muted/50` to make the affordance discoverable. ~10 min. Captured 2026-05-18 from UAT. **SHIPPED in c6dcf49.**
|
||||
> - **Residential namespace breadcrumb link is 404** — _src/components/layout/breadcrumbs.tsx_ (the breadcrumb generator splits the URL and makes every segment a link) + missing _src/app/(dashboard)/[portSlug]/residential/page.tsx_ — on any `/{portSlug}/residential/clients` or `/{portSlug}/residential/interests` page, the breadcrumb renders "Residential" as a link to `/{portSlug}/residential` but no `page.tsx` exists at that path (only `clients/` and `interests/` subdirectories). Clicking the breadcrumb yields a 404. Two reasonable fixes:
|
||||
|
||||
Reference in New Issue
Block a user