2025-09-20 21:28:10 +02:00

167 lines
5.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// /src/app/components/TeamCard.tsx
'use client'
import { useState, useMemo } from 'react'
import { useRouter } from 'next/navigation'
import Button from './Button'
import TeamPremierRankBadge from './TeamPremierRankBadge'
import type { Team } from '../types/team'
type Props = {
team: Team
currentUserSteamId: string
invitationId?: string
onUpdateInvitation: (teamId: string, newValue: string | null | 'pending') => void
adminMode?: boolean
/** Vom Page-Container gesetzt: ob der Nutzer grundsätzlich Beitritte anfragen darf
* (false, wenn /api/user ein team liefert). Default: true (abwärtskompatibel). */
canRequestJoin?: boolean
}
export default function TeamCard({
team,
currentUserSteamId,
invitationId,
onUpdateInvitation,
adminMode = false,
canRequestJoin = true,
}: Props) {
const router = useRouter()
const [joining, setJoining] = useState(false)
const isRequested = Boolean(invitationId)
// Bin ich bereits in DIESEM Team (Leader, aktiv oder inaktiv)?
const isMemberOfThisTeam = useMemo(() => {
const inActive = (team.activePlayers ?? []).some(p => String(p.steamId) === String(currentUserSteamId))
const inInactive = (team.inactivePlayers ?? []).some(p => String(p.steamId) === String(currentUserSteamId))
const isLeader = team.leader?.steamId && String(team.leader.steamId) === String(currentUserSteamId)
return Boolean(inActive || inInactive || isLeader)
}, [team, currentUserSteamId])
// Button sperren, wenn:
// - gerade Request läuft
// - bereits Mitglied dieses Teams
// - global keine Join-Anfragen erlaubt (User hat bereits ein Team)
const isDisabled = joining || isMemberOfThisTeam || !canRequestJoin
const handleClick = async () => {
if (joining || isDisabled) return
setJoining(true)
try {
if (isRequested) {
await fetch('/api/user/invitations/reject', {
method : 'POST',
headers: { 'Content-Type': 'application/json' },
body : JSON.stringify({ invitationId }),
})
onUpdateInvitation(team.id, null)
} else {
await fetch('/api/team/request-join', {
method : 'POST',
headers: { 'Content-Type': 'application/json' },
body : JSON.stringify({ teamId: team.id }),
})
onUpdateInvitation(team.id, 'pending')
}
} catch (err) {
console.error('[TeamCard] Join/Reject-Fehler:', err)
} finally {
setJoining(false)
}
}
const targetHref = adminMode ? `/admin/teams/${team.id}` : `/team/${team.id}`
// Label & Farbe abhängig vom Status
const buttonLabel = joining
? (
<>
<span
className="animate-spin inline-block size-4 border-[3px] border-current border-t-transparent rounded-full mr-1"
role="status"
aria-label="loading"
/>
Lädt
</>
)
: (!canRequestJoin || isMemberOfThisTeam)
? 'Beitritt nicht möglich'
: isRequested
? 'Angefragt (zurückziehen)'
: 'Beitritt anfragen'
const buttonColor =
isDisabled ? 'gray' : (isRequested ? 'gray' : 'blue')
return (
<div
role="button"
tabIndex={0}
onClick={() => router.push(targetHref)}
onKeyDown={e => (e.key === 'Enter') && router.push(targetHref)}
className="p-4 border rounded-lg bg-white dark:bg-neutral-800
dark:border-neutral-700 shadow-sm hover:shadow-md
transition cursor-pointer focus:outline-none
hover:scale-105 hover:bg-neutral-200 hover:dark:bg-neutral-700"
>
<div className="flex items-center justify-between gap-3 mb-3">
<div className="flex items-center gap-3">
<img
src={team.logo ? `/assets/img/logos/${team.logo}` : `/assets/img/logos/cs2.webp`}
alt={team.name ?? 'Teamlogo'}
className="w-12 h-12 rounded-full object-cover border
border-gray-200 dark:border-neutral-600"
/>
<div className="flex items-center gap-2">
<span className="font-medium truncate text-gray-800 dark:text-neutral-200">
{team.name ?? 'Team'}
</span>
<TeamPremierRankBadge players={team.activePlayers} />
</div>
</div>
{adminMode ? (
<Button
title="Verwalten"
size="md"
color="blue"
variant="solid"
onClick={e => {
e.stopPropagation()
router.push(`/admin/teams/${team.id}`)
}}
>
Verwalten
</Button>
) : (
// 👉 Button immer zeigen falls nicht möglich: disabled + anderes Label
<Button
title={typeof buttonLabel === 'string' ? buttonLabel : undefined}
size="sm"
color={buttonColor as any}
disabled={isDisabled}
onClick={e => { e.stopPropagation(); handleClick() }}
aria-disabled={isDisabled ? 'true' : undefined}
>
{buttonLabel}
</Button>
)}
</div>
<div className="flex -space-x-3">
{[...team.activePlayers, ...team.inactivePlayers].map(p => (
<img
key={p.steamId}
src={p.avatar}
alt={p.name}
title={p.name}
className="w-8 h-8 rounded-full border-2 border-white
dark:border-neutral-800 object-cover"
/>
))}
</div>
</div>
)
}