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 (
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 EndTimeCell = ({ epoch }: { epoch?: number }) => {
const { data, loading } = useEpochInfoQuery({
variables: {
epochId: epoch ? epoch.toString() : undefined,
},
fetchPolicy: 'cache-and-network',
});
if (loading) return ;
if (data) {
return format(
new Date(data.epoch.timestamps.expiry),
'yyyy/MM/dd hh:mm:ss'
);
}
return null;
};
const RewardAssetCell = ({ asset }: { asset: Asset }) => {
const open = useAssetDetailsDialogStore((state) => state.open);
const ref = useRef(null);
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 (
);
};