Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,10 @@ yarn-error.log*

# secrets
secrets.md
notes/

# one-off scripts
scripts/migrate-itinerary-ids.ts

# generated reports
ga4-dashboard.html

# typescript
*.tsbuildinfo
next-env.d.ts
Expand Down
632 changes: 501 additions & 131 deletions handoff.md

Large diffs are not rendered by default.

10 changes: 0 additions & 10 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
"mapbox-gl": "^3.18.1",
"next": "^16.1.7",
"openai": "^6.34.0",
"qrcode.react": "^4.2.0",
"react": "19.2.3",
"react-dom": "19.2.3",
"react-map-gl": "^8.1.0",
Expand Down
236 changes: 236 additions & 0 deletions plans/ad-placements.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
# Ad Placement Plan for plan.wtf

## Top 5 Placements by ROI

| Rank | Placement | Revenue | Disruption | Effort |
|------|-----------|---------|------------|--------|
| 1 | **Sponsored Event Cards (ListView)** | HIGH | LOW | LOW-MED |
| 2 | **Enhanced SponsorsTicker** | MEDIUM | VERY LOW | VERY LOW |
| 3 | **Featured Event Upsell (Submit Modal)** | HIGH | LOW | MEDIUM |
| 4 | **Sponsored Map Pins** | HIGH | LOW-MED | MEDIUM |
| 5 | **Itinerary Page Sponsor Banner** | MED-HIGH | LOW | LOW |

---

## All Placement Opportunities

### 1. Sponsored Event Cards in ListView (Native Ad)

**Location:** `src/components/ListView.tsx`, injected between real `EventCard` components within each date group. Insert after every 5th-8th card inside the `{group.events.map(...)}` block (~line 113).

**Format:** Native ad identical to `EventCard` layout (OG image, name, time, address, tags) with a subtle "Sponsored" badge. Highest-value placement β€” blends naturally with real content.

**Size:** Same as EventCard (~120px height mobile, full content width ~768px).

**UX Disruption:** LOW. Users are already scanning cards; a labeled "Sponsored" card is minimally disruptive. Limit to 1 per date group or 1 per 5-8 events.

**Revenue Potential:** HIGH. Conference organizers and sponsors will pay premium rates for this exact audience.

**Implementation:** Create `SponsoredEventCard` wrapping `EventCard` with "Sponsored" label. Data source: Supabase table or constants. Insert into ListView map loop with index-based gating.

---

### 2. Enhanced SponsorsTicker (Existing Component)

**Location:** `src/components/SponsorsTicker.tsx` β€” already rendered between Header and FilterBar in EventApp.tsx (line 379).

**Currently:** Simple scrolling text ticker with one sponsor ("Stand With Crypto"). Hardcoded array.

**Enhancement:** Expand to support multiple sponsors with logos, clickable CTAs, rotation. Support per-conference sponsors (different for SXSW vs ETHCC).

**Size:** Current ~28px height, could expand to ~36px with small logos.

**UX Disruption:** VERY LOW. Already exists and accepted by users.

**Revenue Potential:** MEDIUM. Good for "presented by" brand awareness.

**Implementation:** VERY LOW effort β€” just expand the sponsors array and optionally add image support.

---

### 3. Featured Event Upsell (Submit Modal)

**Location:** `src/components/SubmitEventModal.tsx`, on the success step (step 3, ~line 504).

**Format:** After success confirmation: "Want to feature your event? Promote it to the top of listings for $X" with CTA. Could also be a checkbox in step 2: "Feature this event ($X)".

**Size:** Small card ~80-100px within modal.

**UX Disruption:** LOW. Only shown to event submitters (organizers), not browsers.

**Revenue Potential:** HIGH. Direct revenue from organizers. Proven model (Eventbrite, Meetup). Even $10-50/event adds up with hundreds of events per conference.

**Implementation:** MEDIUM. Requires payment flow (Stripe), "featured" flag in data model, rendering logic to boost featured events.

---

### 4. Sponsored Map Pins

**Location:** `src/components/MapView.tsx`, rendered as additional `Marker` components after the event markers block (~line 473).

**Format:** Branded map pin with sponsor logo or distinct color + "AD" indicator. Clicking opens styled `EventPopup` with sponsor branding + CTA.

**Size:** Same as regular markers (19px pin) with branded label.

**UX Disruption:** LOW-MEDIUM. Users expect pins on maps. Must limit to 3-5 sponsored pins max to avoid clutter.

**Revenue Potential:** HIGH. Location-based ads during conferences are extremely valuable for venue sponsors, afterparties, nearby businesses.

**Implementation:** MEDIUM. New `SponsoredMapMarker` component, data source with lat/lng, popup integration.

---

### 5. Itinerary Page Sponsor Banner

