Skip to content

feat(layout): replace sidebar with unified AppHeader + BottomTabBar#1141

Open
timothyfroehlich wants to merge 10 commits intomainfrom
feat/design-consistency-phase2
Open

feat(layout): replace sidebar with unified AppHeader + BottomTabBar#1141
timothyfroehlich wants to merge 10 commits intomainfrom
feat/design-consistency-phase2

Conversation

@timothyfroehlich
Copy link
Copy Markdown
Owner

Summary

  • Replaces the 4-piece navigation shell (Sidebar + MobileHeader + inline desktop header + BottomTabBar) with a single unified AppHeader that adapts at the md: (768px) breakpoint
  • Eliminates the sidebar entirely — PinPoint's 3-item flat nav doesn't justify a sidebar, and it was consuming 64–256px of content width
  • Desktop: logo + APC logo + nav links + Report Issue + HelpMenu dropdown (Feedback, What's New, Help, About with changelog badge) + notifications + user menu
  • Mobile: slim header with logo + notifications + user menu; nav handled by existing BottomTabBar
  • Admin Panel link moves into the UserMenu dropdown (role-gated)
  • Reverts experimental @custom-variant desktop / pointer: fine approach in favour of standard viewport md: breakpoints (matching GitHub's approach)
  • Playwright desktop viewport updated from 1280×720 → 1024×768
  • All stale mobile-nav-signin / mobile-user-menu-button E2E testids updated to unified variants

New files

  • AppHeader.tsx + AppHeader.test.tsx — unified responsive header
  • HelpMenu.tsx — help dropdown with changelog badge
  • nav-utils.ts + nav-utils.test.ts — shared active-state logic for AppHeader + BottomTabBar

Deleted files

  • Sidebar.tsx, Sidebar.test.tsx
  • MobileHeader.tsx, MobileHeader.test.tsx
  • PLAN-desktop-layout-stability.md

Test plan

  • pnpm run check — 766 unit tests, types, lint all pass
  • pnpm run smoke — 85 passed, 0 failed, 4 skipped
  • Visual verification: desktop (1024px) shows full AppHeader with nav links; mobile (375px) shows slim header + bottom tab bar
  • Resize through 768px — nav links and HelpMenu appear/disappear cleanly

🤖 Generated with Claude Code

timothyfroehlich and others added 4 commits March 29, 2026 16:12
Add @media (pointer: fine) min-width rule so desktop browsers always show
the sidebar/desktop header. Mobile devices (pointer: coarse) unaffected.
overflow-x: hidden moved to pointer: coarse media query so desktop can
scroll horizontally when window is narrower than 768px.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add @custom-variant desktop (pointer: fine) for shell toggles
- Convert MainLayout, MobileHeader, BottomTabBar to desktop: variant
- Convert issue detail page mode switches to desktop:
- Add container queries for IssueTimeline, AddCommentForm, ImageGallery
- Sidebar auto-collapse at <1280px viewport
- Swap comment/upload photo positions
- min-width 1088px on html for scroll floor

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Use shorthand @custom-variant desktop (@media (pointer: fine))
  instead of block syntax with @slot (which silently failed)
- Sidebar auto-expands when viewport widens past 1280px threshold

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace the 4-piece navigation shell (Sidebar, MobileHeader, inline
desktop header, BottomTabBar) with a single AppHeader that adapts at
the md: (768px) breakpoint, plus the existing BottomTabBar (mobile).

Changes:
- Add AppHeader (client component): logo + APC logo (desktop), nav
  links (desktop), HelpMenu dropdown, notifications, user menu
- Add HelpMenu: help circle icon with changelog badge; items for
  Feedback, What's New, Help, About
- Add nav-utils: isNavItemActive() shared between AppHeader + BottomTabBar
- Add admin link to UserMenu dropdown (role-gated)
- Rewrite MainLayout: flex-col layout, AppHeader replaces all shell pieces
- Update BottomTabBar: use shared nav-utils, revert desktop: → md:
- Update issue detail page: revert desktop: → md:, keep container queries
- Revert globals.css: remove @custom-variant desktop / pointer:fine
- Remove sidebar cookies: SIDEBAR_COLLAPSED_KEY, getSidebarCollapsed,
  storeSidebarCollapsed
- Delete: Sidebar.tsx, MobileHeader.tsx (+ tests)
- Playwright: desktop viewport 1280×720 → 1024×768
- E2E: update all testid refs (mobile-nav-signin → nav-signin,
  mobile-user-menu-button → user-menu-button)
- Update design bible skill doc (sections 3, 4, 8, component inventory)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings March 30, 2026 04:02
@vercel
Copy link
Copy Markdown

vercel bot commented Mar 30, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
pin-point Ready Ready Preview, Comment Mar 30, 2026 3:20pm

@supabase
Copy link
Copy Markdown

supabase bot commented Mar 30, 2026

Updates to Preview Branch (feat/design-consistency-phase2) ↗︎

Deployments Status Updated
Database Mon, 30 Mar 2026 15:19:30 UTC
Services Mon, 30 Mar 2026 15:19:30 UTC
APIs Mon, 30 Mar 2026 15:19:30 UTC

Tasks are run on every commit but only new migration files are pushed.
Close and reopen this PR if you want to apply changes from existing seed or migration files.

Tasks Status Updated
Configurations Mon, 30 Mar 2026 15:19:31 UTC
Migrations Mon, 30 Mar 2026 15:19:31 UTC
Seeding Mon, 30 Mar 2026 15:19:31 UTC
Edge Functions Mon, 30 Mar 2026 15:19:31 UTC

View logs for this Workflow Run ↗︎.
Learn more about Supabase for Git ↗︎.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR refactors the app’s navigation shell by removing the sidebar/mobile header split and introducing a unified responsive AppHeader (desktop header + mobile slim header), while keeping the mobile BottomTabBar for primary navigation.

Changes:

  • Removed sidebar UI and its persisted “collapsed” cookie preference.
  • Added AppHeader, HelpMenu, and shared isNavItemActive() logic (used by both header + bottom tabs).
  • Updated Playwright viewport and adjusted E2E selectors/flows to the unified header test IDs.

Reviewed changes

Copilot reviewed 35 out of 35 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/lib/cookies/preferences.ts Removes sidebar-collapsed cookie helpers now that sidebar is gone.
src/lib/cookies/preferences.test.ts Removes tests for the deleted sidebar-collapsed cookie helpers.
src/lib/cookies/constants.ts Removes SIDEBAR_COLLAPSED_KEY.
src/lib/cookies/client.ts Removes client helper for storing sidebar-collapsed state.
src/lib/cookies/client.test.ts Removes tests/fixtures for sidebar-collapsed cookie.
src/components/layout/user-menu-client.tsx Adds admin-only “Admin Panel” link inside the user menu dropdown.
src/components/layout/nav-utils.ts New shared active-state helper for navigation items.
src/components/layout/nav-utils.test.ts Unit tests for the shared nav active-state logic.
src/components/layout/Sidebar.tsx Deleted sidebar implementation.
src/components/layout/Sidebar.test.tsx Deleted sidebar tests.
src/components/layout/MobileHeader.tsx Deleted old mobile header component.
src/components/layout/MobileHeader.test.tsx Deleted old mobile header tests.
src/components/layout/MainLayout.tsx Switches layout to use AppHeader and removes sidebar/mobile header composition.
src/components/layout/HelpMenu.tsx Adds desktop help dropdown with feedback + “What’s New” badge behavior.
src/components/layout/BottomTabBar.tsx Uses shared isNavItemActive() instead of local logic.
src/components/layout/AppHeader.tsx New unified header component (desktop nav + mobile slim header behavior).
src/components/layout/AppHeader.test.tsx Adds tests for new header behavior (but mocks currently don’t match AppHeader imports).
src/components/issues/IssueTimeline.tsx Moves some breakpoint styling from md:/sm: to container-query variants.
src/components/issues/AddCommentForm.tsx Adjusts layout using container-query variants; reorders submit button position.
src/components/images/ImageGallery.tsx Converts responsive grid breakpoints to container-query variants.
src/app/(app)/m/[initials]/i/[issueNumber]/page.tsx Adds @container to scope container-query responsive behavior on issue detail.
src/app/(app)/m/[initials]/i/[issueNumber]/editable-issue-title.tsx Uses container-query variants for title sizing.
playwright.config.ts Sets desktop viewport to 1024×768 for Chromium/Firefox.
e2e/support/actions.ts Updates shared E2E helpers to assert unified app-header and unified user menu test id.
e2e/smoke/public-reporting.spec.ts Updates sign-in click target to unified nav-signin.
e2e/smoke/navigation.spec.ts Updates assertions from sidebar/mobile header to unified AppHeader.
e2e/smoke/landing-page.spec.ts Updates header testid assertions to unified variants.
e2e/smoke/auth-flows.spec.ts Updates post-login assertions to unified AppHeader visibility.
e2e/full/issue-list-extended.spec.ts Updates wording/behavior for issues link persistence with AppHeader.
e2e/full/invite-signup.spec.ts Updates user menu trigger test id to unified variant.
e2e/full/email-and-notifications.spec.ts Updates comment wording referencing sidebar interaction.
e2e/full/auth-flows-extended.spec.ts Updates signup/signin selectors to unified header test ids.
e2e/full/about-page.spec.ts Updates About navigation to use HelpMenu on desktop (direct on mobile).
e2e/auth.setup.ts Updates auth-setup layout assertion from sidebar to AppHeader.
.agent/skills/pinpoint-design-bible/SKILL.md Updates documented shell/navigation contract for AppHeader + HelpMenu + BottomTabBar.

Comment on lines +66 to +88
vi.mock("./header-sign-in-button", () => ({
HeaderSignInButton: ({
testId = "nav-signin",
}: {
testId?: string;
className?: string;
}) => (
<a href="/login" data-testid={testId}>
Sign In
</a>
),
}));

vi.mock("./HelpMenu", () => ({
HelpMenu: ({ newChangelogCount }: { newChangelogCount: number }) => (
<button
data-testid="help-menu-trigger"
data-changelog-count={newChangelogCount}
>
Help
</button>
),
}));
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These mocks target relative paths ("./header-sign-in-button", "./HelpMenu"), but AppHeader imports those modules via the "~/components/layout/..." alias. That means the mocks won't be used and the test will exercise the real components instead. Adjust the vi.mock() specifiers to exactly match the import paths used by AppHeader so the intended stubs are applied.

Copilot uses AI. Check for mistakes.
Comment on lines +51 to +64
vi.mock("./user-menu-client", () => ({
UserMenu: ({
userName,
role,
}: {
userName: string;
role?: string;
testId?: string;
}) => (
<button data-testid="user-menu-button" data-role={role}>
{userName}
</button>
),
}));
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The module specifier used in this mock ("./user-menu-client") doesn't match the path AppHeader imports ("~/components/layout/user-menu-client"). As a result, this mock won't apply and the test will render the real UserMenu, making the test more brittle and potentially failing in jsdom. Update the vi.mock() target to match AppHeader's import path (or change AppHeader to use the relative import consistently).

Copilot uses AI. Check for mistakes.
AppHeader nav items now collapse to icon-only at md: (768px) and expand
to icon+text at lg: (1024px). APC logo deferred to lg: to save space.
This fixes the header overflowing at 768px where it needed ~828px.

Add assertNoHorizontalOverflow() E2E helper that checks
document.scrollWidth <= document.clientWidth. Sprinkled into 6 smoke
test files covering dashboard, issues list, issue detail, machines list,
machine detail, and report pages.

Update design bible: document two-tier responsive nav pattern,
overflow testing requirement, updated component inventory.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
timothyfroehlich and others added 5 commits March 30, 2026 09:55
Extract NAV_ITEMS to nav-config.ts, used by both AppHeader and
BottomTabBar. Standardizes on LayoutDashboard icon (was Home in
BottomTabBar). Memoizes nav link rendering in AppHeader.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Extract NAV_ITEMS to nav-config.ts, used by both AppHeader and
BottomTabBar. Standardizes on LayoutDashboard icon (was Home in
BottomTabBar). Memoizes nav link rendering in AppHeader. Extracts
duplicate tab base class in BottomTabBar. Fixes relative import
to use ~/ alias.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- nav-utils.ts: hoist ISSUE_DETAIL_PATTERN regex to module level
- actions.ts: remove unused _isMobile param from visibleUserMenu()
- date-range-picker.tsx: remove window.innerWidth + resize listener,
  always render 2 calendar months (CSS handles narrow viewports)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Visits every major route at both desktop (1024×768) and mobile (375×667)
viewports, asserting document.scrollWidth <= document.clientWidth.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add AGENTS.md rule #16, NON_NEGOTIABLES.md CORE-RESP-001 through 004,
and update design bible with container query size table, decision tree,
z-index hierarchy, and testing guidance.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 44 out of 44 changed files in this pull request and generated 1 comment.

Comment on lines 101 to 105
{...(date?.from ? { defaultMonth: date.from } : {})}
selected={date}
onSelect={handleSelect}
numberOfMonths={isMobile ? 1 : 2}
numberOfMonths={2}
/>
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DateRangePicker now always renders numberOfMonths={2} inside a PopoverContent that has no max-height/overflow handling. Because the Calendar stacks months vertically below md: (months class is flex-col md:flex-row), this can make the popover taller than the viewport on mobile/tablet and clip the second month with no way to scroll to it. Consider constraining the popover (e.g., max-height based on viewport + overflow-y-auto) or otherwise ensuring the calendar remains fully reachable on small viewports without reintroducing JS viewport detection.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants