Skip to content

Commit

Permalink
Admin Dashboard (#64)
Browse files Browse the repository at this point in the history
* filter by utorid

* timestamp filter

* filter by date, seperate page for admin

* fixed user role

* removed admin from nav bar

* fixed indentation

* added start and end of timestamp

* user role

* added admin dashboard link to navbar

* removed commented code
  • Loading branch information
alexapostolu authored May 1, 2024
1 parent 77a6b2e commit a56a7b3
Show file tree
Hide file tree
Showing 6 changed files with 240 additions and 14 deletions.
22 changes: 11 additions & 11 deletions client/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { createBrowserRouter, RouterProvider } from "react-router-dom";
import Home from "./pages/Home.tsx";
import AppShell from "./components/AppShell.tsx";
import ErrorElement from "./pages/ErrorElement.tsx";
import Challenges from "./pages/Challenges.tsx";
import Challenge from "./pages/Challenge.tsx";
import Solutions from "./pages/Solutions.tsx";
import PostSolution from "./pages/PostSolution.tsx";
import Solution from "./pages/Solution.tsx";
import Profile from "./pages/Profile.tsx"
import Editor from "./pages/Editor.tsx";
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
import Home from './pages/Home.tsx';
import AppShell from './components/AppShell.tsx';
import ErrorElement from './pages/ErrorElement.tsx';
import Challenges from './pages/Challenges.tsx';
import Challenge from './pages/Challenge.tsx';
import Solutions from './pages/Solutions.tsx';
import PostSolution from './pages/PostSolution.tsx';
import Solution from './pages/Solution.tsx';
import Profile from './pages/Profile.tsx';
import Editor from './pages/Editor.tsx';
import AddChallenge from "./pages/AddChallenge.tsx";


Expand Down
7 changes: 7 additions & 0 deletions client/src/components/NavigationBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { PersonCircle } from "react-bootstrap-icons";
import { NAV_CONFIG } from "../App.tsx";
import NewUserPopup from './NewUserPopup'; // Make sure this path is correct
import { QuestionCircle } from "react-bootstrap-icons";
import { UserData } from '../types/UserData';
import { useQuery } from '../helpers/useQuery.tsx';

function NavigationBar() {
Expand Down Expand Up @@ -63,6 +64,12 @@ function NavigationBar() {
{
userRole === "admin" &&
<>
<Nav.Link
onClick={() => navigate('/admin')}
className={location === '/admin' ? 'text-primary' : ''}
>
Admin
</Nav.Link>
<Nav.Link onClick={() => {navigate("/challenges/add")}} className={location === "/challenges/add" ? "text-primary" : ""}>
Add Challenge
</Nav.Link>
Expand Down
120 changes: 120 additions & 0 deletions client/src/pages/Admin.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import { useState, useEffect } from 'react';
import { useParams, useNavigate } from 'react-router-dom';
import { Container, Row, Button, Col, Card, Form } from 'react-bootstrap';
import { UserData } from '../types/UserData';
import { SolutionData } from '../types/SolutionData';
import SolutionCard from "../components/SolutionCard.tsx";

const Admin = () => {
const { username } = useParams();
const [user, setUser] = useState<UserData>();
const [filteredSolutions, setFilteredSolutions] = useState<SolutionData[]>([]);
const [filteredUsername, setFilteredUsername] = useState<string>('');
const [filteredTimestamp, setFilteredTimestamp] = useState<[string, string]>(['', '']);


const [comments, setComments] = useState([]);
const [error, setError] = useState<string>('');

useEffect(() => {
// Filter soltuions by username and timestamp
fetch(`/api/solutions?username=${filteredUsername}`)
.then(response => {
if (!response.ok) {
throw new Error('Failed to fetch solution data');
}
return response.json() as Promise<SolutionData[]>;
})
.then(data => {
const filteredSolutionsByDate = data.filter(solution => {
// Assuming solution.date is a string in format "YYYY-MM-DD"
const date1 = solution.createdAt.split('T')[0];
return (filteredUsername === "" || solution.userId === filteredUsername) &&
(filteredTimestamp[0] === "" || date1 >= filteredTimestamp[0]) &&
(filteredTimestamp[1] === "" || date1 <= filteredTimestamp[1]);
});
setFilteredSolutions(filteredSolutionsByDate);
})
.catch(error => {
setError('Error fetching solution data: ' + error.message);
});
}, [username, filteredUsername, filteredTimestamp]);

const navigate = useNavigate();

const handleFilterChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setFilteredUsername(event.target.value);
};

const handleTimestampChange = (event: React.ChangeEvent<HTMLInputElement>, index: number) => {
const newTimestamp = [...filteredTimestamp];
newTimestamp[index] = event.target.value;
setFilteredTimestamp(newTimestamp);
};

const handleFilterSubmit = (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
};

return (
<Container>
<h1>Admin Dashboard</h1>
<Row className="mb-3">
<Col>
<Form onSubmit={handleFilterSubmit}>
<Form.Group controlId="usernameFilter">
<Form.Label>Filter by Username</Form.Label>
<Form.Control
type="text"
placeholder="Enter username"
value={filteredUsername}
onChange={handleFilterChange}
/>
</Form.Group>
<Form.Group controlId="timestampFilterStart">
<Form.Label>Filter by Start Timestamp</Form.Label>
<Form.Control
type="date"
value={filteredTimestamp[0]}
onChange={(event) => handleTimestampChange(event, 0)}
/>
</Form.Group>
<Form.Group controlId="timestampFilterEnd">
<Form.Label>Filter by End Timestamp</Form.Label>
<Form.Control
type="date"
value={filteredTimestamp[1]}
onChange={(event) => handleTimestampChange(event, 1)}
/>
</Form.Group>
</Form>
</Col>
</Row>
<Row className="mb-5 border-0 rounded-3 overflow-hidden shadow-sm">
<Col>
<h2 className="mb-4">All Solutions</h2>
{filteredSolutions.length !== 0 ? (
<Row sm={1} lg={3}>
{filteredSolutions.map((solution) => (
<Col key={solution.id} className="mb-4">
<SolutionCard
title={solution.title}
description={solution.description}
imgSrc={solution.diagram}
id={solution.id.toString()}
author={solution.User.username}
createdAt={solution.createdAt}
/>
</Col>
))}
</Row>
) : (
"No solutions found :("
)}
</Col>
</Row>
</Container>
);
};

export default Admin;
13 changes: 10 additions & 3 deletions client/src/pages/Profile.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useState, useEffect } from 'react';
import { useParams } from 'react-router-dom';
import { Container, Row, Col, Card } from 'react-bootstrap';
import { useParams, useNavigate } from 'react-router-dom';
import { Container, Row, Button, Col, Card, Form } from 'react-bootstrap';
import { UserData } from '../types/UserData';
import { SolutionData } from '../types/SolutionData';
import { CommentData } from '../types/CommentData';
Expand All @@ -12,6 +12,12 @@ import MasonryGrid from '../components/MasonryGrid';
const Profile = () => {
const { username } = useParams();
const [user, setUser] = useState<UserData>();
const [filteredSolutions, setFilteredSolutions] = useState<SolutionData[]>([]);
const [filteredUsername, setFilteredUsername] = useState<string>('');
//const [comments, setComments] = useState([]);
const [error, setError] = useState<string>('');

const navigate = useNavigate();
const [myProfile, setMyProfile] = useState<boolean>(false);
const [solutions, setSolutions] = useState<SolutionData[]>([]);
const [comments, setComments] = useState<CommentData[]>([]);
Expand All @@ -22,7 +28,7 @@ const Profile = () => {
fetch(`/api/users/${username}`)
.then(response => {
if (!response.ok) {
throw new Error('Failed to fetch user data:(');
throw new Error('Failed to fetch user data :(');
}
return response.json() as Promise<UserData>;
})
Expand Down Expand Up @@ -104,6 +110,7 @@ const Profile = () => {

return (
<Container>
{error && <div>{error}</div>} {/* Display error message if there's an error */}
{user && (
<Row className="bg-secondary-subtle text-dark p-4">
<Col>
Expand Down
91 changes: 91 additions & 0 deletions package-lock.json

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

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"multer": "^1.4.5-lts.1",
"mysql": "^2.18.1",
"mysql2": "^3.9.1",
"react-router-dom": "^6.22.3",
"sequelize": "^6.37.0",
"sqlite3": "^5.1.7"
},
Expand Down

0 comments on commit a56a7b3

Please sign in to comment.