Skip to content

Error states, consistent search, and handle sparse ingested events - #43

Open
prishaakapasi wants to merge 6 commits into
TigerAppsOrg:stagingfrom
prishaakapasi:TIG-247-error-states-search-ingestion
Open

Error states, consistent search, and handle sparse ingested events#43
prishaakapasi wants to merge 6 commits into
TigerAppsOrg:stagingfrom
prishaakapasi:TIG-247-error-states-search-ingestion

Conversation

@prishaakapasi

@prishaakapasi prishaakapasi commented Aug 25, 2026

Copy link
Copy Markdown

TIG-247.

Merge #42 first. This branches off #42, so until that lands GitHub shows
its commits here too. Once #42 is merged into staging this diff collapses to
just the four changes below, no rebase needed.

Files actually new here: explore-client.tsx, event-card.tsx,
notification-dropdown.tsx, page-shell.tsx.

Error states

ErrorState existed in the shared states file but nothing used it, so server
action failures were invisible.

  • Explore shows a retry when the feed fetch fails, instead of an empty list.
  • The notification dropdown had a literal // silently fail. It records the
    failure and shows a retry when you open it. No toast, since it polls every 60s.
  • RSVP and save updated optimistically and never rolled back, so a server error
    left the button flipped while the database disagreed. Both revert and toast now.

Search consistency

Explore required Enter, Orgs debounced as you typed. Explore debounces at 300ms
now to match, with a result count when a search or filter is active.

Sparse ingested events

The server substitutes "TBD" for a missing location and the card rendered it
verbatim next to a map pin. Treated as absent now. Org, flyer, description and
tags were already conditional.

Settings on a phone

TOP_BAR_CLEARANCE reserved 140px on the right, over a third of a 375px screen.
Content drops below the bar on phones and only insets from the right at sm and
up. Fixes the Events page heading too, same constant.

Verification

  • biome check, tsc --noEmit, next build all clean
  • Smoke-tested the routes against a local dev server with seeded data

Picks up where TigerAppsOrg#40 left off. Covers TIG-247, TIG-248, TIG-249.

The big one is the event card. Explore, My Events, the map rail and org
pages all had their own version, so the same event looked different
depending on where you ran into it. Now there's one card with three
densities (feed / compact / wide) and the actions only show up if you
pass a handler, so the map doesn't need an RSVP button it can't use.

While doing that I found getMyEvents was returning tags: [],
friendsAttending: [], rsvpCount: 0 etc. as literals, so My Events could
never have shown a tag or a friend no matter how it was styled. Fixed
that with batched queries.

RSVP/save/share/hide now all confirm with a toast, and RSVP gets a check
+ colour change — the label flip on its own was way too easy to miss.
Hide used to just drop the event from state with no way back, so it
collapses to a stub with an Unhide now. Turns out hide was never
reachable anyway: the prop existed and Explore passed a handler, but
nothing ever called it. "N attending" is clickable and lists people.
Created events got Edit/Delete with a confirm.

Map: clicking a pin now opens the real thing instead of that cramped
popup (whose carousel arrows sat on top of the start time). One event
opens the card, multiple open the sidebar. Timeline moved below the map
so overlays stop landing on it, and added a ResizeObserver because
Mapbox doesn't watch its own container and was leaving a blank strip
after layout changes.

Nav: rail collapses to icons and expands on hover, pushes content
instead of covering it, and there's a proper bottom tab bar on phones
since hover obviously doesn't work on touch. Responsive is only about
half done — nav, overflow fixes, the event forms and Explore are
sorted, the map isn't yet.

Also fixed a nasty one in db.ts: it made a new Postgres pool on every
hot reload and leaked the old one, so after a long dev session you'd hit
the 100 connection cap and everything died with 53300, including auth.
Cached on globalThis now. Same idea in getMapEvents, which was firing
2N+1 queries at once.

Few other things while I was in there: nested <button> in the map list
(invalid HTML, inner one unreachable in some browsers), two stacked
close buttons on the map modal, and the friends remove button which was
opacity-0 until hover AND #ececec, so basically invisible either way.
TIG-247, TIG-248.

Rebuilt the Explore sidebar to match the Figma — Find My Friends is a real
list now ("Sophia added X to their calendar" + VIEW EVENT), and Upcoming
Events reads "X is happening tomorrow!". Dropped the hand-drawn fake map
that was in there with made-up landmarks and made-up people on it. Nudged
the whole rail down 60px so it lines up with the "Today is..." line rather
than the greeting.

Kept finding the same bug while doing this: getSavedEvents,
getSimilarEvents, getFriendsEvents and getMyEvents were all returning
tags: [], rsvpCount: 0, friendsAttending: [] and isRsvped/isSaved: false
as literals. So those screens could never show a tag or the right button
state no matter what we did to the styling. Pulled the batched queries out
into loadEventEnrichment() and pointed all four at it — was about to write
it a fourth time by hand which felt like a hint. getSimilarEvents also
never bound userId despite authenticating, which is why it couldn't work
out per-user state in the first place.

