Dashboard: replace "Peer Support" card and "Book a peer session"
quick action with community ecology links.
Welcome: replace "Peer Support" resource card with "Community
Ecology" heading and link to /ecology.
Members index: rename active filter tag from "Offering Peer
Support" to "Offering Support". The underlying filter still works
correctly (queries communityEcology.offerPeerSupport).
The Skills Exchange + Peer Support feature was replaced by Community
Connections on 2026-04-05, but several files and code paths were left
in place as backward-compat. None are reachable from the live UI:
- usePeerSupport.js composable: not imported anywhere
- PeerSupportBadge.vue: not imported anywhere
- peer-support.vue: stub redirect with no incoming links
- /api/peer-support.get.js: only consumed by usePeerSupport
- /api/members/me/peer-support.patch.js: same
- profile.patch.js offering/lookingFor write branches: profile form
no longer sends these fields (only writes communityConnections.*)
- PEER_SUPPORT_ENABLED/DISABLED activity types and renderers: only
written by the deleted peer-support.patch endpoint. The activityText
formatter has a fallback for unknown types so existing records
still display ("peer support enabled" with a generic icon).
Tests updated to drop peerSupportUpdateSchema coverage and the
offering/lookingFor passthrough assertion.
schemas.js cleanup deferred — concurrent communityConnections →
communityEcology rename is in flight in the working tree.
Page Layout Simplification — Member Area Full-Bleed Pattern.
Converges the member-area surface on a canonical PageShell + ColumnsLayout +
PageSection vocabulary, replacing 3 ad-hoc two-column implementations and ~12
hand-rolled flex-chain blocks. PageShell owns the flex chain so individual
pages can no longer break it; PageSection enforces symmetric padding so
asymmetric drift is structurally impossible.
Visual snapshot coverage was expanded BEFORE component changes (15 of the 26
authoritative snapshots are new in this branch), then 9 pages were migrated
one commit at a time. Hydration mismatch on auth-conditional UI fixed by
wrapping v-if/v-else chains in <ClientOnly>. SidebarLayout deleted.
463/463 unit tests + 26/26 visual regressions green.
Now that all member-area pages have migrated to PageShell +
ColumnsLayout, SidebarLayout has no consumers and can be deleted.
PageShell owns the flex chain, so the .dashboard-body wrapper on
member/dashboard.vue (flex: 1; display: flex; flex-direction: column;
min-height: 0) is redundant. Update stale SidebarLayout comments on
members/[id].vue to reference ColumnsLayout.
Vue hydration silently drops class attribute updates when SSR and client
render different branches of a v-if chain — per the project's Auth SSR
Pattern, useAuth is client-only and server always renders unauthenticated,
so PageHeader (v-else branch) was rendering inside a leftover .loading /
.loading-state div from the v-else-if branch. On mobile that div was
being masked by the visual-test commonMasks (.loading-state), producing
a large fuchsia block in the snapshot.
Wrapping the v-if/v-else-if/v-else chain in <ClientOnly> ensures the
server renders nothing for the auth-gated content and the client performs
a clean first render, matching the pattern already used in dashboard.vue.
Also update admin-dashboard-desktop for minor anti-aliasing drift.
Replace .activity-page wrapper + SidebarLayout with PageShell +
ColumnsLayout cols="events-sidebar". Delete the now-redundant
flex-chain CSS that PageShell owns. Adds .loading-state to the
visual mask list to handle the connections-mobile race between
the loading and loaded states.
Wrap members directory page in PageShell. Also expand visual mask
selectors to cover .filter-bar, .skills-bar, and .connections-section
because filter content varies based on dynamic tag/topic state and
async fetch ordering. Rebaselines several existing snapshots that now
mask wider regions but capture the same structural layout.
Replace outer div.connections-page wrapper and explicit PageHeader with
PageShell component. Update connections-mobile-auth snapshot to match
the stable full-suite render state (filter bar absent when test admin
has no cooperative topics configured).
Add PLAYWRIGHT_PORT env var to playwright.config.js so a worktree can run
its own dev server on a different port without disrupting the main dev
server. Rebaseline connections-mobile-auth which had drifted from the
suggestions data state captured during Phase 1.
Introduces three new layout primitives (no consumers yet). Adds
--page-pad-x/y/collapse CSS tokens to :root and .dark. Updates
PageHeader to read padding from tokens. Removes ignored size="large"
props from welcome and series pages. Fixes stray markdown in SidebarLayout.
Add about to authenticatedMobilePages to capture an authenticated baseline
needed for regression detection during the Phase 3.5 about.vue migration.
Rename all auth-mobile snapshots from *-mobile to *-mobile-auth to avoid
name collision with the public loop's about-mobile snapshot.
Adds 13 new visual regression baselines:
- Public: about (desktop + mobile), members (desktop + mobile)
- Authenticated desktop: member-account, member-activity, connections,
admin-dashboard, members-detail
- Authenticated mobile: member-dashboard, member-profile,
member-account, connections
Switches to a single serial test.describe with a beforeAll that logs in
once and saves the auth cookie via storageState. This avoids repeated
/api/dev/test-login calls that exhausted the dev server's MongoDB
connections under parallel execution.
Masks added: .tl-time, .stat-val, .item-date, .mc-avatar, .cc-avatar,
.profile-avatar, .filter-count — covering activity timestamps, stat
values, member join dates, avatars, and member counts.
Admins can now surface dismissed alert types without waiting for the
underlying data to change. Adds a collapsible "Restore dismissed"
section below the active alerts with per-type checkboxes.
- ALERT_METADATA map in adminAlerts.js as the single source of truth
for slug → title/severity; detectors refactored to reference it
- GET /api/admin/alerts/dismissed returns this admin's dismissals
joined with metadata (title, severity, dismissedAt)
- POST /api/admin/alerts/restore deletes dismissals by alertType[],
returns the deleted count
- AdminAlertsPanel fetches both active + dismissed; stays visible
when either is non-empty; checkboxes + "Restore selected" button
- adminAlertRestoreSchema validates the POST body against the enum
- Auth guards test covers both new routes
Top-level `await useFetch` inside a ClientOnly boundary prevented the
component from mounting at all — the slot stayed an empty span and no
alerts ever rendered. Nuxt forwards request cookies on same-origin
server calls, so `requireAdmin` is satisfied during SSR and the panel
can render normally.
- Add `middleware: 'auth'` to member/profile.vue (was missing)
- Harden loginAsAdmin helper to wait for networkidle after redirect so
auth-init plugin and admin middleware finish before tests navigate
- Regenerate visual baselines to reflect updated profile page UI
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Admin interface to review, filter, and batch-invite the 95 pre-registrants
from Baby Ghosts. Accept-invitation page pre-fills their data and collects
circle, pronouns, motivation, contribution tier, and agreement before
creating their member record.
Add aria-labels to form controls (selects, checkboxes, switches), set
html lang attribute and page title, fix color contrast for --candle-dim
and --text-faint tokens, underline inline links, remove opacity hack.
Harden dev login endpoints with atomic findOneAndUpdate and tokenVersion
in JWT. Update Playwright timeouts and E2E test helpers.