SkateSpots/UX_AUDIT.md

8 KiB
Raw Blame History

SkateSpots — UI/UX Audit & Fix Backlog

Findings from a code-level audit across every area (spots/shops, lab, crews, invitations, marketplace, stash, wallet, events, tours, calendars, messaging, community stats, profile, contacts, near-me, rider card, settings). Each item has an evidence pointer.

Work order = top-down, one batch per change-set, shipped through the normal pipeline (npm test → boot smoke test → auto-deploy).


P0 — Broken flows & trust (dead controls, false claims)

# Issue Evidence
1 "Claim This Shop" is a dead button (no handler) SpotDetailPage.tsx:512
2 Tour "Contact Guide to Book" links to /messages?to= which nothing reads → dead CTA TourDetailPage.tsx:55; route is /messages/:npub
3 Contacts "Message" goes to the generic inbox, not the contact ContactsPage.tsx:75
4 Stash "Share via DM" has no handler; edit/delete/list are hover-only (unreachable on touch) StashPage.tsx:222, :207
5 Marketplace cannot be bought: no listing detail route; "Pay with Cashu" is a "Coming Soon" stub; every card navigates straight to a DM MarketplaceItemCard.tsx:133-152, GearSwapItemCard.tsx:165
6 Map marker opens the Leaflet popup AND the bottom sheet at once SpotMap.tsx:1125, MapPage.tsx:1609/1927
7 Near Me on native: one denial = permanent dead end (shows web browser help) MapPage.tsx:1726 + :723-738
8 Settings deep links ?section=follows|language|stash render a blank page with no back SettingsPage.tsx:57-70 vs 1194-1215
9 Events calendar silently inherits an invisible dateWindow filter; Past+Calendar opens the current (empty) month EventsPage.tsx:1118, :926-929
10 Crew invite accept says "membership confirmed" even when only the founder can add the member CrewInviteCard.tsx:38-46
11 Crew "Share link" promises a one-click invite; recipient only gets a join-request CrewInviteDialog.tsx:251-287
12 Logged-out DM / conversation are dead ends (no LoginArea), unlike Contacts DirectMessagesPage.tsx:171, ConversationPage.tsx:152
13 Message send has no error feedback ConversationPage.tsx:126-137

P0 — Errors masquerade as empty / 404

Issue Evidence
Detail pages map network errors to NotFound (implies deleted) SpotDetailPage.tsx:240, ShopDetailPage.tsx:188, CollectionDetailPage.tsx:224, CoachDetailPage.tsx:119, CrewDetailPage.tsx:245, EventDetailPage.tsx:272
Leaderboards/pages ignore isError → "no content yet" EventsPage.tsx:814/1220/1265, BountyLeaderboardPage.tsx:141, ZapLeaderboardPage.tsx:159
DM inbox error has no retry (Conversation does) DirectMessagesPage.tsx:278

P1 — Loading / empty-state defects

Issue Evidence
Spot picker shows false "No spots found" while loading (also in CreateListing) SpotPicker.tsx:26,179; CreateListingPage.tsx:140-151,967
Collection skeleton grid ≠ real grid → layout jump on load CollectionDetailPage.tsx:54 vs :314
Map view has no empty state when filters hide everything MapPage.tsx:1591-1868
EmptyState nests <button> inside <Link> (invalid) EmptyState.tsx:67-71

P1 — Near Me (geolocation) timing & reliability

Issue Evidence
Non-denied errors (TIMEOUT/UNAVAILABLE) are swallowed → promise only fails at the 25s deadline; native path ≈40s worst case geolocation.ts:328-343, :230-245
Permission requested on page load without a user action MapPage.tsx:804-817
No locate/Near Me control on desktop MapPage.tsx:1719 (md:hidden)
Crews "Near me" shows active state even when no location was obtained CrewsPage.tsx:198-205, :296
Location-help overlay has no dialog semantics / Escape / focus trap MapPage.tsx:1669-1710

P1 — Money correctness

