mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-04-25 07:32:19 -05:00
Delete organization feature for admin
This commit is contained in:
parent
48ec1f1c26
commit
24c86882b9
|
|
@ -618,6 +618,13 @@ export function updateIsEstablished(
|
|||
.execute();
|
||||
}
|
||||
|
||||
export function deleteById(organizationId: number) {
|
||||
return db
|
||||
.deleteFrom("TournamentOrganization")
|
||||
.where("id", "=", organizationId)
|
||||
.execute();
|
||||
}
|
||||
|
||||
export function findAllSeriesWithTierHistory() {
|
||||
return db
|
||||
.selectFrom("TournamentOrganizationSeries")
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { isFuture } from "date-fns";
|
||||
import type { ActionFunctionArgs } from "react-router";
|
||||
import { type ActionFunctionArgs, redirect } from "react-router";
|
||||
import { requireUser } from "~/features/auth/core/user.server";
|
||||
import {
|
||||
requirePermission,
|
||||
|
|
@ -89,6 +89,17 @@ export const action = async ({ request, params }: ActionFunctionArgs) => {
|
|||
|
||||
break;
|
||||
}
|
||||
case "DELETE_ORGANIZATION": {
|
||||
requireRole(user, "ADMIN");
|
||||
|
||||
await TournamentOrganizationRepository.deleteById(organization.id);
|
||||
|
||||
logger.info(
|
||||
`Organization deleted: organization=${organization.name} (${organization.id}), deleted by userId=${user.id}`,
|
||||
);
|
||||
|
||||
throw redirect("/");
|
||||
}
|
||||
default: {
|
||||
assertUnreachable(data);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,13 +3,14 @@ import type { MetaFunction } from "react-router";
|
|||
import { Link, useLoaderData, useSearchParams } from "react-router";
|
||||
import { Avatar } from "~/components/Avatar";
|
||||
import { Divider } from "~/components/Divider";
|
||||
import { LinkButton } from "~/components/elements/Button";
|
||||
import { LinkButton, SendouButton } from "~/components/elements/Button";
|
||||
import {
|
||||
SendouTab,
|
||||
SendouTabList,
|
||||
SendouTabPanel,
|
||||
SendouTabs,
|
||||
} from "~/components/elements/Tabs";
|
||||
import { FormWithConfirm } from "~/components/FormWithConfirm";
|
||||
import { Image } from "~/components/Image";
|
||||
import { EditIcon } from "~/components/icons/Edit";
|
||||
import { LinkIcon } from "~/components/icons/Link";
|
||||
|
|
@ -97,7 +98,6 @@ export default function TournamentOrganizationPage() {
|
|||
return (
|
||||
<Main className="stack lg">
|
||||
<LogoHeader />
|
||||
<AdminControls />
|
||||
<InfoTabs />
|
||||
{data.organization.series.length > 0 ? (
|
||||
<SeriesSelector series={data.organization.series} />
|
||||
|
|
@ -142,32 +142,10 @@ function LogoHeader() {
|
|||
);
|
||||
}
|
||||
|
||||
function AdminControls() {
|
||||
const data = useLoaderData<typeof loader>();
|
||||
const isAdmin = useHasRole("ADMIN");
|
||||
|
||||
if (!isAdmin) return null;
|
||||
|
||||
return (
|
||||
<div className="stack sm">
|
||||
<div className="text-sm font-semi-bold">Admin Controls</div>
|
||||
<SendouForm
|
||||
className=""
|
||||
schema={updateIsEstablishedSchema}
|
||||
defaultValues={{
|
||||
isEstablished: Boolean(data.organization.isEstablished),
|
||||
}}
|
||||
autoSubmit
|
||||
>
|
||||
{({ FormField }) => <FormField name="isEstablished" />}
|
||||
</SendouForm>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function InfoTabs() {
|
||||
const { t } = useTranslation(["org"]);
|
||||
const data = useLoaderData<typeof loader>();
|
||||
const isAdmin = useHasRole("ADMIN");
|
||||
const canBanPlayers = useHasPermission(data.organization, "BAN");
|
||||
|
||||
const hasSocials =
|
||||
|
|
@ -200,6 +178,11 @@ function InfoTabs() {
|
|||
{t("org:banned.title")}
|
||||
</SendouTab>
|
||||
) : null}
|
||||
{isAdmin ? (
|
||||
<SendouTab id="admin" icon={<LockIcon />}>
|
||||
Admin
|
||||
</SendouTab>
|
||||
) : null}
|
||||
</SendouTabList>
|
||||
<SendouTabPanel id="socials">
|
||||
<SocialLinksList links={data.organization.socials ?? []} />
|
||||
|
|
@ -215,11 +198,43 @@ function InfoTabs() {
|
|||
<BannedUsersList bannedUsers={data.bannedUsers} />
|
||||
</SendouTabPanel>
|
||||
) : null}
|
||||
{isAdmin ? (
|
||||
<SendouTabPanel id="admin">
|
||||
<AdminControls />
|
||||
</SendouTabPanel>
|
||||
) : null}
|
||||
</SendouTabs>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function AdminControls() {
|
||||
const data = useLoaderData<typeof loader>();
|
||||
|
||||
return (
|
||||
<div className="stack sm">
|
||||
<SendouForm
|
||||
className=""
|
||||
schema={updateIsEstablishedSchema}
|
||||
defaultValues={{
|
||||
isEstablished: Boolean(data.organization.isEstablished),
|
||||
}}
|
||||
autoSubmit
|
||||
>
|
||||
{({ FormField }) => <FormField name="isEstablished" />}
|
||||
</SendouForm>
|
||||
<FormWithConfirm
|
||||
dialogHeading={`Delete organization "${data.organization.name}"?`}
|
||||
fields={[["_action", "DELETE_ORGANIZATION"]]}
|
||||
>
|
||||
<SendouButton variant="minimal-destructive">
|
||||
Delete organization
|
||||
</SendouButton>
|
||||
</FormWithConfirm>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function MembersList() {
|
||||
const { t } = useTranslation(["org"]);
|
||||
const data = useLoaderData<typeof loader>();
|
||||
|
|
|
|||
|
|
@ -112,8 +112,13 @@ export const updateIsEstablishedSchema = z.object({
|
|||
}),
|
||||
});
|
||||
|
||||
const deleteOrganizationActionSchema = z.object({
|
||||
_action: _action("DELETE_ORGANIZATION"),
|
||||
});
|
||||
|
||||
export const orgPageActionSchema = z.union([
|
||||
banUserActionSchema,
|
||||
unbanUserActionSchema,
|
||||
updateIsEstablishedSchema,
|
||||
deleteOrganizationActionSchema,
|
||||
]);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user