This document is a full feature reference for I'm In.
It is meant to answer:
- what features the app currently has
- how each feature works in practice
- who each feature is for
- what parts are solid vs partial
It reflects the app as currently implemented, not planned future behavior.
I'm In is a lightweight community activity app for organizing, sharing, discovering, and joining real-world activities.
It is designed to work alongside the WhatsApp groups and community networks people already use. The app does not try to replace chat. Instead, it provides a clearer layer for:
- publishing an activity
- managing who is coming
- handling waitlists
- sharing the right link
- keeping host/admin tasks simple
Although the codebase still uses event language internally, the live product language is activity / activities.
The app currently supports three practical user modes:
Can:
- browse public activities
- open activity pages
- fill the create form before authenticating
- RSVP as a guest
- request access to semi-public activities
Can:
- create and edit activities
- host or co-host activities
- RSVP using their signed-in identity
- manage hosted activities if authorized
- use the signed-in dashboard on
/my-activities - update their profile name/email on
/profile - link WhatsApp verification and, when returned by Lalo, see the verified WhatsApp number on
/profile
This is a user who is not necessarily using a full signed-in auth session, but does have a guest profile/session token.
Can:
- RSVP and have that participation remembered
- view their guest bookings in
/bookings - restore the guest session through
/recover?token=... - add an email later (optional) to unlock stronger recovery and cross-activity continuity
Implemented in src/pages/Home.tsx.
Features:
- public home page for signed-in users as well
- primary CTAs for:
Create an ActivityWhat's OnMy Activitiesfor signed-in users, orActivities I'm Infor guest-session recovery
- "Why this exists" modal
- "Help build it" modal
- "Send feedback" modal (bug / feature / feedback) with optional screenshot upload
- shared hamburger menu with consistent styling/actions, including top-of-list quick links for
Create ActivityandExplore Activities - feedback success state includes a link to the public dev board
- long modals keep their own internal scroll and lock the page behind them while open
- roadmap and email links
Implemented in:
src/pages/AdminHome.tsxsrc/pages/AdminModeration.tsxsrc/pages/AdminGalleryReview.tsxsrc/pages/AdminFeedback.tsx
Features:
- hidden
/adminlanding page for internal tooling - allowlisted signed-in admins can reach
/admindirectly from the top-bar account menu via anAdmin Panelentry - moderation review page
- gallery review page for image preview moderation/report handling
- feedback review page for review, passed, blocked, failed, archived, and all items
- retry sending eligible feedback to the Trello board
- archive / restore internal feedback items
- permanent delete for feedback items with typed
DELETEconfirmation
Implemented in src/pages/MyActivities.tsx.
Features:
- hosting view
- attending view
- public search field that routes into
/calendar?q=... - list of hosted activities
- list of joined activities
- list of "thinking about it" activities merged into attending
- pending access-request panel for hosted semi-public activities
Implemented in src/pages/Calendar.tsx.
Features:
- shows future activities where
is_public = true - only lists activities where
public_discovery_enabled = true - search by query string
- public list styling for browse mode
- groups activities with minimal separators for
Today,Tomorrow, named weekdays within the next 7 days, andLaterfor anything a week or more away - semi-public previews show reduced detail
- if a signed-in attendee already has access to a semi-public activity, the app prefers the private link path when they click it
- shows a subtle count of other upcoming activities in the next 7 days that are not currently visible in public discovery, excluding spam-marked items
- when the public list is empty, offers a
Create your own activityCTA - links to the public moderation transparency page
Implemented in src/pages/Changelog.tsx with build support from scripts/generate-changelog-summary.mjs.
Features:
- two-tab changelog experience:
What changed for you(friendly layman summary)Full changelog(technical markdown-derived details)
- friendly tab reads from generated
src/generated/changelogSummary.ts - generated summaries are refreshed during
predevandprebuild - when
OPENAI_API_KEYis available, build-time AI rewrite is attempted withOPENAI_CHANGELOG_MODEL(default:gpt-4.1-mini) - when AI is unavailable or generation fails, deterministic fallback summaries are emitted so build and page rendering continue
- opening
/changelognever performs runtime AI requests; page views consume static generated content only
Implemented in src/pages/EventDetail.tsx.
Features:
- read activity details
- view attendance state
- RSVP
- cancel RSVP
- join waitlist
- add another person
- mark "thinking about it"
- request access for semi-public activities
- host-configurable extra signup question on join, supporting one
text,number, or dropdown/multiple-choice field per activity - host visibility/contact display when configured
- share actions
- compact icon-only share action in the sticky header, adjacent to the shared hamburger menu control
- Google Calendar action
.icsdownload action- floating feedback button is available on this page and anchors to the top-right below the sticky header on
/events/:slug(it remains bottom-floating on non-detail routes) - modal forms now avoid auto-opening the keyboard on arrival, which keeps the sheet from jumping down on mobile before the user taps a field
- when a required custom join field is enabled, direct
I'm inopens the RSVP modal first so the extra answer is collected before submit
Implemented in src/pages/CreateEvent.tsx.
Features:
- create activity
- edit activity
- delayed-auth save flow
- post-magic-link return resumes on Step 3
One Last Stepprofile capture is only shown when the account still lacks trusted profile details- host name hydration for signed-in hosts
- visibility controls
- timezone select
- duration select
- capacity and waitlist settings
- per-activity setting for requiring guest email before join
- per-activity custom join field builder with label, required/optional toggle, and dropdown options when the field type is
select - public vs private summary/location inputs
- public location is currently restricted to an approved dropdown list, with
Hoi An, Vietnamas the only available UI option - optional Google Maps link
- explicit
Fill from linkaction that can parse a shared Google Maps link and prefill the exact location while preserving the locked public location value - host-managed activity gallery uploads (up to 8 images)
- gallery upload flow accepts iPhone HEIC/HEIF photos and converts them to JPEG before upload
- gallery visibility control (
private_onlyvspublic_preview) that is forced to private-only for private activities - image upload queue with removal before save, plus persisted gallery management during edits
- auth/profile modals now open without auto-focusing inputs so the mobile keyboard does not immediately shove the sheet upward
Implemented in src/pages/HostDashboard.tsx.
Features:
- manage one activity
- attendee and waitlist lists
- add attendee manually
- remove attendee
- share/copy links
- WhatsApp share actions
- see WhatsApp numbers more consistently across hosts, attendees, join requests, interests, lookup matches, and notification recipients when available on linked profiles
- post-create success modal with quick actions for WhatsApp sharing, returning to
My Activities, or returningHome - access request review
- join request review (when host approval is enabled)
- host-only display of submitted custom join answers across join requests, attendees, pending approvals, and waitlist rows
- send manual in-app activity notifications to eligible recipients
- receive guest replies back as
guest_replynotifications when attendees respond to host messages - receive
host_joinnotifications when someone newly lands inconfirmed,pending_approval, orwaitliststate for the hosted activity - batch rapid same-actor additions for the same activity into one delayed
host_joinnotification listing all collected names - host-facing activity notifications now deep-link into
/host/events/:idinstead of falling back to a generic destination - host/co-host management
- duplicate activity, with new copies resetting the public-location filter value to the current approved option
- duplicate activity preserves
gallery_visibilitywhile leaving image assets tied to the original activity - delete activity
Implemented in src/pages/ProfileSettings.tsx.
Features:
- signed-in profile page at
/profile - locked-by-default name display with an edit toggle
- email update form for signed-in users
- WhatsApp verification/link state
- verified WhatsApp number display when returned by Lalo
- name propagation across hosted activities and self-joined records where sync succeeds
- installed-app push notification controls with per-category toggles once the account is WhatsApp-linked and the app is opened in standalone mode
- push categories now include a host-side
Someone joined your activityalert toggle for the newhost_joinnotification type - push notifications now preserve stored activity-specific deep links when available instead of defaulting activity alerts to
/
Implemented in:
src/pages/Bookings.tsxsrc/pages/Recovery.tsx- partially through
src/pages/Login.tsx
Features:
- guest bookings list
- guest interests list merged into bookings
- restore session by token URL
- optional guest email-upgrade flow
- guest-email upgrades now use a dedicated SQL merge RPC so attendee ownership, inviter attribution, sessions, interests, and join requests move together
Implemented through:
events.custom_join_field_configevent_signup_field_answersrequest_or_submit_rsvp_with_custom_answer(...)add_proxy_attendee_with_custom_answer(...)
Notes:
- only one custom field is currently supported per activity
- supported field types are
text,number, andselect - custom join answers are intentionally stored outside
event_attendeesso they are not exposed by broader attendee-read policies on public activities - rollout requires the April 12 migrations for the feature itself plus the follow-up unique-index fix used by the custom-answer upsert path
- a separate April 12 trigger reconciliation migration keeps the new host-join notification path compatible with the real
event_attendeesrow shape - a later April 12 batching migration groups same-actor host-join alerts for 30 seconds before creating the final
host_joinnotification row
Important caveat:
- the guest recovery story is only partially implemented as a product flow
Lets visitors submit bug reports, feature requests, or general feedback from the app feedback surfaces (home modal plus global floating button), then routes those submissions into a Trello-driven review flow.
- home page
Send feedbackmodal and the global floating feedback button both send data tosubmit-feedbackEdge Function - on attendee activity detail (
/events/:slug), the floating feedback button is positioned top-right below the sticky header; on other routes it remains bottom-floating - payload supports:
- type (
bug,feature,feedback) - title
- details
- optional name/email
- optional screenshot
- type (
- function runs a lightweight abuse-only AI check
- non-blocked submissions create a sanitized card in Trello intake list
- Codex prompt generation is intentionally deferred
- when any Trello card is moved into the configured prompt-trigger list,
trello-prompt-syncgenerates a Codex prompt and writes it into the card description - recommended production setup uses a Trello board webhook so the list move triggers automatically
- manual admin-triggered sync remains available as a fallback
- raw submission text and optional screenshot are stored in Supabase, not posted in full to the public Trello board
- Trello intake cards are sanitized/minimized by design
- prompt-generation runs are tracked in
trello_prompt_jobsto avoid duplicate generation on repeated moves - prompt generation can apply to cards created manually in Trello as well as cards originating from the app
- internal review of non-board items happens through
/admin/feedback - successful board submissions can still be reviewed later under the
Passedbucket
Lets a host create a new activity with scheduling, visibility, capacity, and host details.
- route:
/create-event - page:
CreateEvent.tsx - uses a 3-step flow:
- visibility
- activity details
- joining settings
- signed-out users can complete the form before auth
- on save, unsigned users are prompted for email and sent a magic link
- draft state is stored locally until the user comes back and completes save
- title
- public summary
- private/full description
- public location text
- private/full location text
- optional Google Maps link
- optional Google Maps-based autofill for location fields
- start date/time
- timezone
- duration
- capacity
- host name
- host contact text
- visibility
- allow waitlist
- newly created activities default to
semi_public - for signed-in users, host name is auto-hydrated and treated as account-backed
- host name is always shown for
publicandsemi_publicactivities - field-level
Public/Privatebadges are used in the form to make visibility clearer - step navigation now uses in-flow back behavior so draft values persist when moving between steps
Lets the primary host or an authorized co-host update an existing activity.
- route:
/host/events/:id/edit - page:
CreateEvent.tsxin edit mode - requires signed-in user
- checks both:
- legacy primary host via
events.host_user_id - co-host membership via
event_hosts
- legacy primary host via
- edit flow also normalizes stale host names for the primary host when possible
- capacity increases can trigger waitlist promotion logic client-side
Allows a signed-out visitor to start creating an activity without being forced to sign in first.
- form is accessible without auth
- draft is stored in local storage
- save triggers magic-link email prompt
- after sign-in, the user returns and can finish saving the same activity
This is one of the most distinctive UX features in the app and should be treated as deliberate product behavior.
Lets users discover upcoming public-facing activities.
- route:
/calendar - page:
Calendar.tsx - fetches only:
- scheduled activities
- future activities
is_public = true
public_discovery_enabled = true
- query string key:
q - search checks:
- title
location_textpublic_location_text
How it works:
- appears in public browse when discovery is enabled
- public detail is fully visible
- exact time is shown publicly
How it works:
- appears in public browse when discovery is enabled
- public preview is intentionally limited
- full access comes from a private host-shared link using
?access=... - non-members can submit a request to view/join
- semi-public public-preview content is included in platform moderation review and can appear in the public moderation log
- semi-public private-link-only content stays outside platform moderation review and outside the public moderation log
How it works:
- not shown in public browse
- intended as link-only / unlisted
- kept outside platform moderation review and outside the public moderation log
The app uses both:
is_publicvisibility
Current practical behavior:
publicandsemi_publicboth end up withis_public = trueprivateends up withis_public = false
Allows a user to request access to a semi-public activity when they do not yet have the private access link.
User side:
- user opens semi-public activity without valid private access
- sees limited preview
- can open a request modal
- submits:
- name
- optional note
Host side:
- request is stored in
event_access_requests - host sees requests in
HostDashboard.tsx - host can:
- approve/share link
- request more info
- decline
- pending requests show in the host UI
- approved and declined requests are archived into separate views/tallies
- request-to-view modal now opens without auto-launching the keyboard, reducing mobile viewport jumpiness
Lets a person join an activity.
- page:
EventDetail.tsx - supports signed-in RSVP
- supports guest RSVP
- supports host-controlled optional guest email for RSVP/interest/proxy
- uses a request-aware RSVP path:
- direct join via
submit_rsvp(...)when host approval is off - pending join request via
request_or_submit_rsvp(...)when host approval is on
- direct join via
The app may match the person through:
user_idattendee_profile_idguest_email
When guest email is optional for an activity, profile/session identity remains the primary guest handle and email can be added later.
- this is a high-risk flow because identity and waitlist behavior overlap here
Lets a host require approval before a person is added as an attendee.
- per-activity setting on create/edit:
require_host_approval_for_join - when enabled, attendee-side join attempts create rows in
event_join_requestswithpendingstatus - the attendee is also shown in
Goingimmediately withevent_attendees.status = pending_approval - hosts review in
HostDashboard.tsxand can approve/reject - approval promotes the pending attendee to
confirmedorwaitlistbased on capacity andallow_waitlist - rejection/cancel marks the pending attendee row as
cancelled
Places additional RSVPs on a waitlist when the activity is full and waitlist is enabled.
- attendee sees waitlist state through RSVP logic
- cancellation can promote the next person
- host dashboard shows waitlist attendees
Waitlist behavior is not fully owned by one layer. It is split across:
- frontend helper logic
- current RPC behavior
- older starter-schema trigger/function logic
Lets an attendee or host cancel/remove attendance.
- attendee-side cancellation runs through
cancel_attendee_with_promotion(...) - host-side attendee removal also uses the same RPC path
This central RPC path is important because it avoids more fragile direct mutation behavior for cancellation and promotion.
Lets a person add someone else to the activity without making that second person sign in themselves.
- page:
EventDetail.tsx - host dashboard also has host-side attendee add flow
- attendee-facing proxy add uses
add_proxy_attendee(...)
- supports adding another person after joining
- supports host-added and proxy-added labeling in attendee lists
- when host approval is enabled, proxy add creates a pending join request and
pending_approvalattendee instead of bypassing approval - proxy-added rows track provenance through:
added_by_typeadded_by_attendee_profile_id
Lets a user save an activity as a possible interest without formally joining.
- backed by
event_interests - toggled via
toggle_event_interest(...) - shown in:
HomeBookingsCalendarEventDetailHostDashboard
- public activities use
count_only - semi-public/private activities can use
named
- appears as a distinct state from RSVP
- on some screens it is merged into attendance-style lists for convenience
Lets guests continue to access their activity participation without a full signed-in auth account.
- profile stored in
attendee_profiles - session token stored in
attendee_sessions - token persisted in browser local storage
- bookings loaded through
guestService
Shows a guest-session user the activities they are in.
- route:
/bookings - page:
Bookings.tsx - reads bookings from guest session token
- also includes “thinking about it” rows
This is not the main signed-in attendee dashboard. Signed-in users mainly use Home for their attending view.
Provides a way to restore a guest session.
/recover?token=...validates and restores a guest session token
/login?recovery=trueuses a “Find my bookings” UI, but currently sends a normal Supabase OTP emailguestService.sendRecoveryEmail()exists but is not currently the active UI path and still has TODO-level email delivery scaffolding
Gives a host a management interface for one activity.
- view date/time and stats
- manage attendees
- manage waitlist
- manage interests
- review access requests
- share/copy public and private links
- open WhatsApp sharing actions
- view and manage hosts
- duplicate the activity
- delete the activity
Lets a host add additional hosts with equal practical management rights.
- backed by
event_hosts - create/edit/manage checks consider:
- primary host through
events.host_user_id - co-hosts through
event_hosts
- primary host through
- add co-host by email
- view host list
- leave as host if not the last host
- duplicate activity with hosts carried forward
- host UI currently enforces a 10-host cap
Lets hosts and attendees share the right activity link for the situation.
- public link:
/events/:slug - semi-public private link:
/events/:slug?access=... - host dashboard offers explicit public/private link handling for semi-public activities
buildEventPath(...)is used in shared navigation logic to prefer private access paths when appropriate
- activity pages and host flows are clearly designed around WhatsApp usage patterns
- host dashboard supports WhatsApp share behavior
- semi-public request handling is WhatsApp-oriented
- host contact text can be used for direct messaging
- there is no full WhatsApp platform integration or bot/backend in this repo
- it is mostly link generation and message-launch behavior
- available after self-RSVP
- also available to hosts even when they are not personally attending
- uses URL-based Google Calendar template generation
- if a Google Maps share link exists, the calendar location field uses that URL
- available after self-RSVP
- also available to hosts even when they are not personally attending
- generated client-side through helper logic in
utils.ts - if a Google Maps share link exists, the
.icslocation field uses that URL and exact location text moves into the description
- this is not two-way synced calendar integration
Makes activity scheduling less dependent on device-local assumptions.
- UTC storage
- explicit activity timezone
- duration-based end time
- 15-minute increments
- default timezone:
Asia/Ho_Chi_Minh
Attempts to keep host names and attendee display names aligned with the best available identity source.
- signed-in users are synchronized into
attendee_profiles - create/edit/manage flows try to normalize stale host names
- profile/account name data can win over older saved host-name strings
- the app has legacy overlap between:
- email-handle-derived names
- profile names
- stored host names
Keeps some host and attendee views refreshed when data changes.
EventDetail.tsx:
- attendee updates
- interest updates
HostDashboard.tsx:
- attendee updates
- access request updates
- interest updates
- detail-page subscriptions are broader/chattier than ideal
The app currently includes:
- "Why this exists" messaging
- "Help build it" messaging
- the earlier separate "How this works" content is now folded into "Why this exists"
- roadmap link
- contact email
- community-oriented
- open-build / open-roadmap tone
- framed as useful alongside existing groups rather than a replacement for them
These are important because they affect how confidently someone should describe the product.
- guest recovery product flow
- schema as a single-source artifact
- waitlist authority consistency
- automated test coverage
- Google OAuth
- a unified single user model
- a dedicated backend server in this repo
- full sync calendar integration
- production-grade guest recovery email delivery
Adds a lightweight discovery gate for public activities so broader public browse can stay open without exposing every listing immediately.
- public activities are reset to
pendingwhen meaningful public-facing fields change - the moderation Edge Function classifies the saved activity and writes structured fields back to
events - trusted hosts may get slightly softer outcomes for some medium-risk low-detail cases
- the public browse page only shows activities where
public_discovery_enabled = true - semi-public public previews stay in platform moderation review, while semi-public private-link-only content stays outside it
- hidden page:
/admin/moderation - frontend access is controlled by
VITE_MODERATION_ADMIN_EMAILS - server-authorized override actions are controlled by
MODERATION_ADMIN_EMAILS - queue buckets include
review,archived, andspam - archive is a reviewer housekeeping action and is separate from hide/review
- admin moderation tooling is scoped to public-facing activity moderation
- force visible
- force limited
- hide / review
- mark safe
- mark spam
- archive / return to review
- re-run AI moderation
Lets the community see a calm, public-facing history of moderation actions for public activities.
- public page:
/moderation - uses a public-safe RPC-backed feed of moderation log entries
- supports filtering by moderation action
- can be linked from a public activity detail page when moderation history exists
- can open the current public-facing activity page in a modal preview
- shows moderator-written public explanations for manual moderation decisions when they are provided
- public activity moderation appears here
- semi-public preview moderation can also appear here
- private content and private-link-only semi-public content are never included
- moderator identities are shown through stable pseudonymous handles such as
Moderator 01
It is easy to assume this is the general attendee dashboard. It is not.
Token restore exists. Email-driven guest recovery is not fully productized.
Contributors need to understand both fields, not just one.
Changes in this area need extra care.
There is no single perfect schema file.
For more detail, read:
README.mdPROJECT_ARCHITECTURE.mdCURRENT_STATE.mdCONTRIBUTING.mdSCHEMA_OR_DATA_MODEL.mdAI_DEV_RULES.mdAUTH_UNIFICATION_PLAN.mdSCHEMA_ALIGNMENT.md
The app currently has a broad, real feature set:
- create/edit
- browse/search
- RSVP/waitlist/cancel
- proxy attendance
- interests
- semi-public access requests
- co-hosts
- guest bookings
- calendar export
Its strongest features are around real community use and host workflow.
Its biggest remaining rough edges are around recovery, identity complexity, schema clarity, and waitlist/RPC/source-of-truth consistency.