Issue Evidence
Marketplace sats price uses hardcoded $1 = 2500 sats MarketplaceItemCard.tsx:119-121,476
Wallet page is Cashu-only + "App only" on web — no balance/send/receive despite the name CashuWalletCard.tsx:57-136, useCashu.ts:168
Cashu backup warning never resolves (no persisted "backed up" state) CashuWalletCard.tsx:702, useCashu.ts:100
ClinkPayButton silent no-op without a CLINK wallet; competes with zaps ClinkPayButton.tsx:189
Orphan /marketplace-listings page; marketplace missing from primary nav AppRouter.tsx:117/158, Layout.tsx:75-80

P2 — Consistency (systemic)

Issue Evidence
No shared page header / container widths — every area differs (list vs detail) Lab max-w-4xl, Crews max-w-3xl, Contacts max-w-2xl, DM max-w-2xl, Wallet max-w-lg, CreateListing max-w-2xl, GearSwap/Stash max-w-7xl, RiderCard max-w-2xl, EventDetail unbounded
EventDetail uses shadcn primary/accent tokens while the app is neon EventDetailPage.tsx:513/548
RiderCard dead AwardBadgeDialog state RiderCardPage.tsx:1129,1639
Lab craft tab grid leaves an orphan row (11 tabs, grid-cols-5) LabPage.tsx:424-455
AboutPage / MarketplacePage use <a href><Button/></a> (full reload, nested interactive); About hardcodes stale v0.1.0 AboutPage.tsx:159, MarketplacePage.tsx:182
FAQ page has no static content — depends entirely on the AI guide opening FAQPage.tsx:42-105
Profile: sidebar (info/contact) buried below content on mobile; duplicated status indicator NIP19Page.tsx:403,709, :249/264

P2 — Accessibility

Issue Evidence
Icon-only buttons without aria-label (send, refresh, stash actions, settings back, profile actions on mobile) ConversationPage.tsx:288, CrewChat.tsx:430, StashPage.tsx:153, SettingsPage.tsx:77, NIP19Page.tsx:323
Form labels not associated (htmlFor/id) — invite dialog, meeting point, search inputs CrewInviteDialog.tsx:190-218, CreateTourPage.tsx:1173-1186, GearSwapPage.tsx:227, StashPage.tsx:843
Nested interactive elements (<button> inside <Link>) SpotCard.tsx:52, EmptyState.tsx:67, AboutPage.tsx:159
Charts have no text alternatives; skeleton uses Math.random() heights CommunityStatsPage.tsx:316-413, :171-179
Touch targets < 44px across many pages CrewInviteCard.tsx:121, StashPage.tsx:153, EventsPage.tsx:1129, SpotCard.tsx:186, ContactsPage.tsx:76, GearSwapItemCard.tsx:237
EventsCalendar single-letter weekday headers (S M T W T F S) ambiguous EventsCalendar.tsx:233

P3 — Low / cleanup

Issue Evidence
Group conversations exist but are unreachable from the inbox (dead hook) useDirectMessages.ts:513, DirectMessagesPage.tsx:153
Bounty rank computed after .slice(0,50); count shown twice per row BountyLeaderboardPage.tsx:156/197, :102-110
Low-contrast informational text (gray-600/700) StashPage.tsx:260/933/1100, WalletPage.tsx:73
Contacts search matches npub only while UI shows names ContactsPage.tsx:167,243
Shop gallery not zoomable (Spot has ImageLightbox); share feedback inconsistent ShopDetailPage.tsx:217-240

Suggested batches

  • Batch A — P0 dead flows: items 1–13 (hide or wire every dead control; fix false crew-invite claim; DM login/error feedback).
  • Batch B — P0 error states: replace NotFound-on-error with error+retry across detail pages & leaderboards.
  • Batch C — Near Me: geolocation rejection semantics, defer permission, desktop control, native retry, dialog a11y.
  • Batch D — Money: live exchange rate, wallet web UX, backup state, Clink fallback, nav/orphan page.
  • Batch E — Foundations: PageHeader + container widths + touch-target + a11y primitives, then sweep areas.
  • Batch F — Per-area polish: loading/empty fixes, calendar, profile mobile, FAQ content, cleanup.