From 2a19e6bb792ad25c1b0acb01f4e255c4f276a687 Mon Sep 17 00:00:00 2001 From: Kalle <38327916+Sendouc@users.noreply.github.com> Date: Sat, 4 Apr 2026 14:15:57 +0300 Subject: [PATCH] Subbed out --- .../match-page/MatchTabs.module.css | 36 +++++++++++-- app/components/match-page/MatchTabs.tsx | 54 ++++++++++++++++++- .../routes/match-page-test.tsx | 8 +++ locales/da/q.json | 1 + locales/de/q.json | 1 + locales/en/q.json | 1 + locales/es-ES/q.json | 1 + locales/es-US/q.json | 1 + locales/fr-CA/q.json | 1 + locales/fr-EU/q.json | 1 + locales/he/q.json | 1 + locales/it/q.json | 1 + locales/ja/q.json | 1 + locales/ko/q.json | 1 + locales/nl/q.json | 1 + locales/pl/q.json | 1 + locales/pt-BR/q.json | 1 + locales/ru/q.json | 1 + locales/zh/q.json | 1 + 19 files changed, 107 insertions(+), 7 deletions(-) diff --git a/app/components/match-page/MatchTabs.module.css b/app/components/match-page/MatchTabs.module.css index 5db591a40..2c1252bb5 100644 --- a/app/components/match-page/MatchTabs.module.css +++ b/app/components/match-page/MatchTabs.module.css @@ -2,11 +2,7 @@ & [class*="tabPanel"] { background-color: var(--color-bg-high); border-radius: 0 0 var(--radius-box) var(--radius-box); - padding: var(--s-4); - - &:has([class*="chatContainer"]) { - padding: var(--s-1) var(--s-4); - } + padding: var(--s-6) var(--s-4); } } @@ -54,6 +50,36 @@ } } +.subbedOutTrigger { + display: flex; + align-items: center; + gap: var(--s-1-5); +} + +.subbedOutIcon { + --subbed-out-icon-size: 24px; + display: flex; + align-items: center; + justify-content: center; + width: var(--subbed-out-icon-size); + height: var(--subbed-out-icon-size); + border-radius: 100%; + background-color: var(--color-bg-higher); +} + +.subbedOutPopover { + display: flex; + flex-direction: column; + gap: var(--s-2); +} + +.subbedOutHeader { + font-size: var(--font-xs); + font-weight: var(--weight-semi); + color: var(--color-text-high); + text-transform: uppercase; +} + .teamOneDot { border-radius: 100%; background-color: var(--color-accent); diff --git a/app/components/match-page/MatchTabs.tsx b/app/components/match-page/MatchTabs.tsx index 0fb1bfbcb..3164f9b03 100644 --- a/app/components/match-page/MatchTabs.tsx +++ b/app/components/match-page/MatchTabs.tsx @@ -1,10 +1,12 @@ -import { DoorOpen, Tally5, Users } from "lucide-react"; +import { Armchair, DoorOpen, Tally5, Users } from "lucide-react"; import { QRCodeSVG } from "qrcode.react"; import type * as React from "react"; import { useTranslation } from "react-i18next"; import { Link, useSearchParams } from "react-router"; import { Alert } from "~/components/Alert"; import { Avatar } from "~/components/Avatar"; +import { SendouButton } from "~/components/elements/Button"; +import { SendouPopover } from "~/components/elements/Popover"; import invariant from "~/utils/invariant"; import type { CommonUser } from "~/utils/kysely.server"; import { userPage } from "~/utils/urls"; @@ -80,6 +82,8 @@ interface RosterTabTeam { avatar?: string; }; members: Array; + /** Sub user ids i.e. those who are not the current active roster */ + subbedOut?: Array; } interface MatchRosterTabProps { @@ -107,6 +111,14 @@ function TeamRoster({ const dotClassName = side === "alpha" ? styles.teamOneDot : styles.teamTwoDot; const label = side === "alpha" ? "Alpha" : "Bravo"; + const subbedOutSet = new Set(team.subbedOut); + const activeMembers = team.members.filter( + (member) => !subbedOutSet.has(member.id), + ); + const subbedOutMembers = team.members.filter((member) => + subbedOutSet.has(member.id), + ); + return (
{team.team ? ( @@ -127,7 +139,7 @@ function TeamRoster({ ) : null} {team.members.length > 0 ? (
    - {team.members.map((member) => ( + {activeMembers.map((member) => (
  • ))} + {subbedOutMembers.length > 0 ? ( +
  • + +
  • + ) : null}
) : null}
); } +function SubbedOutPopover({ members }: { members: Array }) { + const { t } = useTranslation(["q"]); + + return ( + +
+
+ +
+ +{members.length} +
+ + } + > +
+
{t("q:match.subbedOut")}
+ {members.map((member) => ( + + + {member.username} + + ))} +
+
+ ); +} + export function MatchActionTab() { return Report content; } diff --git a/app/features/match-page-test/routes/match-page-test.tsx b/app/features/match-page-test/routes/match-page-test.tsx index b4b74cf74..69493e564 100644 --- a/app/features/match-page-test/routes/match-page-test.tsx +++ b/app/features/match-page-test/routes/match-page-test.tsx @@ -171,7 +171,15 @@ export default function MatchPageTestRoute() { discordAvatar: null, customUrl: null, }, + { + id: 9, + username: "Poppy", + discordId: "567", + discordAvatar: null, + customUrl: null, + }, ], + subbedOut: [9], }, { team: { name: "Question Mark", url: "/t/question-mark" }, diff --git a/locales/da/q.json b/locales/da/q.json index d7ff33bf8..c52bd2bd9 100644 --- a/locales/da/q.json +++ b/locales/da/q.json @@ -164,6 +164,7 @@ "match.outcome.loss": "", "match.screen.ban": "", "match.screen.allowed": "", + "match.subbedOut": "", "preparing.joinQ": "", "tiers.currentCriteria": "", "tiers.info.p1": "", diff --git a/locales/de/q.json b/locales/de/q.json index bc86a45b4..87e259850 100644 --- a/locales/de/q.json +++ b/locales/de/q.json @@ -164,6 +164,7 @@ "match.outcome.loss": "", "match.screen.ban": "", "match.screen.allowed": "", + "match.subbedOut": "", "preparing.joinQ": "", "tiers.currentCriteria": "", "tiers.info.p1": "", diff --git a/locales/en/q.json b/locales/en/q.json index a15317c85..d9b83ffe3 100644 --- a/locales/en/q.json +++ b/locales/en/q.json @@ -164,6 +164,7 @@ "match.outcome.loss": "loss", "match.screen.ban": "Weapons with {{special}} are not allowed in this match", "match.screen.allowed": "Weapons with {{special}} are allowed in this match", + "match.subbedOut": "Subbed out", "preparing.joinQ": "Join the queue", "tiers.currentCriteria": "Current criteria", "tiers.info.p1": "For example, Leviathan is the top 5% of players. Diamond is the 85th percentile etc.", diff --git a/locales/es-ES/q.json b/locales/es-ES/q.json index 96de840be..ec17e8078 100644 --- a/locales/es-ES/q.json +++ b/locales/es-ES/q.json @@ -164,6 +164,7 @@ "match.outcome.loss": "perdida", "match.screen.ban": "Armas con {{special}} son prohibidas para este partido", "match.screen.allowed": "Armas con {{special}} se permiten para este partido", + "match.subbedOut": "", "preparing.joinQ": "Unirte a la fila", "tiers.currentCriteria": "Criterios actuales", "tiers.info.p1": "Por ejemplo, Leviathan se encuentra entre el 5% de los mejores jugadores. Diamond es el percentil 85, etc.", diff --git a/locales/es-US/q.json b/locales/es-US/q.json index 987f82a33..f608789f5 100644 --- a/locales/es-US/q.json +++ b/locales/es-US/q.json @@ -164,6 +164,7 @@ "match.outcome.loss": "perdida", "match.screen.ban": "Armas con {{special}} son prohibidas para este partido", "match.screen.allowed": "Armas con {{special}} se permiten para este partido", + "match.subbedOut": "", "preparing.joinQ": "Unirte a la fila", "tiers.currentCriteria": "Criterios actuales", "tiers.info.p1": "Por ejemplo, Leviathan se encuentra entre el 5% de los mejores jugadores. Diamond es el percentil 85, etc.", diff --git a/locales/fr-CA/q.json b/locales/fr-CA/q.json index 5fbdf5d21..316270ded 100644 --- a/locales/fr-CA/q.json +++ b/locales/fr-CA/q.json @@ -164,6 +164,7 @@ "match.outcome.loss": "", "match.screen.ban": "", "match.screen.allowed": "", + "match.subbedOut": "", "preparing.joinQ": "", "tiers.currentCriteria": "", "tiers.info.p1": "", diff --git a/locales/fr-EU/q.json b/locales/fr-EU/q.json index b80fa887d..2b7e3e4eb 100644 --- a/locales/fr-EU/q.json +++ b/locales/fr-EU/q.json @@ -164,6 +164,7 @@ "match.outcome.loss": "Perdu", "match.screen.ban": "Les armes avec {{special}} ne sont pas autoriser pendant ce match", "match.screen.allowed": "Les armes avec {{special}} sont autoriser pendant ce match", + "match.subbedOut": "", "preparing.joinQ": "Rejoindre la queue", "tiers.currentCriteria": "Critères actuels", "tiers.info.p1": "Par exemple, Les Léviathans font partie des 5 % des meilleurs joueurs. Le diamant est le top 15%, etc.", diff --git a/locales/he/q.json b/locales/he/q.json index 1145a2285..b5c6255a7 100644 --- a/locales/he/q.json +++ b/locales/he/q.json @@ -164,6 +164,7 @@ "match.outcome.loss": "", "match.screen.ban": "", "match.screen.allowed": "", + "match.subbedOut": "", "preparing.joinQ": "", "tiers.currentCriteria": "", "tiers.info.p1": "", diff --git a/locales/it/q.json b/locales/it/q.json index 0d4dc44bd..412f12414 100644 --- a/locales/it/q.json +++ b/locales/it/q.json @@ -164,6 +164,7 @@ "match.outcome.loss": "sconfitta", "match.screen.ban": "Armi con {{special}} non sono permesse in questo match", "match.screen.allowed": "Armi con {{special}} non sono permesse in questo match", + "match.subbedOut": "", "preparing.joinQ": "Unisciti alla coda", "tiers.currentCriteria": "Criterio corrente", "tiers.info.p1": "Per esempio Leviathan è la top 5% dei giocatori. Diamante è l' 85esimo percentile etc.", diff --git a/locales/ja/q.json b/locales/ja/q.json index 5423c11a8..4c5b59bb6 100644 --- a/locales/ja/q.json +++ b/locales/ja/q.json @@ -164,6 +164,7 @@ "match.outcome.loss": "負け", "match.screen.ban": "{{special}}が付いてる武器はこのマッチでは禁止です", "match.screen.allowed": "{{special}}が付いてる武器は使用可能です", + "match.subbedOut": "", "preparing.joinQ": "列に入る", "tiers.currentCriteria": "現在の基準", "tiers.info.p1": "例として、Leviathanはプレイヤーの上位5%、Diamondは上位15%", diff --git a/locales/ko/q.json b/locales/ko/q.json index bc86a45b4..87e259850 100644 --- a/locales/ko/q.json +++ b/locales/ko/q.json @@ -164,6 +164,7 @@ "match.outcome.loss": "", "match.screen.ban": "", "match.screen.allowed": "", + "match.subbedOut": "", "preparing.joinQ": "", "tiers.currentCriteria": "", "tiers.info.p1": "", diff --git a/locales/nl/q.json b/locales/nl/q.json index bc86a45b4..87e259850 100644 --- a/locales/nl/q.json +++ b/locales/nl/q.json @@ -164,6 +164,7 @@ "match.outcome.loss": "", "match.screen.ban": "", "match.screen.allowed": "", + "match.subbedOut": "", "preparing.joinQ": "", "tiers.currentCriteria": "", "tiers.info.p1": "", diff --git a/locales/pl/q.json b/locales/pl/q.json index bc86a45b4..87e259850 100644 --- a/locales/pl/q.json +++ b/locales/pl/q.json @@ -164,6 +164,7 @@ "match.outcome.loss": "", "match.screen.ban": "", "match.screen.allowed": "", + "match.subbedOut": "", "preparing.joinQ": "", "tiers.currentCriteria": "", "tiers.info.p1": "", diff --git a/locales/pt-BR/q.json b/locales/pt-BR/q.json index 85285095d..b0b36156a 100644 --- a/locales/pt-BR/q.json +++ b/locales/pt-BR/q.json @@ -164,6 +164,7 @@ "match.outcome.loss": "derrota", "match.screen.ban": "Armas com o/a {{special}} não são permitidas nessa partida", "match.screen.allowed": "Armas com o/a {{special}} são permitidas nessa partida", + "match.subbedOut": "", "preparing.joinQ": "Entrar na fila", "tiers.currentCriteria": "Critérios atuais", "tiers.info.p1": "Por exemplo, Leviathan é o top 5% dos jogadores. Diamond é top 15% e etc.", diff --git a/locales/ru/q.json b/locales/ru/q.json index 295a99898..0279a10b2 100644 --- a/locales/ru/q.json +++ b/locales/ru/q.json @@ -164,6 +164,7 @@ "match.outcome.loss": "проигрыш", "match.screen.ban": "Оружия с {{special}} запрещены в этом матче", "match.screen.allowed": "Оружия с {{special}} разрешены в этом матче", + "match.subbedOut": "", "preparing.joinQ": "Присоединиться к очереди", "tiers.currentCriteria": "Текущие критерии", "tiers.info.p1": "Например, Leviathan - топ 5% игроков, Diamond - 85 процентиль и т.д.", diff --git a/locales/zh/q.json b/locales/zh/q.json index 0e8a201ba..9afba39bc 100644 --- a/locales/zh/q.json +++ b/locales/zh/q.json @@ -164,6 +164,7 @@ "match.outcome.loss": "负", "match.screen.ban": "本次对战禁止使用特殊武器为 {{special}} 的武器", "match.screen.allowed": "本次对战允许使用特殊武器为 {{special}} 的武器", + "match.subbedOut": "", "preparing.joinQ": "开始匹配", "tiers.currentCriteria": "当前规则", "tiers.info.p1": "比如说,Leviathan是前5%的玩家,Diamond是前15%的玩家。",