diff --git a/peerprep-fe/src/app/(main)/components/filter/FilterBar.tsx b/peerprep-fe/src/app/(main)/components/filter/FilterBar.tsx
index 5ad1fabe2e..bb6ef52337 100644
--- a/peerprep-fe/src/app/(main)/components/filter/FilterBar.tsx
+++ b/peerprep-fe/src/app/(main)/components/filter/FilterBar.tsx
@@ -8,17 +8,7 @@ import { TopicsPopover } from './TopicsPopover';
import { FilterState } from '@/hooks/useFilteredProblems';
import { useState, useEffect } from 'react';
import { useDebounce } from '@/hooks/useDebounce';
-
-const DIFFICULTY_OPTIONS = [
- { value: '1', label: 'Easy' },
- { value: '2', label: 'Medium' },
- { value: '3', label: 'Hard' },
-];
-
-const STATUS_OPTIONS = [
- { value: 'todo', label: 'Todo' },
- { value: 'solved', label: 'Solved' },
-];
+import { DIFFICULTY_OPTIONS, STATUS_OPTIONS } from '@/lib/constants';
interface FilterBarProps {
filters: FilterState;
@@ -27,12 +17,16 @@ interface FilterBarProps {
value: string | string[] | null,
) => void;
removeFilter: (key: keyof FilterState, value?: string) => void;
+ isAdmin?: boolean;
+ buttonCallback?: () => void;
}
export default function FilterBar({
filters,
updateFilter,
removeFilter,
+ isAdmin = false,
+ buttonCallback,
}: FilterBarProps) {
const [searchTerm, setSearchTerm] = useState('');
const debouncedSearchTerm = useDebounce(searchTerm, 300); // 300ms delay
@@ -83,9 +77,18 @@ export default function FilterBar({
>
-
+ {!isAdmin ? (
+
+ ) : (
+
+ )}
{filters.difficulty && (
diff --git a/peerprep-fe/src/app/admin/page.tsx b/peerprep-fe/src/app/admin/page.tsx
index 5c9219cc23..7b2a44e65c 100644
--- a/peerprep-fe/src/app/admin/page.tsx
+++ b/peerprep-fe/src/app/admin/page.tsx
@@ -1,13 +1,17 @@
'use client';
-import React from 'react';
+import React, { useState } from 'react';
import { useFilteredProblems } from '@/hooks/useFilteredProblems';
import FilterBar from '../(main)/components/filter/FilterBar';
import ProblemTable from '../../components/problems/ProblemTable';
import { axiosQuestionClient } from '@/network/axiosClient';
import { Problem } from '@/types/types';
import { isAxiosError } from 'axios';
+import ProblemInputDialog from '@/components/problems/ProblemInputDialog';
+import InformationDialog from '@/components/dialogs/InformationDialog';
function AdminPage() {
+ const [isAddDialogOpen, setIsAddDialogOpen] = useState(false);
+ const [informationDialog, setInformationDialog] = useState('');
const {
problems,
filters,
@@ -61,6 +65,51 @@ function AdminPage() {
}
};
+ const handleAdd = async (problem: Problem) => {
+ // TODO: Add proper validation of fields
+ if (
+ problem.description === '' ||
+ problem.title === '' ||
+ problem.tags.length === 0
+ ) {
+ setInformationDialog('Please fill in all required fields');
+ return;
+ }
+ try {
+ const res = await axiosQuestionClient.post(`/questions`, {
+ difficulty: problem.difficulty,
+ description: problem.description,
+ examples: problem.examples,
+ constraints: problem.constraints,
+ tags: problem.tags,
+ title_slug: problem.title_slug,
+ title: problem.title,
+ });
+
+ refetchFilter();
+ setIsAddDialogOpen(false);
+ return res;
+ } catch (e: unknown) {
+ if (isAxiosError(e)) {
+ switch (e.status) {
+ case 400:
+ throw new Error('Invalid question data. Please check your input.');
+ case 409:
+ throw new Error('Question already exists');
+ case 404:
+ throw new Error('Question not found');
+ default:
+ throw new Error('Failed to update question');
+ }
+ }
+ if (e instanceof Error) {
+ throw new Error(e.message);
+ } else {
+ throw new Error('An unknown error occurred');
+ }
+ }
+ };
+
return (
@@ -68,6 +117,8 @@ function AdminPage() {
filters={filters}
updateFilter={updateFilter}
removeFilter={removeFilter}
+ isAdmin
+ buttonCallback={() => setIsAddDialogOpen(true)}
/>
+
setIsAddDialogOpen(false)}
+ requestCallback={handleAdd}
+ requestTitle="Add"
+ />
+
+ setInformationDialog('')}
+ title="Status"
+ description={informationDialog}
+ />
);
}
diff --git a/peerprep-fe/src/app/signin/page.tsx b/peerprep-fe/src/app/signin/page.tsx
index 7818aad824..2663cb1c0e 100644
--- a/peerprep-fe/src/app/signin/page.tsx
+++ b/peerprep-fe/src/app/signin/page.tsx
@@ -33,13 +33,12 @@ export default function LoginForm() {
const token = data.accessToken;
const res = await login(token);
if (res) {
- setAuth(true, token);
+ setAuth(true, token, data);
router.push('/');
return;
}
}
setError(data.error || 'Please provide correct email and password');
- console.error('Login failed');
};
return (
diff --git a/peerprep-fe/src/app/signup/page.tsx b/peerprep-fe/src/app/signup/page.tsx
index b60cbba15f..a862e3cf4e 100644
--- a/peerprep-fe/src/app/signup/page.tsx
+++ b/peerprep-fe/src/app/signup/page.tsx
@@ -38,7 +38,6 @@ export default function SignUpPage() {
password: password,
}),
});
- console.log('did i manage to fetch?, result: ', result);
const data = await result.json();
diff --git a/peerprep-fe/src/components/navbar/Navbar.tsx b/peerprep-fe/src/components/navbar/Navbar.tsx
index 09f254e9b0..184d6a3771 100644
--- a/peerprep-fe/src/components/navbar/Navbar.tsx
+++ b/peerprep-fe/src/components/navbar/Navbar.tsx
@@ -13,7 +13,7 @@ import {
import { useAuthStore } from '@/state/useAuthStore';
export default function Navbar() {
- const { isAuth, clearAuth } = useAuthStore();
+ const { isAuth, clearAuth, user } = useAuthStore();
const handleLogout = async () => {
const res = await logout();
@@ -30,6 +30,11 @@ export default function Navbar() {
PeerPrep
+ {user?.isAdmin && (
+
+ Admin
+
+ )}
Questions
diff --git a/peerprep-fe/src/components/problems/ProblemInputDialog.tsx b/peerprep-fe/src/components/problems/ProblemInputDialog.tsx
index ea89ea853d..293111b997 100644
--- a/peerprep-fe/src/components/problems/ProblemInputDialog.tsx
+++ b/peerprep-fe/src/components/problems/ProblemInputDialog.tsx
@@ -12,11 +12,12 @@ import { Textarea } from '../ui/textarea';
import { Input } from '../ui/input';
import { FilterSelect } from '@/app/(main)/components/filter/FilterSelect';
import { TopicsPopover } from '@/app/(main)/components/filter/TopicsPopover';
+import { DIFFICULTY_OPTIONS, INITIAL_PROBLEM_DATA } from '@/lib/constants';
type Props = {
isOpen: boolean;
onClose: () => void;
- problem: Problem;
+ problem?: Problem;
requestCallback: (problem: Problem) => void;
requestTitle: string;
};
@@ -28,28 +29,30 @@ function ProblemInputDialog({
requestCallback,
requestTitle,
}: Props) {
- const [problemData, setProblemData] = useState
(problem);
+ const [problemData, setProblemData] = useState(
+ problem || INITIAL_PROBLEM_DATA,
+ );
const handleSubmit = async () => {
requestCallback(problemData);
};
- if (!problem) return null;
-
return (