Needed a real date for "tomorrow" (datetime is a pre-formatted string you
can't parse back) so added rawDatetime + formatRelativeDay, which compares
calendar days rather than hours — a 9am event tomorrow should say tomorrow
even though it's 20 hours out.

Responsive: My Events and Friends tabs shrink so three fit on a phone,
friend request rows wrap, map event list goes full width, timeline drops
its legend and the filter pills scroll sideways.

Landing page: the FORUM wordmark was cut off — -mb-6 inside an
overflow-hidden parent was clipping the letterforms, and at a fixed 130px
it was wider than a phone anyway. It's 28vw now so it still spans the
screen, locked back to 130/160px on desktop. Hero h1 was fixed at 72px and
overflowing too, and the turquoise section had min-h-[200vh] on mobile
which was two screens of mostly nothing.
TIG-247.

ErrorState was sitting in the shared states file unused by anything, so
every server action failure was invisible. Wired it into Explore (retry
button instead of an empty feed, which reads as "no events" and is a
totally different message) and into the notification dropdown, which had
an actual `// silently fail` in it.

Worse than the missing UI: RSVP and save were updating optimistically and
never rolling back. If the server threw, the button stayed flipped while
the DB disagreed. Both revert now and toast.

Didn't toast on the notification failure — it polls every 60s so that
would just be spam. It shows the error inside the dropdown when you open
it instead.

Search: Explore needed you to press Enter, Orgs debounced as you typed.
No reason for them to differ, so Explore debounces now too, plus a result
count when a search or filter is active.

Ingested events: the server swaps in the literal string "TBD" when an
event has no location, and the card was rendering that next to a map pin,
which just looks broken. Treats it as absent now. Org, flyer, description
and tags were already conditional so a sparse event degrades fine.

Settings on a phone: TOP_BAR_CLEARANCE reserved 140px on the right to
clear the floating bell/avatar, which is over a third of a 375px screen.
Drops below the bar on phones instead, only insets from the right at sm+.
Fixes the Events page heading too since it uses the same constant.
Comment thread apps/web/src/app/(app)/explore/explore-client.tsx
Comment thread apps/web/src/app/(app)/explore/explore-client.tsx
Comment thread apps/web/src/app/(app)/explore/explore-client.tsx Outdated

@angelina-ji angelina-ji left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Looks good overall, but I found a couple edge cases around the new error handling/search behavior that can leave the Explore UI inconsistent with the underlying state. Left comments inline.

Home feed now matches the Figma: event cards in a two-column grid with
the highlights rail pinned to the right, so only the feed shifts when the
nav rail expands. The shell runs full width instead of max-w-7xl. With a
capped shell the whole row re-centred and the rail travelled with it.

Card, on Explore and the map's expanded view alike:
- utility icons (save, share, hide, open) go coral, hovering to a coral
  wash rather than the ghost variant's full-strength turquoise --accent
- tags alternate yellow/turquoise, friends line reads "… added this event
  to their calendar!", description gains a See Details link
- footer wraps as two units with the redundant Users glyph dropped, so
  "4 attending" stops breaking across two lines in a narrow column
- the map modal picks up the shared date formatter and the "+ Calendar"
  action; buildGCalUrl was extracted for that call site and never wired up

Fixes from review:
- feed requests carry a monotonic id and a queued search is cancelled when
  filters change, so a debounce armed with the previous filters can no
  longer land last and overwrite the feed
- save/RSVP flip optimistically and genuinely revert on failure; the catch
  used to invert a value that was still correct. Handlers rethrow so the
  card announces success only once the request resolves, instead of
  showing a success toast beside the error one
- the result count uses the returned total, not the 20-row page length
- getFeedEvents carries rawDatetime through; it was declared on FeedEvent
  for calendar links but dropped with the sort key

Rail backdrop goes near-opaque over the map, where 50% let street names
read straight through the nav labels.
- Explore no longer swaps in a fake event when the feed comes back empty,
  so the zero-events state can actually render on first load.
- toggleRsvp now returns the post-toggle attendee list along with the
  count. Every caller renders an avatar stack from it, so patching just
  the count left the viewer's own face in the stack after un-RSVPing.
- My Events drops a card from Saved / RSVP'd when the server says it's no
  longer saved / RSVP'd, instead of leaving it sitting in a list it just
  left until you reload.
- getSavedEvents filters to future events. "Upcoming Events" reads from it
  and was happily announcing a past event as happening "yesterday".
- Search placeholder no longer promises people search that the feed query
  doesn't actually do.
@prishaakapasi
prishaakapasi force-pushed the TIG-247-error-states-search-ingestion branch from 93de1aa to b53816b Compare August 26, 2026 19:05
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