'use client' import { useEffect, useMemo, useState } from 'react' import { useSession } from 'next-auth/react' import TeamCard from './TeamCard' import type { Team, Player } from '../types/team' import { useSSEStore } from '@/app/lib/useSSEStore' import { TEAM_EVENTS, INVITE_EVENTS } from '../lib/sseEvents' type Props = { initialTeams: Team[] initialInvitationMap: Record } /* helpers */ const sortPlayers = (ps: Player[] = []) => [...ps].sort((a, b) => a.steamId.localeCompare(b.steamId)) const eqPlayers = (a: Player[] = [], b: Player[] = []) => { if (a.length !== b.length) return false for (let i = 0; i < a.length; i++) if (a[i].steamId !== b[i].steamId) return false return true } const eqTeam = (a: Team, b: Team) => { if (a.id !== b.id) return false if ((a.name ?? '') !== (b.name ?? '')) return false if ((a.logo ?? '') !== (b.logo ?? '')) return false if ((a.leader as any) !== (b.leader as any)) return false return ( eqPlayers(sortPlayers(a.activePlayers), sortPlayers(b.activePlayers)) && eqPlayers(sortPlayers(a.inactivePlayers), sortPlayers(b.inactivePlayers)) ) } const eqTeamList = (a: Team[], b: Team[]) => { if (a.length !== b.length) return false const mapA = new Map(a.map(t => [t.id, t])) for (const t of b) { const x = mapA.get(t.id) if (!x || !eqTeam(x, t)) return false } return true } function parseTeamsResponse(raw: any): Team[] { if (Array.isArray(raw)) return raw as Team[] if (raw && Array.isArray(raw.teams)) return raw.teams as Team[] return [] } export default function NoTeamView({ initialTeams, initialInvitationMap }: Props) { const { data: session } = useSession() const currentSteamId = session?.user?.steamId || '' const { lastEvent } = useSSEStore() const [teams, setTeams] = useState(initialTeams) const [teamToInvitationId, setTeamToInvitationId] = useState>(initialInvitationMap) const [query, setQuery] = useState('') const [sortBy, setSortBy] = useState<'name-asc' | 'members-desc'>('name-asc') const fetchTeamsAndInvitations = async () => { try { const [teamRes, invitesRes] = await Promise.all([ fetch('/api/teams', { cache: 'no-store' }), fetch('/api/user/invitations', { cache: 'no-store' }), ]) if (!teamRes.ok || !invitesRes.ok) return const rawTeams = await teamRes.json() const rawInv = await invitesRes.json() const nextTeams: Team[] = parseTeamsResponse(rawTeams) const mapping: Record = {} for (const inv of rawInv?.invitations || []) { if (inv.type === 'team-join-request') mapping[inv.teamId] = inv.id } setTeams(prev => (eqTeamList(prev, nextTeams) ? prev : nextTeams)) setTeamToInvitationId(prev => { const same = Object.keys(prev).length === Object.keys(mapping).length && Object.keys(prev).every(k => prev[k] === mapping[k]) return same ? prev : mapping }) } catch (e) { console.error('[NoTeamView] fetch error:', e) } } useEffect(() => { fetchTeamsAndInvitations() }, []) useEffect(() => { if (!lastEvent) return const { type, payload } = lastEvent if (TEAM_EVENTS.has(type)) { if (!payload?.teamId || teams.some(t => t.id === payload.teamId)) fetchTeamsAndInvitations() return } if (INVITE_EVENTS.has(type)) fetchTeamsAndInvitations() // eslint-disable-next-line react-hooks/exhaustive-deps }, [lastEvent, teams]) const visibleTeams = useMemo(() => { const q = query.trim().toLowerCase() let list = q ? teams.filter(t => (t.name ?? '').toLowerCase().includes(q)) : teams.slice() if (sortBy === 'name-asc') { list.sort((a, b) => (a.name ?? '').localeCompare(b.name ?? '', 'de', { sensitivity: 'base' })) } else { const count = (t: Team) => (t.activePlayers?.length ?? 0) + (t.inactivePlayers?.length ?? 0) list.sort((a, b) => count(b) - count(a)) } return list }, [teams, query, sortBy]) return (
{/* Header-Panel */}
{/* Linker Rand: Orbit um linken Panelrand, rotiert vorwärts */}
{/* Rechter Rand: Orbit um rechten Panelrand, rotiert rückwärts */}

Finde dein Team

Stöbere durch die Teams oder starte selbst eins – du kannst später jederzeit wechseln.

{/* Suche */}
setQuery(e.target.value)} placeholder="Teams suchen …" className="w-full pl-10 pr-3 py-2 rounded-lg border border-gray-300 focus:border-blue-500 focus:ring-blue-500 dark:border-neutral-600 dark:bg-neutral-900 dark:text-neutral-100 text-sm" />
{visibleTeams.length} Team{visibleTeams.length === 1 ? '' : 's'} gefunden
{/* Teamliste */} {visibleTeams.length === 0 ? (
Keine Treffer. Passe die Suche oder Sortierung an — oder erstelle ein eigenes Team.
) : (
{visibleTeams.map(team => ( { setTeamToInvitationId(prev => { const updated = { ...prev } if (!newValue) delete updated[teamId] else if (newValue === 'pending') updated[teamId] = updated[teamId] ?? 'pending' else updated[teamId] = newValue return updated }) }} adminMode={false} /> ))}
)} {/* Globale Styles nur für dieses Component */}
) }