Skip to content

Commit

Permalink
Merge pull request #16 from DSC-best/queue-page
Browse files Browse the repository at this point in the history
Add bot approval queue + layout changes
  • Loading branch information
mikaib authored Dec 3, 2023
2 parents 23f0fd4 + 28ec0e5 commit 82fecd5
Show file tree
Hide file tree
Showing 15 changed files with 309 additions and 19 deletions.
2 changes: 2 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,12 @@
"@smui/checkbox": "^7.0.0-beta.15",
"@smui/chips": "^7.0.0-beta.15",
"@smui/common": "^7.0.0-beta.15",
"@smui/data-table": "^7.0.0-beta.15",
"@smui/dialog": "^7.0.0-beta.15",
"@smui/form-field": "^7.0.0-beta.15",
"@smui/icon-button": "^7.0.0-beta.15",
"@smui/layout-grid": "^7.0.0-beta.15",
"@smui/linear-progress": "^7.0.0-beta.15",
"@smui/menu": "^7.0.0-beta.15",
"@smui/paper": "^7.0.0-beta.15",
"@smui/segmented-button": "^7.0.0-beta.15",
Expand Down
6 changes: 0 additions & 6 deletions src/lib/components/botSearchBar.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,6 @@
</div>

<style>
.solo-demo-container {
padding: 10px 18px;
background-color: var(--mdc-theme-background, #f8f8f8);
border: 1px solid var(--mdc-theme-text-hint-on-background, rgba(0, 0, 0, 0.1));
}
.solo-container {
display: flex;
justify-content: center;
Expand Down
18 changes: 18 additions & 0 deletions src/lib/components/header.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,12 @@
import Tooltip, { Wrapper } from '@smui/tooltip';
import type { User } from '@prisma/client';
import { goto } from '$app/navigation';
import Badge from '@smui-extra/badge';
export let actor: User | null;
export let isApprover: boolean;
export let botQueueCount: number;
let menu: Menu;
function onUserClick() {
Expand All @@ -31,6 +35,10 @@
function logout() {
goto('/api/v1/auth/logout');
}
function viewBotQueue() {
goto('/admin/queue');
}
</script>

<nav class="dsc-navbar">
Expand Down Expand Up @@ -64,6 +72,16 @@
<Item on:SMUI:action={viewProfile}>
<Text>Profile</Text>
</Item>
{#if isApprover}
<Item on:SMUI:action={viewBotQueue} style="position: relative;">
<Text>Bot Queue</Text>
{#if botQueueCount > 0}
<Badge aria-label="Bot Queue Count" position="inset">
{botQueueCount}
</Badge>
{/if}
</Item>
{/if}
<Separator />
<Item on:SMUI:action={logout}>
<Text>Logout</Text>
Expand Down
2 changes: 1 addition & 1 deletion src/lib/utils/date.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import moment from 'moment';

export function dateFormatEurope(date: Date) {
return moment(date).format('DD/MM/YYYY');
return moment(date).format('DD/MM/YYYY, HH:mm');
}
29 changes: 26 additions & 3 deletions src/routes/+layout.server.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,33 @@
import SafeUser from '$lib/structures/user.js';
import SafeUser from '$lib/structures/user';
import type { User } from '@prisma/client';
import '$lib/server/discord/bot.js';
import '$lib/server/discord/bot';
import { RoleUtility, Roles } from '$lib/server/roles';
import prisma from '$lib/server/prisma';

/** @type {import('./$types').LayoutServerLoad} */
export async function load({ locals }) {
const isApprover = RoleUtility.hasRole(locals?.actor?.role!, Roles.Approver);
let botQueueCount = 0;

if (isApprover) {
botQueueCount = await prisma.bot.count({
where: {
approval_status: 'PENDING',
OR: [
{
approver_id: locals.actor?.id!
},
{
approver_id: null
}
]
}
});
}

return {
actor: locals?.actor ? SafeUser(locals.actor as User | null) : null
actor: locals?.actor ? SafeUser(locals.actor as User | null) : null,
isApprover,
botQueueCount
};
}
4 changes: 2 additions & 2 deletions src/routes/+layout.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@

<svelte:head>
<title>DSC.best</title>
<meta name="theme-color" content="#454FBF">
<meta name="theme-color" content="#454FBF" />
<meta name="description" content="Find the best Discord bots!" />
</svelte:head>

<Header actor={data.actor} />
<Header actor={data.actor} isApprover={data.isApprover} botQueueCount={data.botQueueCount} />
<slot />
<Footer />
41 changes: 41 additions & 0 deletions src/routes/admin/queue/+page.server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import requireActorRole from '$lib/server/middleware/requireActorRole';
import prisma from '$lib/server/prisma';
import { Roles } from '$lib/server/roles';
import { BotApprovalStatus } from '@prisma/client';
import { redirect } from '@sveltejs/kit';

export async function load({ locals }) {
await requireActorRole(locals, Roles.Approver);

const myPendingReview = await prisma.bot.findFirst({
where: {
approval_status: BotApprovalStatus.PENDING,
approver_id: locals.actor?.id!
}
});

if (myPendingReview) throw redirect(302, `/bots/${myPendingReview.id}`); // Approver should finish their review before reviewing another bot

const pendingBots = await prisma.bot.findMany({
where: {
approval_status: BotApprovalStatus.PENDING,
approver_id: null
},
take: 10,
orderBy: {
approval_request_time: 'asc'
}
});

const pendingBotsCount = await prisma.bot.count({
where: {
approval_status: BotApprovalStatus.PENDING,
approver_id: null
}
});

return {
pendingBots,
pendingBotsCount
};
}
113 changes: 113 additions & 0 deletions src/routes/admin/queue/+page.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
<script lang="ts">
import DataTable, { Head, Body, Row, Cell } from '@smui/data-table';
import Paper, { Title, Subtitle, Content } from '@smui/paper';
import LinearProgress from '@smui/linear-progress';
import Button from '@smui/button';
import axios from 'axios';
import { goto } from '$app/navigation';
export let data;
let errorMessage: string = '';
let tableLoaded = true;
function reviewBot(uid: string) {
tableLoaded = false;
axios
.post(`/api/v1/bots/${uid}/claim`)
.then((d) => {
goto(`/bots/${uid}`);
})
.catch((e) => {
errorMessage = e?.response?.data?.message ?? 'Unable to send request.';
})
.finally(() => {
tableLoaded = true;
});
}
</script>

<div class="paper-container align-flex-center">
<Paper class="paper">
<Title>Approval Queue</Title>
<Subtitle>
{data?.pendingBotsCount ?? 0} bots in queue. You can only approve one bot at a time, and will be
forced to finish the approval process before claiming another bot.
</Subtitle>
<Content>
{#if errorMessage}
<div class="mb-2">
<Paper color="primary" class="paper-theme--outline-danger" style="padding: 10px;">
<Content>
{errorMessage}
</Content>
</Paper>
</div>
{/if}

<DataTable table$aria-label="People list" style="max-width: 100%;">
<Head>
<Row>
<Cell>Avatar</Cell>
<Cell>Name</Cell>
<Cell>NSFW</Cell>
<Cell>Action</Cell>
</Row>
</Head>
<Body>
{#each data?.pendingBots as bot}
<Row>
<Cell>
<img src={bot?.avatar} alt="Avatar" class="bot-avatar" />
</Cell>
<Cell>{bot?.username}</Cell>
<Cell>{bot?.nsfw ? 'Contains NSFW' : 'Not NSFW'}</Cell>
<Cell>
<Button
variant="outlined"
disabled={!tableLoaded}
on:click={() => {
reviewBot(bot?.id);
}}
>
Review
</Button>
</Cell>
</Row>
{/each}
<!-- no data -->
{#if data?.pendingBots.length === 0}
<Row>
<Cell colspan={3}>No bots in queue.</Cell>
</Row>
{/if}
</Body>

<LinearProgress
indeterminate
bind:closed={tableLoaded}
aria-label="Data is being loaded..."
slot="progress"
/>
</DataTable>
</Content>
</Paper>
</div>

<style>
.paper-container {
padding: 24px;
}
.bot-avatar {
width: 30px;
height: 30px;
border-radius: 10px;
}
.mb-2 {
margin-bottom: 16px;
}
</style>
47 changes: 47 additions & 0 deletions src/routes/api/v1/bots/[bot_id]/claim/+server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import requireActorRole from '$lib/server/middleware/requireActorRole';
import prisma from '$lib/server/prisma';
import { Roles } from '$lib/server/roles';
import snowflake from '$lib/server/snowflake';
import { BotApprovalStatus } from '@prisma/client';
import { json } from '@sveltejs/kit';

/** @type {import('@sveltejs/kit').RequestHandler} */
export async function POST({ locals, params }) {
await requireActorRole(locals, Roles.Approver);

const bot = await prisma.bot.findUnique({
where: {
id: params.bot_id
}
});

if (!bot) return json({ message: 'Bot not found' }, { status: 404 });

if (bot.approval_status !== BotApprovalStatus.PENDING)
return json({ message: 'Bot is not pending approval' }, { status: 400 });

if (bot.approver_id !== null) return json({ message: 'Bot is already claimed' }, { status: 400 });

await prisma.bot.update({
where: {
id: bot.id
},
data: {
approver_id: locals.actor!.id!,
approval_reason: null,
approved_time: null
}
});

await prisma.botApprovalLog.create({
data: {
id: snowflake.getUniqueID()?.toString(),
bot_id: bot.id,
op_id: locals.actor!.id!,
status: BotApprovalStatus.PENDING,
reason: 'Bot claimed by approver'
}
});

return json({ message: 'Claimed bot' });
}
3 changes: 2 additions & 1 deletion src/routes/bots/[bot_id]/+page.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ export async function load({ locals, params }) {
if (locals.actor) {
if (
RoleUtility.hasRole(locals.actor.role, Roles.Approver) &&
bot.approval_status === BotApprovalStatus.PENDING
bot.approval_status === BotApprovalStatus.PENDING &&
bot.approver_id === locals.actor.id
)
isPendingAndActorIsApprover = true;
}
Expand Down
Loading

0 comments on commit 82fecd5

Please sign in to comment.