This commit is contained in:
Linrador 2025-08-06 20:41:25 +02:00
parent 63c6c9f87a
commit 404e577983
9 changed files with 51 additions and 70 deletions

View File

@ -8,14 +8,14 @@ import { useSession } from 'next-auth/react'
import LoadingSpinner from '@/app/components/LoadingSpinner'
import TeamMemberView from '@/app/components/TeamMemberView'
import { useTeamManager } from '@/app/hooks/useTeamManager'
//import { useTeamManager } from '@/app/hooks/useTeamManager'
export default function TeamAdminClient({ teamId }: { teamId: string }) {
const [refetchKey, setRefetchKey] = useState<string>()
const { data: session } = useSession()
// jetzt wird die ID korrekt übergeben ➜ /api/team/[id]
const teamManager = useTeamManager({ teamId, refetchKey }, null)
//const teamManager = useTeamManager({ teamId, refetchKey }, null)
if (teamManager.isLoading) return <LoadingSpinner />

View File

@ -141,6 +141,32 @@ export async function POST(
data: { read: true, actionType: null, actionData: null },
})
// 1. Teamdaten laden (inkl. Leader)
const team = await prisma.team.findUnique({
where: { id: teamId },
select: { leader: true },
})
// 2. Admins holen
const admins = await prisma.user.findMany({
where: { isAdmin: true },
select: { steamId: true },
})
// 3. Zielnutzer: Leader + Admins
const targetUserIds = [
team?.leader,
...admins.map(admin => admin.steamId),
].filter(Boolean) // entfernt null/undefined
// 4. SSE senden
await sendServerSSEMessage({
type: 'team-updated',
teamId,
targetUserIds,
})
return NextResponse.json({ message: 'Einladung gelöscht' })
}

View File

@ -28,7 +28,7 @@ export default function NotificationCenter() {
const [notifications, setNotifications] = useState<Notification[]>([])
const [open, setOpen] = useState(false)
const { source, connect } = useSSE()
const { markAllAsRead, markOneAsRead, handleInviteAction } = useTeamManager({}, null)
//const { markAllAsRead, markOneAsRead, handleInviteAction } = useTeamManager({}, null)
const router = useRouter()
const [previewText, setPreviewText] = useState<string | null>(null)
const [showPreview, setShowPreview] = useState(false)

View File

@ -4,7 +4,7 @@ import { useSession } from 'next-auth/react'
import { useEffect } from 'react'
import { useSSE } from '@/app/lib/useSSEStore'
export default function SSEManager() {
export default function SSEListener() {
const { data: session } = useSession()
const connect = useSSE((s) => s.connect)
const disconnect = useSSE((s) => s.disconnect)
@ -18,6 +18,7 @@ export default function SSEManager() {
eventSource.onmessage = (event) => {
try {
console.error('[SSE] Nachricht empfangen:', event.data)
const data = JSON.parse(event.data)
switch (data.type) {

View File

@ -25,6 +25,7 @@ import Button from './Button'
import Image from 'next/image'
import TeamPremierRankBadge from './TeamPremierRankBadge'
import Link from 'next/link'
import { useWebSocketListener } from '../hooks/useWebSocketListener'
type Props = {
team: Team | null
@ -63,7 +64,7 @@ export default function TeamMemberView({
adminMode = false,
}: Props) {
const { data: session } = useSession()
const { source, connect } = useSSE()
const { source } = useSSE()
const [kickCandidate, setKickCandidate] = useState<Player | null>(null)
const [promoteCandidate, setPromoteCandidate] = useState<Player | null>(null)
@ -72,7 +73,6 @@ export default function TeamMemberView({
const canManage = adminMode || isLeader
const canInvite = isLeader && !adminMode
const canAddDirect = adminMode
//const { leaveTeam, reloadTeam, renameTeam, revokeInvitation } = useTeamManager({}, null)
const [showRenameModal, setShowRenameModal] = useState(false)
const [showDeleteModal, setShowDeleteModal] = useState(false)
const [isEditingName, setIsEditingName] = useState(false)
@ -84,11 +84,7 @@ export default function TeamMemberView({
const [saveSuccess, setSaveSuccess] = useState(false)
const [invitedPlayers, setInvitedPlayers] = useState<InvitedPlayer[]>([])
useEffect(() => {
if (session?.user?.steamId) {
connect(session.user.steamId)
}
}, [session?.user?.steamId])
useWebSocketListener('ws-team-update', () => console.log("yeah?"))
useEffect(() => {
setTeamState(team)
@ -105,61 +101,6 @@ export default function TeamMemberView({
}
}, [team])
useEffect(() => {
if (!source || !teamState?.id) return
const handleMessage = (e: MessageEvent) => {
try {
const data = JSON.parse(e.data)
const relevant = [
'team-updated',
'team-leader-changed',
'team-leader-self',
'team-member-joined',
'team-member-left',
'team-kick',
'team-kick-other',
'team-renamed',
'team-logo-updated',
]
if (data.teamId !== teamState.id || !relevant.includes(data.type)) return
/* EIN Aufruf genügt holt Team + Spieler + setzt States */
fetch(`/api/team/${encodeURIComponent(data.teamId)}`)
.then(r => r.json())
.then(({ team }) => {
if (!team) return
setTeamState(team)
setactivePlayers(team.activePlayers.sort((a: Player, b: Player) => a.name.localeCompare(b.name)))
setInactivePlayers(team.inactivePlayers.sort((a: Player, b: Player) => a.name.localeCompare(b.name)))
setInvitedPlayers(team.invitedPlayers.sort((a: Player, b: Player) => a.name.localeCompare(b.name)))
})
} catch (err) {
console.error('SSE parse error:', err)
}
}
const eventNames = [
'team-updated',
'team-leader-changed',
'team-leader-self',
'team-member-joined',
'team-member-left',
'team-kick',
'team-kick-other',
'team-renamed',
'team-logo-updated',
]
eventNames.forEach(evt => source.addEventListener(evt, handleMessage))
source.onmessage = handleMessage
return () => {
eventNames.forEach(evt => source.removeEventListener(evt, handleMessage))
source.onmessage = null
}
}, [source, teamState?.id, reloadTeam])
const handleDragStart = (event: any) => {
const id = event.active.id
const item = activePlayers.find(p => p.steamId === id) || inactivePlayers.find(p => p.steamId === id)
@ -558,7 +499,7 @@ export default function TeamMemberView({
<div className="w-full rounded-lg p-4 transition-colors min-h-[200px] border border-gray-300 dark:border-neutral-700">
<div className="grid gap-4 justify-start grid-cols-[repeat(auto-fill,minmax(160px,1fr))]">
<AnimatePresence>
{invitedPlayers.map((player: Player) => (
{invitedPlayers.map((player: InvitedPlayer) => (
<motion.div
key={player.steamId}
initial={{ opacity: 0, y: 10 }}

View File

@ -1,3 +1,5 @@
// useTeamManager.tsx
import { useEffect, useState, useImperativeHandle } from 'react'
import { Player, Team } from '../types/team'
import { useSession } from 'next-auth/react'

View File

@ -8,7 +8,7 @@ import ThemeProvider from "@/theme/theme-provider";
import Script from "next/script";
import NotificationCenter from './components/NotificationCenter'
import Navbar from "./components/Navbar";
import SSEManager from "./components/SSEManager";
import SSEListener from "./components/SSEListener";
const geistSans = Geist({
variable: "--font-geist-sans",
@ -40,7 +40,7 @@ export default function RootLayout({
disableTransitionOnChange
>
<Providers>
<SSEManager />
<SSEListener />
{/* Sidebar und Content direkt nebeneinander */}
<Sidebar>
{children}

View File

@ -1,9 +1,9 @@
// sse-server-client.ts
const host = 'localhost'
export async function sendServerSSEMessage(message: any) {
try {
console.log('[SSE Client] Nachricht senden:', message)
await fetch(`http://${host}:3001/send`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },

View File

@ -17,11 +17,22 @@ export const useSSE = create<SSEState>((set, get) => {
const source = new EventSource(`http://localhost:3001/events?steamId=${steamId}`)
source.onopen = () => {
console.log('[SSE] Verbunden!')
set({ source, isConnected: true })
}
source.onmessage = (event) => {
console.log('[SSE] Nachricht:', event.data)
try {
const data = JSON.parse(event.data)
// Zentrale Weiterleitung aller Events, die ein "type" haben
if (data?.type) {
window.dispatchEvent(new CustomEvent(`sse-${data.type}`, { detail: data }))
}
} catch (err) {
console.error('[SSE] Ungültige Nachricht:', event.data)
}
}
source.addEventListener('notification', (event) => {