Initial commit v1.1
This commit is contained in:
@@ -1,45 +1,45 @@
|
||||
import { createClient } from '@/lib/supabase/server'
|
||||
import { redirect } from 'next/navigation'
|
||||
import Link from 'next/link'
|
||||
import { auth } from '@/lib/auth'
|
||||
import { db } from '@/lib/db'
|
||||
import { rounds, roundPlayers, courses, tees } from '@/lib/db/schema'
|
||||
import { eq, inArray } from 'drizzle-orm'
|
||||
import { buttonVariants } from '@/components/ui/button'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { headers } from 'next/headers'
|
||||
|
||||
export default async function DashboardPage() {
|
||||
const supabase = await createClient()
|
||||
const {
|
||||
data: { user },
|
||||
} = await supabase.auth.getUser()
|
||||
const session = await auth.api.getSession({ headers: await headers() })
|
||||
if (!session) redirect('/login')
|
||||
|
||||
if (!user) redirect('/login')
|
||||
const memberships = await db
|
||||
.select({ roundId: roundPlayers.roundId })
|
||||
.from(roundPlayers)
|
||||
.where(eq(roundPlayers.userId, session.user.id))
|
||||
|
||||
type RoundRow = {
|
||||
round: {
|
||||
id: string
|
||||
name: string | null
|
||||
date: string
|
||||
status: string
|
||||
holes_count: number
|
||||
course: { name: string } | null
|
||||
tee: { name: string; color: string } | null
|
||||
} | null
|
||||
}
|
||||
const roundIds = memberships.map((m) => m.roundId)
|
||||
|
||||
// Fetch rounds the user is participating in
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const { data: rounds } = (await (supabase as any)
|
||||
.from('round_players')
|
||||
.select(
|
||||
`
|
||||
round:rounds (
|
||||
id, name, date, status, holes_count,
|
||||
course:courses (name),
|
||||
tee:tees (name, color)
|
||||
)
|
||||
`,
|
||||
)
|
||||
.eq('user_id', user.id)
|
||||
.in('round.status', ['lobby', 'active'])
|
||||
.order('round(date)', { ascending: false })) as { data: RoundRow[] | null }
|
||||
const activeRounds =
|
||||
roundIds.length > 0
|
||||
? await db
|
||||
.select({
|
||||
id: rounds.id,
|
||||
name: rounds.name,
|
||||
date: rounds.date,
|
||||
status: rounds.status,
|
||||
holesCount: rounds.holesCount,
|
||||
courseName: courses.name,
|
||||
teeName: tees.name,
|
||||
teeColor: tees.color,
|
||||
})
|
||||
.from(rounds)
|
||||
.innerJoin(courses, eq(rounds.courseId, courses.id))
|
||||
.innerJoin(tees, eq(rounds.teeId, tees.id))
|
||||
.where(inArray(rounds.id, roundIds))
|
||||
.orderBy(rounds.date)
|
||||
: []
|
||||
|
||||
const visibleRounds = activeRounds.filter((r) => r.status !== 'completed')
|
||||
|
||||
return (
|
||||
<div className="p-4 space-y-4">
|
||||
@@ -50,48 +50,47 @@ export default async function DashboardPage() {
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{!rounds?.length && (
|
||||
<p className="text-muted-foreground text-sm py-8 text-center">
|
||||
{visibleRounds.length === 0 && (
|
||||
<p className="py-8 text-center text-sm text-muted-foreground">
|
||||
No active rounds. Start one!
|
||||
</p>
|
||||
)}
|
||||
|
||||
<div className="space-y-3">
|
||||
{rounds?.map(({ round }) => {
|
||||
if (!round) return null
|
||||
return (
|
||||
<Link
|
||||
key={round.id}
|
||||
href={round.status === 'active' ? `/rounds/${round.id}/scorecard` : `/rounds/${round.id}`}
|
||||
className="block rounded-xl border p-4 space-y-1 hover:bg-muted transition-colors"
|
||||
>
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="font-semibold">{round.course?.name}</span>
|
||||
<StatusBadge status={round.status} />
|
||||
</div>
|
||||
<div className="text-sm text-muted-foreground">
|
||||
{round.name && <span>{round.name} · </span>}
|
||||
{round.tee?.name} tees · {round.holes_count} holes ·{' '}
|
||||
{new Date(round.date).toLocaleDateString()}
|
||||
</div>
|
||||
</Link>
|
||||
)
|
||||
})}
|
||||
{visibleRounds.map((round) => (
|
||||
<Link
|
||||
key={round.id}
|
||||
href={round.status === 'active' ? `/rounds/${round.id}/scorecard` : `/rounds/${round.id}`}
|
||||
className="block rounded-xl border p-4 space-y-1 transition-colors hover:bg-muted"
|
||||
>
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="font-semibold">{round.courseName}</span>
|
||||
<StatusBadge status={round.status} />
|
||||
</div>
|
||||
<div className="text-sm text-muted-foreground">
|
||||
{round.name && <span>{round.name} · </span>}
|
||||
<span
|
||||
className="inline-block h-2.5 w-2.5 rounded-full border align-middle mr-1"
|
||||
style={{ backgroundColor: round.teeColor }}
|
||||
/>
|
||||
{round.teeName} · {round.holesCount} holes ·{' '}
|
||||
{new Date(round.date).toLocaleDateString()}
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function StatusBadge({ status }: { status: string }) {
|
||||
const styles = {
|
||||
lobby: 'bg-yellow-100 text-yellow-800',
|
||||
active: 'bg-green-100 text-green-800',
|
||||
const styles: Record<string, string> = {
|
||||
lobby: 'bg-yellow-100 text-yellow-800',
|
||||
active: 'bg-green-100 text-green-800',
|
||||
completed: 'bg-gray-100 text-gray-600',
|
||||
}
|
||||
return (
|
||||
<span
|
||||
className={`text-xs font-medium px-2 py-0.5 rounded-full ${styles[status as keyof typeof styles] ?? styles.completed}`}
|
||||
>
|
||||
<span className={`rounded-full px-2 py-0.5 text-xs font-medium ${styles[status] ?? styles.completed}`}>
|
||||
{status}
|
||||
</span>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user