Conversation
…ty cap, seeded daily nudge, popularity/org-history signals, ranking docs
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.
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.
…-ingestion Error states, consistent search, and handle sparse ingested events
scrape_listserv.yml failed daily and printed listserv subjects into public Actions logs; pipeline-poll.yml was disabled and broken.
…ta-layer Brings in rohanmatta11's Explore ranking work as-is (smooth time decay, org diversity cap, soon-event quota, seeded daily nudge, popularity and past-RSVP org affinity, batched enrichment, docs/ranking.md). The branch head does not compile yet; review fixes follow in the next commits.
Seed creates users with plausible real NetIDs. Abort when NODE_ENV=production or DATABASE_URL host is not localhost/127.0.0.1 unless ALLOW_REMOTE_SEED=1.
It scraped officer/member personal data using browser cookies and contained an execSync shell injection. Org data will come from the shared InboxEngine service.
Without it, bunx tsc from the repo root resolved typescript@latest (TS 7).
Centralize date helpers in lib/date-format.ts (zoned parts, date keys, wall-clock to instant conversion, formatters) and use them for the explore header, event detail, map timeline/labels/day bucketing, map list cards, and notification timestamps. Server-rendered card strings now also use Eastern time instead of the host's UTC.
Friends shown on event cards have RSVP'd; the app never reads anyone's calendar. Also pluralize the '+ N others' overflow.
Copy-only change in explore-client.tsx, kept in its own commit so it can be dropped cleanly if it conflicts with the data-layer work there.
Addresses the open review points on PR #38: - Fix the missing closing brace in diversifyByOrg (compile error). - Return the finalized, paginated page instead of the full enriched list. - Apply scoring, the org-diversity cap and the soon-event quota to the whole candidate list in a single greedy pass (finalizeFeedOrder), then slice. Soon injection only picks events whose org is under the cap. - Org cap is now a sliding window (max 3 per org in any 20 positions) rather than deferring over-cap events to the very end. - Candidate pool: all published upcoming events within a 45-day horizon (cap 1000), plus a personalised second stage (followed orgs, friend RSVPs, interest tags) only if the first stage saturates. - Enrichment is batched with one inArray query per signal across the candidate set; attendee rosters are loaded for the returned page only. - Responses carry asOf; later pages reuse it as 'now' so every page is a slice of the same ranking. Ranking logic moves to lib/feed-ranking.ts (pure) and lib/feed.ts (data), getFeedEvents validates its params with zod, and docs/ranking.md is updated. Co-authored-by: Rohan Matta <rohan.matta11@gmail.com>
Class years are computed from the current Princeton academic year (rolling over after Commencement) plus Grad/Other, instead of a hardcoded list that had already gone stale. Interest and campus-region options are shared between onboarding and settings, keyed by the existing event_tag / campus_region enum values. Friends list no longer renders 'Grad' as "'ad".
- Remove the hardcoded 'Princeton TigerApps / Design Lead' rows, dead Edit Role buttons, the duplicate Friends/Organizations block with its non-working search, and the fake 'Suggested tags' (Jane Street etc.) - Interests: add/remove from the real event_tag list with a filter; saving stays on the page, toasts success/failure, and refreshes from the server so the saved state survives a reload (ideas from PR #44, without its enum rename) - Send the user's actual regions instead of regions: [] (which wiped them) and add a campus-region picker - Editable name (updateProfile accepts displayName), read-only NetID, dynamic class years, major picker, org-leader toggle - Organizations panel lists orgs the user actually manages - Avatar upload checks the S3 response and surfaces errors via toast - Links to Privacy, Terms and contact
It rendered a duplicate of Settings and nothing linked to it.
apps/web/src/lib/cas.ts: state generation + timing-safe compare, return-path sanitizing, service/login/serviceValidate/logout URL builders, service-URL pinning, and serviceValidate XML parsing (fast-xml-parser, removeNSPrefix, processEntities:false) with NetID validation. Adds fast-xml-parser, a 'test' script, and drops the unused @auth/drizzle-adapter dependency.
Entra derived netId from the UPN/preferred_username with no domain check, allowing account takeover. Login now goes through Princeton CAS (fed.princeton.edu/cas):
- /api/auth/cas/login stores a 32-byte state + sanitized return path in short-lived httpOnly SameSite=Lax cookies and redirects to CAS.
- /api/auth/cas/callback timing-safe checks state, then calls Auth.js signIn('cas') in-process.
- The 'cas' Credentials provider's authorize() re-validates the ticket with CAS serviceValidate (15s timeout), pins the service URL to our own callback/origin, and upserts the user by NetID without clobbering edited displayName/email. CAS is the sole identity authority.
- Direct POSTs to /api/auth/{signin,callback}/cas are rejected.
- Sign-out clears the Auth.js session, then ends the CAS SSO session via /api/auth/cas/logout.
- New shadcn auth error page (pages.error = /auth/error), public in middleware.
- jwt callback tolerates DB errors, keeping last known token values.
- Env: drop AUTH_AZURE_*; add optional AUTH_URL and CAS_BASE_URL; document AUTH_TRUST_HOST.
Next 16 deprecates middleware in favor of proxy, which always runs on Node.js and rejects a runtime segment config.
Adds lib/event-visibility.ts as the single definition of who may see an event: (status='published' AND is_public) OR creator OR owner/officer of the event's org. Applied to: - Explore feed and search (published AND visible) - Map, which also drops events at the (0,0) 'other' placeholder location - getEvent: returns null (404) for drafts/private events the viewer can't see - Similar events, friends' activity, saved/RSVP'd lists in My Events and the Explore sidebar - Org page upcoming events (drafts/private labelled for officers); org recommendations only count published public events - Notifications: reminders only for visible events, and notifications that reference an event the viewer can no longer see are hidden. Reminder generation is batched instead of one query per event. - toggleRsvp/toggleSave no-op on events the viewer can't see - createEvent only notifies followers for published public events
- Name is saved (completeOnboarding accepts displayName) - NetID and email are displayed read-only from the user's record instead of editable inputs that were never saved - Remove the Residential College picker (no column to store it in) - Class years are computed dynamically; interests use the real event_tag values instead of lossy label aliases (Film -> visual arts) - Save on Finish Setup and only show 'You are all set' after the server confirms; failures toast instead of a fake success - Mobile: responsive padding/stacking, h-dvh, no clipped tall steps
turbo.json: declare build env (NEXT_PUBLIC_* hashed; SKIP_ENV_VALIDATION, DATABASE_URL, AUTH_*, CAS_BASE_URL, AWS_* passed through). Turbo 2 strict env mode otherwise strips them, so 'turbo build' could not validate or skip env validation.
Small flyer beside a 24px title, host link, a tight date/time/place list, one row of actions (RSVP, Save, Add to calendar, Share), attendance on one line, tags, description, and owner Edit/Delete as quiet links. Similar events become a compact list. RSVP/save/delete now roll back and toast on failure instead of claiming success.
The four-step 'timeline' sidebar was navigation for a one-screen form. Narrow column, smaller title input and cover drop zone, tighter spacing.
Narrower panel, 13px sans title, 32px avatars, tight rows and sentence-case action buttons instead of the 28px italic serif header and 56px tiles.
SearchInput gains an opt-in shortcut: '/' focuses it from anywhere outside another field and Escape leaves it, with a small kbd hint. Enabled on Explore, Orgs and Friends.
…e legacy pipeline - packages/inbox-engine git submodule (TigerAppsOrg/InboxEngine) - migration 0004: org MyPrincetonU identity/profile fields, event source_url and location_detail, sync_state; organizations.creator_id nullable for imported groups - db:sync-engine: upserts 739 MyPrincetonU orgs (logos, descriptions, links), ~350 campus venues and the revisioned event feed (official + listserv) - Org profile rebuilt: logo header, MyPrincetonU/website/social links, About, upcoming events, followers + friends who follow; rosters stay on MyPrincetonU - Event detail shows where imported events came from and room details - Remove backends/fastapi, apps/admin-web, apps/listserv-scraper, models/ Co-Authored-By: Claude <noreply@anthropic.com>
- Preset cover grid sits behind a 'Pick a preset cover' disclosure - Smaller drop zone; Publish/Save use the regular button size - My Events is only highlighted on /events itself, not on event pages opened from Explore
Hide/share (hover affordances on desktop) are left out below md so the title isn't squeezed to a few characters; titles may wrap to two lines on small screens.
…ed rows; RSVP-only external link
Integrates #48 (data layer: ranking/asOf pagination, visibility, validation, rate limits) and #49 (Princeton CAS auth, proxy.ts). Conflict resolution: - actions/map.ts: staging's visibility/validation imports and logic, plus the Eastern-time formatTime label - actions/users.ts: staging's zod schemas, transactions and helpers; optional displayName re-added to the schema and both updates - explore-client.tsx: staging's asOf/offset pagination and load-more state; compact row layout with a small 'Load more (N left)' button - event-detail-client.tsx: compact layout; Edit gated on staging's canEdit (org officers), Delete on isOwner - app/page.tsx: staging's CAS GET forms; landing previews + SiteFooter - mobile-nav.tsx / sidebar.tsx: no Log out tab, sidebar stays deleted; the header account menu uses signOut({ redirectTo: '/api/auth/cas/logout' }) - proxy.ts: staging's file with /privacy, /terms and the OG/Twitter image routes added to the public list
Staging's httpsUrlSchema rejects http:// and credentialed URLs, which the client accepted and then failed with a generic error.
Release: simpler, honest UI (compact shell, real settings, event forms, polish)
Adopt InboxEngine: MyPrincetonU orgs, logos, venues and events; retire legacy pipeline
Additive: FeedEvent gains optional orgLogoUrl (organizations.logo_url) and locationDetail (events.location_detail), and EventDetail gains orgLogoUrl, so cards can show MyPrincetonU logos and rooms. No query filters or ordering change.
- FloatingSidebar: detached 28px-radius card, turquoise to blush gradient, icon-only nav (Home, My Events, Map, Friends, Organizations) with turquoise active tile, tooltips and aria-labels; Log out pinned at the bottom behind a confirmation, using the CAS sign-out redirect - 'g' then h/e/m/f/o jumps between sections (shown in tooltips) - Header becomes a light utility bar (New event, bell, account menu); the wordmark and menu Log out only appear on phones, which keep the bottom tab bar - Faint ambient pastel blobs behind content
- Card: 24px radius; 40px org logo (object-contain on white) with a pastel initials fallback for the ~200 orgs without one; semibold org name; 20px serif title; room detail after the venue; free food tags yellow, others turquoise; hide is hover-revealed on desktop - Event photo shows as a thumbnail only when it's real: MyPrincetonU's generic orange banner (shared by hundreds of events) is treated as no image - Description preview drops raw links from listserv emails - Search is the Figma pill (pale turquoise, blue icon); chips are outlined pills with a coral/peach active state and use the exact event_tag values
'Hello <Name>,' with today's Princeton date, the pill search and topic chips, then events as a two-column card grid or compact rows (toggle remembered per browser). The side column keeps Friends going and shows Saved only when there is something saved (the old 'Coming up' repeated the top of the feed). Pagination/asOf/load-more logic is unchanged.
- Layout preference is remembered per page (Home defaults to cards, My Events to the denser list) - Full cards show Edit/Delete for your own events and always offer '+ Calendar', building the Google Calendar link themselves
…e sync timer - next.config: output standalone traced from the repo root - apps/database/src/migrate.ts (drizzle-orm migrator) for deploys without drizzle-kit - deploy/: build-release.sh, run-release.sh (migrate, switch, health check, rollback), systemd units (server + 5-minute sync timer), nginx sites, SSM document - .github/workflows/deploy.yml: CI then deploy (staging → forumdev, main → forum) - turbo passes DB/engine env to db:* tasks; README deployment section Co-Authored-By: Claude <noreply@anthropic.com>
Orgs list rows use the shared OrgAvatar (logo or initials fallback) and a pill New organization button; list containers, panels and skeletons move to 20px radii on white. Orgs stay a dense list: 739 groups read better as rows than as a card grid.
Header, About and the side column sit on white 24px-radius cards; the title is serif; link chips become pills; the logo uses the shared OrgAvatar (MyPrincetonU logo or initials).
Content sits on a 24px-radius card, 'Hosted by' shows the org's logo, and MyPrincetonU's generic banner falls back to the cover art.
Deployment: forumdev (staging) and forum (production) with CI/CD
'Add a tagline here' is MyPrincetonU's empty-field placeholder, not an org's tagline.
Deploy fix: release directory permissions
Design: floating sidebar and Figma event cards, built on real InboxEngine data
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Promotes staging to production. Includes #43 (sparse ingested events), #48 (ranking, visibility, hardening, indexes), #49 (Princeton CAS, CI, hygiene), #50 (simpler UI), #51 (InboxEngine: MyPrincetonU orgs, logos, venues, events), #52/#54 (deployment + CI/CD), #53 (floating sidebar, Figma cards on real data).
Merging deploys https://forum.tigerapps.org (production DB
theforum), then syncs from InboxEngine every five minutes. Staging stays at https://forumdev.tigerapps.org.Release report: https://claude.ai/artifact/Vhjswni5iyjqiBjLtu87n3