import { useState, type ButtonHTMLAttributes, useRef } from 'react'; import { Link, useParams } from 'react-router-dom'; import orderBy from 'lodash/orderBy'; import { Splash, truncateMiddle, Loader, Dialog, Button, VegaIcon, VegaIconNames, } from '@vegaprotocol/ui-toolkit'; import { TransferStatus, type Asset } from '@vegaprotocol/types'; import classNames from 'classnames'; import { useT } from '../../lib/use-t'; import { Table } from '../../components/table'; import { addDecimalsFormatNumberQuantum, formatNumber, getDateTimeFormat, } from '@vegaprotocol/utils'; import { useTeam, type TeamStats as ITeamStats, type Team as TeamType, type Member, } from '../../lib/hooks/use-team'; import { DApp, EXPLORER_PARTIES, useLinks } from '@vegaprotocol/environment'; import { TeamAvatar } from '../../components/competitions/team-avatar'; import { TeamStats } from '../../components/competitions/team-stats'; import { usePageTitle } from '../../lib/hooks/use-page-title'; import { ErrorBoundary } from '../../components/error-boundary'; import { LayoutWithGradient } from '../../components/layouts-inner'; import { useVegaWallet } from '@vegaprotocol/wallet'; import { JoinTeam } from './join-team'; import { UpdateTeamButton } from './update-team-button'; import { type TeamGame, useGames, areTeamGames, } from '../../lib/hooks/use-games'; import { useEpochInfoQuery } from '../../lib/hooks/__generated__/Epoch'; import { useAssetDetailsDialogStore } from '@vegaprotocol/assets'; import { ActiveRewardCard, DispatchMetricInfo, } from '../../components/rewards-container/active-rewards'; import { type MarketMap, useMarketsMapProvider } from '@vegaprotocol/markets'; import format from 'date-fns/format'; import { type EnrichedRewardTransfer, isScopedToTeams, useRewards, } from '../../lib/hooks/use-rewards'; export const CompetitionsTeam = () => { const t = useT(); const { teamId } = useParams<{ teamId: string }>(); usePageTitle([t('Competitions'), t('Team')]); return ( ); }; const TeamPageContainer = ({ teamId }: { teamId: string | undefined }) => { const t = useT(); const { pubKey } = useVegaWallet(); const { data, team, partyTeam, stats, members, loading, refetch } = useTeam( teamId, pubKey || undefined ); const { data: games, loading: gamesLoading } = useGames(teamId); const { data: transfersData, loading: transfersLoading } = useRewards({ onlyActive: false, scopeToTeams: true, }); const { data: markets } = useMarketsMapProvider(); // only show spinner on first load so when users join teams its smoother if (!data && loading) { return ( ); } if (!team) { return (

{t('Page not found')}

); } return ( ); }; const TeamPage = ({ team, partyTeam, stats, members, games, gamesLoading, transfers, transfersLoading, allMarkets, refetch, }: { team: TeamType; partyTeam?: TeamType; stats?: ITeamStats; members?: Member[]; games?: TeamGame[]; gamesLoading?: boolean; transfers?: EnrichedRewardTransfer[]; transfersLoading?: boolean; allMarkets?: MarketMap; refetch: () => void; }) => { const t = useT(); const [showGames, setShowGames] = useState(true); return (

{team.name}

setShowGames(true)} data-testid="games-toggle" > {t('Results {{games}}', { replace: { games: gamesLoading ? '' : games ? `(${games.length})` : '(0)', }, })} setShowGames(false)} data-testid="members-toggle" > {t('Members ({{count}})', { count: members ? members.length : 0, })}
{showGames ? ( ) : ( )}
); }; const Games = ({ games, gamesLoading, transfers, transfersLoading, allMarkets, }: { games?: TeamGame[]; gamesLoading?: boolean; transfers?: EnrichedRewardTransfer[]; transfersLoading?: boolean; allMarkets?: MarketMap; }) => { const t = useT(); if (gamesLoading) { return (
); } if (!games?.length) { return

{t('No game results available')}

; } const dependable = (value: string | JSX.Element) => { if (transfersLoading) return ; return value; }; return ( ({ ...c, headerClassName: 'text-left' }))} data={games.map((game) => { let transfer = transfers?.find((t) => { if (!isScopedToTeams(t)) return false; const idMatch = t.transfer.gameId === game.id; const metricMatch = t.transfer.kind.dispatchStrategy?.dispatchMetric === game.team.rewardMetric; const start = t.transfer.kind.startEpoch <= game.epoch; const end = t.transfer.kind.endEpoch ? t.transfer.kind.endEpoch >= game.epoch : true; const rejected = t.transfer.status === TransferStatus.STATUS_REJECTED; return idMatch && metricMatch && start && end && !rejected; }); if (!transfer || !isScopedToTeams(transfer)) transfer = undefined; const asset = transfer?.transfer.asset; const dailyAmount = asset && transfer ? addDecimalsFormatNumberQuantum( transfer.transfer.amount, asset.decimals, asset.quantum ) : '-'; const earnedAmount = asset ? addDecimalsFormatNumberQuantum( game.team.rewardEarned, asset.decimals, asset.quantum ) : '-'; const totalAmount = asset ? addDecimalsFormatNumberQuantum( game.team.totalRewardsEarned, asset.decimals, asset.quantum ) : '-'; const assetSymbol = asset ? : '-'; return { id: game.id, amount: dependable(earnedAmount), asset: dependable(assetSymbol), daily: dependable(dailyAmount), endtime: , epoch: game.epoch, participatingMembers: game.numberOfParticipants, participatingTeams: game.entities.length, rank: game.team.rank, total: totalAmount, // type: DispatchMetricLabels[game.team.rewardMetric as DispatchMetric], type: dependable( ), }; })} noCollapse={false} /> ); }; const Members = ({ members }: { members?: Member[] }) => { const t = useT(); if (!members?.length) { return

{t('No members')}

; } const data = orderBy( members.map((m) => ({ referee: , rewards: formatNumber(m.totalQuantumRewards), volume: formatNumber(m.totalQuantumVolume), gamesPlayed: formatNumber(m.totalGamesPlayed), joinedAt: getDateTimeFormat().format(new Date(m.joinedAt)), joinedAtEpoch: Number(m.joinedAtEpoch), })), 'joinedAtEpoch', 'desc' ); return (
); }; const RefereeLink = ({ pubkey, isCreator, }: { pubkey: string; isCreator: boolean; }) => { const t = useT(); const linkCreator = useLinks(DApp.Explorer); const link = linkCreator(EXPLORER_PARTIES.replace(':id', pubkey)); return ( <> {truncateMiddle(pubkey)} {' '} {isCreator ? t('Owner') : ''} ); }; const ToggleButton = ({ active, ...props }: ButtonHTMLAttributes & { active: boolean }) => { return ( ); }; const GameTypeCell = ({ transfer, allMarkets, }: { transfer?: EnrichedRewardTransfer; allMarkets?: MarketMap; }) => { const [open, setOpen] = useState(false); const ref = useRef(null); if (!transfer) return '-'; return ( <> setOpen(isOpen)} trigger={ref.current} transfer={transfer} allMarkets={allMarkets} /> ); }; const ActiveRewardCardDialog = ({ open, onChange, trigger, transfer, allMarkets, }: { open: boolean; onChange: (isOpen: boolean) => void; trigger?: HTMLElement | null; transfer: EnrichedRewardTransfer; allMarkets?: MarketMap; }) => { const t = useT(); const { data } = useEpochInfoQuery(); return ( onChange(isOpen)} icon={} onCloseAutoFocus={(e) => { /** * This mimics radix's default behaviour that focuses the dialog's * trigger after closing itself */ if (trigger) { e.preventDefault(); trigger.focus(); } }} >
); };