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
+18 -71
View File
@@ -2,20 +2,21 @@
import { useState } from 'react'
import { useRouter } from 'next/navigation'
import { createClient } from '@/lib/supabase/client'
import { joinRound } from '@/app/actions/rounds'
import { Button } from '@/components/ui/button'
type Round = {
id: string
name: string | null
date: string
holes_count: number
status: string
course: { name: string } | null
tee: { name: string; color: string } | null
type Props = {
roundId: string
round: {
name: string | null
date: string
holesCount: number
course: { name: string }
tee: { name: string; color: string }
}
}
export function JoinRound({ round, userId }: { round: Round; userId: string }) {
export function JoinRound({ roundId, round }: Props) {
const router = useRouter()
const [loading, setLoading] = useState(false)
const [error, setError] = useState<string | null>(null)
@@ -24,69 +25,15 @@ export function JoinRound({ round, userId }: { round: Round; userId: string }) {
setLoading(true)
setError(null)
const supabase = createClient()
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const db = supabase as any
const result = await joinRound(roundId)
// Get current user's handicap for snapshot
const { data: profile } = await db
.from('profiles')
.select('handicap_index')
.eq('id', userId)
.single()
// Get tee data for course handicap
const { data: tee } = await db
.from('tees')
.select('course_rating, slope_rating')
.eq('id', round.id) // note: round.tee.id would be better — using server action is cleaner
.single()
const { data: roundData } = await db
.from('rounds')
.select('tee_id, course:courses(par)')
.eq('id', round.id)
.single()
const { data: teeData } = roundData?.tee_id
? await db
.from('tees')
.select('course_rating, slope_rating')
.eq('id', roundData.tee_id)
.single()
: { data: null }
const handicapIndex = profile?.handicap_index ?? null
const courseHandicap =
handicapIndex !== null && teeData && roundData?.course
? Math.round(
handicapIndex * (teeData.slope_rating / 113) +
(teeData.course_rating - roundData.course.par),
)
: null
const { error: joinError } = await db.from('round_players').insert({
round_id: round.id,
user_id: userId,
role: 'player',
handicap_index: handicapIndex,
course_handicap: courseHandicap,
})
if (joinError) {
setError(joinError.message)
if (result.error) {
setError(result.error)
setLoading(false)
return
}
// Mark invite as accepted
await db
.from('round_invites')
.update({ accepted_at: new Date().toISOString() })
.eq('round_id', round.id)
.eq('email', (await supabase.auth.getUser()).data.user?.email)
router.push(`/rounds/${round.id}`)
router.push(`/rounds/${roundId}`)
}
return (
@@ -98,15 +45,15 @@ export function JoinRound({ round, userId }: { round: Round; userId: string }) {
</div>
<div className="rounded-xl border p-4 space-y-2">
<div className="font-semibold">{round.course?.name}</div>
<div className="font-semibold">{round.course.name}</div>
<div className="text-sm text-muted-foreground space-y-1">
{round.name && <div>{round.name}</div>}
<div className="flex items-center gap-2">
<div
className="h-3 w-3 rounded-full border"
style={{ backgroundColor: round.tee?.color }}
style={{ backgroundColor: round.tee.color }}
/>
{round.tee?.name} tees · {round.holes_count} holes
{round.tee.name} tees · {round.holesCount} holes
</div>
<div>{new Date(round.date).toLocaleDateString()}</div>
</div>
+15 -15
View File
@@ -12,16 +12,16 @@ import Link from 'next/link'
type Player = {
id: string
role: string
handicap_index: number | null
course_handicap: number | null
profile: { id: string; display_name: string; avatar_url: string | null } | null
handicapIndex: string | null
courseHandicap: number | null
profile: { id: string; displayName: string; avatarUrl: string | null } | null
}
type Invite = {
id: string
email: string
accepted_at: string | null
created_at: string
acceptedAt: Date | null
createdAt: Date
}
type Round = {
@@ -29,10 +29,10 @@ type Round = {
name: string | null
date: string
status: string
holes_count: number
created_by: string
course: { id: string; name: string; par: number } | null
tee: { id: string; name: string; color: string; course_rating: number; slope_rating: number } | null
holesCount: number
createdBy: string
course: { name: string } | null
tee: { name: string; color: string } | null
}
export function RoundLobby({
@@ -95,7 +95,7 @@ export function RoundLobby({
</h1>
<p className="text-xs text-muted-foreground">
{round.course?.name} · {round.tee?.name} tees ·{' '}
{round.holes_count} holes ·{' '}
{round.holesCount} holes ·{' '}
{new Date(round.date).toLocaleDateString()}
</p>
</div>
@@ -114,13 +114,13 @@ export function RoundLobby({
{players.map((p) => (
<div key={p.id} className="flex items-center gap-3 rounded-xl border p-3">
<Avatar
name={p.profile?.display_name ?? '?'}
url={p.profile?.avatar_url}
name={p.profile?.displayName ?? '?'}
url={p.profile?.avatarUrl}
/>
<div className="flex-1 min-w-0">
<div className="flex items-center gap-2">
<span className="font-medium truncate">
{p.profile?.display_name}
{p.profile?.displayName}
</span>
{p.profile?.id === currentUserId && (
<span className="text-xs text-muted-foreground">(you)</span>
@@ -132,8 +132,8 @@ export function RoundLobby({
)}
</div>
<div className="text-xs text-muted-foreground">
{p.handicap_index !== null
? `HCP ${p.handicap_index} · Course HCP ${p.course_handicap}`
{p.handicapIndex !== null
? `HCP ${p.handicapIndex} · Course HCP ${p.courseHandicap}`
: 'No handicap'}
</div>
</div>