Initial commit v1.1

This commit is contained in:
Rolf
2026-03-18 13:34:41 +01:00
parent cb04bf2ab8
commit b982584244
38 changed files with 3196 additions and 1103 deletions
+45 -36
View File
@@ -1,26 +1,29 @@
'use server'
import { z } from 'zod'
import { createClient } from '@/lib/supabase/server'
import { db } from '@/lib/db'
import { courses, tees, holes } from '@/lib/db/schema'
import { auth } from '@/lib/auth'
import { headers } from 'next/headers'
import { revalidatePath } from 'next/cache'
const TeeInput = z.object({
name: z.string().min(1, 'Tee name required'),
color: z.string().regex(/^#[0-9a-fA-F]{6}$/),
course_rating: z.coerce.number().min(50).max(80),
slope_rating: z.coerce.number().int().min(55).max(155),
name: z.string().min(1, 'Tee name required'),
color: z.string().regex(/^#[0-9a-fA-F]{6}$/),
courseRating: z.coerce.number().min(50).max(80),
slopeRating: z.coerce.number().int().min(55).max(155),
})
const HoleInput = z.object({
hole_number: z.number().int().min(1).max(18),
par: z.number().int().min(3).max(5),
stroke_index: z.number().int().min(1).max(18),
holeNumber: z.number().int().min(1).max(18),
par: z.number().int().min(3).max(5),
strokeIndex: z.number().int().min(1).max(18),
})
export const CreateCourseSchema = z.object({
name: z.string().min(1, 'Course name required').max(100),
par: z.number().int().min(27).max(90),
tees: z.array(TeeInput).min(1, 'At least one tee required'),
name: z.string().min(1, 'Course name required').max(100),
par: z.number().int().min(27).max(90),
tees: z.array(TeeInput).min(1, 'At least one tee required'),
holes: z.array(HoleInput).min(9),
})
@@ -28,35 +31,41 @@ export type CreateCourseInput = z.infer<typeof CreateCourseSchema>
export async function createCourse(data: CreateCourseInput) {
const parsed = CreateCourseSchema.safeParse(data)
if (!parsed.success) {
return { error: parsed.error.issues[0]?.message ?? 'Invalid data' }
if (!parsed.success) return { error: parsed.error.issues[0]?.message ?? 'Invalid data' }
const session = await auth.api.getSession({ headers: await headers() })
if (!session) return { error: 'Not authenticated' }
// Validate par sum equals declared course par
const parSum = parsed.data.holes.reduce((s, h) => s + h.par, 0)
if (parSum !== parsed.data.par) {
return { error: `Sum of hole pars (${parSum}) does not equal course par (${parsed.data.par})` }
}
const supabase = await createClient()
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const db = supabase as any
const {
data: { user },
} = await supabase.auth.getUser()
if (!user) return { error: 'Not authenticated' }
const [course] = await db
.insert(courses)
.values({ name: parsed.data.name, par: parsed.data.par, createdBy: session.user.id })
.returning({ id: courses.id })
const { data: course, error: courseError } = await db
.from('courses')
.insert({ name: parsed.data.name, par: parsed.data.par, created_by: user.id })
.select('id')
.single()
if (courseError) return { error: courseError.message }
await db.insert(tees).values(
parsed.data.tees.map((t) => ({
courseId: course.id,
name: t.name,
color: t.color,
courseRating: String(t.courseRating),
slopeRating: t.slopeRating,
})),
)
const { error: teesError } = await db
.from('tees')
.insert(parsed.data.tees.map((t) => ({ ...t, course_id: course.id })))
if (teesError) return { error: teesError.message }
const { error: holesError } = await db
.from('holes')
.insert(parsed.data.holes.map((h) => ({ ...h, course_id: course.id })))
if (holesError) return { error: holesError.message }
await db.insert(holes).values(
parsed.data.holes.map((h) => ({
courseId: course.id,
holeNumber: h.holeNumber,
par: h.par,
strokeIndex: h.strokeIndex,
})),
)
revalidatePath('/rounds/new')
return { courseId: course.id as string }
return { courseId: course.id }
}