Skip to content

add "find a time" scheduling flow#18

Draft
zzstoatzz wants to merge 4 commits into
flo-bit:mainfrom
zzstoatzz:schedule-flow
Draft

add "find a time" scheduling flow#18
zzstoatzz wants to merge 4 commits into
flo-bit:mainfrom
zzstoatzz:schedule-flow

Conversation

@zzstoatzz

@zzstoatzz zzstoatzz commented Apr 13, 2026

Copy link
Copy Markdown

what this does

adds a "find a time" flow — a precursor to event creation where participants coordinate availability before committing to a time.

demo: https://bsky.app/profile/zzstoatzz.io/post/3mjdqgzh5b225

the flow

  1. someone creates a "find a time" with a title, rough timeframe, and expected duration
  2. they share the link — anyone who opens it can log in and paint their availability on a grid
  3. the organizer locks availability, and the best overlapping time slots (sized to the requested duration) become votable options
  4. participants vote (or say "none of these work")
  5. the organizer picks the winner → pre-fills the existing EventEditor with the chosen time

what's NOT here

  • no new lexicons — scheduling data is stored in D1, not as atproto records
  • no changes to atproto record handling, OAuth, or Contrail
  • the only atproto record created is the final community.lexicon.calendar.event via the existing flow

reading guide

changes to existing files are minimal — 3 files touched, ~65 lines:

  • +layout.svelte — adds "find a time" nav link (5 lines)
  • create/+page.server.ts — reads prefill params from URL (14 lines)
  • create/+page.svelte — passes prefill to EventEditor + resolves scheduling intent on publish (39 lines)

new code is all in two new directories:

src/lib/scheduling/ — backend logic (566 lines)

  • types.ts — data types
  • db.ts — D1 table setup + queries (CREATE TABLE IF NOT EXISTS, same pattern as Contrail)
  • timezones.ts — UTC ↔ local timezone conversion (all slots stored as UTC, converted at display time)
  • overlap.ts — finds overlapping windows, slices to requested duration

src/lib/components/scheduling/ — UI (431 lines)

  • AvailabilityGrid.svelte — when2meet-style drag-to-paint grid with heatmap overlay
  • CreateScheduleForm.svelte — title + timeframe + duration form

src/routes/(app)/schedule/ — routes (795 lines)

  • new/ — create form
  • [id]/ — main page (grid + sidebar with avatars, overlap preview, voting, event creation)
  • api/ — JSON mutation endpoint
  • listing page for finding your previous "find a time" items

open questions

  • naming: currently "find a time" — open to alternatives
  • D1 storage: scheduling data lives alongside Contrail tables. no migration system needed since these are brand new tables, but worth discussing if there's a preferred pattern
  • access model: organizer can lock/delete, anyone with the link can respond/vote. should there be more granular control?

test plan

  • create a "find a time", share the link
  • multiple accounts paint availability, verify heatmap shows overlap
  • hover participant avatars → highlights their slots on grid
  • lock availability → overlap candidates appear as votable options
  • vote, verify "none of these work" option
  • pick winner → verify EventEditor pre-fills with correct time + timezone
  • verify timezone handling across different browser timezones

🤖 Generated with Claude Code

zzstoatzz and others added 4 commits April 12, 2026 20:56
availability collection → overlap detection → voting → event creation

- new D1 tables for scheduling requests, availability, and votes
- availability grid with drag-to-paint, heatmap overlay, timezone-aware display
- overlap algorithm slices candidates to match requested event duration
- participant avatars with hover-to-highlight individual availability
- voting phase with "none of these work" option
- resolved state links back to created event
- prefills EventEditor with winning time slot

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- add try/catch around JSON.parse for poll_options
- add try/catch around request.json() in API endpoint
- replace location.reload() with invalidateAll() for proper SvelteKit data refresh

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- delete unused SchedulingResults.svelte (inlined into [id]/+page.svelte)
- make wallClockToUtc private (only used internally by generateSlots)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
P1: add organizer-only guard to lock availability action
P1: rewrite overlap algorithm to slide across all slots and intersect
    participants, fixing missed windows at count boundaries
P1: fix DST conversion with iterative offset probing instead of
    single-probe approach that shifts by an hour at transitions
P2: single-pass participant slot grouping + concurrent profile loading
    (was O(n*m) sequential)

also fix type errors caught by svelte-check

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant