diff --git a/app/features/sendouq/QRepository.server.ts b/app/features/sendouq/QRepository.server.ts index b3ac34015..836d172e4 100644 --- a/app/features/sendouq/QRepository.server.ts +++ b/app/features/sendouq/QRepository.server.ts @@ -278,9 +278,10 @@ export function deletePrivateUserNote({ } export async function usersThatTrusted(userId: number) { - const teamIds = await db + const teams = await db .selectFrom("TeamMemberWithSecondary") - .select("teamId") + .innerJoin("Team", "Team.id", "TeamMemberWithSecondary.teamId") + .select(["Team.id", "Team.name", "TeamMemberWithSecondary.isMainTeam"]) .where("userId", "=", userId) .execute(); @@ -288,24 +289,47 @@ export async function usersThatTrusted(userId: number) { .selectFrom("TeamMemberWithSecondary") .innerJoin("User", "User.id", "TeamMemberWithSecondary.userId") .innerJoin("UserFriendCode", "UserFriendCode.userId", "User.id") - .select([...COMMON_USER_FIELDS, "User.inGameName"]) + .select([ + ...COMMON_USER_FIELDS, + "User.inGameName", + "TeamMemberWithSecondary.teamId", + ]) .where( "TeamMemberWithSecondary.teamId", "in", - teamIds.map((t) => t.teamId), + teams.map((t) => t.id), ) .union((eb) => eb .selectFrom("TrustRelationship") .innerJoin("User", "User.id", "TrustRelationship.trustGiverUserId") .innerJoin("UserFriendCode", "UserFriendCode.userId", "User.id") - .select([...COMMON_USER_FIELDS, "User.inGameName"]) + .select([ + ...COMMON_USER_FIELDS, + "User.inGameName", + sql.raw("null").as("teamId"), + ]) .where("TrustRelationship.trustReceiverUserId", "=", userId), ) - .orderBy("User.username asc") .execute(); const rowsWithoutBanned = rows.filter((row) => !userIsBanned(row.id)); - return rowsWithoutBanned; + const teamMemberIds = rowsWithoutBanned + .filter((row) => row.teamId) + .map((row) => row.id); + + // we want user to show twice if member of two different teams + // but we don't want a user from the team to show in teamless section + const deduplicatedRows = rowsWithoutBanned.filter( + (row) => row.teamId || !teamMemberIds.includes(row.id), + ); + + // done here at not sql just because it was easier to do here ignoring case + deduplicatedRows.sort((a, b) => a.username.localeCompare(b.username)); + + return { + teams: teams.sort((a, b) => b.isMainTeam - a.isMainTeam), + trusters: deduplicatedRows, + }; } diff --git a/app/features/sendouq/components/MemberAdder.tsx b/app/features/sendouq/components/MemberAdder.tsx index 4c53eb49b..16b3d7112 100644 --- a/app/features/sendouq/components/MemberAdder.tsx +++ b/app/features/sendouq/components/MemberAdder.tsx @@ -98,7 +98,7 @@ function TrusterDropdown({ groupMemberIds: number[]; }) { const { t } = useTranslation(["q"]); - const { trusters } = useTrusted(); + const { trusters, teams } = useTrusted(); if (!trusters || trusters.length === 0) { return ( @@ -115,6 +115,16 @@ function TrusterDropdown({ (truster) => !groupMemberIds.includes(truster.id), ); + const othersOptions = trustersNotInGroup + .filter((player) => !player.teamId) + .map((player) => { + return ( + + ); + }); + return ( ); } diff --git a/app/features/sendouq/routes/q.preparing.tsx b/app/features/sendouq/routes/q.preparing.tsx index 42c9301a8..b42254e04 100644 --- a/app/features/sendouq/routes/q.preparing.tsx +++ b/app/features/sendouq/routes/q.preparing.tsx @@ -88,7 +88,7 @@ export const action = async ({ request }: ActionFunctionArgs) => { } validate( - (await QRepository.usersThatTrusted(user.id)).some( + (await QRepository.usersThatTrusted(user.id)).trusters.some( (trusterUser) => trusterUser.id === data.id, ), "Not trusted", diff --git a/app/features/tournament/actions/to.$id.register.server.ts b/app/features/tournament/actions/to.$id.register.server.ts index d14be7702..7880a84d0 100644 --- a/app/features/tournament/actions/to.$id.register.server.ts +++ b/app/features/tournament/actions/to.$id.register.server.ts @@ -200,7 +200,7 @@ export const action: ActionFunction = async ({ request, params }) => { ); validate(ownTeam); validate( - (await QRepository.usersThatTrusted(user.id)).some( + (await QRepository.usersThatTrusted(user.id)).trusters.some( (trusterPlayer) => trusterPlayer.id === data.userId, ), "No trust given from this user", diff --git a/app/features/tournament/routes/to.$id.register.tsx b/app/features/tournament/routes/to.$id.register.tsx index 178572f98..5c836e702 100644 --- a/app/features/tournament/routes/to.$id.register.tsx +++ b/app/features/tournament/routes/to.$id.register.tsx @@ -903,7 +903,7 @@ function FillRoster({ (ownTeamCheckedIn && ownTeamMembers.length > tournament.minMembersPerTeam); const playersAvailableToDirectlyAdd = (() => { - return (data!.trusterPlayers ?? []).filter((user) => { + return (data!.trusterPlayers?.trusters ?? []).filter((user) => { const isNotInTeam = tournament.ctx.teams.every((team) => team.members.every((member) => member.userId !== user.id), ); @@ -926,7 +926,10 @@ function FillRoster({
{playersAvailableToDirectlyAdd.length > 0 && canAddMembers ? ( <> - + {t("common:or")} ) : null} @@ -1015,13 +1018,25 @@ function FillRoster({ function DirectlyAddPlayerSelect({ players, + teams, }: { - players: { id: number; username: string }[]; + players: { id: number; username: string; teamId?: number }[]; + teams: { id: number; name: string }[]; }) { const { t } = useTranslation(["tournament", "common"]); const fetcher = useFetcher(); const id = React.useId(); + const othersOptions = players + .filter((player) => !player.teamId) + .map((player) => { + return ( + + ); + }); + return (
@@ -1029,13 +1044,26 @@ function DirectlyAddPlayerSelect({ {t("tournament:pre.roster.addTrusted.header")}