101 lines
3.2 KiB
TypeScript
101 lines
3.2 KiB
TypeScript
// /src/app/(admin)/admin/teams/[teamId]/TeamAdminClient.tsx
|
|
'use client'
|
|
|
|
import { useCallback, useEffect, useState, useRef } from 'react'
|
|
import { useSession } from 'next-auth/react'
|
|
import LoadingSpinner from '@/app/components/LoadingSpinner'
|
|
import TeamMemberView from '@/app/components/TeamMemberView'
|
|
import { useTeamStore } from '@/app/lib/stores'
|
|
import { reloadTeam } from '@/app/lib/sse-actions'
|
|
import type { Player } from '@/app/types/team'
|
|
|
|
type Props = { teamId: string }
|
|
|
|
export default function TeamAdminClient({ teamId }: Props) {
|
|
const { team, setTeam } = useTeamStore()
|
|
const { data: session } = useSession()
|
|
const currentUserSteamId = session?.user?.steamId || ''
|
|
const [loading, setLoading] = useState(true)
|
|
const [activeDragItem, setActiveDragItem] = useState<Player | null>(null)
|
|
const [isDragging, setIsDragging] = useState(false)
|
|
const [showLeaveModal, setShowLeaveModal] = useState(false)
|
|
const [showInviteModal, setShowInviteModal] = useState(false)
|
|
|
|
|
|
const fetchTeam = useCallback(async () => {
|
|
const result = await reloadTeam(teamId)
|
|
if (result) setTeam(result)
|
|
setLoading(false)
|
|
}, [teamId, setTeam])
|
|
|
|
useEffect(() => {
|
|
if (teamId) fetchTeam()
|
|
}, [teamId, fetchTeam])
|
|
|
|
// 👇 WICHTIG: subscribe by steamId (passt zu deinem SSE-Server)
|
|
useEffect(() => {
|
|
const steamId = session?.user?.steamId
|
|
if (!steamId) return
|
|
|
|
// ggf. .env nutzen: z. B. NEXT_PUBLIC_SSE_URL=http://localhost:3001
|
|
const base = process.env.NEXT_PUBLIC_SSE_URL ?? 'http://localhost:3001'
|
|
const url = `${base}/events?steamId=${encodeURIComponent(steamId)}`
|
|
let es: EventSource | null = new EventSource(url, { withCredentials: false })
|
|
|
|
const onTeamUpdated = (ev: MessageEvent) => {
|
|
try {
|
|
const msg = JSON.parse(ev.data)
|
|
if (msg.teamId === teamId) {
|
|
fetchTeam()
|
|
}
|
|
} catch (e) {
|
|
console.error('[SSE] parse error:', e)
|
|
}
|
|
}
|
|
|
|
es.addEventListener('team-updated', onTeamUpdated)
|
|
|
|
es.onerror = () => {
|
|
// sanftes Reconnect
|
|
es?.close()
|
|
es = null
|
|
setTimeout(() => {
|
|
// neuer EventSource
|
|
const next = new EventSource(url, { withCredentials: false })
|
|
next.addEventListener('team-updated', onTeamUpdated)
|
|
next.onerror = () => { next.close() }
|
|
es = next
|
|
}, 2000)
|
|
}
|
|
|
|
return () => {
|
|
es?.removeEventListener('team-updated', onTeamUpdated as any)
|
|
es?.close()
|
|
}
|
|
}, [session?.user?.steamId, teamId, fetchTeam])
|
|
|
|
if (loading || !team) return <LoadingSpinner />
|
|
|
|
return (
|
|
<div className="mx-auto">
|
|
<TeamMemberView
|
|
key={
|
|
team
|
|
? `${team.id}|A:${team.activePlayers.map(p=>p.steamId).join(',')}|I:${team.inactivePlayers.map(p=>p.steamId).join(',')}|V:${team.invitedPlayers.map(p=>p.steamId).join(',')}`
|
|
: 'no-team'
|
|
}
|
|
currentUserSteamId={currentUserSteamId}
|
|
adminMode
|
|
activeDragItem={activeDragItem}
|
|
isDragging={isDragging}
|
|
showLeaveModal={showLeaveModal}
|
|
showInviteModal={showInviteModal}
|
|
setShowLeaveModal={setShowLeaveModal}
|
|
setShowInviteModal={setShowInviteModal}
|
|
setActiveDragItem={setActiveDragItem}
|
|
setIsDragging={setIsDragging}
|
|
/>
|
|
</div>
|
|
)
|
|
}
|