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": "快速添加",