import type { GroupRideSummary } from '../components/group-rides/types'; export interface GroupRidePayloadContext { rideId: string; uid: string; leadHandle: string; leadDisplayName: string; now: Date; } export type WebGroupRideInput = GroupRideSummary & { visibility?: string; clubId?: string; clubName?: string; }; /** * Builds the Firestore `group_rides/{id}` document for a ride created from the web hub. * * The web create form captures a free-text rally point only (there is no map picker or * geocoder), so the meeting point stores `displayName`/`address` text and deliberately omits * `latitude`/`longitude`. We never fabricate coordinates: a missing coordinate means * "location not pinned yet", which `mapFirestoreRideToSummary` already handles (it derives * `meetingPointCoords` only when both lat and lon are present). * * Pure and dependency-free so it can be unit-tested without Firebase. */ export function buildWebGroupRidePayload( newRide: WebGroupRideInput, ctx: GroupRidePayloadContext ): Record { const { rideId, uid, leadHandle, leadDisplayName, now } = ctx; let scheduledStartAt = new Date(now.getTime() + 3600000).toISOString(); let departureAt = new Date(now.getTime() + 3600000 + 1800000).toISOString(); if (newRide.scheduledDate) { const parsed = new Date(`${newRide.scheduledDate} ${newRide.scheduledTime}`); if (!isNaN(parsed.getTime()) && parsed.getTime() > now.getTime() - 86400000) { scheduledStartAt = parsed.toISOString(); departureAt = new Date(parsed.getTime() + 30 * 60000).toISOString(); } } const payload: Record = { id: rideId, organizerUid: uid, organizerHandle: leadHandle, organizerDisplayName: leadDisplayName, title: newRide.title.trim(), description: newRide.description?.trim() || '', visibility: newRide.visibility || 'public', joinPolicy: newRide.joinPolicy || 'requestToJoin', status: 'scheduled', scheduledStartAt, departureAt, participantLimit: Number(newRide.participantLimit) || 12, participantCount: 1, accessUids: [uid], meetingPoint: { displayName: newRide.meetingPoint || newRide.region || 'Designated Rally Point', address: newRide.region || '', locationType: 'meetup', notes: '', }, stops: [], createdAt: now.toISOString(), updatedAt: now.toISOString(), paceClassification: newRide.paceClassification || 'Brisk Pace', discipline: newRide.discipline || 'Sport / Canyon', formationProfile: newRide.formationProfile || 'Staggered Dual Column [B]', routeTitle: newRide.routeSnippet || '', }; if (newRide.clubId) { payload.clubId = newRide.clubId; if (newRide.clubName) payload.clubName = newRide.clubName; } return payload; }