Add team renaming functionality to API and frontend. Implement backend logic for renaming teams, including validation and synchronization with event rosters. Update frontend components to allow team name editing and ensure proper state management. Enhance translations and styles for new features.
This commit is contained in:
@@ -7,7 +7,7 @@ import {
|
||||
import { QueryClient, QueryClientProvider, useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
|
||||
import {
|
||||
Link, Outlet, RouterProvider, createRootRouteWithContext, createRoute,
|
||||
createRouter, redirect,
|
||||
createRouter, redirect, useRouterState,
|
||||
} from '@tanstack/react-router'
|
||||
import {
|
||||
api, subscribeToEvents, type EventInput, type MapOutcome, type MixEvent,
|
||||
@@ -81,6 +81,7 @@ function AppShell() {
|
||||
void client.invalidateQueries({ queryKey: ['tournament'] })
|
||||
} else if (scope === 'team') {
|
||||
void client.invalidateQueries({ queryKey: ['teams'] })
|
||||
void client.invalidateQueries({ queryKey: ['series'] })
|
||||
} else if (message.topic === 'events') {
|
||||
void client.invalidateQueries({ queryKey: ['events'] })
|
||||
} else if (message.topic === 'players') {
|
||||
@@ -645,9 +646,31 @@ function LiveSeriesPage() {
|
||||
}
|
||||
|
||||
function Scoreboard({ series }: { series: Series }) {
|
||||
const controlPage = useRouterState({ select: (state) => state.location.pathname.startsWith('/series/') })
|
||||
const session = useQuery({ queryKey: ['session'], queryFn: api.session, enabled: controlPage, staleTime: Infinity })
|
||||
const alphaCaptain = series.teamAlpha.members.find(({ player }) => player.id === series.teamAlpha.captainId)?.player.displayName
|
||||
const betaCaptain = series.teamBeta.members.find(({ player }) => player.id === series.teamBeta.captainId)?.player.displayName
|
||||
return <section className="scoreboard" aria-label="Current series score"><div className="score-team alpha"><span>Team Alpha</span><h2>{series.teamAlpha.name}</h2><small>{alphaCaptain ? `Captain · ${alphaCaptain}` : 'Captain not assigned'}</small></div><div className="series-score"><span>Best of 3</span><strong>{series.score.alpha}<i>:</i>{series.score.beta}</strong><Badge tone="live"><Radio />{series.status}</Badge></div><div className="score-team beta"><span>Team Beta</span><h2>{series.teamBeta.name}</h2><small>{betaCaptain ? `Captain · ${betaCaptain}` : 'Captain not assigned'}</small></div></section>
|
||||
const canRenameAlpha = controlPage && (isStaff(session.data?.account.role) || session.data?.player.id === series.teamAlpha.captainId)
|
||||
const canRenameBeta = controlPage && (isStaff(session.data?.account.role) || session.data?.player.id === series.teamBeta.captainId)
|
||||
return <><section className="scoreboard" aria-label="Current series score"><div className="score-team alpha"><span>Team Alpha</span><h2>{series.teamAlpha.name}</h2><small>{alphaCaptain ? `Captain · ${alphaCaptain}` : 'Captain not assigned'}</small></div><div className="series-score"><span>Best of 3</span><strong>{series.score.alpha}<i>:</i>{series.score.beta}</strong><Badge tone="live"><Radio />{series.status}</Badge></div><div className="score-team beta"><span>Team Beta</span><h2>{series.teamBeta.name}</h2><small>{betaCaptain ? `Captain · ${betaCaptain}` : 'Captain not assigned'}</small></div></section>{(canRenameAlpha || canRenameBeta) && <div className="team-name-controls">{canRenameAlpha && <TeamNameEditor team={series.teamAlpha} seriesId={series.id} />}{canRenameBeta && <TeamNameEditor team={series.teamBeta} seriesId={series.id} />}</div>}</>
|
||||
}
|
||||
|
||||
function TeamNameEditor({ team, seriesId }: { team: Team; seriesId: string }) {
|
||||
const client = useQueryClient()
|
||||
const [name, setName] = useState(team.name)
|
||||
useEffect(() => setName(team.name), [team.name])
|
||||
const rename = useMutation({
|
||||
mutationFn: () => api.renameTeam(team.id, name),
|
||||
onSuccess: (updated) => {
|
||||
client.setQueryData<Series>(['series', seriesId], (current) => current ? {
|
||||
...current,
|
||||
teamAlpha: current.teamAlpha.id === updated.id ? { ...current.teamAlpha, name: updated.name } : current.teamAlpha,
|
||||
teamBeta: current.teamBeta.id === updated.id ? { ...current.teamBeta, name: updated.name } : current.teamBeta,
|
||||
} : current)
|
||||
void client.invalidateQueries({ queryKey: ['teams'] })
|
||||
},
|
||||
})
|
||||
return <form className="team-name-editor" onSubmit={(event) => { event.preventDefault(); rename.mutate() }}><label><span>Team name</span><input minLength={2} maxLength={32} required value={name} onChange={(event) => setName(event.target.value)} /></label><button className="button secondary" disabled={rename.isPending || name.trim() === team.name}>{rename.isPending ? 'Saving…' : 'Rename'}</button>{rename.isError && <small className="error-note">{rename.error.message}</small>}</form>
|
||||
}
|
||||
function MapTimeline({ series }: { series: Series }) { return <section className="card map-timeline"><div className="section-title"><div><h2>Map timeline</h2><p>First to 2 wins</p></div></div>{series.maps.length === 0 ? <p className="empty-note">No results recorded.</p> : series.maps.map((map) => <div className={`map-row ${map.status}`} key={map.number}><span>0{map.number}</span><div><strong>{map.name}</strong><small>{map.mode} · {map.status}</small></div>{map.winner && <Badge tone={map.winner === 'alpha' ? 'warning' : 'blue'}>{map.winner === 'alpha' ? 'A' : map.winner === 'beta' ? 'B' : 'Draw'}</Badge>}</div>)}</section> }
|
||||
function AuditLog({ series }: { series: Series }) { return <section className="card audit-log"><div className="section-title"><div><h2>Match history</h2><p>Auditable server actions</p></div></div>{series.audit.length === 0 ? <p className="empty-note">No actions yet.</p> : series.audit.slice(-4).reverse().map((a) => <div key={a.id}><span className="history-dot" /><p><strong>{a.summary}</strong><small>{a.actorName} · {new Date(a.createdAt).toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit' })}</small></p></div>)}</section> }
|
||||
|
||||
Reference in New Issue
Block a user