ironie-nextjs/src/app/components/InvitePlayersModal.tsx
2025-08-05 23:39:54 +02:00

292 lines
9.6 KiB
TypeScript

'use client'
import { useState, useEffect } from 'react'
import Modal from './Modal'
import MiniCard from './MiniCard'
import { useSession } from 'next-auth/react'
import LoadingSpinner from './LoadingSpinner'
import { Player, Team } from '../types/team'
import Pagination from './Pagination'
import { AnimatePresence, motion } from 'framer-motion'
type Props = {
show: boolean
onClose: () => void
onSuccess: () => void
team: Team
directAdd?: boolean
}
export default function InvitePlayersModal({ show, onClose, onSuccess, team, directAdd = false }: Props) {
const { data: session } = useSession()
const steamId = session?.user?.steamId
const [allUsers, setAllUsers] = useState<Player[]>([])
const [selectedIds, setSelectedIds] = useState<string[]>([])
const [invitedIds, setInvitedIds] = useState<string[]>([])
const [isLoading, setIsLoading] = useState(false)
const [isSuccess, setIsSuccess] = useState(false)
const [sentCount, setSentCount] = useState(0)
const [searchTerm, setSearchTerm] = useState('')
const usersPerPage = 9
const [currentPage, setCurrentPage] = useState(1)
useEffect(() => {
if (show) {
fetchUsersNotInTeam()
setIsSuccess(false)
setInvitedIds([])
}
}, [show])
const fetchUsersNotInTeam = async () => {
try {
setIsLoading(true)
const res = await fetch('/api/team/available-users')
const data = await res.json()
setAllUsers(data.users || [])
} catch (err) {
console.error('Fehler beim Laden der Benutzer:', err)
} finally {
setIsLoading(false)
}
}
const handleSelect = (steamId: string) => {
setSelectedIds((prev) =>
prev.includes(steamId)
? prev.filter((id) => id !== steamId)
: [...prev, steamId]
)
}
const handleInvite = async () => {
if (selectedIds.length === 0 || !steamId) return
try {
const url = directAdd ? '/api/team/add-players' : '/api/team/invite'
const body = directAdd
? { teamId: team.id, steamIds: selectedIds }
: { teamId: team.id, userIds: selectedIds, invitedBy: steamId }
const res = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body),
})
if (!res.ok) {
const error = await res.json()
console.error('Fehler beim Einladen:', error.message)
} else {
setSentCount(selectedIds.length)
setInvitedIds(selectedIds) // 👈 Einladungsliste speichern
setIsSuccess(true)
setSelectedIds([]) // ⛔ nicht zu früh löschen!
onSuccess()
}
} catch (err) {
console.error('Fehler beim Einladen:', err)
}
}
useEffect(() => {
if (isSuccess) {
const timeout = setTimeout(() => {
const modalEl = document.getElementById('invite-members-modal')
if (modalEl && window.HSOverlay?.close) {
window.HSOverlay.close(modalEl)
}
onClose()
}, 2000)
return () => clearTimeout(timeout)
}
}, [isSuccess, onClose])
useEffect(() => {
setCurrentPage(1)
}, [selectedIds, searchTerm])
const filteredUsers = allUsers.filter(user =>
user.name?.toLowerCase().includes(searchTerm.toLowerCase())
)
const sortedUsers = [...filteredUsers].sort((a, b) => {
const aSelected = selectedIds.includes(a.steamId) ? -1 : 0
const bSelected = selectedIds.includes(b.steamId) ? -1 : 0
return aSelected - bSelected
})
const unselectedUsers = filteredUsers.filter((user) =>
!selectedIds.includes(user.steamId) &&
(!isSuccess || !invitedIds.includes(user.steamId))
)
const totalPages = Math.ceil(unselectedUsers.length / usersPerPage)
const startIdx = (currentPage - 1) * usersPerPage
const paginatedUsers = unselectedUsers.slice(startIdx, startIdx + usersPerPage)
return (
<Modal
id="invite-members-modal"
title={directAdd ? 'Spieler hinzufügen' : 'Spieler einladen'}
show={show}
onClose={onClose}
onSave={handleInvite}
closeButtonColor={isSuccess ? 'teal' : 'blue'}
closeButtonTitle={
isSuccess
? directAdd ? 'Spieler hinzugefügt' : 'Einladungen versendet'
: directAdd ? 'Hinzufügen' : 'Einladungen senden'
}
>
<p className="text-sm text-gray-700 dark:text-neutral-300 mb-2">
{directAdd
? 'Wähle Spieler aus, die du direkt zum Team hinzufügen möchtest:'
: 'Wähle Spieler aus, die du in dein Team einladen möchtest:'}
</p>
{/* Ausgewählte Benutzer anzeigen */}
{selectedIds.length > 0 && (
<div className="col-span-full">
<h3 className="text-sm font-semibold text-gray-700 dark:text-neutral-300 mb-2">
Ausgewählte Spieler:
</h3>
<div className="grid grid-cols-2 sm:grid-cols-3 gap-3 mb-2">
<AnimatePresence initial={false}>
{selectedIds.map((id) => {
const user = allUsers.find((u) => u.steamId === id)
if (!user) return null
return (
<motion.div
key={user.steamId}
layout
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.2 }}
>
<MiniCard
steamId={user.steamId}
title={user.name}
avatar={user.avatar}
location={user.location}
selected={true}
onSelect={handleSelect}
draggable={false}
currentUserSteamId={steamId!}
teamLeaderSteamId={team.leader}
hideActions={true}
rank={user.premierRank}
/>
</motion.div>
)
})}
</AnimatePresence>
</div>
</div>
)}
<input
type="text"
placeholder="Suchen..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="mt-2 w-full rounded border px-3 py-2 text-sm focus:outline-none focus:ring focus:ring-blue-400 dark:bg-neutral-800 dark:border-neutral-600 dark:text-neutral-100"
/>
{isSuccess && (
<div className="mt-2 px-4 py-2 text-sm text-green-700 bg-green-100 border border-green-200 rounded-lg">
{directAdd
? `${sentCount} Mitglied${sentCount === 1 ? '' : 'er'} hinzugefügt!`
: `${sentCount} Einladung${sentCount === 1 ? '' : 'en'} versendet!`}
</div>
)}
<div className="grid grid-cols-2 sm:grid-cols-3 gap-4 mt-4">
{isLoading ? (
<LoadingSpinner />
) : filteredUsers.length === 0 ? (
<div className="col-span-full text-center text-gray-500 dark:text-neutral-400">
{allUsers.length === 0
? directAdd
? 'Keine Benutzer verfügbar :('
: 'Niemand zum Einladen verfügbar :('
: 'Keine Benutzer gefunden.'}
</div>
) : (
<>
<AnimatePresence mode="popLayout" initial={false}>
{!isSuccess && paginatedUsers.map((user) => (
<motion.div
key={user.steamId}
layout
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.2 }}
>
<MiniCard
steamId={user.steamId}
title={user.name}
avatar={user.avatar}
location={user.location}
selected={false}
onSelect={handleSelect}
draggable={false}
currentUserSteamId={steamId!}
teamLeaderSteamId={team.leader}
hideActions={true}
rank={user.premierRank}
/>
</motion.div>
))}
{isSuccess &&
invitedIds.map((id) => {
const user = allUsers.find((u) => u.steamId === id)
if (!user) return null
return (
<motion.div
key={`invited-${user.steamId}`}
layout
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.2 }}
>
<MiniCard
steamId={user.steamId}
title={user.name}
avatar={user.avatar}
location={user.location}
selected={false}
draggable={false}
currentUserSteamId={steamId!}
teamLeaderSteamId={team.leader}
hideActions={true}
rank={user.premierRank}
message="Eingeladen"
/>
</motion.div>
)
})}
</AnimatePresence>
{ !isSuccess && (
<div className="col-span-full flex justify-center mt-2">
<Pagination
currentPage={currentPage}
totalPages={totalPages}
onPageChange={(page) => setCurrentPage(page)}
/>
</div>
)
}
</>
)}
</div>
</Modal>
)
}