Skip to content

[BACKEND] Event type on the profile filter — persist event_type and fall back to classifier tags #408

Description

@linear

The event type a poster picks in Create Event never reaches the database, and the profile's My Events chips filter on a different vocabulary entirely — so a user can't filter their saved events by the type they were posted as.

Current state

Two disconnected vocabularies:

  • Create Event offers 8 types in app/components/create-event/EventDetails.tsx: general_meeting, social, career, workshop, performance, fundraiser, sports, other. app/components/create-event/OptionalExtras.tsx:57 appends event_type to the FormData.
  • **The server drops it. **server/src/routes/events.worker.ts never reads event_type, and there is no event_type column in server/schema.sql or any migration. The creator's choice goes nowhere.
  • The profile chips (shared/profileEventFilters.ts) are All / General / Academic / Social, each mapping to a set of taxonomy bucket ids and filtered with EXISTS (SELECT 1 FROM event_tags …) in server/src/routes/users.worker.ts:713 and server/src/routes/orgs.worker.ts:981.

Approach

The creator's pick is authoritative when present; the classifier's bucket covers everything else. Scraped events never go through the create flow, so the fallback is the majority path, not an edge case.

Build steps

  1. **Migration **server/migrations/0020_add_event_type.sql:
ALTER TABLE events ADD COLUMN event_type TEXT;
CREATE INDEX IF NOT EXISTS idx_events_event_type ON events (event_type);

Mirror the change into server/schema.sql or test_schema_migration_parity.ts will fail.

  1. Shared module — move the 8 event type ids out of EventDetails.tsx into shared/ (alongside profileEventFilters.ts) so the client picker, the server validator, and the filter mapping all read one list. Add two maps: event type id → chip, and taxonomy bucket id → chip.
  2. Accept it on writePOST /events and PATCH /events/:id in events.worker.ts read event_type, validate against the shared list, reject unknown values, and persist. Unset stays valid.
  3. Filter SQL — in users.worker.ts and orgs.worker.ts, prefer the column and fall back to tags:
AND (
  (e.event_type IS NOT NULL AND e.event_type IN (?, ?, …))
  OR (e.event_type IS NULL AND EXISTS (
        SELECT 1 FROM event_tags t
        WHERE t.event_id = e.id AND t.bucket_id IN (?, ?, …)
      ))
)
  1. Chips render from the shared list in app/(tabs)/profile.tsx. app/components/org/OrgEventsTab.tsx uses the same chip row and picks the change up for free — verify both.

Notes

  • **Fix the phantom buckets while in here. **ACADEMIC_BUCKETS in shared/profileEventFilters.ts is ['science', 'education', 'tech', 'business'], but the taxonomy's 12 real ids are arts, education, food, gaming, nightlife, outdoors, performing, social, spirituality, sports, tech, travel. science and business do not exist, so the Academic chip has silently been filtering on education + tech only. general is the complement of the claimed set, so the two phantoms are otherwise harmless.
  • **Known skew in the fallback. **classifyEvent in server/src/lib/classifier.ts guarantees at least one tag by falling back to social / Meetups & Mixers, which maps to the Social chip. Scraped events with thin descriptions will pile into Social. Worth a count by source before shipping — if one scraper dominates, its descriptions are the problem, not the mapping.
  • Design input needed on the chip set. The row currently holds 4 chips; the create list has 8. Confirm with design whether all 8 become chips, or whether they group into a shorter row the way the current 3 do.
  • Existing events all have event_type IS NULL, so they keep their current tag-based behaviour with no backfill. A backfill from event_tags is possible later but is not needed for this to ship.
  • Splitting this into a backend ticket (steps 1–4) and a frontend one (step 5) is reasonable if it's easier to parallelise.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions