From 13b48061f85242e4d53e12c9c0c770ca03175cb8 Mon Sep 17 00:00:00 2001 From: Pranav Ramesh Date: Mon, 5 Aug 2024 20:11:47 -0400 Subject: [PATCH] feat: lots of fixes --- src/components/moderation/ViewChallenge.jsx | 51 +++++++-- src/pages/create/edit.jsx | 2 +- src/pages/create/new.jsx | 12 ++- src/pages/moderation.jsx | 108 ++++++++++++++++++-- 4 files changed, 155 insertions(+), 18 deletions(-) diff --git a/src/components/moderation/ViewChallenge.jsx b/src/components/moderation/ViewChallenge.jsx index 76c8fe75..720ad602 100644 --- a/src/components/moderation/ViewChallenge.jsx +++ b/src/components/moderation/ViewChallenge.jsx @@ -3,6 +3,7 @@ import { Dialog, Transition } from '@headlessui/react'; import { XMarkIcon } from '@heroicons/react/24/outline'; import { useEffect, useState } from 'react'; import request from '@/utils/request'; +import { MarkdownViewer } from '../MarkdownViewer'; const ViewChallenge = ({ open, setOpen, selected }) => { @@ -50,7 +51,7 @@ const ViewChallenge = ({ open, setOpen, selected }) => { leaveTo="translate-x-full" > - +
@@ -73,15 +74,21 @@ const ViewChallenge = ({ open, setOpen, selected }) => {
+
+

Don't forget to make sure the challenge is including an explanation.

+ +
+

Description

-

{challenge && challenge.content}

+ +

Flag

-

{challenge && challenge.solution || "N/A"}

+

{challenge && challenge.solution && challenge.solution.keyword || "N/A"}

Difficulty

@@ -120,7 +127,16 @@ const ViewChallenge = ({ open, setOpen, selected }) => {



-

Set Bonus Points

+
+

Set Base Points

+
+

Beginner: 100, + Easy: 200, + Medium: 300, + Hard: 400, + Insane: 500 +

+
@@ -135,11 +151,22 @@ const ViewChallenge = ({ open, setOpen, selected }) => { className="rounded-md px-3 py-2 text-sm font-semibold text-white shadow-sm ring-1 ring-inset ring-gray-300 hover:ring-gray-400" onClick={() => setOpen(false)} > +
diff --git a/src/pages/create/edit.jsx b/src/pages/create/edit.jsx index 00c48234..99275485 100644 --- a/src/pages/create/edit.jsx +++ b/src/pages/create/edit.jsx @@ -369,7 +369,7 @@ export default function Createchall() { // setPenalty(challenge.penalty); setContentPreview(challenge.content); setSolution(challenge.solution); - setDifficulty(challenge.difficulty); + setDifficulty(challenge.difficulty.toLowerCase()); setNewConfig(challenge.commands.replace(/ && /g, '\n')); } } catch (error) { diff --git a/src/pages/create/new.jsx b/src/pages/create/new.jsx index 655f89c5..380165e3 100644 --- a/src/pages/create/new.jsx +++ b/src/pages/create/new.jsx @@ -307,6 +307,7 @@ export default function Createchall() { category, commands: nConfig, fileId: fileId, + isRoot: document.getElementById('root').checked, }; const url = `${process.env.NEXT_PUBLIC_API_URL}/challenges/create`; @@ -625,7 +626,7 @@ export default function Createchall() {
-
+

@@ -721,9 +722,16 @@ export default function Createchall() { Please assume that files are placed in the home directory.

+ +
+ + +
+ +

-
+

Hosted Web Challenges diff --git a/src/pages/moderation.jsx b/src/pages/moderation.jsx index cc7c5b20..6e7a1e9a 100644 --- a/src/pages/moderation.jsx +++ b/src/pages/moderation.jsx @@ -162,6 +162,31 @@ export default function Competitions() { } }; + const deleteBulk = async () => { + if (selectedChallenges.length === 0) { + alert("No challenges selected."); + return; + } + + if (!confirm("Are you sure you want to delete the selected challenges?")) { + return; + } + try { + const response = await request(`${process.env.NEXT_PUBLIC_API_URL}/admin/deleteChallenges`, "POST", { challengeIds: selectedChallenges }); + if (response.success) { + alert("Selected challenges deleted successfully!"); + setSelectedChallenges([]); // Clear selected challenges + fetchPendingChallenges(); // Refresh pending challenges + } else { + alert("Failed to delete selected challenges."); + } + } catch (error) { + console.error(error); + alert("An error occurred while deleting the selected challenges."); + } + }; + + const handleResetBanner = async () => { const username = document.getElementById('usernameInput').value; const reason = document.getElementById('reasonInput').value; @@ -280,6 +305,21 @@ export default function Competitions() { } }; + // Fetch platform stats + const [stats, setStats] = useState(null); + + useEffect(() => { + const fetchStats = async () => { + try { + const response = await request(`${process.env.NEXT_PUBLIC_API_URL}/admin/stats`, "GET"); + setStats(response); + } catch (error) { + console.error("Failed to fetch platform stats", error); + } + }; + fetchStats(); + }, []); + return ( <> @@ -310,16 +350,31 @@ export default function Competitions() {
+

USER ACTIONS

- - - - - - - +
+ + + + + + + +
@@ -336,11 +391,13 @@ export default function Competitions() {

Challenges Pending Approval

+ {selectedChallenges.length > 0 && }
{pendingChallenges.length > 0 ? ( pendingChallenges.map((challenge) => (
{setSelectedId(challenge.id); setChallengeIsOpen(true);}} className='bg-neutral-800 w-full mb-2 border focus:bg-blue-900 focus:border-blue-500 border-neutral-700 hover:bg-neutral-700/50 cursor-pointer px-2 py-1 flex items-center text-sm'> e.stopPropagation()} // Prevent click event propagation type="checkbox" checked={selectedChallenges.includes(challenge.id)} onChange={() => handleSelectChallenge(challenge.id)} @@ -383,6 +440,43 @@ export default function Competitions() {
+
+

Platform Stats

+ {stats ? ( +
+
+
+

{stats.userCount}

+

Total Users

+
+
+

{stats.challengeCount}

+

Total Challenges

+
+
+

{stats.verifiedChallengeCount}

+

Verified Challenges

+
+ +
+ +

Recent Sign-Ups

+
+ {stats.recentSignUps.map((user) => ( +
window.open(`/users/${user.username}`, '_blank')}> + {`${user.username}'s +
+

{user.username}

+

{new Date(user.createdAt).toLocaleString()}

+
+
+ ))} +
+
+ ) : ( +

Failed to load stats

+ )} +

Selected Content ID's