This commit is contained in:
Linrador 2025-10-10 13:51:20 +02:00
parent c4c714e5ca
commit b2d718738e
18 changed files with 603 additions and 151 deletions

View File

@ -113,6 +113,11 @@ model FaceitGameStat {
@@index([game, elo])
}
enum TeamJoinPolicy {
REQUEST
INVITE_ONLY
}
model Team {
id String @id @default(uuid())
name String @unique
@ -137,6 +142,9 @@ model Team {
schedulesAsTeamB Schedule[] @relation("ScheduleTeamB")
mapVoteSteps MapVoteStep[] @relation("VoteStepTeam")
// Default bleibt REQUEST
joinPolicy TeamJoinPolicy @default(REQUEST)
}
model TeamInvite {

View File

@ -1,3 +1,5 @@
// /src/app/[locale]/components/NoTeamView.tsx
'use client'
import { useEffect, useMemo, useState } from 'react'
@ -24,6 +26,7 @@ const eqTeam = (a: Team, b: Team) => {
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
if (a.joinPolicy !== b.joinPolicy) return false
return (
eqPlayers(sortPlayers(a.activePlayers), sortPlayers(b.activePlayers)) &&
eqPlayers(sortPlayers(a.inactivePlayers), sortPlayers(b.inactivePlayers))
@ -47,7 +50,7 @@ function parseTeamsResponse(raw: any): Team[] {
export default function NoTeamView({ initialTeams, initialInvitationMap }: Props) {
const { data: session } = useSession()
const currentSteamId = session?.user?.steamId || ''
const { lastEvent } = useSSEStore()
const { connect, isConnected, lastEvent } = useSSEStore()
const [teams, setTeams] = useState<Team[]>(initialTeams)
const [teamToInvitationId, setTeamToInvitationId] = useState<Record<string, string>>(initialInvitationMap)
@ -82,7 +85,12 @@ export default function NoTeamView({ initialTeams, initialInvitationMap }: Props
}
}
useEffect(() => {
if (currentSteamId && !isConnected) connect(currentSteamId)
}, [currentSteamId, isConnected, connect])
useEffect(() => { fetchTeamsAndInvitations() }, [])
useEffect(() => {
if (!lastEvent) return
const { type, payload } = lastEvent

View File

@ -1,11 +1,15 @@
// /src/app/components/TeamCard.tsx
'use client'
import { useState, useMemo } from 'react'
import { useState, useMemo, useEffect } from 'react'
import { useRouter } from 'next/navigation'
import Button from './Button'
import TeamPremierRankBadge from './TeamPremierRankBadge'
import type { Team } from '../../../types/team'
import type { Team, TeamJoinPolicy } from '../../../types/team'
// ⬇️ NEU: SSE-Hooks / Type-Guard
import { useSSEStore } from '@/lib/useSSEStore'
import { isSseEventType } from '@/lib/sseEvents'
type Props = {
team: Team
@ -13,8 +17,6 @@ type Props = {
invitationId?: string
onUpdateInvitation: (teamId: string, newValue: string | null | 'pending') => void
adminMode?: boolean
/** (historisch) Ob Join-Anfragen grundsätzlich erlaubt sind.
* Mehrere Teams sind jetzt erlaubt diese Prop wird nicht mehr zum Sperren verwendet. */
canRequestJoin?: boolean
}
@ -24,43 +26,84 @@ export default function TeamCard({
invitationId,
onUpdateInvitation,
adminMode = false,
canRequestJoin = true, // bleibt für Abwärtskompatibilität erhalten, hat aber keinen Einfluss mehr auf disabled
canRequestJoin = true,
}: Props) {
const router = useRouter()
const [joining, setJoining] = useState(false)
const isRequested = Boolean(invitationId)
// ⬇️ NEU: SSE
const { connect, lastEvent, isConnected } = useSSEStore()
// ⬇️ NEU: lokale, “wirksame” Policy startet mit Prop
const [effectivePolicy, setEffectivePolicy] = useState<TeamJoinPolicy>(team.joinPolicy)
// Wenn sich Props ändern (neues Team oder Server-Refetch), Policy nachziehen
useEffect(() => {
setEffectivePolicy(team.joinPolicy)
}, [team.id, team.joinPolicy])
// SSE-Verbindung herstellen
useEffect(() => {
if (!currentUserSteamId) return
if (!isConnected) connect(currentUserSteamId)
}, [currentUserSteamId, isConnected, connect])
// Auf team-updated hören und ggf. Policy übernehmen
useEffect(() => {
if (!lastEvent || !isSseEventType(lastEvent.type)) return
if (lastEvent.type !== 'team-updated') return
const payload = lastEvent.payload ?? {}
if (payload.teamId !== team.id) return
const jp = payload.joinPolicy as TeamJoinPolicy | undefined
if (jp === 'REQUEST' || jp === 'INVITE_ONLY') {
setEffectivePolicy(jp)
}
}, [lastEvent, team.id])
// ── Stati ableiten (jetzt von effectivePolicy!)
const isInviteOnly = effectivePolicy === 'INVITE_ONLY'
const hasRealInvitation = Boolean(invitationId && invitationId !== 'pending')
const hasPendingRequest = invitationId === 'pending'
const isRequested = hasRealInvitation || hasPendingRequest
// 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))
// robust: leader?.steamId ODER leaderId unterstützen
const leaderSteamId = team.leader?.steamId ?? (team as any).leaderId
const isLeader = leaderSteamId && String(leaderSteamId) === String(currentUserSteamId)
const isLeader = team.leader?.steamId && String(team.leader.steamId) === String(currentUserSteamId)
return Boolean(inActive || inInactive || isLeader)
}, [team, currentUserSteamId])
// ❗Mehrere Teams erlaubt → NICHT mehr wegen "hat schon Team" blocken
// Gesperrt nur, wenn bereits Mitglied DIESES Teams oder Request läuft
const isDisabled = joining || isMemberOfThisTeam
const isDisabled =
joining ||
isMemberOfThisTeam ||
(isInviteOnly && !hasRealInvitation && !hasPendingRequest)
const handleClick = async () => {
if (joining || isDisabled) return
setJoining(true)
try {
if (isRequested) {
if (hasRealInvitation) {
await fetch('/api/user/invitations/reject', {
method : 'POST',
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body : JSON.stringify({ invitationId }),
body: JSON.stringify({ invitationId }),
})
onUpdateInvitation(team.id, null)
} else if (hasPendingRequest) {
await fetch('/api/user/invitations/revoke', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ teamId: team.id, type: 'team-join-request' }),
})
onUpdateInvitation(team.id, null)
} else {
if (isInviteOnly) return
await fetch('/api/team/request-join', {
method : 'POST',
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body : JSON.stringify({ teamId: team.id }),
body: JSON.stringify({ teamId: team.id }),
})
onUpdateInvitation(team.id, 'pending')
}
@ -73,26 +116,21 @@ export default function TeamCard({
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
</>
)
: isMemberOfThisTeam
? 'Schon Mitglied'
: isRequested
? 'Angefragt (zurückziehen)'
: 'Beitritt anfragen'
const buttonLabel = joining ? (
<>
<span className="animate-spin inline-block size-4 border-[3px] border-current border-t-transparent rounded-full mr-1" />
Lädt
</>
) : isMemberOfThisTeam ? 'Schon Mitglied'
: hasRealInvitation ? 'Einladung ablehnen'
: hasPendingRequest ? 'Angefragt (zurückziehen)'
: isInviteOnly ? 'Nur Einladungen'
: 'Beitritt anfragen'
const buttonColor =
isDisabled ? 'gray' : (isRequested ? 'gray' : 'blue')
hasRealInvitation ? 'red' :
isDisabled ? 'gray' :
(isRequested ? 'gray' : 'blue')
return (
<div

View File

@ -1,4 +1,4 @@
// TeamMemberView.tsx
// /src/app/[locale]/components/TeamMemberView.tsx
'use client'
@ -12,14 +12,13 @@ import SortableMiniCard from './SortableMiniCard'
import LeaveTeamModal from './LeaveTeamModal'
import InvitePlayersModal from './InvitePlayersModal'
import Modal from './Modal'
import type { Player, InvitedPlayer } from '@/types/team'
import type { Player, InvitedPlayer, Team, TeamJoinPolicy } from '@/types/team'
import { AnimatePresence, motion } from 'framer-motion'
import { leaveTeam, reloadTeam, renameTeam } from '@/lib/sse-actions'
import Button from './Button'
import NextImage from 'next/image'
import TeamPremierRankBadge from './TeamPremierRankBadge'
import Link from 'next/link'
import { Team } from '../../../types/team'
import { useTeamStore } from '@/lib/stores'
import { useSSEStore } from '@/lib/useSSEStore'
import {
@ -59,6 +58,9 @@ export default function TeamMemberView({
const { team: storeTeam, setTeam } = useTeamStore()
const team = teamProp ?? storeTeam
if (!team) return null
const teamId = team.id
const teamLeaderSteamId = team.leader?.steamId ?? ''
const RELEVANT: ReadonlySet<SSEEventType> = new Set([...TEAM_EVENTS, ...SELF_EVENTS])
@ -85,6 +87,12 @@ export default function TeamMemberView({
const [editedName, setEditedName] = useState(team.name || '')
const [saveSuccess, setSaveSuccess] = useState(false)
const [joinPolicy, setJoinPolicy] = useState<TeamJoinPolicy>(
(team as any).joinPolicy ?? 'REQUEST'
)
const [savingPolicy, setSavingPolicy] = useState(false)
const [policySaved, setPolicySaved] = useState(false)
const [inviteKey, setInviteKey] = useState(0)
const openInvite = () => {
setInviteKey(k => k + 1) // erzwingt frischen Mount
@ -121,6 +129,10 @@ export default function TeamMemberView({
return aa === bb
}
useEffect(() => {
setJoinPolicy(((team as any)?.joinPolicy ?? 'REQUEST') as TeamJoinPolicy)
}, [team?.id, (team as any)?.joinPolicy])
// Team-Listen lokal synchronisieren
useEffect(() => {
if (!team) return
@ -148,7 +160,7 @@ export default function TeamMemberView({
// Relevante SSE-Events
useEffect(() => {
if (!lastEvent || !team?.id) return
if (!lastEvent || !teamId) return
if (!isSseEventType(lastEvent.type)) return
const payload = lastEvent.payload ?? {}
@ -169,7 +181,7 @@ export default function TeamMemberView({
if (payload.teamId && payload.teamId !== team.id) return
;(async () => {
const updated = await reloadTeam(team.id)
const updated = await reloadTeam(teamId)
if (!updated) return
setTeam(updated)
setEditedName(updated.name || '')
@ -188,7 +200,7 @@ export default function TeamMemberView({
setInvitedPlayers(nextInvited)
setRemountKey(k => k + 1)
})()
}, [lastEvent, team?.id, setTeam])
}, [lastEvent, teamId, setTeam])
const handleDragStart = (event: any) => {
const id = event.active.id as string
@ -202,6 +214,31 @@ export default function TeamMemberView({
}
}
const [showPolicyMenu, setShowPolicyMenu] = useState(false)
const policyMenuRef = useRef<HTMLDivElement>(null)
const applyPolicy = async (p: TeamJoinPolicy) => {
if (p === joinPolicy) { setShowPolicyMenu(false); return }
setJoinPolicy(p) // optimistisch
await saveJoinPolicy(p) // serverseitig speichern
setShowPolicyMenu(false)
}
useEffect(() => {
if (!showPolicyMenu) return
const onDocClick = (e: MouseEvent) => {
if (!policyMenuRef.current) return
if (!policyMenuRef.current.contains(e.target as Node)) setShowPolicyMenu(false)
}
const onEsc = (e: KeyboardEvent) => { if (e.key === 'Escape') setShowPolicyMenu(false) }
document.addEventListener('mousedown', onDocClick)
document.addEventListener('keydown', onEsc)
return () => {
document.removeEventListener('mousedown', onDocClick)
document.removeEventListener('keydown', onEsc)
}
}, [showPolicyMenu])
const updateTeamMembers = async (teamId: string, active: Player[], inactive: Player[]) => {
try {
const res = await fetch('/api/team/update-players', {
@ -290,7 +327,7 @@ export default function TeamMemberView({
setActivePlayers(nextActive)
setInactivePlayers(nextInactive)
updateTeamMembers(team.id, nextActive, nextInactive).catch(console.error)
updateTeamMembers(teamId, nextActive, nextInactive).catch(console.error)
setSaveSuccess(true)
setTimeout(()=>setSaveSuccess(false), 3000)
@ -324,7 +361,7 @@ export default function TeamMemberView({
await fetch('/api/team/kick', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ steamId: kickCandidate.steamId, teamId: team.id }),
body: JSON.stringify({ steamId: kickCandidate.steamId, teamId }),
})
await updateTeamMembers(team.id, newActive, newInactive)
setKickCandidate(null)
@ -335,7 +372,7 @@ export default function TeamMemberView({
const res = await fetch('/api/team/transfer-leader', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ teamId: team.id, newLeaderSteamId: newLeaderId }),
body: JSON.stringify({ teamId, newLeaderSteamId: newLeaderId }),
})
if (!res.ok) {
const data = await res.json()
@ -355,6 +392,36 @@ export default function TeamMemberView({
square?: boolean; // center-crop auf Quadrat
};
async function saveJoinPolicy(next: TeamJoinPolicy = joinPolicy) {
const prev = joinPolicy
try {
setSavingPolicy(true)
const res = await fetch('/api/team/update-join-policy', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'same-origin', // oder 'include' same-origin reicht bei relativer URL
cache: 'no-store',
body: JSON.stringify({ teamId, joinPolicy: next }),
})
if (!res.ok) {
const data = await res.json().catch(() => ({}))
throw new Error(data?.message ?? `Speichern fehlgeschlagen (${res.status})`)
}
setPolicySaved(true)
setTimeout(() => setPolicySaved(false), 2000)
const updated = await reloadTeam(teamId)
if (updated) setTeam(updated)
} catch (e) {
// 🔙 Optimistisches Set zurückrollen
setJoinPolicy(prev)
console.error(e)
alert((e as Error).message || 'Speichern fehlgeschlagen')
} finally {
setSavingPolicy(false)
}
}
async function canEncode(mime: string): Promise<boolean> {
try {
// OffscreenCanvas hat die zuverlässigste Blob-API
@ -480,7 +547,7 @@ export default function TeamMemberView({
return new Promise<void>((resolve, reject) => {
const formData = new FormData()
formData.append('logo', file)
formData.append('teamId', team!.id)
formData.append('teamId', teamId)
const xhr = new XMLHttpRequest()
xhr.open('POST', '/api/team/upload-logo')
@ -510,7 +577,7 @@ export default function TeamMemberView({
if (!adminMode && !currentUserSteamId) return null
const manageSteam: string = adminMode ? (team.leader?.steamId ?? '') : currentUserSteamId
const manageSteam: string = adminMode ? teamLeaderSteamId : currentUserSteamId
const renderMemberList = (players: Player[]) => (
<AnimatePresence>
@ -533,7 +600,7 @@ export default function TeamMemberView({
onKick={setKickCandidate}
onPromote={() => setPromoteCandidate(player)}
currentUserSteamId={manageSteam}
teamLeaderSteamId={team.leader?.steamId}
teamLeaderSteamId={teamLeaderSteamId}
isAdmin={adminMode}
isDraggingGlobal={isDragging}
hideOverlay={isDragging}
@ -695,20 +762,83 @@ export default function TeamMemberView({
</h2>
<TeamPremierRankBadge players={activePlayers} />
</div>
{/* Beitritts-Einstellungen (nur Leader/Admin) */}
{canManage && (
<Button
title="Bearbeiten"
color="blue"
size="sm"
variant="soft"
onClick={() => {
setIsEditingName(true)
setEditedName(team.name || '')
}}
className="h-[34px] px-3 flex items-center justify-center"
>
Bearbeiten
</Button>
<>
<Button
title="Bearbeiten"
color="blue"
size="sm"
variant="soft"
onClick={() => {
setIsEditingName(true)
setEditedName(team.name || '')
}}
className="h-[34px] px-3 flex items-center justify-center"
>
Bearbeiten
</Button>
{/* 🔽 Dezente Policy-Pill */}
<div className="relative" ref={policyMenuRef}>
<button
type="button"
onClick={() => setShowPolicyMenu(v => !v)}
className="h-[32px] px-2.5 rounded-full text-xs border border-gray-300 dark:border-neutral-600
bg-white dark:bg-neutral-800 text-gray-700 dark:text-neutral-200
hover:bg-gray-100 hover:dark:bg-neutral-700 inline-flex items-center gap-1"
title="Beitrittsmodus ändern"
>
{joinPolicy === 'INVITE_ONLY' ? (
<svg className="w-3.5 h-3.5" viewBox="0 0 24 24" fill="currentColor">
<path d="M17 8V7a5 5 0 1 0-10 0v1H5v12h14V8h-2Zm-8 0V7a3 3 0 1 1 6 0v1H9Zm-2 2h10v8H7v-8Z"/>
</svg>
) : (
<svg className="w-3.5 h-3.5" viewBox="0 0 24 24" fill="currentColor">
<path d="M12 2a5 5 0 0 0-5 5v1H5v12h14V8h-2V7a5 5 0 0 0-5-5Zm-3 6V7a3 3 0 1 1 6 0v1H9Z"/>
</svg>
)}
<span>{joinPolicy === 'INVITE_ONLY' ? 'Nur Einladung' : 'Mit Genehmigung'}</span>
{savingPolicy && (
<span className="ml-1 inline-block size-3 border-2 border-current border-t-transparent rounded-full animate-spin" />
)}
{policySaved && !savingPolicy && <span className="ml-1 text-green-600"></span>}
</button>
{showPolicyMenu && (
<div className="absolute right-0 z-10 mt-1 w-56 rounded-md border border-gray-200 dark:border-neutral-700
bg-white dark:bg-neutral-800 shadow-lg p-1">
<button
onClick={() => applyPolicy('REQUEST')}
className={`w-full text-left px-2.5 py-2 rounded-md text-sm
${joinPolicy === 'REQUEST'
? 'bg-blue-50 dark:bg-blue-900/20 text-blue-700 dark:text-blue-200'
: 'hover:bg-gray-100 dark:hover:bg-neutral-700 text-gray-800 dark:text-neutral-200'}`}
>
<div className="font-medium">Mit Genehmigung</div>
<div className="text-xs text-gray-500 dark:text-neutral-400">
Spieler stellen eine Anfrage; Leader entscheidet.
</div>
</button>
<button
onClick={() => applyPolicy('INVITE_ONLY')}
className={`w-full text-left px-2.5 py-2 rounded-md text-sm
${joinPolicy === 'INVITE_ONLY'
? 'bg-blue-50 dark:bg-blue-900/20 text-blue-700 dark:text-blue-200'
: 'hover:bg-gray-100 dark:hover:bg-neutral-700 text-gray-800 dark:text-neutral-200'}`}
>
<div className="font-medium">Nur Einladung</div>
<div className="text-xs text-gray-500 dark:text-neutral-400">
Beitritt nur per Einladung.
</div>
</button>
</div>
)}
</div>
{/* 🔼 Ende Policy-Pill */}
</>
)}
</>
)}
@ -738,7 +868,7 @@ export default function TeamMemberView({
</div>
<DndContext
key={`dnd-${team.id}-${remountKey}`}
key={`dnd-${teamId}-${remountKey}`}
collisionDetection={closestCenter}
onDragStart={handleDragStart}
onDragEnd={handleDragEnd}

View File

@ -41,7 +41,7 @@ export default function SettingsLayout({ children }: { children: React.ReactNode
</aside>
{/* rechte Spalte scrollt */}
<div ref={scrollRef} className="min-h-0 p-4">
<div ref={scrollRef} className="min-h-0 p-4 overflow-auto">
<main className="min-h-0">{children}</main>
</div>
</div>

View File

@ -1,7 +1,7 @@
// /src/app/api/team/[teamId]/route.ts
import { NextResponse, type NextRequest } from 'next/server'
import { prisma } from '@/lib/prisma'
import type { Player, InvitedPlayer } from '@/types/team'
import type { Player, InvitedPlayer, TeamJoinPolicy } from '@/types/team'
export const dynamic = 'force-dynamic'
export const revalidate = 0
@ -94,6 +94,7 @@ export async function GET(
logo: team.logo,
leader, // ⬅ jetzt Player statt String
createdAt: team.createdAt,
joinPolicy: (team.joinPolicy ?? 'REQUEST') as TeamJoinPolicy,
activePlayers,
inactivePlayers,
invitedPlayers,

View File

@ -7,78 +7,100 @@ import { sendServerSSEMessage } from '@/lib/sse-server-client'
export async function POST(req: NextRequest) {
try {
/* ───────────────── Session prüfen ────────────────────── */
// ── Session ──────────────────────────────────────────────
const session = await getServerSession(authOptions(req))
if (!session?.user?.steamId) {
return NextResponse.json({ message: 'Nicht eingeloggt' }, { status: 401 })
}
const requesterSteamId = session.user.steamId
/* ───────────────── Body validieren ────────────────────── */
// ── Body ────────────────────────────────────────────────
const { teamId } = await req.json()
if (!teamId) {
return NextResponse.json({ message: 'teamId fehlt' }, { status: 400 })
}
/* ───────────────── Team holen ─────────────────────────── */
const team = await prisma.team.findUnique({ where: { id: teamId } })
// ── Daten holen ─────────────────────────────────────────
const [team, requester] = await Promise.all([
prisma.team.findUnique({
where: { id: teamId },
select: {
id: true,
name: true,
leaderId: true,
joinPolicy: true,
activePlayers: true,
inactivePlayers: true,
},
}),
prisma.user.findUnique({
where: { steamId: requesterSteamId },
select: { steamId: true, name: true, teamId: true },
}),
])
if (!team) {
return NextResponse.json({ message: 'Team nicht gefunden' }, { status: 404 })
}
/* ───────────────── Bereits Mitglied? ──────────────────── */
// ── Bereits in irgendeinem Team? ─────────────────────────
if (requester?.teamId && requester.teamId !== team.id) {
return NextResponse.json({ message: 'Du bist bereits in einem anderen Team' }, { status: 400 })
}
// ── Schon (irgendwie) Mitglied dieses Teams? ────────────
if (
requesterSteamId === team.leaderId ||
team.activePlayers.includes(requesterSteamId) ||
team.inactivePlayers.includes(requesterSteamId)
) {
return NextResponse.json({ message: 'Du bist bereits Mitglied' }, { status: 400 })
return NextResponse.json({ message: 'Du bist bereits Mitglied' }, { status: 200 })
}
/* ───────────────── Doppelte Anfrage vermeiden ─────────── */
// ── Policy Switch ───────────────────────────────────────
if (team.joinPolicy === 'INVITE_ONLY') {
return NextResponse.json(
{ message: 'Dieses Team akzeptiert nur Einladungen' },
{ status: 403 }
)
}
// ── Policy: REQUEST (default) → Anfrage anlegen
const existingInvite = await prisma.teamInvite.findFirst({
where: {
steamId: requesterSteamId,
teamId,
type : 'team-join-request',
},
where: { steamId: requesterSteamId, teamId, type: 'team-join-request' },
select: { id: true },
})
if (existingInvite) {
return NextResponse.json({ message: 'Anfrage läuft bereits' }, { status: 200 })
}
/* ───────────────── Invitation anlegen ─────────────────── */
const invitation = await prisma.teamInvite.create({
data: {
steamId: requesterSteamId,
teamId ,
type : 'team-join-request',
},
data: { steamId: requesterSteamId, teamId, type: 'team-join-request' },
})
/* ───────────────── Leader benachrichtigen ─────────────── */
const notification = await prisma.notification.create({
data: {
steamId : team.leaderId!, // garantiert vorhanden
title : 'Beitrittsanfrage',
message : `${session.user.name ?? 'Ein Spieler'} möchte deinem Team beitreten.`,
steamId: team.leaderId!,
title: 'Beitrittsanfrage',
message: `${session.user.name ?? 'Ein Spieler'} möchte deinem Team beitreten.`,
actionType: 'team-join-request',
actionData: invitation.id, // ← WICHTIG: invitationId
actionData: invitation.id,
},
})
/* ───────────────── SSE Event auslösen ─────────────────── */
// SSE an Leader
await sendServerSSEMessage({
type : notification.actionType ?? 'notification',
type: notification.actionType ?? 'notification',
targetUserIds: [team.leaderId],
message : notification.message,
id : notification.id,
actionType : notification.actionType ?? undefined,
actionData : notification.actionData ?? undefined, // invitation.id
createdAt : notification.createdAt.toISOString(),
message: notification.message,
id: notification.id,
actionType: notification.actionType ?? undefined,
actionData: notification.actionData ?? undefined,
createdAt: notification.createdAt.toISOString(),
payload: { teamId: team.id },
})
return NextResponse.json({ message: 'Anfrage gesendet' }, { status: 200 })
return NextResponse.json({ message: 'Anfrage gesendet' }, { status: 200 })
} catch (err) {
console.error('POST /api/team/request-join', err)
return NextResponse.json({ message: 'Interner Serverfehler' }, { status: 500 })

View File

@ -0,0 +1,91 @@
// /src/app/api/team/update-join-policy/route.ts
import { NextResponse, type NextRequest } from 'next/server'
import { prisma } from '@/lib/prisma'
import { getServerSession } from 'next-auth'
import { authOptions } from '@/lib/auth'
import { sendServerSSEMessage } from '@/lib/sse-server-client'
import type { TeamJoinPolicy } from '@/types/team'
export const runtime = 'nodejs' // ✅ Prisma-kompatibel
export const dynamic = 'force-dynamic' // (nur Vorsicht, POST ist eh dynamisch)
const ALLOWED = ['REQUEST', 'INVITE_ONLY'] as const
type AllowedPolicy = (typeof ALLOWED)[number]
export async function POST(req: NextRequest) {
try {
// ─ Session ─
const session = await getServerSession(authOptions(req))
const meId = session?.user?.steamId
if (!meId) {
return NextResponse.json({ message: 'Nicht eingeloggt' }, { status: 401 })
}
// ─ Input ─
const body = await req.json().catch(() => ({} as any))
const teamId: string | undefined = body?.teamId
const joinPolicy: TeamJoinPolicy | undefined = body?.joinPolicy
if (!teamId) {
return NextResponse.json({ message: 'teamId fehlt' }, { status: 400 })
}
if (!joinPolicy || !ALLOWED.includes(joinPolicy as AllowedPolicy)) {
return NextResponse.json({ message: 'Ungültige joinPolicy' }, { status: 400 })
}
// ─ Daten ─
const [team, me] = await Promise.all([
prisma.team.findUnique({
where: { id: teamId },
select: {
id: true,
leaderId: true,
joinPolicy: true,
},
}),
prisma.user.findUnique({
where: { steamId: meId },
select: { steamId: true, isAdmin: true },
}),
])
if (!team) {
return NextResponse.json({ message: 'Team nicht gefunden' }, { status: 404 })
}
const isLeader = meId === team.leaderId
const isAdmin = !!me?.isAdmin
if (!isLeader && !isAdmin) {
return NextResponse.json({ message: 'Keine Berechtigung' }, { status: 403 })
}
if (team.joinPolicy === joinPolicy) {
return NextResponse.json({ message: 'Unverändert', joinPolicy }, { status: 200 })
}
const updated = await prisma.team.update({
where: { id: teamId },
data: { joinPolicy },
select: { id: true, joinPolicy: true },
})
// ─ SSE (nicht blockierend!) ─
// Kein await → Request-Antwort wird NICHT aufgehalten
Promise.resolve().then(() =>
sendServerSSEMessage({
type: 'team-updated',
message: 'Beitrittsmodus wurde geändert.',
payload: { teamId: updated.id, joinPolicy: updated.joinPolicy },
}).catch(e => console.warn('SSE send failed (ignored):', e))
)
return NextResponse.json(
{ message: 'Beitrittsmodus aktualisiert', teamId: updated.id, joinPolicy: updated.joinPolicy },
{ status: 200 }
)
} catch (err) {
console.error('POST /api/team/update-join-policy', err)
return NextResponse.json({ message: 'Interner Serverfehler' }, { status: 500 })
}
}

View File

@ -7,7 +7,7 @@ export const dynamic = 'force-dynamic'
export async function GET() {
try {
// 1) Teams laden (ohne leaderId im späteren Response)
// 1) Teams laden
const teams = await prisma.team.findMany({
select: {
id: true,
@ -16,7 +16,7 @@ export async function GET() {
createdAt: true,
activePlayers: true,
inactivePlayers: true,
// Leader direkt als User-Objekt laden
joinPolicy: true, // 👈 NEU
leader: {
select: {
steamId: true,
@ -40,13 +40,15 @@ export async function GET() {
const teamIds = teams.map(t => t.id)
// 2) Ausstehende Einladungen pro Team holen
// (falls du "revoked/accepted" Flags hast, hier mitfiltern)
const invites = await prisma.teamInvite.findMany({
where: { teamId: { in: teamIds } },
where: {
teamId: { in: teamIds },
// OPTIONAL sauberer, falls du auch Join-Requests in dieser Tabelle hast:
// type: 'team-invite',
},
select: { id: true, teamId: true, steamId: true },
})
// Map: teamId -> [{steamId, invitationId}]
const invitedByTeam = new Map<string, { steamId: string; invitationId: string }[]>()
for (const inv of invites) {
const arr = invitedByTeam.get(inv.teamId) ?? []
@ -54,7 +56,7 @@ export async function GET() {
invitedByTeam.set(inv.teamId, arr)
}
// 3) Alle benötigten SteamIDs sammeln (aktive, inaktive, invited)
// 3) Alle benötigten SteamIDs sammeln
const uniqueIds = new Set<string>()
for (const t of teams) {
t.activePlayers.forEach(id => uniqueIds.add(id))
@ -62,10 +64,8 @@ export async function GET() {
const invited = invitedByTeam.get(t.id) ?? []
invited.forEach(i => uniqueIds.add(i.steamId))
}
// Leader müssen nicht in uniqueIds, da oben bereits als Objekt geladen.
// (könnten aber optional dazu; ist hier nicht nötig)
// 4) Nutzer-Daten für alle IDs holen
// 4) Nutzer-Daten holen
const users = await prisma.user.findMany({
where: { steamId: { in: [...uniqueIds] } },
select: {
@ -77,7 +77,6 @@ export async function GET() {
},
})
// Lookup-Map
const byId: Record<string, Player | undefined> = {}
const DEFAULT_AVATAR = '/assets/img/avatars/default.png'
const UNKNOWN_NAME = 'Unbekannt'
@ -94,7 +93,6 @@ export async function GET() {
// 5) Ergebnis formen
const result = teams.map(t => {
// Leader (bereits komplett geladen); mit Defaults absichern
const leader: Player | undefined = t.leader
? {
steamId: t.leader.steamId,
@ -105,43 +103,35 @@ export async function GET() {
}
: undefined
// Aktive & Inaktive Spieler aus Map befüllen
const activePlayers: Player[] = t.activePlayers
.map(id => byId[id])
.filter(Boolean) as Player[]
const activePlayers: Player[] = t.activePlayers.map(id => byId[id]).filter(Boolean) as Player[]
const inactivePlayers: Player[] = t.inactivePlayers.map(id => byId[id]).filter(Boolean) as Player[]
const inactivePlayers: Player[] = t.inactivePlayers
.map(id => byId[id])
.filter(Boolean) as Player[]
// Eingeladene Spieler inkl. invitationId
const invitedRaw = invitedByTeam.get(t.id) ?? []
const invitedPlayers: (Player & { invitationId?: string })[] = invitedRaw
.map(({ steamId, invitationId }) => {
const base = byId[steamId]
// Falls User (noch) nicht existiert, mit Fallbacks liefern
if (!base) {
return {
steamId,
name: UNKNOWN_NAME,
avatar: DEFAULT_AVATAR,
location: '',
premierRank: 0,
invitationId,
}
const invitedPlayers: (Player & { invitationId?: string })[] = invitedRaw.map(({ steamId, invitationId }) => {
const base = byId[steamId]
if (!base) {
return {
steamId,
name: UNKNOWN_NAME,
avatar: DEFAULT_AVATAR,
location: '',
premierRank: 0,
invitationId,
}
return { ...base, invitationId }
})
}
return { ...base, invitationId }
})
return {
id: t.id,
name: t.name,
logo: t.logo,
createdAt: t.createdAt,
leader, // ✅ voll befüllt
activePlayers, // ✅ Player[]
inactivePlayers, // ✅ Player[]
invitedPlayers, // ✅ Player[] mit invitationId
joinPolicy: t.joinPolicy, // 👈 NEU ins Response geben
leader,
activePlayers,
inactivePlayers,
invitedPlayers,
}
})

File diff suppressed because one or more lines are too long

View File

@ -176,7 +176,8 @@ exports.Prisma.TeamScalarFieldEnum = {
leaderId: 'leaderId',
createdAt: 'createdAt',
activePlayers: 'activePlayers',
inactivePlayers: 'inactivePlayers'
inactivePlayers: 'inactivePlayers',
joinPolicy: 'joinPolicy'
};
exports.Prisma.TeamInviteScalarFieldEnum = {
@ -397,6 +398,11 @@ exports.FaceitGameId = exports.$Enums.FaceitGameId = {
cs2: 'cs2'
};
exports.TeamJoinPolicy = exports.$Enums.TeamJoinPolicy = {
REQUEST: 'REQUEST',
INVITE_ONLY: 'INVITE_ONLY'
};
exports.ScheduleStatus = exports.$Enums.ScheduleStatus = {
PENDING: 'PENDING',
CONFIRMED: 'CONFIRMED',

View File

@ -115,6 +115,14 @@ export const FaceitGameId: {
export type FaceitGameId = (typeof FaceitGameId)[keyof typeof FaceitGameId]
export const TeamJoinPolicy: {
REQUEST: 'REQUEST',
INVITE_ONLY: 'INVITE_ONLY'
};
export type TeamJoinPolicy = (typeof TeamJoinPolicy)[keyof typeof TeamJoinPolicy]
export const ScheduleStatus: {
PENDING: 'PENDING',
CONFIRMED: 'CONFIRMED',
@ -144,6 +152,10 @@ export type FaceitGameId = $Enums.FaceitGameId
export const FaceitGameId: typeof $Enums.FaceitGameId
export type TeamJoinPolicy = $Enums.TeamJoinPolicy
export const TeamJoinPolicy: typeof $Enums.TeamJoinPolicy
export type ScheduleStatus = $Enums.ScheduleStatus
export const ScheduleStatus: typeof $Enums.ScheduleStatus
@ -5654,6 +5666,7 @@ export namespace Prisma {
logoUpdatedAt: Date | null
leaderId: string | null
createdAt: Date | null
joinPolicy: $Enums.TeamJoinPolicy | null
}
export type TeamMaxAggregateOutputType = {
@ -5663,6 +5676,7 @@ export namespace Prisma {
logoUpdatedAt: Date | null
leaderId: string | null
createdAt: Date | null
joinPolicy: $Enums.TeamJoinPolicy | null
}
export type TeamCountAggregateOutputType = {
@ -5674,6 +5688,7 @@ export namespace Prisma {
createdAt: number
activePlayers: number
inactivePlayers: number
joinPolicy: number
_all: number
}
@ -5685,6 +5700,7 @@ export namespace Prisma {
logoUpdatedAt?: true
leaderId?: true
createdAt?: true
joinPolicy?: true
}
export type TeamMaxAggregateInputType = {
@ -5694,6 +5710,7 @@ export namespace Prisma {
logoUpdatedAt?: true
leaderId?: true
createdAt?: true
joinPolicy?: true
}
export type TeamCountAggregateInputType = {
@ -5705,6 +5722,7 @@ export namespace Prisma {
createdAt?: true
activePlayers?: true
inactivePlayers?: true
joinPolicy?: true
_all?: true
}
@ -5789,6 +5807,7 @@ export namespace Prisma {
createdAt: Date
activePlayers: string[]
inactivePlayers: string[]
joinPolicy: $Enums.TeamJoinPolicy
_count: TeamCountAggregateOutputType | null
_min: TeamMinAggregateOutputType | null
_max: TeamMaxAggregateOutputType | null
@ -5817,6 +5836,7 @@ export namespace Prisma {
createdAt?: boolean
activePlayers?: boolean
inactivePlayers?: boolean
joinPolicy?: boolean
leader?: boolean | Team$leaderArgs<ExtArgs>
members?: boolean | Team$membersArgs<ExtArgs>
invites?: boolean | Team$invitesArgs<ExtArgs>
@ -5838,6 +5858,7 @@ export namespace Prisma {
createdAt?: boolean
activePlayers?: boolean
inactivePlayers?: boolean
joinPolicy?: boolean
leader?: boolean | Team$leaderArgs<ExtArgs>
}, ExtArgs["result"]["team"]>
@ -5850,6 +5871,7 @@ export namespace Prisma {
createdAt?: boolean
activePlayers?: boolean
inactivePlayers?: boolean
joinPolicy?: boolean
leader?: boolean | Team$leaderArgs<ExtArgs>
}, ExtArgs["result"]["team"]>
@ -5862,9 +5884,10 @@ export namespace Prisma {
createdAt?: boolean
activePlayers?: boolean
inactivePlayers?: boolean
joinPolicy?: boolean
}
export type TeamOmit<ExtArgs extends $Extensions.InternalArgs = $Extensions.DefaultArgs> = $Extensions.GetOmit<"id" | "name" | "logo" | "logoUpdatedAt" | "leaderId" | "createdAt" | "activePlayers" | "inactivePlayers", ExtArgs["result"]["team"]>
export type TeamOmit<ExtArgs extends $Extensions.InternalArgs = $Extensions.DefaultArgs> = $Extensions.GetOmit<"id" | "name" | "logo" | "logoUpdatedAt" | "leaderId" | "createdAt" | "activePlayers" | "inactivePlayers" | "joinPolicy", ExtArgs["result"]["team"]>
export type TeamInclude<ExtArgs extends $Extensions.InternalArgs = $Extensions.DefaultArgs> = {
leader?: boolean | Team$leaderArgs<ExtArgs>
members?: boolean | Team$membersArgs<ExtArgs>
@ -5906,6 +5929,7 @@ export namespace Prisma {
createdAt: Date
activePlayers: string[]
inactivePlayers: string[]
joinPolicy: $Enums.TeamJoinPolicy
}, ExtArgs["result"]["team"]>
composites: {}
}
@ -6346,6 +6370,7 @@ export namespace Prisma {
readonly createdAt: FieldRef<"Team", 'DateTime'>
readonly activePlayers: FieldRef<"Team", 'String[]'>
readonly inactivePlayers: FieldRef<"Team", 'String[]'>
readonly joinPolicy: FieldRef<"Team", 'TeamJoinPolicy'>
}
@ -22642,7 +22667,8 @@ export namespace Prisma {
leaderId: 'leaderId',
createdAt: 'createdAt',
activePlayers: 'activePlayers',
inactivePlayers: 'inactivePlayers'
inactivePlayers: 'inactivePlayers',
joinPolicy: 'joinPolicy'
};
export type TeamScalarFieldEnum = (typeof TeamScalarFieldEnum)[keyof typeof TeamScalarFieldEnum]
@ -22992,6 +23018,20 @@ export namespace Prisma {
/**
* Reference to a field of type 'TeamJoinPolicy'
*/
export type EnumTeamJoinPolicyFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'TeamJoinPolicy'>
/**
* Reference to a field of type 'TeamJoinPolicy[]'
*/
export type ListEnumTeamJoinPolicyFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'TeamJoinPolicy[]'>
/**
* Reference to a field of type 'Json'
*/
@ -23394,6 +23434,7 @@ export namespace Prisma {
createdAt?: DateTimeFilter<"Team"> | Date | string
activePlayers?: StringNullableListFilter<"Team">
inactivePlayers?: StringNullableListFilter<"Team">
joinPolicy?: EnumTeamJoinPolicyFilter<"Team"> | $Enums.TeamJoinPolicy
leader?: XOR<UserNullableScalarRelationFilter, UserWhereInput> | null
members?: UserListRelationFilter
invites?: TeamInviteListRelationFilter
@ -23414,6 +23455,7 @@ export namespace Prisma {
createdAt?: SortOrder
activePlayers?: SortOrder
inactivePlayers?: SortOrder
joinPolicy?: SortOrder
leader?: UserOrderByWithRelationInput
members?: UserOrderByRelationAggregateInput
invites?: TeamInviteOrderByRelationAggregateInput
@ -23437,6 +23479,7 @@ export namespace Prisma {
createdAt?: DateTimeFilter<"Team"> | Date | string
activePlayers?: StringNullableListFilter<"Team">
inactivePlayers?: StringNullableListFilter<"Team">
joinPolicy?: EnumTeamJoinPolicyFilter<"Team"> | $Enums.TeamJoinPolicy
leader?: XOR<UserNullableScalarRelationFilter, UserWhereInput> | null
members?: UserListRelationFilter
invites?: TeamInviteListRelationFilter
@ -23457,6 +23500,7 @@ export namespace Prisma {
createdAt?: SortOrder
activePlayers?: SortOrder
inactivePlayers?: SortOrder
joinPolicy?: SortOrder
_count?: TeamCountOrderByAggregateInput
_max?: TeamMaxOrderByAggregateInput
_min?: TeamMinOrderByAggregateInput
@ -23474,6 +23518,7 @@ export namespace Prisma {
createdAt?: DateTimeWithAggregatesFilter<"Team"> | Date | string
activePlayers?: StringNullableListFilter<"Team">
inactivePlayers?: StringNullableListFilter<"Team">
joinPolicy?: EnumTeamJoinPolicyWithAggregatesFilter<"Team"> | $Enums.TeamJoinPolicy
}
export type TeamInviteWhereInput = {
@ -25062,6 +25107,7 @@ export namespace Prisma {
createdAt?: Date | string
activePlayers?: TeamCreateactivePlayersInput | string[]
inactivePlayers?: TeamCreateinactivePlayersInput | string[]
joinPolicy?: $Enums.TeamJoinPolicy
leader?: UserCreateNestedOneWithoutLedTeamInput
members?: UserCreateNestedManyWithoutTeamInput
invites?: TeamInviteCreateNestedManyWithoutTeamInput
@ -25082,6 +25128,7 @@ export namespace Prisma {
createdAt?: Date | string
activePlayers?: TeamCreateactivePlayersInput | string[]
inactivePlayers?: TeamCreateinactivePlayersInput | string[]
joinPolicy?: $Enums.TeamJoinPolicy
members?: UserUncheckedCreateNestedManyWithoutTeamInput
invites?: TeamInviteUncheckedCreateNestedManyWithoutTeamInput
matchPlayers?: MatchPlayerUncheckedCreateNestedManyWithoutTeamInput
@ -25100,6 +25147,7 @@ export namespace Prisma {
createdAt?: DateTimeFieldUpdateOperationsInput | Date | string
activePlayers?: TeamUpdateactivePlayersInput | string[]
inactivePlayers?: TeamUpdateinactivePlayersInput | string[]
joinPolicy?: EnumTeamJoinPolicyFieldUpdateOperationsInput | $Enums.TeamJoinPolicy
leader?: UserUpdateOneWithoutLedTeamNestedInput
members?: UserUpdateManyWithoutTeamNestedInput
invites?: TeamInviteUpdateManyWithoutTeamNestedInput
@ -25120,6 +25168,7 @@ export namespace Prisma {
createdAt?: DateTimeFieldUpdateOperationsInput | Date | string
activePlayers?: TeamUpdateactivePlayersInput | string[]
inactivePlayers?: TeamUpdateinactivePlayersInput | string[]
joinPolicy?: EnumTeamJoinPolicyFieldUpdateOperationsInput | $Enums.TeamJoinPolicy
members?: UserUncheckedUpdateManyWithoutTeamNestedInput
invites?: TeamInviteUncheckedUpdateManyWithoutTeamNestedInput
matchPlayers?: MatchPlayerUncheckedUpdateManyWithoutTeamNestedInput
@ -25139,6 +25188,7 @@ export namespace Prisma {
createdAt?: Date | string
activePlayers?: TeamCreateactivePlayersInput | string[]
inactivePlayers?: TeamCreateinactivePlayersInput | string[]
joinPolicy?: $Enums.TeamJoinPolicy
}
export type TeamUpdateManyMutationInput = {
@ -25149,6 +25199,7 @@ export namespace Prisma {
createdAt?: DateTimeFieldUpdateOperationsInput | Date | string
activePlayers?: TeamUpdateactivePlayersInput | string[]
inactivePlayers?: TeamUpdateinactivePlayersInput | string[]
joinPolicy?: EnumTeamJoinPolicyFieldUpdateOperationsInput | $Enums.TeamJoinPolicy
}
export type TeamUncheckedUpdateManyInput = {
@ -25160,6 +25211,7 @@ export namespace Prisma {
createdAt?: DateTimeFieldUpdateOperationsInput | Date | string
activePlayers?: TeamUpdateactivePlayersInput | string[]
inactivePlayers?: TeamUpdateinactivePlayersInput | string[]
joinPolicy?: EnumTeamJoinPolicyFieldUpdateOperationsInput | $Enums.TeamJoinPolicy
}
export type TeamInviteCreateInput = {
@ -26957,6 +27009,13 @@ export namespace Prisma {
isEmpty?: boolean
}
export type EnumTeamJoinPolicyFilter<$PrismaModel = never> = {
equals?: $Enums.TeamJoinPolicy | EnumTeamJoinPolicyFieldRefInput<$PrismaModel>
in?: $Enums.TeamJoinPolicy[] | ListEnumTeamJoinPolicyFieldRefInput<$PrismaModel>
notIn?: $Enums.TeamJoinPolicy[] | ListEnumTeamJoinPolicyFieldRefInput<$PrismaModel>
not?: NestedEnumTeamJoinPolicyFilter<$PrismaModel> | $Enums.TeamJoinPolicy
}
export type UserNullableScalarRelationFilter = {
is?: UserWhereInput | null
isNot?: UserWhereInput | null
@ -26981,6 +27040,7 @@ export namespace Prisma {
createdAt?: SortOrder
activePlayers?: SortOrder
inactivePlayers?: SortOrder
joinPolicy?: SortOrder
}
export type TeamMaxOrderByAggregateInput = {
@ -26990,6 +27050,7 @@ export namespace Prisma {
logoUpdatedAt?: SortOrder
leaderId?: SortOrder
createdAt?: SortOrder
joinPolicy?: SortOrder
}
export type TeamMinOrderByAggregateInput = {
@ -26999,6 +27060,17 @@ export namespace Prisma {
logoUpdatedAt?: SortOrder
leaderId?: SortOrder
createdAt?: SortOrder
joinPolicy?: SortOrder
}
export type EnumTeamJoinPolicyWithAggregatesFilter<$PrismaModel = never> = {
equals?: $Enums.TeamJoinPolicy | EnumTeamJoinPolicyFieldRefInput<$PrismaModel>
in?: $Enums.TeamJoinPolicy[] | ListEnumTeamJoinPolicyFieldRefInput<$PrismaModel>
notIn?: $Enums.TeamJoinPolicy[] | ListEnumTeamJoinPolicyFieldRefInput<$PrismaModel>
not?: NestedEnumTeamJoinPolicyWithAggregatesFilter<$PrismaModel> | $Enums.TeamJoinPolicy
_count?: NestedIntFilter<$PrismaModel>
_min?: NestedEnumTeamJoinPolicyFilter<$PrismaModel>
_max?: NestedEnumTeamJoinPolicyFilter<$PrismaModel>
}
export type TeamScalarRelationFilter = {
@ -28696,6 +28768,10 @@ export namespace Prisma {
push?: string | string[]
}
export type EnumTeamJoinPolicyFieldUpdateOperationsInput = {
set?: $Enums.TeamJoinPolicy
}
export type UserUpdateOneWithoutLedTeamNestedInput = {
create?: XOR<UserCreateWithoutLedTeamInput, UserUncheckedCreateWithoutLedTeamInput>
connectOrCreate?: UserCreateOrConnectWithoutLedTeamInput
@ -29951,6 +30027,23 @@ export namespace Prisma {
_max?: NestedEnumFaceitGameIdFilter<$PrismaModel>
}
export type NestedEnumTeamJoinPolicyFilter<$PrismaModel = never> = {
equals?: $Enums.TeamJoinPolicy | EnumTeamJoinPolicyFieldRefInput<$PrismaModel>
in?: $Enums.TeamJoinPolicy[] | ListEnumTeamJoinPolicyFieldRefInput<$PrismaModel>
notIn?: $Enums.TeamJoinPolicy[] | ListEnumTeamJoinPolicyFieldRefInput<$PrismaModel>
not?: NestedEnumTeamJoinPolicyFilter<$PrismaModel> | $Enums.TeamJoinPolicy
}
export type NestedEnumTeamJoinPolicyWithAggregatesFilter<$PrismaModel = never> = {
equals?: $Enums.TeamJoinPolicy | EnumTeamJoinPolicyFieldRefInput<$PrismaModel>
in?: $Enums.TeamJoinPolicy[] | ListEnumTeamJoinPolicyFieldRefInput<$PrismaModel>
notIn?: $Enums.TeamJoinPolicy[] | ListEnumTeamJoinPolicyFieldRefInput<$PrismaModel>
not?: NestedEnumTeamJoinPolicyWithAggregatesFilter<$PrismaModel> | $Enums.TeamJoinPolicy
_count?: NestedIntFilter<$PrismaModel>
_min?: NestedEnumTeamJoinPolicyFilter<$PrismaModel>
_max?: NestedEnumTeamJoinPolicyFilter<$PrismaModel>
}
export type NestedBigIntNullableFilter<$PrismaModel = never> = {
equals?: bigint | number | BigIntFieldRefInput<$PrismaModel> | null
in?: bigint[] | number[] | ListBigIntFieldRefInput<$PrismaModel> | null
@ -30113,6 +30206,7 @@ export namespace Prisma {
createdAt?: Date | string
activePlayers?: TeamCreateactivePlayersInput | string[]
inactivePlayers?: TeamCreateinactivePlayersInput | string[]
joinPolicy?: $Enums.TeamJoinPolicy
leader?: UserCreateNestedOneWithoutLedTeamInput
invites?: TeamInviteCreateNestedManyWithoutTeamInput
matchPlayers?: MatchPlayerCreateNestedManyWithoutTeamInput
@ -30132,6 +30226,7 @@ export namespace Prisma {
createdAt?: Date | string
activePlayers?: TeamCreateactivePlayersInput | string[]
inactivePlayers?: TeamCreateinactivePlayersInput | string[]
joinPolicy?: $Enums.TeamJoinPolicy
invites?: TeamInviteUncheckedCreateNestedManyWithoutTeamInput
matchPlayers?: MatchPlayerUncheckedCreateNestedManyWithoutTeamInput
matchesAsTeamA?: MatchUncheckedCreateNestedManyWithoutTeamAInput
@ -30154,6 +30249,7 @@ export namespace Prisma {
createdAt?: Date | string
activePlayers?: TeamCreateactivePlayersInput | string[]
inactivePlayers?: TeamCreateinactivePlayersInput | string[]
joinPolicy?: $Enums.TeamJoinPolicy
members?: UserCreateNestedManyWithoutTeamInput
invites?: TeamInviteCreateNestedManyWithoutTeamInput
matchPlayers?: MatchPlayerCreateNestedManyWithoutTeamInput
@ -30172,6 +30268,7 @@ export namespace Prisma {
createdAt?: Date | string
activePlayers?: TeamCreateactivePlayersInput | string[]
inactivePlayers?: TeamCreateinactivePlayersInput | string[]
joinPolicy?: $Enums.TeamJoinPolicy
members?: UserUncheckedCreateNestedManyWithoutTeamInput
invites?: TeamInviteUncheckedCreateNestedManyWithoutTeamInput
matchPlayers?: MatchPlayerUncheckedCreateNestedManyWithoutTeamInput
@ -30672,6 +30769,7 @@ export namespace Prisma {
createdAt?: DateTimeFieldUpdateOperationsInput | Date | string
activePlayers?: TeamUpdateactivePlayersInput | string[]
inactivePlayers?: TeamUpdateinactivePlayersInput | string[]
joinPolicy?: EnumTeamJoinPolicyFieldUpdateOperationsInput | $Enums.TeamJoinPolicy
leader?: UserUpdateOneWithoutLedTeamNestedInput
invites?: TeamInviteUpdateManyWithoutTeamNestedInput
matchPlayers?: MatchPlayerUpdateManyWithoutTeamNestedInput
@ -30691,6 +30789,7 @@ export namespace Prisma {
createdAt?: DateTimeFieldUpdateOperationsInput | Date | string
activePlayers?: TeamUpdateactivePlayersInput | string[]
inactivePlayers?: TeamUpdateinactivePlayersInput | string[]
joinPolicy?: EnumTeamJoinPolicyFieldUpdateOperationsInput | $Enums.TeamJoinPolicy
invites?: TeamInviteUncheckedUpdateManyWithoutTeamNestedInput
matchPlayers?: MatchPlayerUncheckedUpdateManyWithoutTeamNestedInput
matchesAsTeamA?: MatchUncheckedUpdateManyWithoutTeamANestedInput
@ -30719,6 +30818,7 @@ export namespace Prisma {
createdAt?: DateTimeFieldUpdateOperationsInput | Date | string
activePlayers?: TeamUpdateactivePlayersInput | string[]
inactivePlayers?: TeamUpdateinactivePlayersInput | string[]
joinPolicy?: EnumTeamJoinPolicyFieldUpdateOperationsInput | $Enums.TeamJoinPolicy
members?: UserUpdateManyWithoutTeamNestedInput
invites?: TeamInviteUpdateManyWithoutTeamNestedInput
matchPlayers?: MatchPlayerUpdateManyWithoutTeamNestedInput
@ -30737,6 +30837,7 @@ export namespace Prisma {
createdAt?: DateTimeFieldUpdateOperationsInput | Date | string
activePlayers?: TeamUpdateactivePlayersInput | string[]
inactivePlayers?: TeamUpdateinactivePlayersInput | string[]
joinPolicy?: EnumTeamJoinPolicyFieldUpdateOperationsInput | $Enums.TeamJoinPolicy
members?: UserUncheckedUpdateManyWithoutTeamNestedInput
invites?: TeamInviteUncheckedUpdateManyWithoutTeamNestedInput
matchPlayers?: MatchPlayerUncheckedUpdateManyWithoutTeamNestedInput
@ -32202,6 +32303,7 @@ export namespace Prisma {
createdAt?: Date | string
activePlayers?: TeamCreateactivePlayersInput | string[]
inactivePlayers?: TeamCreateinactivePlayersInput | string[]
joinPolicy?: $Enums.TeamJoinPolicy
leader?: UserCreateNestedOneWithoutLedTeamInput
members?: UserCreateNestedManyWithoutTeamInput
matchPlayers?: MatchPlayerCreateNestedManyWithoutTeamInput
@ -32221,6 +32323,7 @@ export namespace Prisma {
createdAt?: Date | string
activePlayers?: TeamCreateactivePlayersInput | string[]
inactivePlayers?: TeamCreateinactivePlayersInput | string[]
joinPolicy?: $Enums.TeamJoinPolicy
members?: UserUncheckedCreateNestedManyWithoutTeamInput
matchPlayers?: MatchPlayerUncheckedCreateNestedManyWithoutTeamInput
matchesAsTeamA?: MatchUncheckedCreateNestedManyWithoutTeamAInput
@ -32359,6 +32462,7 @@ export namespace Prisma {
createdAt?: DateTimeFieldUpdateOperationsInput | Date | string
activePlayers?: TeamUpdateactivePlayersInput | string[]
inactivePlayers?: TeamUpdateinactivePlayersInput | string[]
joinPolicy?: EnumTeamJoinPolicyFieldUpdateOperationsInput | $Enums.TeamJoinPolicy
leader?: UserUpdateOneWithoutLedTeamNestedInput
members?: UserUpdateManyWithoutTeamNestedInput
matchPlayers?: MatchPlayerUpdateManyWithoutTeamNestedInput
@ -32378,6 +32482,7 @@ export namespace Prisma {
createdAt?: DateTimeFieldUpdateOperationsInput | Date | string
activePlayers?: TeamUpdateactivePlayersInput | string[]
inactivePlayers?: TeamUpdateinactivePlayersInput | string[]
joinPolicy?: EnumTeamJoinPolicyFieldUpdateOperationsInput | $Enums.TeamJoinPolicy
members?: UserUncheckedUpdateManyWithoutTeamNestedInput
matchPlayers?: MatchPlayerUncheckedUpdateManyWithoutTeamNestedInput
matchesAsTeamA?: MatchUncheckedUpdateManyWithoutTeamANestedInput
@ -32599,6 +32704,7 @@ export namespace Prisma {
createdAt?: Date | string
activePlayers?: TeamCreateactivePlayersInput | string[]
inactivePlayers?: TeamCreateinactivePlayersInput | string[]
joinPolicy?: $Enums.TeamJoinPolicy
leader?: UserCreateNestedOneWithoutLedTeamInput
members?: UserCreateNestedManyWithoutTeamInput
invites?: TeamInviteCreateNestedManyWithoutTeamInput
@ -32618,6 +32724,7 @@ export namespace Prisma {
createdAt?: Date | string
activePlayers?: TeamCreateactivePlayersInput | string[]
inactivePlayers?: TeamCreateinactivePlayersInput | string[]
joinPolicy?: $Enums.TeamJoinPolicy
members?: UserUncheckedCreateNestedManyWithoutTeamInput
invites?: TeamInviteUncheckedCreateNestedManyWithoutTeamInput
matchPlayers?: MatchPlayerUncheckedCreateNestedManyWithoutTeamInput
@ -32640,6 +32747,7 @@ export namespace Prisma {
createdAt?: Date | string
activePlayers?: TeamCreateactivePlayersInput | string[]
inactivePlayers?: TeamCreateinactivePlayersInput | string[]
joinPolicy?: $Enums.TeamJoinPolicy
leader?: UserCreateNestedOneWithoutLedTeamInput
members?: UserCreateNestedManyWithoutTeamInput
invites?: TeamInviteCreateNestedManyWithoutTeamInput
@ -32659,6 +32767,7 @@ export namespace Prisma {
createdAt?: Date | string
activePlayers?: TeamCreateactivePlayersInput | string[]
inactivePlayers?: TeamCreateinactivePlayersInput | string[]
joinPolicy?: $Enums.TeamJoinPolicy
members?: UserUncheckedCreateNestedManyWithoutTeamInput
invites?: TeamInviteUncheckedCreateNestedManyWithoutTeamInput
matchPlayers?: MatchPlayerUncheckedCreateNestedManyWithoutTeamInput
@ -33059,6 +33168,7 @@ export namespace Prisma {
createdAt?: DateTimeFieldUpdateOperationsInput | Date | string
activePlayers?: TeamUpdateactivePlayersInput | string[]
inactivePlayers?: TeamUpdateinactivePlayersInput | string[]
joinPolicy?: EnumTeamJoinPolicyFieldUpdateOperationsInput | $Enums.TeamJoinPolicy
leader?: UserUpdateOneWithoutLedTeamNestedInput
members?: UserUpdateManyWithoutTeamNestedInput
invites?: TeamInviteUpdateManyWithoutTeamNestedInput
@ -33078,6 +33188,7 @@ export namespace Prisma {
createdAt?: DateTimeFieldUpdateOperationsInput | Date | string
activePlayers?: TeamUpdateactivePlayersInput | string[]
inactivePlayers?: TeamUpdateinactivePlayersInput | string[]
joinPolicy?: EnumTeamJoinPolicyFieldUpdateOperationsInput | $Enums.TeamJoinPolicy
members?: UserUncheckedUpdateManyWithoutTeamNestedInput
invites?: TeamInviteUncheckedUpdateManyWithoutTeamNestedInput
matchPlayers?: MatchPlayerUncheckedUpdateManyWithoutTeamNestedInput
@ -33106,6 +33217,7 @@ export namespace Prisma {
createdAt?: DateTimeFieldUpdateOperationsInput | Date | string
activePlayers?: TeamUpdateactivePlayersInput | string[]
inactivePlayers?: TeamUpdateinactivePlayersInput | string[]
joinPolicy?: EnumTeamJoinPolicyFieldUpdateOperationsInput | $Enums.TeamJoinPolicy
leader?: UserUpdateOneWithoutLedTeamNestedInput
members?: UserUpdateManyWithoutTeamNestedInput
invites?: TeamInviteUpdateManyWithoutTeamNestedInput
@ -33125,6 +33237,7 @@ export namespace Prisma {
createdAt?: DateTimeFieldUpdateOperationsInput | Date | string
activePlayers?: TeamUpdateactivePlayersInput | string[]
inactivePlayers?: TeamUpdateinactivePlayersInput | string[]
joinPolicy?: EnumTeamJoinPolicyFieldUpdateOperationsInput | $Enums.TeamJoinPolicy
members?: UserUncheckedUpdateManyWithoutTeamNestedInput
invites?: TeamInviteUncheckedUpdateManyWithoutTeamNestedInput
matchPlayers?: MatchPlayerUncheckedUpdateManyWithoutTeamNestedInput
@ -33333,6 +33446,7 @@ export namespace Prisma {
createdAt?: Date | string
activePlayers?: TeamCreateactivePlayersInput | string[]
inactivePlayers?: TeamCreateinactivePlayersInput | string[]
joinPolicy?: $Enums.TeamJoinPolicy
leader?: UserCreateNestedOneWithoutLedTeamInput
members?: UserCreateNestedManyWithoutTeamInput
invites?: TeamInviteCreateNestedManyWithoutTeamInput
@ -33352,6 +33466,7 @@ export namespace Prisma {
createdAt?: Date | string
activePlayers?: TeamCreateactivePlayersInput | string[]
inactivePlayers?: TeamCreateinactivePlayersInput | string[]
joinPolicy?: $Enums.TeamJoinPolicy
members?: UserUncheckedCreateNestedManyWithoutTeamInput
invites?: TeamInviteUncheckedCreateNestedManyWithoutTeamInput
matchesAsTeamA?: MatchUncheckedCreateNestedManyWithoutTeamAInput
@ -33618,6 +33733,7 @@ export namespace Prisma {
createdAt?: DateTimeFieldUpdateOperationsInput | Date | string
activePlayers?: TeamUpdateactivePlayersInput | string[]
inactivePlayers?: TeamUpdateinactivePlayersInput | string[]
joinPolicy?: EnumTeamJoinPolicyFieldUpdateOperationsInput | $Enums.TeamJoinPolicy
leader?: UserUpdateOneWithoutLedTeamNestedInput
members?: UserUpdateManyWithoutTeamNestedInput
invites?: TeamInviteUpdateManyWithoutTeamNestedInput
@ -33637,6 +33753,7 @@ export namespace Prisma {
createdAt?: DateTimeFieldUpdateOperationsInput | Date | string
activePlayers?: TeamUpdateactivePlayersInput | string[]
inactivePlayers?: TeamUpdateinactivePlayersInput | string[]
joinPolicy?: EnumTeamJoinPolicyFieldUpdateOperationsInput | $Enums.TeamJoinPolicy
members?: UserUncheckedUpdateManyWithoutTeamNestedInput
invites?: TeamInviteUncheckedUpdateManyWithoutTeamNestedInput
matchesAsTeamA?: MatchUncheckedUpdateManyWithoutTeamANestedInput
@ -34293,6 +34410,7 @@ export namespace Prisma {
createdAt?: Date | string
activePlayers?: TeamCreateactivePlayersInput | string[]
inactivePlayers?: TeamCreateinactivePlayersInput | string[]
joinPolicy?: $Enums.TeamJoinPolicy
leader?: UserCreateNestedOneWithoutLedTeamInput
members?: UserCreateNestedManyWithoutTeamInput
invites?: TeamInviteCreateNestedManyWithoutTeamInput
@ -34312,6 +34430,7 @@ export namespace Prisma {
createdAt?: Date | string
activePlayers?: TeamCreateactivePlayersInput | string[]
inactivePlayers?: TeamCreateinactivePlayersInput | string[]
joinPolicy?: $Enums.TeamJoinPolicy
members?: UserUncheckedCreateNestedManyWithoutTeamInput
invites?: TeamInviteUncheckedCreateNestedManyWithoutTeamInput
matchPlayers?: MatchPlayerUncheckedCreateNestedManyWithoutTeamInput
@ -34334,6 +34453,7 @@ export namespace Prisma {
createdAt?: Date | string
activePlayers?: TeamCreateactivePlayersInput | string[]
inactivePlayers?: TeamCreateinactivePlayersInput | string[]
joinPolicy?: $Enums.TeamJoinPolicy
leader?: UserCreateNestedOneWithoutLedTeamInput
members?: UserCreateNestedManyWithoutTeamInput
invites?: TeamInviteCreateNestedManyWithoutTeamInput
@ -34353,6 +34473,7 @@ export namespace Prisma {
createdAt?: Date | string
activePlayers?: TeamCreateactivePlayersInput | string[]
inactivePlayers?: TeamCreateinactivePlayersInput | string[]
joinPolicy?: $Enums.TeamJoinPolicy
members?: UserUncheckedCreateNestedManyWithoutTeamInput
invites?: TeamInviteUncheckedCreateNestedManyWithoutTeamInput
matchPlayers?: MatchPlayerUncheckedCreateNestedManyWithoutTeamInput
@ -34649,6 +34770,7 @@ export namespace Prisma {
createdAt?: DateTimeFieldUpdateOperationsInput | Date | string
activePlayers?: TeamUpdateactivePlayersInput | string[]
inactivePlayers?: TeamUpdateinactivePlayersInput | string[]
joinPolicy?: EnumTeamJoinPolicyFieldUpdateOperationsInput | $Enums.TeamJoinPolicy
leader?: UserUpdateOneWithoutLedTeamNestedInput
members?: UserUpdateManyWithoutTeamNestedInput
invites?: TeamInviteUpdateManyWithoutTeamNestedInput
@ -34668,6 +34790,7 @@ export namespace Prisma {
createdAt?: DateTimeFieldUpdateOperationsInput | Date | string
activePlayers?: TeamUpdateactivePlayersInput | string[]
inactivePlayers?: TeamUpdateinactivePlayersInput | string[]
joinPolicy?: EnumTeamJoinPolicyFieldUpdateOperationsInput | $Enums.TeamJoinPolicy
members?: UserUncheckedUpdateManyWithoutTeamNestedInput
invites?: TeamInviteUncheckedUpdateManyWithoutTeamNestedInput
matchPlayers?: MatchPlayerUncheckedUpdateManyWithoutTeamNestedInput
@ -34696,6 +34819,7 @@ export namespace Prisma {
createdAt?: DateTimeFieldUpdateOperationsInput | Date | string
activePlayers?: TeamUpdateactivePlayersInput | string[]
inactivePlayers?: TeamUpdateinactivePlayersInput | string[]
joinPolicy?: EnumTeamJoinPolicyFieldUpdateOperationsInput | $Enums.TeamJoinPolicy
leader?: UserUpdateOneWithoutLedTeamNestedInput
members?: UserUpdateManyWithoutTeamNestedInput
invites?: TeamInviteUpdateManyWithoutTeamNestedInput
@ -34715,6 +34839,7 @@ export namespace Prisma {
createdAt?: DateTimeFieldUpdateOperationsInput | Date | string
activePlayers?: TeamUpdateactivePlayersInput | string[]
inactivePlayers?: TeamUpdateinactivePlayersInput | string[]
joinPolicy?: EnumTeamJoinPolicyFieldUpdateOperationsInput | $Enums.TeamJoinPolicy
members?: UserUncheckedUpdateManyWithoutTeamNestedInput
invites?: TeamInviteUncheckedUpdateManyWithoutTeamNestedInput
matchPlayers?: MatchPlayerUncheckedUpdateManyWithoutTeamNestedInput
@ -35739,6 +35864,7 @@ export namespace Prisma {
createdAt?: Date | string
activePlayers?: TeamCreateactivePlayersInput | string[]
inactivePlayers?: TeamCreateinactivePlayersInput | string[]
joinPolicy?: $Enums.TeamJoinPolicy
leader?: UserCreateNestedOneWithoutLedTeamInput
members?: UserCreateNestedManyWithoutTeamInput
invites?: TeamInviteCreateNestedManyWithoutTeamInput
@ -35758,6 +35884,7 @@ export namespace Prisma {
createdAt?: Date | string
activePlayers?: TeamCreateactivePlayersInput | string[]
inactivePlayers?: TeamCreateinactivePlayersInput | string[]
joinPolicy?: $Enums.TeamJoinPolicy
members?: UserUncheckedCreateNestedManyWithoutTeamInput
invites?: TeamInviteUncheckedCreateNestedManyWithoutTeamInput
matchPlayers?: MatchPlayerUncheckedCreateNestedManyWithoutTeamInput
@ -35925,6 +36052,7 @@ export namespace Prisma {
createdAt?: DateTimeFieldUpdateOperationsInput | Date | string
activePlayers?: TeamUpdateactivePlayersInput | string[]
inactivePlayers?: TeamUpdateinactivePlayersInput | string[]
joinPolicy?: EnumTeamJoinPolicyFieldUpdateOperationsInput | $Enums.TeamJoinPolicy
leader?: UserUpdateOneWithoutLedTeamNestedInput
members?: UserUpdateManyWithoutTeamNestedInput
invites?: TeamInviteUpdateManyWithoutTeamNestedInput
@ -35944,6 +36072,7 @@ export namespace Prisma {
createdAt?: DateTimeFieldUpdateOperationsInput | Date | string
activePlayers?: TeamUpdateactivePlayersInput | string[]
inactivePlayers?: TeamUpdateinactivePlayersInput | string[]
joinPolicy?: EnumTeamJoinPolicyFieldUpdateOperationsInput | $Enums.TeamJoinPolicy
members?: UserUncheckedUpdateManyWithoutTeamNestedInput
invites?: TeamInviteUncheckedUpdateManyWithoutTeamNestedInput
matchPlayers?: MatchPlayerUncheckedUpdateManyWithoutTeamNestedInput

File diff suppressed because one or more lines are too long

View File

@ -1,5 +1,5 @@
{
"name": "prisma-client-593c7e86a193b89a0b02a9e811c3a30c125592905a16d5c01f1f9522bc8c8086",
"name": "prisma-client-e9f85ef18a659dea3be523c367453b73bb1f9ec232929885020f472f49703f84",
"main": "index.js",
"types": "index.d.ts",
"browser": "default.js",

View File

@ -113,6 +113,11 @@ model FaceitGameStat {
@@index([game, elo])
}
enum TeamJoinPolicy {
REQUEST
INVITE_ONLY
}
model Team {
id String @id @default(uuid())
name String @unique
@ -137,6 +142,9 @@ model Team {
schedulesAsTeamB Schedule[] @relation("ScheduleTeamB")
mapVoteSteps MapVoteStep[] @relation("VoteStepTeam")
// Default bleibt REQUEST
joinPolicy TeamJoinPolicy @default(REQUEST)
}
model TeamInvite {

File diff suppressed because one or more lines are too long

View File

@ -26,11 +26,14 @@ export type InvitedPlayer = Player & {
invitationId: string
}
export type TeamJoinPolicy = 'REQUEST' | 'INVITE_ONLY'
export type Team = {
id: string
name?: string | null
logo?: string | null
leader?: Player
joinPolicy: TeamJoinPolicy
activePlayers: Player[]
inactivePlayers: Player[]
invitedPlayers: InvitedPlayer[]