Skip to content

Latest commit

 

History

History
965 lines (662 loc) · 29.6 KB

File metadata and controls

965 lines (662 loc) · 29.6 KB

FEATURES

Purpose

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.

Product Summary

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.

User Types

The app currently supports three practical user modes:

1. Signed-out visitor

Can:

  • browse public activities
  • open activity pages
  • fill the create form before authenticating
  • RSVP as a guest
  • request access to semi-public activities

2. Signed-in user

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

3. Guest-session user

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

Main Product Areas

Landing page

Implemented in src/pages/Home.tsx.

Features:

  • public home page for signed-in users as well
  • primary CTAs for:
    • Create an Activity
    • What's On
    • My Activities for signed-in users, or Activities I'm In for 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 Activity and Explore 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

Admin tooling

Implemented in:

  • src/pages/AdminHome.tsx
  • src/pages/AdminModeration.tsx
  • src/pages/AdminGalleryReview.tsx
  • src/pages/AdminFeedback.tsx

Features:

  • hidden /admin landing page for internal tooling
  • allowlisted signed-in admins can reach /admin directly from the top-bar account menu via an Admin Panel entry
  • 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 DELETE confirmation

Signed-in dashboard

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

Public browse / search

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, and Later for 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 activity CTA
  • links to the public moderation transparency page

Changelog 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 predev and prebuild
  • when OPENAI_API_KEY is available, build-time AI rewrite is attempted with OPENAI_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 /changelog never performs runtime AI requests; page views consume static generated content only

Activity detail page

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
  • .ics download 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 in opens the RSVP modal first so the extra answer is collected before submit

Create / edit page

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 Step profile 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, Vietnam as the only available UI option
  • optional Google Maps link
  • explicit Fill from link action 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_only vs public_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

Host dashboard

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 returning Home
  • 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_reply notifications when attendees respond to host messages
  • receive host_join notifications when someone newly lands in confirmed, pending_approval, or waitlist state for the hosted activity
  • batch rapid same-actor additions for the same activity into one delayed host_join notification listing all collected names
  • host-facing activity notifications now deep-link into /host/events/:id instead 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_visibility while leaving image assets tied to the original activity
  • delete activity

Profile settings

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 activity alert toggle for the new host_join notification type
  • push notifications now preserve stored activity-specific deep links when available instead of defaulting activity alerts to /

Guest bookings / recovery

Implemented in:

  • src/pages/Bookings.tsx
  • src/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

Custom join field data model

Implemented through:

  • events.custom_join_field_config
  • event_signup_field_answers
  • request_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, and select
  • custom join answers are intentionally stored outside event_attendees so 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_attendees row shape
  • a later April 12 batching migration groups same-actor host-join alerts for 30 seconds before creating the final host_join notification row

Important caveat:

  • the guest recovery story is only partially implemented as a product flow

Feature Reference

0. Feedback Intake And Trello Pipeline

What it does

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.

How it works

  • home page Send feedback modal and the global floating feedback button both send data to submit-feedback Edge 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
  • 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-sync generates 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

Privacy notes

  • 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_jobs to 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 Passed bucket

1. Activity Creation

What it does

Lets a host create a new activity with scheduling, visibility, capacity, and host details.

How it works

  • 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

Current fields supported

  • 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

Notes

  • 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 public and semi_public activities
  • field-level Public / Private badges 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

2. Activity Editing

What it does

Lets the primary host or an authorized co-host update an existing activity.

How it works

  • route: /host/events/:id/edit
  • page: CreateEvent.tsx in edit mode
  • requires signed-in user
  • checks both:
    • legacy primary host via events.host_user_id
    • co-host membership via event_hosts

Notes

  • edit flow also normalizes stale host names for the primary host when possible
  • capacity increases can trigger waitlist promotion logic client-side

3. Delayed-Auth Create Flow

What it does

Allows a signed-out visitor to start creating an activity without being forced to sign in first.

How it works

  • 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

Why it matters

This is one of the most distinctive UX features in the app and should be treated as deliberate product behavior.

4. Public Browse

What it does

Lets users discover upcoming public-facing activities.

