Skip to content
Permalink
Newer
Older
100644 311 lines (269 sloc) 7.97 KB
October 19, 2023 16:49
1
import {
2
Box,
3
Button,
October 19, 2023 16:49
4
Container,
5
Flex,
October 19, 2023 16:49
6
Popover,
7
PopoverArrow,
8
PopoverBody,
9
PopoverCloseButton,
10
PopoverContent,
11
PopoverHeader,
12
PopoverTrigger,
October 19, 2023 16:49
13
Select,
14
useBreakpointValue,
15
useDisclosure,
16
useToast,
October 19, 2023 16:49
17
VStack,
18
} from "@chakra-ui/react";
19
import { type ReactElement, useEffect, useState } from "react";
October 19, 2023 16:49
20
import AppealCard from "../../components/AppealCard.js";
October 19, 2023 16:49
21
import GameAppealCard from "../../components/GameAppealCard.js";
22
import NewGameBan from "../../components/NewGameBan.js";
23
import NewInfractionModal from "../../components/NewInfractionModal.js";
October 19, 2023 16:49
24
import ReportCard from "../../components/ReportCard.js";
October 19, 2023 16:49
25
import { useLoaderData } from "@remix-run/react";
26
import NewInactivityNotice from "../../components/NewInactivityNotice.js";
27
import InactivityNoticeCard from "../../components/InactivityNoticeCard.js";
October 19, 2023 16:49
28
October 19, 2023 16:49
29
export async function loader({ context }: { context: RequestContext }) {
30
const { current_user: currentUser } = context.data;
October 19, 2023 16:49
32
if (!currentUser)
33
throw new Response(null, {
34
status: 401,
35
});
October 19, 2023 16:49
36
37
const departments = {
38
DM: 1 << 2,
39
ET: 1 << 3,
40
FM: 1 << 10,
41
WM: 1 << 9,
42
};
43
October 19, 2023 16:49
44
const newItemPermissions = {
45
game_ban: [1 << 5],
46
inactivity: [1 << 2, 1 << 9, 1 << 10],
47
infraction: [1 << 0, 1 << 2, 1 << 6, 1 << 7],
48
};
49
50
const newItemNames: { [k: string]: string } = {
51
game_ban: "Game Ban",
52
inactivity: "Inactivity Notice",
53
infraction: "Infraction",
54
};
55
56
const typePermissions = {
57
appeal: [1 << 0, 1 << 1],
58
gma: [1 << 5],
59
report: [1 << 5],
60
};
61
62
const typeNames: { [k: string]: string } = {
63
appeal: "Discord Appeals",
64
gma: "Game Appeals",
65
report: "Game Reports",
66
};
67
68
const allowedNewItems = [];
69
const allowedTypes = [];
70
71
for (const [item, ints] of Object.entries(newItemPermissions)) {
72
if (ints.find((i) => currentUser.permissions & i))
73
allowedNewItems.push({ name: newItemNames[item], value: item });
74
}
75
76
for (const [type, ints] of Object.entries(typePermissions)) {
77
if (ints.find((i) => currentUser.permissions & i))
78
allowedTypes.push({ name: typeNames[type], value: type });
79
}
80
81
if (!allowedTypes.length)
82
throw new Response(null, {
October 19, 2023 16:49
83
status: 403,
84
});
85
86
return {
87
can_edit_ban_users: [
88
"165594923586945025",
89
"289372404541554689",
90
"396347223736057866",
91
].includes(currentUser.id),
92
departments: Object.entries(departments)
93
.filter((d) => d[1] & currentUser.permissions)
94
.map((arr) => arr[0]),
October 19, 2023 16:49
95
entry_types: allowedTypes,
96
item_types: allowedNewItems,
97
};
98
}
99
October 19, 2023 16:49
100
export function meta() {
October 19, 2023 16:49
101
return [
102
{
103
title: "Moderation Queue - Car Crushers",
104
},
105
];
October 19, 2023 16:49
106
}
107
October 19, 2023 16:49
108
export default function () {
109
const pageProps = useLoaderData<typeof loader>();
October 19, 2023 16:49
110
const isDesktop = useBreakpointValue({ base: false, lg: true });
111
const entryTypes = [];
112
const [entries, setEntries] = useState([] as ReactElement[]);
113
const [before, setBefore] = useState(Date.now());
October 19, 2023 16:49
114
115
for (const type of pageProps.entry_types)
October 19, 2023 16:49
116
entryTypes.push(
117
<option key={type.value} value={type.value}>
118
{type.name}
119
</option>,
October 19, 2023 16:49
120
);
October 19, 2023 16:49
121
October 19, 2023 16:49
122
async function updateQueue(
123
queue_type: string,
124
before: number,
125
show_closed = false,
October 19, 2023 16:49
126
): Promise<void> {
127
const queueReq = await fetch(
128
`/api/mod-queue/list?before=${before}&showClosed=${show_closed}&type=${queue_type}`,
October 19, 2023 16:49
129
);
130
131
if (!queueReq.ok) {
132
const errorData: { error: string } = await queueReq.json();
133
134
useToast()({
135
description: errorData.error,
136
duration: 10000,
137
isClosable: true,
138
status: "error",
139
title: "Failed to load queue",
140
});
141
142
return;
143
}
October 19, 2023 16:49
144
145
const searchParams = new URLSearchParams(location.search);
146
const itemId = searchParams.get("id");
147
const itemType = searchParams.get("type");
148
October 19, 2023 16:49
149
const entryData: { [k: string]: any }[] = await queueReq.json();
October 19, 2023 16:49
150
const newEntries = [...entries];
October 19, 2023 16:49
151
152
if (
153
itemId &&
154
itemType &&
155
["appeal", "gma", "inactivity", "report"].includes(itemType)
156
) {
157
const itemReq = await fetch(`/api/mod-queue/${itemType}/${itemId}`);
158
159
if (!itemReq.ok) {
160
useToast()({
161
description: ((await itemReq.json()) as { error: string }).error,
162
duration: 10000,
163
isClosable: true,
164
status: "error",
165
title: "Failed to load item with id " + itemId,
166
});
167
} else {
168
const itemData: { [k: string]: any } = await itemReq.json();
169
170
entryData.unshift(itemData);
171
}
172
}
173
174
if (!entryData.length) return;
175
October 19, 2023 16:49
176
for (const entry of entryData) {
177
switch (queue_type) {
178
case "appeal":
October 19, 2023 16:49
179
newEntries.push(<AppealCard {...(entry as AppealCardProps)} />);
180
181
break;
182
October 19, 2023 16:49
183
case "gma":
184
newEntries.push(<GameAppealCard {...(entry as GameAppealProps)} />);
185
186
break;
187
188
case "inactivity":
189
newEntries.push(
190
<InactivityNoticeCard {...(entry as InactivityNoticeProps)} />,
191
);
192
193
break;
194
October 19, 2023 16:49
195
case "report":
196
newEntries.push(<ReportCard {...(entry as ReportCardProps)} />);
October 19, 2023 16:49
197
198
break;
October 19, 2023 16:49
199
}
200
}
201
202
setEntries(newEntries);
October 19, 2023 16:49
203
setBefore(entryData[entryData.length - 1].created_at);
204
}
October 19, 2023 16:49
205
206
const itemModals: {
207
[k: string]: {
208
isOpen: boolean;
209
onOpen: () => void;
210
onClose: () => void;
211
[k: string]: any;
212
};
213
} = {
214
game_ban: useDisclosure(),
215
inactivity: useDisclosure(),
216
infraction: useDisclosure(),
217
};
218
219
useEffect(() => {
221
await updateQueue(pageProps.entry_types[0].value, before);
224
const searchParams = new URLSearchParams(location.search);
225
const modal = searchParams.get("modal");
226
227
if (!modal || !pageProps.item_types.find((m) => m.value === modal)) return;
228
229
itemModals[modal].onOpen();
230
}, []);
October 19, 2023 16:49
232
return (
233
<Container maxW="container.lg">
234
<NewGameBan
235
isOpen={itemModals.game_ban.isOpen}
236
onClose={itemModals.game_ban.onClose}
237
/>
238
<NewInactivityNotice
239
departments={pageProps.departments}
240
isOpen={itemModals.inactivity.isOpen}
241
onClose={itemModals.inactivity.onClose}
242
/>
243
<NewInfractionModal
244
isOpen={itemModals.infraction.isOpen}
245
onClose={itemModals.infraction.onClose}
246
/>
October 19, 2023 16:49
247
<Flex>
October 19, 2023 16:49
248
<VStack w={isDesktop ? "container.md" : "container.lg"}>
249
{entries}
250
</VStack>
251
<Box display={isDesktop ? undefined : "none"} ml="16px" w="248px">
October 19, 2023 16:50
252
<Select
253
onChange={async (v) => {
254
setEntries([]);
255
setBefore(Date.now());
256
257
const { target } = v;
258
259
await updateQueue(
260
target.options[target.selectedIndex].value,
261
Date.now(),
262
);
263
}}
264
>
265
{entryTypes}
266
</Select>
October 19, 2023 16:49
267
</Box>
268
</Flex>
October 19, 2023 16:50
269
<Popover placement="top-end">
October 19, 2023 16:49
270
<PopoverTrigger>
271
<Button
272
borderRadius="50%"
273
bottom="10vh"
274
h="16"
275
position="absolute"
276
right="10vh"
277
w="16"
278
>
October 19, 2023 16:49
279
<svg
280
xmlns="http://www.w3.org/2000/svg"
281
width="32"
282
height="32"
October 19, 2023 16:49
283
fill="currentColor"
284
viewBox="0 0 16 16"
285
>
286
<path d="M8 4a.5.5 0 0 1 .5.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3A.5.5 0 0 1 8 4z" />
287
</svg>
288
</Button>
289
</PopoverTrigger>
290
<PopoverContent>
291
<PopoverArrow />
292
<PopoverCloseButton />
293
<PopoverHeader>Create New</PopoverHeader>
294
<PopoverBody>
295
<VStack>
296
{pageProps.item_types.map((item) => (
297
<Button
298
key={item.value}
299
onClick={() => itemModals[item.value].onOpen()}
300
w="100%"
301
>
302
{item.name}
303
</Button>
304
))}
October 19, 2023 16:49
305
</VStack>
306
</PopoverBody>
307
</PopoverContent>
308
</Popover>
October 19, 2023 16:49
309
</Container>
310
);
311
}