From 23bfa19403632b37182e9455be6f3eae5ca8c4f3 Mon Sep 17 00:00:00 2001 From: Kalle <38327916+Sendouc@users.noreply.github.com> Date: Sat, 2 May 2026 13:36:15 +0300 Subject: [PATCH] Allow TO to remove groups from LFG before registration ends Closes #2948 --- .../actions/to.$id.looking.server.ts | 17 ++++++++ .../components/LFGGroupCard.tsx | 43 ++++++++++++++++++- .../tournament-lfg/tournament-lfg-schemas.ts | 4 ++ 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, 79 insertions(+), 1 deletion(-) diff --git a/app/features/tournament-lfg/actions/to.$id.looking.server.ts b/app/features/tournament-lfg/actions/to.$id.looking.server.ts index b9b128976..0537a1d9f 100644 --- a/app/features/tournament-lfg/actions/to.$id.looking.server.ts +++ b/app/features/tournament-lfg/actions/to.$id.looking.server.ts @@ -280,6 +280,23 @@ export const action = async ({ request, params }: ActionFunctionArgs) => { break; } + case "DELETE_GROUP": { + const tournament = await tournamentFromDBCached({ + tournamentId, + user, + }); + errorToastIfFalsy( + tournament.isOrganizer(user), + "Only tournament organizers can remove other groups", + ); + + await TournamentLFGRepository.leaveLfg({ + userId: data.userId, + tournamentId, + }); + + break; + } case "ADD_SUB": { const tournament = await tournamentFromDBCached({ tournamentId, diff --git a/app/features/tournament-lfg/components/LFGGroupCard.tsx b/app/features/tournament-lfg/components/LFGGroupCard.tsx index 7f88e36ee..0924e535a 100644 --- a/app/features/tournament-lfg/components/LFGGroupCard.tsx +++ b/app/features/tournament-lfg/components/LFGGroupCard.tsx @@ -1,5 +1,5 @@ import clsx from "clsx"; -import { Mic, Star, Volume2, VolumeX } from "lucide-react"; +import { Mic, Star, Trash, Volume2, VolumeX } from "lucide-react"; import * as React from "react"; import { Flipped } from "react-flip-toolkit"; import { useTranslation } from "react-i18next"; @@ -8,11 +8,13 @@ import { Avatar } from "~/components/Avatar"; import { Divider } from "~/components/Divider"; import { SendouButton } from "~/components/elements/Button"; import { SendouPopover } from "~/components/elements/Popover"; +import { FormWithConfirm } from "~/components/FormWithConfirm"; import { Image, WeaponImage } from "~/components/Image"; import { SubmitButton } from "~/components/SubmitButton"; import type { Pronouns } from "~/db/tables"; import { useUser } from "~/features/auth/core/user"; import { IS_Q_LOOKING_MOBILE_BREAKPOINT } from "~/features/sendouq/q-constants"; +import { useTournament } from "~/features/tournament/routes/to.$id"; import { SendouForm } from "~/form/SendouForm"; import { useMainContentWidth } from "~/hooks/useMainContentWidth"; import { languagesUnified } from "~/modules/i18n/config"; @@ -58,6 +60,7 @@ export function LFGGroupCard({ const { t } = useTranslation(["common", "q"]); const fetcher = useFetcher(); const user = useUser(); + const tournament = useTournament(); const isOwnGroup = group.id === ownGroup?.id; const showActions = isOwnGroup && group.usersRole === "OWNER"; @@ -66,6 +69,8 @@ export function LFGGroupCard({ ? group.members.find((m) => m.id === user.id) : undefined; + const showOrganizerDelete = !currentMember && tournament.isOrganizer(user); + return (
@@ -118,11 +123,47 @@ export function LFGGroupCard({ ) : null} + {showOrganizerDelete ? ( + + ) : null}
); } +function LFGOrganizerGroupRemover({ group }: { group: LFGGroup }) { + const { t } = useTranslation(["common", "q"]); + + const targetUserId = ( + group.members.find((m) => m.role === "OWNER") ?? group.members[0] + )?.id; + + if (typeof targetUserId !== "number") return null; + + return ( +
+ + } + > + {t("common:actions.delete")} + + +
+ ); +} + function LFGGroupCardContainer({ isOwnGroup, groupId, diff --git a/app/features/tournament-lfg/tournament-lfg-schemas.ts b/app/features/tournament-lfg/tournament-lfg-schemas.ts index f2f1a25e5..edaac4d6c 100644 --- a/app/features/tournament-lfg/tournament-lfg-schemas.ts +++ b/app/features/tournament-lfg/tournament-lfg-schemas.ts @@ -55,6 +55,10 @@ export const lookingSchema = z.union([ z.object({ _action: _action("LEAVE_GROUP"), }), + z.object({ + _action: _action("DELETE_GROUP"), + userId: id, + }), addSubFormSchema, z.object({ _action: _action("DELETE_SUB"), diff --git a/locales/da/q.json b/locales/da/q.json index ff80aa05d..53f9f1924 100644 --- a/locales/da/q.json +++ b/locales/da/q.json @@ -100,6 +100,7 @@ "looking.groups.actions.leaveGroup": "", "looking.groups.actions.stopLooking": "", "looking.groups.actions.stopLooking.confirm": "", + "looking.groups.actions.organizerRemove.confirm": "", "looking.groups.actions.leaveQ": "", "looking.groups.actions.goBack": "", "looking.groups.adder.quickAdd": "", diff --git a/locales/de/q.json b/locales/de/q.json index 3355040aa..287f73782 100644 --- a/locales/de/q.json +++ b/locales/de/q.json @@ -100,6 +100,7 @@ "looking.groups.actions.leaveGroup": "", "looking.groups.actions.stopLooking": "", "looking.groups.actions.stopLooking.confirm": "", + "looking.groups.actions.organizerRemove.confirm": "", "looking.groups.actions.leaveQ": "", "looking.groups.actions.goBack": "", "looking.groups.adder.quickAdd": "", diff --git a/locales/en/q.json b/locales/en/q.json index 76316120d..3ebb2012b 100644 --- a/locales/en/q.json +++ b/locales/en/q.json @@ -100,6 +100,7 @@ "looking.groups.actions.leaveGroup": "Leave group", "looking.groups.actions.stopLooking": "Stop looking", "looking.groups.actions.stopLooking.confirm": "Do you want to stop looking (any invitations given/received deleted)?", + "looking.groups.actions.organizerRemove.confirm": "Remove {{name}} from LFG?", "looking.groups.actions.leaveQ": "Leave queue", "looking.groups.actions.goBack": "Go back", "looking.groups.adder.quickAdd": "Quick add", diff --git a/locales/es-ES/q.json b/locales/es-ES/q.json index a74997146..b58189ad3 100644 --- a/locales/es-ES/q.json +++ b/locales/es-ES/q.json @@ -100,6 +100,7 @@ "looking.groups.actions.leaveGroup": "Dejar grupo", "looking.groups.actions.stopLooking": "", "looking.groups.actions.stopLooking.confirm": "", + "looking.groups.actions.organizerRemove.confirm": "", "looking.groups.actions.leaveQ": "Dejar fila", "looking.groups.actions.goBack": "Regresar", "looking.groups.adder.quickAdd": "Añadir rapido", diff --git a/locales/es-US/q.json b/locales/es-US/q.json index c101ef4c3..3fbf4ca49 100644 --- a/locales/es-US/q.json +++ b/locales/es-US/q.json @@ -100,6 +100,7 @@ "looking.groups.actions.leaveGroup": "Dejar grupo", "looking.groups.actions.stopLooking": "", "looking.groups.actions.stopLooking.confirm": "", + "looking.groups.actions.organizerRemove.confirm": "", "looking.groups.actions.leaveQ": "Dejar fila", "looking.groups.actions.goBack": "Regresar", "looking.groups.adder.quickAdd": "Añadir rápido", diff --git a/locales/fr-CA/q.json b/locales/fr-CA/q.json index f3deee494..29831249f 100644 --- a/locales/fr-CA/q.json +++ b/locales/fr-CA/q.json @@ -100,6 +100,7 @@ "looking.groups.actions.leaveGroup": "", "looking.groups.actions.stopLooking": "", "looking.groups.actions.stopLooking.confirm": "", + "looking.groups.actions.organizerRemove.confirm": "", "looking.groups.actions.leaveQ": "", "looking.groups.actions.goBack": "", "looking.groups.adder.quickAdd": "", diff --git a/locales/fr-EU/q.json b/locales/fr-EU/q.json index ae751f9d9..09046831a 100644 --- a/locales/fr-EU/q.json +++ b/locales/fr-EU/q.json @@ -100,6 +100,7 @@ "looking.groups.actions.leaveGroup": "Quitter le groupe", "looking.groups.actions.stopLooking": "", "looking.groups.actions.stopLooking.confirm": "", + "looking.groups.actions.organizerRemove.confirm": "", "looking.groups.actions.leaveQ": "Quitter la queue", "looking.groups.actions.goBack": "Retourner en arrière", "looking.groups.adder.quickAdd": "Ajouter rapidement", diff --git a/locales/he/q.json b/locales/he/q.json index 51b86abed..d22ea66a5 100644 --- a/locales/he/q.json +++ b/locales/he/q.json @@ -100,6 +100,7 @@ "looking.groups.actions.leaveGroup": "", "looking.groups.actions.stopLooking": "", "looking.groups.actions.stopLooking.confirm": "", + "looking.groups.actions.organizerRemove.confirm": "", "looking.groups.actions.leaveQ": "", "looking.groups.actions.goBack": "", "looking.groups.adder.quickAdd": "", diff --git a/locales/it/q.json b/locales/it/q.json index 885ff5a71..a9bfbe41a 100644 --- a/locales/it/q.json +++ b/locales/it/q.json @@ -100,6 +100,7 @@ "looking.groups.actions.leaveGroup": "Lascia gruppo", "looking.groups.actions.stopLooking": "", "looking.groups.actions.stopLooking.confirm": "", + "looking.groups.actions.organizerRemove.confirm": "", "looking.groups.actions.leaveQ": "Lascia coda", "looking.groups.actions.goBack": "Indietro", "looking.groups.adder.quickAdd": "Aggiungi direttamente", diff --git a/locales/ja/q.json b/locales/ja/q.json index 6cd595f88..6fc276e11 100644 --- a/locales/ja/q.json +++ b/locales/ja/q.json @@ -100,6 +100,7 @@ "looking.groups.actions.leaveGroup": "グループを出る", "looking.groups.actions.stopLooking": "", "looking.groups.actions.stopLooking.confirm": "", + "looking.groups.actions.organizerRemove.confirm": "", "looking.groups.actions.leaveQ": "列から出る", "looking.groups.actions.goBack": "戻る", "looking.groups.adder.quickAdd": "クイック追加", diff --git a/locales/ko/q.json b/locales/ko/q.json index 3355040aa..287f73782 100644 --- a/locales/ko/q.json +++ b/locales/ko/q.json @@ -100,6 +100,7 @@ "looking.groups.actions.leaveGroup": "", "looking.groups.actions.stopLooking": "", "looking.groups.actions.stopLooking.confirm": "", + "looking.groups.actions.organizerRemove.confirm": "", "looking.groups.actions.leaveQ": "", "looking.groups.actions.goBack": "", "looking.groups.adder.quickAdd": "", diff --git a/locales/nl/q.json b/locales/nl/q.json index 3355040aa..287f73782 100644 --- a/locales/nl/q.json +++ b/locales/nl/q.json @@ -100,6 +100,7 @@ "looking.groups.actions.leaveGroup": "", "looking.groups.actions.stopLooking": "", "looking.groups.actions.stopLooking.confirm": "", + "looking.groups.actions.organizerRemove.confirm": "", "looking.groups.actions.leaveQ": "", "looking.groups.actions.goBack": "", "looking.groups.adder.quickAdd": "", diff --git a/locales/pl/q.json b/locales/pl/q.json index 3355040aa..287f73782 100644 --- a/locales/pl/q.json +++ b/locales/pl/q.json @@ -100,6 +100,7 @@ "looking.groups.actions.leaveGroup": "", "looking.groups.actions.stopLooking": "", "looking.groups.actions.stopLooking.confirm": "", + "looking.groups.actions.organizerRemove.confirm": "", "looking.groups.actions.leaveQ": "", "looking.groups.actions.goBack": "", "looking.groups.adder.quickAdd": "", diff --git a/locales/pt-BR/q.json b/locales/pt-BR/q.json index dc50c38ab..98bc4fa6b 100644 --- a/locales/pt-BR/q.json +++ b/locales/pt-BR/q.json @@ -100,6 +100,7 @@ "looking.groups.actions.leaveGroup": "Sair do grupo", "looking.groups.actions.stopLooking": "", "looking.groups.actions.stopLooking.confirm": "", + "looking.groups.actions.organizerRemove.confirm": "", "looking.groups.actions.leaveQ": "Sair da fila", "looking.groups.actions.goBack": "Voltar", "looking.groups.adder.quickAdd": "Adicionar rapidamente (quick add)", diff --git a/locales/ru/q.json b/locales/ru/q.json index 65fbf5144..a63b4d310 100644 --- a/locales/ru/q.json +++ b/locales/ru/q.json @@ -100,6 +100,7 @@ "looking.groups.actions.leaveGroup": "Покинуть группу", "looking.groups.actions.stopLooking": "", "looking.groups.actions.stopLooking.confirm": "", + "looking.groups.actions.organizerRemove.confirm": "", "looking.groups.actions.leaveQ": "Покинуть очередь", "looking.groups.actions.goBack": "Вернуться назад", "looking.groups.adder.quickAdd": "Быстро добавить", diff --git a/locales/zh/q.json b/locales/zh/q.json index 0b694df57..a09dcc17c 100644 --- a/locales/zh/q.json +++ b/locales/zh/q.json @@ -100,6 +100,7 @@ "looking.groups.actions.leaveGroup": "离开小队", "looking.groups.actions.stopLooking": "", "looking.groups.actions.stopLooking.confirm": "", + "looking.groups.actions.organizerRemove.confirm": "", "looking.groups.actions.leaveQ": "停止匹配", "looking.groups.actions.goBack": "返回", "looking.groups.adder.quickAdd": "快速添加",