forked from administration/panel
feat: create user strikes
parent
f005456f0d
commit
006acdb6ac
|
@ -106,7 +106,11 @@ export default async function User({
|
|||
)}
|
||||
|
||||
<Separator />
|
||||
<RelevantModerationNotices strikes={strikes} notices={notices} />
|
||||
<RelevantModerationNotices
|
||||
userId={user._id}
|
||||
strikes={strikes}
|
||||
notices={notices}
|
||||
/>
|
||||
|
||||
<Separator />
|
||||
<RelevantObjects
|
||||
|
|
|
@ -3,19 +3,144 @@
|
|||
import { AccountStrike, Message } from "revolt-api";
|
||||
import { ListCompactor } from "../common/ListCompactor";
|
||||
import { CompactMessage } from "../cards/CompactMessage";
|
||||
import { useRef, useState } from "react";
|
||||
import { Button } from "../ui/button";
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogAction,
|
||||
AlertDialogCancel,
|
||||
AlertDialogContent,
|
||||
AlertDialogFooter,
|
||||
AlertDialogHeader,
|
||||
AlertDialogTitle,
|
||||
AlertDialogTrigger,
|
||||
} from "../ui/alert-dialog";
|
||||
import { Input } from "../ui/input";
|
||||
import { createStrike } from "@/lib/actions";
|
||||
import { useToast } from "../ui/use-toast";
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "../ui/table";
|
||||
import dayjs from "dayjs";
|
||||
import { decodeTime } from "ulid";
|
||||
|
||||
/**
|
||||
*
|
||||
* @param param0 You have received an account strike, for one or more reasons:
|
||||
- REASON_HERE
|
||||
|
||||
Further violations will result in suspension or a permanent ban depending on severity, please abide by the [Acceptable Usage Policy](https://revolt.chat/aup).
|
||||
* @returns
|
||||
*/
|
||||
|
||||
export function RelevantModerationNotices({
|
||||
userId,
|
||||
strikes,
|
||||
notices,
|
||||
}: {
|
||||
userId: string;
|
||||
strikes: AccountStrike[];
|
||||
notices: Message[];
|
||||
}) {
|
||||
const { toast } = useToast();
|
||||
const [strikesDraft, setStrikesDraft] = useState(strikes);
|
||||
|
||||
const givenReason = useRef("");
|
||||
const additionalContext = useRef("");
|
||||
|
||||
return (
|
||||
<div className="flex gap-2">
|
||||
<div className="flex-1 min-w-0 flex flex-col gap-2">
|
||||
<h2 className="text-md text-center pb-2">Strikes</h2>
|
||||
<ListCompactor data={strikes} Component={({ item }) => null} />
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>Reason</TableHead>
|
||||
<TableHead>Created</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{strikesDraft.map((strike) => (
|
||||
<TableRow key={strike._id}>
|
||||
<TableCell>{strike.reason}</TableCell>
|
||||
<TableCell>{dayjs(decodeTime(strike._id)).fromNow()}</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
|
||||
<AlertDialog>
|
||||
<AlertDialogTrigger asChild>
|
||||
<Button className="mx-auto" variant="outline">
|
||||
Create
|
||||
</Button>
|
||||
</AlertDialogTrigger>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>Create Strike</AlertDialogTitle>
|
||||
</AlertDialogHeader>
|
||||
<p className="text-sm flex flex-col gap-2">
|
||||
<span>
|
||||
You have received an account strike, for one or more reasons:
|
||||
</span>
|
||||
<Input
|
||||
defaultValue=""
|
||||
placeholder="Short reason"
|
||||
onChange={(e) => (givenReason.current = e.currentTarget.value)}
|
||||
/>
|
||||
<span>
|
||||
Further violations will result in suspension or a permanent ban
|
||||
depending on severity, please abide by the{" "}
|
||||
<a href="https://revolt.chat/aup" target="_blank">
|
||||
Acceptable Usage Policy
|
||||
</a>
|
||||
.
|
||||
</span>
|
||||
<Input
|
||||
defaultValue=""
|
||||
placeholder="Additional context (only moderators see this)"
|
||||
onChange={(e) =>
|
||||
(additionalContext.current = e.currentTarget.value)
|
||||
}
|
||||
/>
|
||||
</p>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
||||
<AlertDialogAction
|
||||
onClick={() =>
|
||||
givenReason.current &&
|
||||
createStrike(
|
||||
userId,
|
||||
givenReason.current,
|
||||
additionalContext.current
|
||||
)
|
||||
.then((strike) => {
|
||||
setStrikesDraft((strikes) => [strike, ...strikes]);
|
||||
toast({ title: "Created strike" });
|
||||
})
|
||||
.catch((err) =>
|
||||
toast({
|
||||
title: "Failed to create strike!",
|
||||
description: String(err),
|
||||
variant: "destructive",
|
||||
})
|
||||
)
|
||||
.finally(() => {
|
||||
givenReason.current = "";
|
||||
additionalContext.current = "";
|
||||
})
|
||||
}
|
||||
>
|
||||
Create
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
</div>
|
||||
<div className="flex-1 min-w-0 flex flex-col gap-2">
|
||||
<h2 className="text-md text-center pb-2">Alerts</h2>
|
||||
|
|
|
@ -16,6 +16,7 @@ import { publishMessage, sendChatMessage } from "./redis";
|
|||
import { ulid } from "ulid";
|
||||
import {
|
||||
AccountInfo,
|
||||
AccountStrike,
|
||||
Bot,
|
||||
File,
|
||||
Member,
|
||||
|
@ -41,6 +42,35 @@ export async function sendAlert(userId: string, content: string) {
|
|||
});
|
||||
}
|
||||
|
||||
export async function createStrike(
|
||||
userId: string,
|
||||
givenReason: string,
|
||||
additionalContext: string
|
||||
) {
|
||||
const strike: AccountStrike & { moderator_id: string } = {
|
||||
_id: ulid(),
|
||||
user_id: userId,
|
||||
moderator_id: "01EX2NCWQ0CHS3QJF0FEQS1GR4", // TODO
|
||||
reason: additionalContext
|
||||
? givenReason + " - " + additionalContext
|
||||
: givenReason,
|
||||
};
|
||||
|
||||
await mongo()
|
||||
.db("revolt")
|
||||
.collection<{ _id: string }>("safety_strikes")
|
||||
.insertOne(strike);
|
||||
await sendAlert(
|
||||
userId,
|
||||
`You have received an account strike, for one or more reasons:
|
||||
- ${givenReason}
|
||||
|
||||
Further violations will result in suspension or a permanent ban depending on severity, please abide by the [Acceptable Usage Policy](https://revolt.chat/aup).`
|
||||
);
|
||||
|
||||
return strike;
|
||||
}
|
||||
|
||||
export async function updateReportNotes(reportId: string, notes: string) {
|
||||
return await mongo()
|
||||
.db("revolt")
|
||||
|
|
Loading…
Reference in New Issue