'use server' import { z } from 'zod' import { createClient, createAdminClient } from '@/lib/supabase/server' import { revalidatePath } from 'next/cache' const CreateRoundSchema = z.object({ course_id: z.string().uuid(), tee_id: z.string().uuid(), name: z.string().max(100).optional(), date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/), holes_count: z.union([z.literal(9), z.literal(18)]), emails: z.array(z.string().email()).default([]), }) export type CreateRoundInput = z.infer export async function createRoundWithInvites(data: CreateRoundInput) { const parsed = CreateRoundSchema.safeParse(data) if (!parsed.success) { return { error: parsed.error.issues[0]?.message ?? 'Invalid data' } } const supabase = await createClient() const adminClient = await createAdminClient() // eslint-disable-next-line @typescript-eslint/no-explicit-any const db = supabase as any // eslint-disable-next-line @typescript-eslint/no-explicit-any const adminDb = adminClient as any const { data: { user }, } = await supabase.auth.getUser() if (!user) return { error: 'Not authenticated' } // Snapshot handicap and compute course handicap const { data: profile } = await db .from('profiles') .select('handicap_index') .eq('id', user.id) .single() const { data: tee } = await db .from('tees') .select('course_rating, slope_rating') .eq('id', parsed.data.tee_id) .single() const { data: course } = await db .from('courses') .select('par') .eq('id', parsed.data.course_id) .single() const handicapIndex: number | null = profile?.handicap_index ?? null const courseHandicap = handicapIndex !== null && tee && course ? Math.round(handicapIndex * (tee.slope_rating / 113) + (tee.course_rating - course.par)) : null // Create round const { data: round, error: roundError } = await db .from('rounds') .insert({ course_id: parsed.data.course_id, tee_id: parsed.data.tee_id, name: parsed.data.name || null, date: parsed.data.date, holes_count: parsed.data.holes_count, created_by: user.id, status: 'lobby', }) .select('id') .single() if (roundError) return { error: roundError.message } // Add creator as admin player await db.from('round_players').insert({ round_id: round.id, user_id: user.id, role: 'admin', handicap_index: handicapIndex, course_handicap: courseHandicap, }) // Send invites const failedInvites: string[] = [] for (const email of parsed.data.emails) { const redirectTo = `${process.env.SITE_URL}/auth/confirm?next=${encodeURIComponent(`/rounds/${round.id}/join`)}` const { error } = await adminDb.auth.admin.inviteUserByEmail(email, { redirectTo }) if (error) { failedInvites.push(email) } else { await db.from('round_invites').insert({ round_id: round.id, email, invited_by: user.id, }) } } return { roundId: round.id as string, failedInvites, } } export async function startRound(roundId: string) { const parsed = z.string().uuid().safeParse(roundId) if (!parsed.success) return { error: 'Invalid round ID' } const supabase = await createClient() const { data: { user }, } = await supabase.auth.getUser() if (!user) return { error: 'Not authenticated' } const { error } = await (supabase as any) .from('rounds') .update({ status: 'active' }) .eq('id', parsed.data) .eq('created_by', user.id) .eq('status', 'lobby') if (error) return { error: error.message } revalidatePath(`/rounds/${roundId}`) return { success: true } }