How it works

  • route: /calendar
  • page: Calendar.tsx
  • fetches only:
    • scheduled activities
    • future activities
    • is_public = true
  • public_discovery_enabled = true

Search behavior

  • query string key: q
  • search checks:
    • title
    • location_text
    • public_location_text

5. Visibility Modes

public

How it works:

  • appears in public browse when discovery is enabled
  • public detail is fully visible
  • exact time is shown publicly

semi_public

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

private

How it works:

  • not shown in public browse
  • intended as link-only / unlisted
  • kept outside platform moderation review and outside the public moderation log

Important implementation nuance

The app uses both:

  • is_public
  • visibility

Current practical behavior:

  • public and semi_public both end up with is_public = true
  • private ends up with is_public = false

6. Semi-Public Access Requests

What it does

Allows a user to request access to a semi-public activity when they do not yet have the private access link.

How it works

User side:

  • user opens semi-public activity without valid private access
  • sees limited preview
  • can open a request modal
  • submits:
    • name
    • WhatsApp
    • 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

Current UX behavior

  • 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

7. RSVP

What it does

Lets a person join an activity.

How it works

  • 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

Identity handling

The app may match the person through:

  • user_id
  • attendee_profile_id
  • guest_email

When guest email is optional for an activity, profile/session identity remains the primary guest handle and email can be added later.

Notes

  • this is a high-risk flow because identity and waitlist behavior overlap here

7.1 Host-Approval Join Requests

What it does

Lets a host require approval before a person is added as an attendee.

How it works

  • per-activity setting on create/edit: require_host_approval_for_join
  • when enabled, attendee-side join attempts create rows in event_join_requests with pending status
  • the attendee is also shown in Going immediately with event_attendees.status = pending_approval
  • hosts review in HostDashboard.tsx and can approve/reject
  • approval promotes the pending attendee to confirmed or waitlist based on capacity and allow_waitlist
  • rejection/cancel marks the pending attendee row as cancelled

8. Waitlist

What it does

Places additional RSVPs on a waitlist when the activity is full and waitlist is enabled.

How it works

  • attendee sees waitlist state through RSVP logic
  • cancellation can promote the next person
  • host dashboard shows waitlist attendees

Important caveat

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

9. Cancellation

What it does

Lets an attendee or host cancel/remove attendance.

How it works

  • attendee-side cancellation runs through cancel_attendee_with_promotion(...)
  • host-side attendee removal also uses the same RPC path

Why it matters

This central RPC path is important because it avoids more fragile direct mutation behavior for cancellation and promotion.

10. Proxy RSVP / Add Another Person

What it does

Lets a person add someone else to the activity without making that second person sign in themselves.

How it works

  • page: EventDetail.tsx
  • host dashboard also has host-side attendee add flow
  • attendee-facing proxy add uses add_proxy_attendee(...)

Current behavior

  • 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_approval attendee instead of bypassing approval
  • proxy-added rows track provenance through:
    • added_by_type
    • added_by_attendee_profile_id

11. Thinking About It

What it does

Lets a user save an activity as a possible interest without formally joining.

How it works

  • backed by event_interests
  • toggled via toggle_event_interest(...)
  • shown in:
    • Home
    • Bookings
    • Calendar
    • EventDetail
    • HostDashboard

Visibility behavior

  • public activities use count_only
  • semi-public/private activities can use named

UX behavior

  • appears as a distinct state from RSVP
  • on some screens it is merged into attendance-style lists for convenience

12. Guest Session Persistence

What it does

Lets guests continue to access their activity participation without a full signed-in auth account.

How it works

  • profile stored in attendee_profiles
  • session token stored in attendee_sessions
  • token persisted in browser local storage
  • bookings loaded through guestService

13. Guest Bookings

What it does

Shows a guest-session user the activities they are in.

How it works

  • route: /bookings
  • page: Bookings.tsx
  • reads bookings from guest session token
  • also includes “thinking about it” rows

Important caveat

This is not the main signed-in attendee dashboard. Signed-in users mainly use Home for their attending view.

14. Recovery

What it does

Provides a way to restore a guest session.

What is implemented

  • /recover?token=... validates and restores a guest session token

What is partial

  • /login?recovery=true uses a “Find my bookings” UI, but currently sends a normal Supabase OTP email
  • guestService.sendRecoveryEmail() exists but is not currently the active UI path and still has TODO-level email delivery scaffolding

15. Host Dashboard

What it does

Gives a host a management interface for one activity.

Current capabilities

  • 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

16. Co-Hosts

What it does

Lets a host add additional hosts with equal practical management rights.

How it works

  • backed by event_hosts
  • create/edit/manage checks consider:
    • primary host through events.host_user_id
    • co-hosts through event_hosts

Current capabilities

  • add co-host by email
  • view host list
  • leave as host if not the last host
  • duplicate activity with hosts carried forward

Current constraint

  • host UI currently enforces a 10-host cap

17. Sharing

What it does

Lets hosts and attendees share the right activity link for the situation.

How it works

  • 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

18. WhatsApp-Oriented Flows

What exists

  • 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

What does not exist

  • there is no full WhatsApp platform integration or bot/backend in this repo
  • it is mostly link generation and message-launch behavior

19. Calendar Export

Google Calendar

  • 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

.ics export

  • 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 .ics location field uses that URL and exact location text moves into the description

Limitation

  • this is not two-way synced calendar integration

20. Timezone And Duration

What it does

Makes activity scheduling less dependent on device-local assumptions.

How it works

  • UTC storage
  • explicit activity timezone
  • duration-based end time
  • 15-minute increments

Current default

  • default timezone: Asia/Ho_Chi_Minh

21. Host Name And Identity Normalization

What it does

Attempts to keep host names and attendee display names aligned with the best available identity source.

How it works

  • 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

Why it exists

  • the app has legacy overlap between:
    • email-handle-derived names
    • profile names
    • stored host names

22. Realtime Updates

What it does

Keeps some host and attendee views refreshed when data changes.

Current use

EventDetail.tsx:

  • attendee updates
  • interest updates

HostDashboard.tsx:

  • attendee updates
  • access request updates
  • interest updates

Caveat

  • detail-page subscriptions are broader/chattier than ideal

23. Landing-Page Messaging / Community Positioning

What exists

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

Current positioning

  • community-oriented
  • open-build / open-roadmap tone
  • framed as useful alongside existing groups rather than a replacement for them

24. Current Feature Gaps / Partial Areas

These are important because they affect how confidently someone should describe the product.

Partially implemented or rough

  • guest recovery product flow
  • schema as a single-source artifact
  • waitlist authority consistency
  • automated test coverage

Explicitly not current features

  • Google OAuth
  • a unified single user model
  • a dedicated backend server in this repo
  • full sync calendar integration
  • production-grade guest recovery email delivery

25. AI Moderation And Review Queue

What it does

Adds a lightweight discovery gate for public activities so broader public browse can stay open without exposing every listing immediately.

How it works

  • public activities are reset to pending when 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

Review tooling

  • 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, and spam
  • archive is a reviewer housekeeping action and is separate from hide/review
  • admin moderation tooling is scoped to public-facing activity moderation

Manual actions

  • force visible
  • force limited
  • hide / review
  • mark safe
  • mark spam
  • archive / return to review
  • re-run AI moderation

26. Public Moderation Transparency

What it does

Lets the community see a calm, public-facing history of moderation actions for public activities.

How it works

  • 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

Scope rules

  • 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

27. Most Important Behavioral Caveats

/bookings is guest-session driven

It is easy to assume this is the general attendee dashboard. It is not.

Recovery is split

Token restore exists. Email-driven guest recovery is not fully productized.

Visibility depends on both is_public and visibility

Contributors need to understand both fields, not just one.

Waitlist logic is not fully single-sourced

Changes in this area need extra care.

Schema truth is distributed

There is no single perfect schema file.

Related Docs

For more detail, read:

  • README.md
  • PROJECT_ARCHITECTURE.md
  • CURRENT_STATE.md
  • CONTRIBUTING.md
  • SCHEMA_OR_DATA_MODEL.md
  • AI_DEV_RULES.md
  • AUTH_UNIFICATION_PLAN.md
  • SCHEMA_ALIGNMENT.md

Short Summary

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.