**Location:** `src/app/itinerary/page.tsx` (~line 364) and shared itinerary page `src/app/itinerary/s/[code]/page.tsx` (~line 233).

**Format:** "Your itinerary is powered by [Sponsor]" or "Conference shuttle sponsored by [Sponsor] β€” book a ride" with CTA.

**Size:** Full content width, 60-80px tall.

**UX Disruption:** LOW. Low-traffic page; single banner feels natural.

**Revenue Potential:** MEDIUM-HIGH for shared itineraries β€” when users share links, recipients see the sponsor (viral distribution).

**Implementation:** LOW. Simple banner component.

---

### 6. Interstitial Between Date Groups in ListView

**Location:** `src/components/ListView.tsx`, between `<section>` elements for date groups (~line 97). Insert after every 2nd-3rd date group.

**Format:** Horizontal leaderboard-style banner with sponsor branding, message, CTA. Dark theme styled.

**Size:** Full content width, 80-120px tall.

**UX Disruption:** LOW-MEDIUM. Natural transition point between date groups.

**Revenue Potential:** MEDIUM.

**Implementation:** LOW.

---

### 7. Sponsored Rows in TableView

**Location:** `src/components/TableView.tsx`, within `DateGroup` component (~line 401). Insert sponsored row at top of each date section.

**Format:** Table row styled like regular rows but with subtle background highlight (`bg-orange-500/5 border-l-2 border-orange-500`) and "Sponsored" tag.

**Size:** Same as regular table row (~40px).

**UX Disruption:** LOW. TableView users are power users who scan quickly.

**Revenue Potential:** MEDIUM.

**Implementation:** LOW.

---

### 8. Event Popup Contextual Ad

**Location:** `src/components/EventPopup.tsx`, after the note block (~line 206) in `SingleEventContent`.

**Format:** Small contextual line: "Nearby: [Sponsor Venue]" or "Getting there? Book a ride with [Sponsor]".

**Size:** Single line, ~20-24px tall.

**UX Disruption:** LOW-MEDIUM. Popup is compact (300px); must be very minimal.

**Revenue Potential:** MEDIUM. Contextual/location-aware ads command higher CPCs.

**Implementation:** MEDIUM. Requires location-aware ad matching.

---

### 9. Desktop Sidebar (Map View)

**Location:** `src/components/EventApp.tsx`, alongside MapView (~line 411). Add 300px sidebar on `lg:` breakpoint.

**Format:** Vertical tower (300x600) or stacked sponsor cards. "Featured Events" or sponsor info.

**Size:** 300px wide, desktop only.

**UX Disruption:** MEDIUM. Reduces map width on desktop.

**Revenue Potential:** MEDIUM.

**Implementation:** MEDIUM.

---

### 10. API Docs Page Sponsorship

**Location:** `src/app/api/page.tsx` hero section (~line 205) or footer (~line 1131).

**Format:** "Powered by [Sponsor]" or "Build with [Sponsor's API]" banner.

**UX Disruption:** VERY LOW. **Revenue Potential:** LOW. **Implementation:** VERY LOW.

---

## Things to Avoid

1. **Full-screen interstitials/modals** β€” utility app used actively during conferences on slow mobile. Blocking modals = abandonment.
2. **Ads in FilterBar or header** β€” primary navigation. Ads here feel like malware.
3. **Auto-playing video** β€” drains battery/data on mobile conference networks.
4. **Ads in AuthModal or profile** β€” trust-critical flows. Ads feel predatory.
5. **Excessive sponsored map pins** β€” more than 3-5 clutters the map (primary value prop).
6. **Ads in FriendsPanel/social features** β€” social features drive engagement; monetizing them kills stickiness.
7. **Pop-unders or redirects** β€” crypto audience is ad-averse and technically savvy. Deceptive patterns get called out on Twitter.

---

## Monetization Models

### Per-Conference Sponsorship Tiers

| Tier | Price Range | Includes |
|------|-------------|----------|
| **Presenting** | $5,000-15,000 | Ticker (top billing), 5 sponsored cards, 3 map pins, itinerary banner, logo on shared itineraries |
| **Gold** | $2,000-5,000 | Ticker, 3 sponsored cards, 1 map pin |
| **Silver** | $500-2,000 | Ticker listing, 1 sponsored card |
| **Featured Event** | $25-100/event | Highlighted card, boosted position |

### Self-Serve (Future)

| Model | Placement | Pricing |
|-------|-----------|---------|
| CPC | Sponsored cards, map pins | $0.50-2.00/click |
| CPM | Banners, ticker | $5-15 CPM |
| Flat Rate | Featured event listing | $25-100/event/conference |
| Affiliate | Ride-share links in popups | Revenue share |

**Recommended start:** Per-conference sponsorship tiers (manual sales). Simplest to implement, most lucrative for niche captive audience during conference windows.

---

## Implementation Phases

**Phase 1 β€” Immediate (0 effort):**
- Expand SponsorsTicker with more paying sponsors
- Add per-conference sponsor filtering

**Phase 2 β€” Sprint 1 (1-2 days):**
- `SponsoredEventCard` component (EventCard wrapper + "Sponsored" badge)
- Sponsored event data source (Supabase table or constants)
- Inject into ListView
- GA4 tracking for impressions/clicks

**Phase 3 β€” Sprint 2 (2-3 days):**
- "Feature this event" upsell in SubmitEventModal
- Itinerary page sponsor banner
- Sponsored row support in TableView

**Phase 4 β€” Sprint 3 (3-5 days):**
- `SponsoredMapMarker` component
- Sponsored pins in MapView
- Contextual sponsor link in EventPopup
- Admin interface for managing sponsor data

**Phase 5 β€” Future:**
- Self-serve ad submission portal
- Stripe integration for featured event payments
- Desktop sidebar ads
- Sponsor analytics dashboard (impressions, clicks, CTR)
58 changes: 58 additions & 0 deletions plans/submit-form-improvements.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Submit Event Form Improvements

## 4 Changes

### 1. Date/time fields β†’ use DateTimePicker dropdowns
**Current**: Plain text inputs (`"Feb 16"`, `"7:00 PM"`)
**Target**: Reuse the `DateTimePicker` component from FilterBar (date dropdown + 30-min time dropdown)

The submit form needs a slightly different setup than the filter:
- **Date**: Single date dropdown (not a datetime range), scoped to selected conference dates
- **Start Time**: Standalone time dropdown (30-min intervals)
- **End Time**: Standalone time dropdown (30-min intervals)

We need to extract the `Dropdown` sub-component and `TIME_OPTIONS`/format helpers from `DateTimePicker.tsx` so the submit form can use date-only and time-only dropdowns independently.

**Changes**:
- `src/components/DateTimePicker.tsx` β€” Export `Dropdown`, `TIME_OPTIONS`, `formatDateShort`, `format12Hour`
- `src/components/SubmitEventModal.tsx` β€” Replace text inputs with Dropdown components. Store date as ISO `"2026-03-10"` and times as 24h `"19:00"`. Convert to display format `"Mar 10"` / `"7:00 PM"` for the sheet on submit.

### 2. Address β†’ Google Places autocomplete (from rsvpizza)
**Current**: Plain text input
**Target**: Google Places autocomplete dropdown

Create `src/components/AddressAutocomplete.tsx` adapted from rsvpizza's `LocationAutocomplete.tsx`:
- Simplified for sheeets (no timezone, no city data, no venue name)
- Uses `NEXT_PUBLIC_GOOGLE_MAPS_API_KEY` (Next.js convention)
- Falls back to plain text input if no API key
- Styled to match sheeets dark theme (slate-900 bg, slate-600 border, orange focus)
- Types: `['geocode', 'establishment']`

**Also need**: `npm install -D @types/google.maps` for TypeScript support

**Env var**: Add `NEXT_PUBLIC_GOOGLE_MAPS_API_KEY` to Vercel (can reuse same GCP project as rsvpizza). Component works as plain input until key is added.

### 3. Auto-fetch on Luma URL paste
**Current**: User pastes URL, clicks "Fetch Event" button
**Target**: Auto-fetch when a valid Luma URL is pasted or typed

In `SubmitEventModal.tsx`:
- Add a `useEffect` watching `lumaUrl` β€” when it matches a Luma URL pattern (`lu.ma/` or `luma.com/`), auto-trigger `handleFetchLuma()` after a short debounce (300ms)
- Keep the "Fetch Event" button as fallback
- Show loading state immediately on paste

### 4. Luma API: fall back to `geo_address_info` for guests-only addresses
**Current**: Only checks `geo_address_json` (null for guests-only events)
**Target**: Fall back to `geo_address_info.city_state` or `geo_address_info.city`

In `src/app/api/luma/route.ts`:
- After checking `geo_address_json`, also check `geo_address_info`
- Use `geo_address_info.full_address` β†’ `geo_address_info.city_state` β†’ `geo_address_info.city` as fallbacks
- This gives at least city-level location for guests-only events

## Files to modify/create
1. `src/components/DateTimePicker.tsx` β€” Export internals
2. `src/components/AddressAutocomplete.tsx` β€” NEW (adapted from rsvpizza)
3. `src/components/SubmitEventModal.tsx` β€” All 4 changes converge here
4. `src/app/api/luma/route.ts` β€” geo_address_info fallback
5. `package.json` β€” Add `@types/google.maps` dev dependency
Loading