/leagues/{leagueId}
/drafts/{draftId}
status: "pending" | "active" | "complete"
leagueId: string
createdBy: uid
rounds: number
scheduledAt:Timestamp|null
pickSeconds: number // per-pick clock
startedAt: Timestamp|null
completedAt: Timestamp|null
// snake state
order: string[] // array of userIds in base order (slot 0..N-1)
current: {
round: number // 1-based
slotIndex: number // 0..N-1
direction: 1 | -1 // 1 forward, -1 backward
onClockUserId: string
pickDeadline: Timestamp // server-set deadline per pick
pickNumber: number // 1..(rounds * order.length)
}
// optional flags
allowAutoPick: boolean
pauseReason?: string
/drafts/{draftId}/participants/{uid}
displayName: string
photoURL?: string
joinedAt: Timestamp
isConnected: boolean
lastSeen: Timestamp
// optional user prerank queue for auto-pick:
prerank: string[] // ordered list of playerIds
/drafts/{draftId}/picks/{pickNumber}
pickNumber: number
round: number
slotIndex: number
userId: string
playerId: string
madeAt: Timestamp
/drafts/{draftId}/playerPool/{playerId}
name: string
team?: string
// availability
takenBy: string|null // uid
takenAt: Timestamp|null
Why playerPool under each draft? It lets you enforce “not already taken” atomically and query quickly without touching global players.
Use Realtime Database for presence (best-practice with Firebase). Cloud Function mirrors presence to Firestore participant docs.
/status/{uid} with { state: 'online'|'offline', last_changed: millis } via onDisconnect.isConnected/lastSeen updated by a function listening to RTDB.This gives accurate online state without chatty Firestore writes.
Key invariants: