Allow TO to remove groups from LFG before registration ends

Closes #2948
This commit is contained in:
Kalle
2026-05-02 13:36:15 +03:00
parent 05aea47d32
commit 23bfa19403
19 changed files with 79 additions and 1 deletions

View File

@@ -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,

View File

@@ -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 (
<LFGGroupCardContainer groupId={group.id} isOwnGroup={isOwnGroup}>
<section className={styles.group}>
@@ -118,11 +123,47 @@ export function LFGGroupCard({
</SubmitButton>
</fetcher.Form>
) : null}
{showOrganizerDelete ? (
<LFGOrganizerGroupRemover group={group} />
) : null}
</section>
</LFGGroupCardContainer>
);
}
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 (
<div className="stack items-center">
<FormWithConfirm
dialogHeading={t("q:looking.groups.actions.organizerRemove.confirm", {
name: group.teamName ?? group.members[0]?.username,
})}
submitButtonText={t("common:actions.delete")}
fields={[
["_action", "DELETE_GROUP"],
["userId", targetUserId],
]}
>
<SendouButton
variant="minimal-destructive"
size="small"
icon={<Trash />}
>
{t("common:actions.delete")}
</SendouButton>
</FormWithConfirm>
</div>
);
}
function LFGGroupCardContainer({
isOwnGroup,
groupId,

View File

@@ -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"),

View File

@@ -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": "",

View File

@@ -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": "",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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": "",

View File

@@ -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",

View File

@@ -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": "",

View File

@@ -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",

View File

@@ -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": "クイック追加",

View File

@@ -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": "",

View File

@@ -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": "",

View File

@@ -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": "",

View File

@@ -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)",

View File

@@ -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": "Быстро добавить",

View File

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