Major spring cleaning: Remove unused code, clarify comments, move assets

This commit is contained in:
Felix Albrigtsen 2022-04-29 10:52:51 +02:00
parent 5e1ed162a3
commit c30d6912e6
24 changed files with 150 additions and 214 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "tournament-server", "name": "tournament-server",
"version": "1.0.0", "version": "1.1.0",
"description": "DCST1008 Project - Server - Asura Tournament Management System", "description": "DCST1008 Project - Server - Asura Tournament Management System",
"author": "felixalb, kristoju, jonajha, krisleri", "author": "felixalb, kristoju, jonajha, krisleri",
"private": true, "private": true,

View File

@ -9,7 +9,7 @@
name="description" name="description"
content="Asura Tournament System" content="Asura Tournament System"
/> />
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" /> <link rel="apple-touch-icon" href="%PUBLIC_URL%/favicon.png" />
<!-- <!--
manifest.json provides metadata used when your web app is installed on a manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/ user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/

View File

@ -8,14 +8,9 @@
"type": "image/x-icon" "type": "image/x-icon"
}, },
{ {
"src": "logo192.png", "src": "favicon.png",
"type": "image/png", "type": "image/png",
"sizes": "192x192" "sizes": "100x100"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
} }
], ],
"start_url": ".", "start_url": ".",

View File

@ -3,11 +3,10 @@ import { BrowserRouter as Router, Link, Route, Routes, useParams } from "react-r
import Appbar from "./components/AsuraBar"; import Appbar from "./components/AsuraBar";
import ErrorSnackbar from "./components/ErrorSnackbar"; import ErrorSnackbar from "./components/ErrorSnackbar";
import LoginPage from "./LoginPage"; import LoginPage from "./LoginPage";
import { Button, Box, TextField, Stack, InputLabel, Paper, TableContainer, Table, TableBody, TableHead, TableCell, TableRow, Typography, Select, MenuItem, FormControl } from '@mui/material'; import { Button, Box, TextField, Stack, Paper, Table, TableBody, TableHead, TableCell, TableRow, Typography, Select, MenuItem, FormControl } from '@mui/material';
import { Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle } from "@mui/material"; import { Dialog, DialogActions, DialogContent, DialogTitle } from "@mui/material";
import AddCircleIcon from '@mui/icons-material/AddCircle'; import AddCircleIcon from '@mui/icons-material/AddCircle';
import DeleteIcon from '@mui/icons-material/Delete'; import DeleteIcon from '@mui/icons-material/Delete';
import EditIcon from '@mui/icons-material/Edit';
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
function AdminCreator(props){ function AdminCreator(props){
@ -43,7 +42,6 @@ function AdminCreator(props){
<div align="center"> <div align="center">
<form> <form>
<TextField id="adminEmailInput" label="Admin Email" variant="outlined" type="email" sx={{width:['auto','50%','60%','70%']}} /> <TextField id="adminEmailInput" label="Admin Email" variant="outlined" type="email" sx={{width:['auto','50%','60%','70%']}} />
{/* <Button variant="contained" color="primary" onClick={postCreate}>Create Team</Button> */}
<Button type="submit" variant="contained" color="success" onClick={postCreate} sx={{marginLeft:['5px'],width:['fit-content','40%','30%','20%']}}> <Button type="submit" variant="contained" color="success" onClick={postCreate} sx={{marginLeft:['5px'],width:['fit-content','40%','30%','20%']}}>
<Box sx={{padding: "10px"}}> <Box sx={{padding: "10px"}}>
Create Admin Create Admin
@ -64,7 +62,6 @@ function UserList(props){
.then(data => { .then(data => {
if(data.status !== "OK"){ if(data.status !== "OK"){
showError(data.data); showError(data.data);
console.log("UWU")
return; return;
} }
props.onUserUpdated(); props.onUserUpdated();
@ -115,7 +112,6 @@ function UserList(props){
</b> </b>
</TableCell> </TableCell>
<TableCell>{user.email}</TableCell> <TableCell>{user.email}</TableCell>
{/* TODO Drop down menu for selecting rank */}
<TableCell> <TableCell>
<FormControl variant="standard"> <FormControl variant="standard">
<Select onChange={updateRank(user.asuraId)} value={user.isManager ? "manager" : "admin"} aria-label="rank" id="rankSelect"> <Select onChange={updateRank(user.asuraId)} value={user.isManager ? "manager" : "admin"} aria-label="rank" id="rankSelect">
@ -124,9 +120,7 @@ function UserList(props){
</Select> </Select>
</FormControl> </FormControl>
</TableCell> </TableCell>
{/* <TableCell align="right">{team.members}</TableCell> */}
<TableCell align="center"> <TableCell align="center">
{/* <Button variant="contained" sx={{margin: "auto 5px"}} color="primary" onClick={() => props.setSelectedTeamId(team.id)} endIcon={<EditIcon />}>Edit</Button> */}
<Button variant="contained" sx={{margin: "auto 5px"}} color="error" onClick={() => {deleteUser(user.asuraId)}} endIcon={<DeleteIcon />}>Delete</Button> <Button variant="contained" sx={{margin: "auto 5px"}} color="error" onClick={() => {deleteUser(user.asuraId)}} endIcon={<DeleteIcon />}>Delete</Button>
</TableCell> </TableCell>
</TableRow> </TableRow>
@ -139,7 +133,6 @@ function UserList(props){
} }
function ConfirmationDialogRaw(props) { function ConfirmationDialogRaw(props) {
const { userId } = useParams();
const { onClose, value: valueProp, open, ...other } = props; const { onClose, value: valueProp, open, ...other } = props;
const [value, setValue] = React.useState(valueProp); const [value, setValue] = React.useState(valueProp);

View File

@ -12,8 +12,10 @@ import Appbar from './components/AsuraBar';
import SuccessSnackbar from "./components/SuccessSnackbar"; import SuccessSnackbar from "./components/SuccessSnackbar";
import ErrorSnackbar from "./components/ErrorSnackbar"; import ErrorSnackbar from "./components/ErrorSnackbar";
import AdminsOverview from "./AdminsOverview"; import AdminsOverview from "./AdminsOverview";
import NoSuchPage from "./components/NoSuchPage.js";
import NoUserPage from "./components/NoUserPage.js";
import { Button, Container, Typography, Box, Stack, Card, CardContent, CardMedia, Paper, Grid, Icon, TextField } from "@mui/material"; import { Button, Container, Typography, Box, Stack, Card, CardContent, CardMedia, Paper, Grid, } from "@mui/material";
import AddCircleIcon from '@mui/icons-material/AddCircle'; import AddCircleIcon from '@mui/icons-material/AddCircle';
import KeyboardDoubleArrowDownIcon from '@mui/icons-material/KeyboardDoubleArrowDown'; import KeyboardDoubleArrowDownIcon from '@mui/icons-material/KeyboardDoubleArrowDown';
import KeyboardDoubleArrowUpIcon from '@mui/icons-material/KeyboardDoubleArrowUp'; import KeyboardDoubleArrowUpIcon from '@mui/icons-material/KeyboardDoubleArrowUp';
@ -164,7 +166,6 @@ function TournamentList(props) {
currenttournaments.push(tournaments[i]) currenttournaments.push(tournaments[i])
} }
} }
// tournaments.filter((tournament) => today - tournament.endTime < 24*60*60*1000)
setTournamentList(currenttournaments); setTournamentList(currenttournaments);
}) })
.catch((err) => console.log(err.message)); .catch((err) => console.log(err.message));
@ -224,17 +225,6 @@ export default function App() {
setUser({ isManager: false, isLoggedIn: false }); setUser({ isManager: false, isLoggedIn: false });
}); });
} }
// Debug mode, allow all:
// let fetchUser = () => {
// setUser({
// name: "TEST USERTEST",
// isManager: true,
// isLoggedIn: true,
// email: "testesen@gmail.com",
// asuraId: "123456789",
// googleId: "234"
// });
// }
React.useEffect(() => { React.useEffect(() => {
fetchUser(); fetchUser();
@ -269,6 +259,8 @@ export default function App() {
<Route path="/login" element={<LoginPage user={user} />} /> <Route path="/login" element={<LoginPage user={user} />} />
<Route path="/profile" element={<ProfilePage user={user} />} /> <Route path="/profile" element={<ProfilePage user={user} />} />
<Route path="/admins" element={<AdminsOverview user={user} />} /> <Route path="/admins" element={<AdminsOverview user={user} />} />
<Route path="/nouser" element={<NoUserPage user={user} />} />
<Route path="*"element={<NoSuchPage user={user} />} />
</Routes> </Routes>
</Router> </Router>

View File

@ -1,13 +1,12 @@
import * as React from "react"; import * as React from "react";
import { BrowserRouter as Router, Link, Route, Routes } from "react-router-dom"; import { BrowserRouter as Router, Link, Route, Routes } from "react-router-dom";
import Appbar from "./components/AsuraBar"; import Appbar from "./components/AsuraBar";
import ErrorSnackbar from "./components/ErrorSnackbar";
import {Button, Textfield, Stack, InputLabel, Paper, Typography} from '@mui/material'; import { Stack, Paper, Typography} from '@mui/material';
export default function LoginPage(props) { export default function LoginPage(props) {
if (props.user.isLoggedIn) { if (props.user.isLoggedIn) {
//Redirect to the front page if the user is logged in // Redirect to the front page if the user is logged in
window.location.href = "/"; window.location.href = "/";
return; return;
} }

View File

@ -2,8 +2,8 @@ import * as React from "react";
import { BrowserRouter as Router, Link, Route, Routes } from "react-router-dom"; import { BrowserRouter as Router, Link, Route, Routes } from "react-router-dom";
import Appbar from "./components/AsuraBar"; import Appbar from "./components/AsuraBar";
import LoginPage from "./LoginPage"; import LoginPage from "./LoginPage";
import ErrorSnackbar from "./components/ErrorSnackbar";
import { Button, TextField, Stack, InputLabel, Select, Container, Slider, Paper, Box, Grid, Typography } from '@mui/material'; import { Stack, Paper, Box } from '@mui/material';
export default function ProfilePage(props) { export default function ProfilePage(props) {
let user = props.user; let user = props.user;

View File

@ -3,7 +3,7 @@ import { BrowserRouter as Router, Link, Route, Routes } from "react-router-dom";
import Appbar from "./components/AsuraBar"; import Appbar from "./components/AsuraBar";
import ErrorSnackbar from "./components/ErrorSnackbar"; import ErrorSnackbar from "./components/ErrorSnackbar";
import LoginPage from "./LoginPage"; import LoginPage from "./LoginPage";
import { Button, TextField, Stack, InputLabel, Select, Container, Slider, Paper, Box, Grid, Typography } from '@mui/material'; import { Button, TextField, Stack, InputLabel, Container, Slider, Paper, Box, Grid } from '@mui/material';
import DateTimePicker from '@mui/lab/DateTimePicker'; import DateTimePicker from '@mui/lab/DateTimePicker';
import AdapterDateFns from '@mui/lab/AdapterDateFns'; import AdapterDateFns from '@mui/lab/AdapterDateFns';
import LocalizationProvider from '@mui/lab/LocalizationProvider'; import LocalizationProvider from '@mui/lab/LocalizationProvider';
@ -57,7 +57,7 @@ function postTournament(tournamentName, tournamentDescription, tournamentStartDa
.then(response => response.json()) .then(response => response.json())
.then(data => { .then(data => {
if (data.status === "OK") { if (data.status === "OK") {
alert("Tournament created successfully"); console.log("Tournament created successfully");
let tournamentId = data.data.tournamentId; let tournamentId = data.data.tournamentId;
if (tournamentId) { if (tournamentId) {
window.location.href = "/tournament/" + tournamentId; window.location.href = "/tournament/" + tournamentId;
@ -106,9 +106,7 @@ function TournamentForm(props) {
<> <>
<form> <form>
<Stack sx={{minHeight: "30vh", margin: "10px auto"}} direction="column" justifyContent="center" spacing={3} align="center"> <Stack sx={{minHeight: "30vh", margin: "10px auto"}} direction="column" justifyContent="center" spacing={3} align="center">
{/* <InputLabel htmlFor="nameInput">Tournament Name: </InputLabel> */}
<TextField type="text" id="nameInput" label="Tournament Name" placeholder="Tournament Name" InputLabelProps={{shrink: true}}/> <TextField type="text" id="nameInput" label="Tournament Name" placeholder="Tournament Name" InputLabelProps={{shrink: true}}/>
{/* <InputLabel htmlFor="descriptionInput">Description: </InputLabel */}
<TextField type="text" multiline={true} id="descriptionInput" label="Description" placeholder="Description" InputLabelProps={{shrink: true}}/> <TextField type="text" multiline={true} id="descriptionInput" label="Description" placeholder="Description" InputLabelProps={{shrink: true}}/>
<TextField type="text" id="prizeInput" label="Prize" placeholder="Prize" InputLabelProps={{shrink: true}}/> <TextField type="text" id="prizeInput" label="Prize" placeholder="Prize" InputLabelProps={{shrink: true}}/>
<Box flexGrow={1}> <Box flexGrow={1}>
@ -129,8 +127,6 @@ function TournamentForm(props) {
/> />
</LocalizationProvider> </LocalizationProvider>
</Grid> </Grid>
{/* <TextField type="datetime-local" id="startDatePicker" label="Start Time" InputLabelProps={{shrink: true}} sx={{width: "48%", marginRight: "2%"}} />
<TextField type="datetime-local" id="endDatePicker" label="End Time" InputLabelProps={{shrink: true}} sx={{width: "48%", marginLeft: "2%"}} /> */}
</Grid> </Grid>
</Box> </Box>
<InputLabel id="max-teams-label">Maximum number of teams</InputLabel> <InputLabel id="max-teams-label">Maximum number of teams</InputLabel>
@ -146,7 +142,6 @@ function TournamentForm(props) {
</Grid> </Grid>
</Box> </Box>
{/* go brrrr */}
<br /><br /> <br /><br />
<Button type="submit" variant="contained" onClick={submitTournament} color="primary">Create Tournament!</Button> <Button type="submit" variant="contained" onClick={submitTournament} color="primary">Create Tournament!</Button>

View File

@ -1,15 +1,13 @@
import * as React from "react"; import * as React from "react";
import { BrowserRouter as Router, Link, Route, Routes } from "react-router-dom"; import { BrowserRouter as Router, Link, Route, Routes } from "react-router-dom";
import { Button, Container, Typography, Box, Stack, Card, CardContent, CardMedia, Paper, Grid, Icon, TextField } from "@mui/material"; import { Button, Container, Typography, Box, Stack, Card, CardContent, CardMedia, Paper, Grid, TextField } from "@mui/material";
import AddCircleIcon from '@mui/icons-material/AddCircle';
import KeyboardDoubleArrowDownIcon from '@mui/icons-material/KeyboardDoubleArrowDown'; import KeyboardDoubleArrowDownIcon from '@mui/icons-material/KeyboardDoubleArrowDown';
import KeyboardDoubleArrowUpIcon from '@mui/icons-material/KeyboardDoubleArrowUp'; import KeyboardDoubleArrowUpIcon from '@mui/icons-material/KeyboardDoubleArrowUp';
import Appbar from './components/AsuraBar'; import Appbar from './components/AsuraBar';
import LoginPage from './LoginPage'; import LoginPage from './LoginPage';
import EmojiEventsIcon from '@mui/icons-material/EmojiEvents'; import EmojiEventsIcon from '@mui/icons-material/EmojiEvents';
function shorten(description, maxLength) {
function shorten(description, maxLength) {
if (description.length > maxLength) { if (description.length > maxLength) {
return description.substring(0, maxLength) + "..."; return description.substring(0, maxLength) + "...";
} }
@ -22,7 +20,7 @@ function shorten(description, maxLength) {
function toggleDescription() { function toggleDescription() {
setLongDescription(!longDescription); setLongDescription(!longDescription);
} }
function Description() { function Description() { // Allows for shortening description if needed
if (longDescription) { if (longDescription) {
return( <Box component={Stack} direction="row"> return( <Box component={Stack} direction="row">
<Typography variant="body1" onClick={toggleDescription}>{props.tournament.description}</Typography> <Typography variant="body1" onClick={toggleDescription}>{props.tournament.description}</Typography>
@ -77,7 +75,7 @@ function shorten(description, maxLength) {
function TournamentList() { function TournamentList() {
let [tournamentList, setTournamentList] = React.useState([]); let [tournamentList, setTournamentList] = React.useState([]);
let [originalList, setOriginalList] = React.useState([]) let [originalList, setOriginalList] = React.useState([]);
React.useEffect(() => { React.useEffect(() => {
fetch(process.env.REACT_APP_API_URL + `/tournament/getTournaments`) fetch(process.env.REACT_APP_API_URL + `/tournament/getTournaments`)
@ -88,60 +86,61 @@ function shorten(description, maxLength) {
return; return;
} }
let tournamenthistory = [] let tournamenthistory = [];
let today = new Date() let today = new Date();
let tournaments = Object.values(data.data); let tournaments = Object.values(data.data);
for (let i = 0; i < tournaments.length; i++) { for (let i = 0; i < tournaments.length; i++) {
tournaments[i].startTime = new Date(tournaments[i].startTime); tournaments[i].startTime = new Date(tournaments[i].startTime);
tournaments[i].endTime = new Date(tournaments[i].endTime); tournaments[i].endTime = new Date(tournaments[i].endTime);
if(today - tournaments[i].endTime >= 2*60*60*1000) { if(today - tournaments[i].endTime >= 2*60*60*1000) {
tournamenthistory.push(tournaments[i]) tournamenthistory.push(tournaments[i]);
} }
} }
setTournamentList(tournamenthistory); setTournamentList(tournamenthistory);
setOriginalList(tournamenthistory) setOriginalList(tournamenthistory) // Stores the original tournament list in case its searched
}) })
.catch((err) => console.log(err.message)); .catch((err) => console.log(err.message));
}, []); }, []);
function search() { function search() {
let searchBase = [] let searchBase = [];
let searchResult = [] let searchResult = [];
originalList.map((tournament) => searchBase.push(tournament.name)) originalList.map((tournament) => searchBase.push(tournament.name));
let input = document.getElementById("searchInput") let input = document.getElementById("searchInput");
let inputUpperCase = input.value.toUpperCase() let inputUpperCase = input.value.toUpperCase();
for (let i = 0; i < searchBase.length; i++) { for (let i = 0; i < searchBase.length; i++) { // Matches search input with any part of the team names
let tournamentName = searchBase[i].toUpperCase() let tournamentName = searchBase[i].toUpperCase();
if(tournamentName.indexOf(inputUpperCase) >= 0) { if(tournamentName.indexOf(inputUpperCase) >= 0) {
searchResult.push(tournamentName) searchResult.push(tournamentName);
} }
} }
let searchedList = [] let searchedList = [];
for (let i = 0; i < originalList.length; i++) { for (let i = 0; i < originalList.length; i++) {
let name = originalList[i].name let name = originalList[i].name;
for (let j = 0; j < searchResult.length; j++) { for (let j = 0; j < searchResult.length; j++) {
if (name.toUpperCase() == searchResult[j]) { if (name.toUpperCase() === searchResult[j]) {
searchedList.push(originalList[i]) searchedList.push(originalList[i]);
} }
} }
} }
if (input.value == "") { if (input.value === "") {
setTournamentList(originalList) setTournamentList(originalList);
} else { } else {
setTournamentList(searchedList) setTournamentList(searchedList);
} }
} }
return <Container sx={{minHeight: "30vh", width: "90vw", padding: "20px 20px", alignContent:'center'}}> return (
<TextField sx={{width: '50%', marginLeft: '25%', color: 'black', fontSize: '1.5em'}} size="large" type="text" id="searchInput" label="Search finished tournaments" placeholder="Tournament Name" InputLabelProps={{shrink: true}} onChange={search}/> <Container sx={{minHeight: "30vh", width: "90vw", padding: "20px 20px", alignContent:'center'}}>
<Stack spacing={3} sx={{margin: "10px auto"}}> <TextField sx={{width: '50%', marginLeft: '25%', color: 'black', fontSize: '1.5em'}} size="large" type="text" id="searchInput" label="Search finished tournaments" placeholder="Tournament Name" InputLabelProps={{shrink: true}} onChange={search}/>
{tournamentList && tournamentList.map((tournamentObject) => <TournamentListItem key={tournamentObject.id.toString()} tournament={tournamentObject} />)} <Stack spacing={3} sx={{margin: "10px auto"}}>
</Stack> {tournamentList && tournamentList.map((tournamentObject) => <TournamentListItem key={tournamentObject.id.toString()} tournament={tournamentObject} />)}
</Stack>
</Container>; </Container>
);
} }
export default function TournamentHistory(props) { export default function TournamentHistory(props) {
@ -152,4 +151,4 @@ export default function TournamentHistory(props) {
<TournamentList /> <TournamentList />
</> </>
); );
} }

View File

@ -1,13 +1,11 @@
import * as React from "react"; import * as React from "react";
import { BrowserRouter as Router, Link, Route, Routes } from "react-router-dom"; import { BrowserRouter as Router, Link, Route, Routes } from "react-router-dom";
// import { AlertContainer, alert } from "react-custom-alert";
import Appbar from "./components/AsuraBar"; import Appbar from "./components/AsuraBar";
import TournamentBar from "./components/TournamentBar"; import TournamentBar from "./components/TournamentBar";
import LoginPage from "./LoginPage"; import LoginPage from "./LoginPage";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
import { Button, TextField, Grid, Box, Container, Paper, Stack } from "@mui/material"; import { Button, TextField, Grid, Box, Container, Paper, Stack } from "@mui/material";
import { Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle } from "@mui/material"; import { Dialog, DialogActions, DialogContent, DialogTitle } from "@mui/material";
import CloseIcon from '@mui/icons-material/Close';
import DeleteIcon from '@mui/icons-material/Delete'; import DeleteIcon from '@mui/icons-material/Delete';
import DateTimePicker from '@mui/lab/DateTimePicker'; import DateTimePicker from '@mui/lab/DateTimePicker';
import AdapterDateFns from '@mui/lab/AdapterDateFns'; import AdapterDateFns from '@mui/lab/AdapterDateFns';
@ -17,10 +15,8 @@ import PropTypes from 'prop-types'
let submitChanges = curryTournamentId => event => { let submitChanges = curryTournamentId => event => {
event.preventDefault(); event.preventDefault();
let tournamentId = curryTournamentId; let tournamentId = curryTournamentId;
//TODO: use refs to get values
let tournamentName = document.getElementById("editName").value; let tournamentName = document.getElementById("editName").value;
let tournamentDescription = document.getElementById("editDesc").value; let tournamentDescription = document.getElementById("editDesc").value;
// let tournamentImageFile = document.getElementById("editImage").files[0];
let tournamentStartDate = document.getElementById("editStartDate").value; let tournamentStartDate = document.getElementById("editStartDate").value;
let tournamentEndDate = document.getElementById("editEndDate").value; let tournamentEndDate = document.getElementById("editEndDate").value;
let tournamentPrize = document.getElementById("editPrize").value let tournamentPrize = document.getElementById("editPrize").value
@ -61,11 +57,10 @@ let submitChanges = curryTournamentId => event => {
formData.append("description", tournamentDescription); formData.append("description", tournamentDescription);
formData.append("startDate", tournamentStartDate); formData.append("startDate", tournamentStartDate);
formData.append("endDate", tournamentEndDate); formData.append("endDate", tournamentEndDate);
// formData.append("teamLimit", tournamentMaxTeams);
formData.append("prize", tournamentPrize) formData.append("prize", tournamentPrize)
let body = new URLSearchParams(formData); let body = new URLSearchParams(formData);
fetch(process.env.REACT_APP_API_URL + `/tournament/${tournamentId}/edit`, { fetch(process.env.REACT_APP_API_URL + `/tournament/${tournamentId}/edit`, { // Sends edited data to api
method: "POST", method: "POST",
body: body, body: body,
}) })
@ -82,7 +77,6 @@ let submitChanges = curryTournamentId => event => {
} }
let deleteTournament = tournamentId => event => { let deleteTournament = tournamentId => event => {
console.log(tournamentId);
event.preventDefault(); event.preventDefault();
fetch(process.env.REACT_APP_API_URL + `/tournament/${tournamentId}`, { fetch(process.env.REACT_APP_API_URL + `/tournament/${tournamentId}`, {
@ -168,7 +162,7 @@ function ManageTournament(props) {
); );
} }
function ConfirmationDialogRaw(props) { function ConfirmationDialogRaw(props) { // Creates required confirmation before tournament deletion
const { tournamentId } = useParams(); const { tournamentId } = useParams();
const { onClose, value: valueProp, open, ...other } = props; const { onClose, value: valueProp, open, ...other } = props;
const [value, setValue] = React.useState(valueProp); const [value, setValue] = React.useState(valueProp);
@ -235,7 +229,6 @@ export default function TournamentManager(props) {
<TournamentBar pageTitle="Edit Tournament"/> <TournamentBar pageTitle="Edit Tournament"/>
<Paper sx={{minHeight: "30vh", width: "90vw", margin: "20px auto", padding: "20px 0"}} component={Container} direction="column" align="center"> <Paper sx={{minHeight: "30vh", width: "90vw", margin: "20px auto", padding: "20px 0"}} component={Container} direction="column" align="center">
<ManageTournament tournamentId={tournamentId} /> <ManageTournament tournamentId={tournamentId} />
{/* <AnnounceButton /> */}
<Box sx={{width: "100%"}}> <Box sx={{width: "100%"}}>
<Button variant="contained" color="error" onClick={handleDialogClickListItem} sx={{margin: "auto 5px"}} endIcon={<DeleteIcon />}> <Button variant="contained" color="error" onClick={handleDialogClickListItem} sx={{margin: "auto 5px"}} endIcon={<DeleteIcon />}>
Delete Tournament Delete Tournament

View File

@ -2,15 +2,12 @@ import * as React from "react";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import Appbar from './components/AsuraBar'; import Appbar from './components/AsuraBar';
import TournamentBar from "./components/TournamentBar"; import TournamentBar from "./components/TournamentBar";
import ErrorSnackbar from "./components/ErrorSnackbar";
import { useParams } from 'react-router-dom' import { useParams } from 'react-router-dom'
import { Button, IconButton, Paper, Stack, CircularProgress, Box, Grid, Typography, Container } from "@mui/material"; import { IconButton, Stack, CircularProgress, Box, Typography } from "@mui/material";
import "./components/tournamentBracket.css"; import "./components/tournamentBracket.css";
import EmojiEventsIcon from '@mui/icons-material/EmojiEvents'; import EmojiEventsIcon from '@mui/icons-material/EmojiEvents';
import DoDisturbIcon from '@mui/icons-material/DoDisturb';
import BackspaceIcon from '@mui/icons-material/Backspace'; import BackspaceIcon from '@mui/icons-material/Backspace';
import AddCircleIcon from '@mui/icons-material/AddCircle'; import AddCircleIcon from '@mui/icons-material/AddCircle';
import { fontSize } from "@mui/system";
function TournamentTier(props){ function TournamentTier(props){
let roundTypes = ["finals", "semifinals", "quarterfinals", "eighthfinals", "sixteenthfinals", "thirtysecondfinals"]; let roundTypes = ["finals", "semifinals", "quarterfinals", "eighthfinals", "sixteenthfinals", "thirtysecondfinals"];
@ -54,7 +51,7 @@ function Match(props){
.then(response => response.json()) .then(response => response.json())
.then(data => { .then(data => {
if (data.status === "OK") { if (data.status === "OK") {
//Refresh when winner is set successfully // Refresh when winner is set successfully
props.onwinnerchange(); props.onwinnerchange();
} else { } else {
showError(data.data) showError(data.data)
@ -84,7 +81,7 @@ function Match(props){
return ( return (
<> <>
{/* Team 1 (Winner-status?) (Team name) */} {/* First team of a match, renders team name and checks if its the winner, includes conditional rendering of buttons to promote and demote */}
<Box component='li' className={`game game-top`}> <Box component='li' className={`game game-top`}>
<Stack direction={"row"} alignItems="center" spacing={1} sx={{justifyContent:['start','space-between']}}> <Stack direction={"row"} alignItems="center" spacing={1} sx={{justifyContent:['start','space-between']}}>
<Typography noWrap className={`${props.match.winnerId !== null ? (props.match.team1Id === props.match.winnerId) ? "winner" : "loser" : ""}`} align={'center'} sx={{ maxWidth:'70%', overflow:'hidden', wordWrap:'none', fontSize:['1em','1em','1.5em','1.75em']}}> <Typography noWrap className={`${props.match.winnerId !== null ? (props.match.team1Id === props.match.winnerId) ? "winner" : "loser" : ""}`} align={'center'} sx={{ maxWidth:'70%', overflow:'hidden', wordWrap:'none', fontSize:['1em','1em','1.5em','1.75em']}}>
@ -104,7 +101,7 @@ function Match(props){
</Stack> </Stack>
</Box> </Box>
<Box component='li' className="game game-spacer">&nbsp;</Box> <Box component='li' className="game game-spacer">&nbsp;</Box>
{/* Team 2 (Winner-status?) (Team name) */} {/* Second team of a match, renders team name and checks if its the winner, includes conditional rendering of buttons to promote and demote */}
<Box component='li' className={`game game-bottom`}> <Box component='li' className={`game game-bottom`}>
<Stack direction={"row"} alignItems="center" sx={{justifyContent:['start','space-between']}}> <Stack direction={"row"} alignItems="center" sx={{justifyContent:['start','space-between']}}>
<Typography noWrap className={`${props.match.winnerId !== null ? (props.match.team2Id === props.match.winnerId) ? "winner" : "loser" : ""}`} sx={{maxWidth:'70%', overflow:'hidden', wordWrap:'none',fontSize:['1em','1em','1.5em','1.75em']}}> <Typography noWrap className={`${props.match.winnerId !== null ? (props.match.team2Id === props.match.winnerId) ? "winner" : "loser" : ""}`} sx={{maxWidth:'70%', overflow:'hidden', wordWrap:'none',fontSize:['1em','1em','1.5em','1.75em']}}>
@ -144,8 +141,6 @@ function WinnerDisplay(props) {
}) })
.catch(error => showError(error)); .catch(error => showError(error));
}; };
if (!props.team) { if (!props.team) {
// Winner is not yet chosen // Winner is not yet chosen
@ -179,8 +174,7 @@ function BracketViewer(props){
.then(res => res.json()) .then(res => res.json())
.then(data => { .then(data => {
if (data.status !== "OK") { if (data.status !== "OK") {
// Do your error thing showError(data);
console.error(data);
return; return;
} }
let allMatches = data.data; let allMatches = data.data;
@ -203,7 +197,7 @@ function BracketViewer(props){
.then(res => res.json()) .then(res => res.json())
.then(data=>{ .then(data=>{
if(data.status !== "OK"){ if(data.status !== "OK"){
console.error(data) showError(data)
return; return;
} }
let teams = data.data; let teams = data.data;
@ -229,7 +223,6 @@ function BracketViewer(props){
return ( return (
(props.tournament && matches && teams) ? (props.tournament && matches && teams) ?
// <div sx={{width: "100vw", height: "80vh", overflow: "scroll"}} className="bracket">
<> <>
<div className="bracket"> <div className="bracket">
{matches.map(tierMatches => { {matches.map(tierMatches => {
@ -244,19 +237,14 @@ function BracketViewer(props){
); );
} }
let showError = (message) => {}; function showError(error) {
console.error(error);
}
export default function TournamentOverview(props) { export default function TournamentOverview(props) {
const { tournamentId } = useParams(); const { tournamentId } = useParams();
const [tournament, setTournament] = React.useState(false); const [tournament, setTournament] = React.useState(false);
const [openError, setOpenError] = React.useState(false);
const [errorMessage, setErrorMessage] = React.useState("");
showError = (message) => {
setOpenError(false);
setErrorMessage(message);
setOpenError(true);
}
React.useEffect(() => { React.useEffect(() => {
fetch(process.env.REACT_APP_API_URL + `/tournament/${tournamentId}`) fetch(process.env.REACT_APP_API_URL + `/tournament/${tournamentId}`)
.then(res => res.json()) .then(res => res.json())

View File

@ -4,13 +4,13 @@ import Appbar from "./components/AsuraBar";
import TournamentBar from "./components/TournamentBar"; import TournamentBar from "./components/TournamentBar";
import ErrorSnackbar from "./components/ErrorSnackbar"; import ErrorSnackbar from "./components/ErrorSnackbar";
import LoginPage from "./LoginPage"; import LoginPage from "./LoginPage";
import { Button, TextField, Stack, MenuItem, Box, InputLabel, Select, Container, TableContainer, Table, TableBody, TableHead, TableCell, TableRow, Paper, Typography} from "@mui/material"; import { Button, TextField, Stack, Box, Table, TableBody, TableHead, TableCell, TableRow, Paper} from "@mui/material";
import AddCircleIcon from '@mui/icons-material/AddCircle'; import AddCircleIcon from '@mui/icons-material/AddCircle';
import DeleteIcon from '@mui/icons-material/Delete'; import DeleteIcon from '@mui/icons-material/Delete';
import EditIcon from '@mui/icons-material/Edit'; import EditIcon from '@mui/icons-material/Edit';
function TeamCreator(props) { function TeamCreator(props) {
function postCreate() { function postCreate() { // Posts new team to api when the form is submitted
let teamName = document.getElementById("teamNameInput").value; let teamName = document.getElementById("teamNameInput").value;
if (!teamName) { if (!teamName) {
showError("Team name is required"); showError("Team name is required");
@ -43,7 +43,6 @@ function TeamCreator(props) {
<div align="center"> <div align="center">
<form> <form>
<TextField id="teamNameInput" sx={{width:['auto','50%','60%','70%'], margin:'1% 0'}} label="Team Name" variant="outlined" /> <TextField id="teamNameInput" sx={{width:['auto','50%','60%','70%'], margin:'1% 0'}} label="Team Name" variant="outlined" />
{/* <Button variant="contained" color="primary" onClick={postCreate}>Create Team</Button> */}
<Button type="submit" variant="contained" color="success" onClick={postCreate} sx={{ margin:'1% 1%',width:['fit-content','40%','30%','20%']}}> <Button type="submit" variant="contained" color="success" onClick={postCreate} sx={{ margin:'1% 1%',width:['fit-content','40%','30%','20%']}}>
<Box sx={{padding: "10px"}}> <Box sx={{padding: "10px"}}>
Create Team Create Team
@ -71,48 +70,47 @@ function TeamList(props) {
.catch(error => showError(error)); .catch(error => showError(error));
} }
function search() { function search() {
let searchBase = [] // Update search criteria and re-render the list
let searchResult = [] let searchBase = [];
let originalList = props.originalList let searchResult = [];
originalList.map((tournament) => searchBase.push(tournament.name)) let originalList = props.originalList; // Stores the original list of teams before searching
let input = document.getElementById("searchInput") originalList.map((tournament) => searchBase.push(tournament.name));
let inputUpperCase = input.value.toUpperCase() let input = document.getElementById("searchInput");
for (let i = 0; i < searchBase.length; i++) { let inputUpperCase = input.value.toUpperCase();
let tournamentName = searchBase[i].toUpperCase() for (let i = 0; i < searchBase.length; i++) { // Matches search input with any part of the team names
let tournamentName = searchBase[i].toUpperCase();
if(tournamentName.indexOf(inputUpperCase) >= 0) { if(tournamentName.indexOf(inputUpperCase) >= 0) {
searchResult.push(tournamentName) searchResult.push(tournamentName);
} }
} }
let searchedList = [] let searchedList = [];
for (let i = 0; i < originalList.length; i++) { for (let i = 0; i < originalList.length; i++) {
let name = originalList[i].name let name = originalList[i].name;
for (let j = 0; j < searchResult.length; j++) { for (let j = 0; j < searchResult.length; j++) {
if (name.toUpperCase() == searchResult[j]) { if (name.toUpperCase() === searchResult[j]) {
searchedList.push(originalList[i]) searchedList.push(originalList[i]);
} }
} }
} }
if (input.value == "") { if (input.value === "") {
props.setTeams(originalList) props.setTeams(originalList);
} else { } else {
props.setTeams(searchedList) props.setTeams(searchedList);
} }
} }
return ( return (
<Paper sx={{minHeight: "30vh", width: "90vw", margin: "10px auto"}} component={Stack} direction="column" justifyContent="center"> <Paper sx={{minHeight: "30vh", width: "90vw", margin: "10px auto"}} component={Stack} direction="column" justifyContent="center">
<div align="center" > <div align="center" >
{/* Make a horizontal stack */}
<TextField sx={{margin:'2.5% 0 0 0', width: '50%'}} type="text" id="searchInput" label="Search" placeholder="Team Name" InputLabelProps={{shrink: true}} onChange={search}/> <TextField sx={{margin:'2.5% 0 0 0', width: '50%'}} type="text" id="searchInput" label="Search" placeholder="Team Name" InputLabelProps={{shrink: true}} onChange={search}/>
<Table aria-label="simple table"> <Table aria-label="simple table">
<TableHead> <TableHead>
<TableRow> <TableRow>
<TableCell><h2>Team Name</h2></TableCell> <TableCell><h2>Team Name</h2></TableCell>
{/* <TableCell align="right">Team Members</TableCell> */}
<TableCell align="center"><h2>Actions</h2></TableCell> <TableCell align="center"><h2>Actions</h2></TableCell>
</TableRow> </TableRow>
</TableHead> </TableHead>
@ -138,6 +136,7 @@ function TeamList(props) {
} }
function TeamEditor(props) { function TeamEditor(props) {
// Component that returns a team name editor if a team is selected in the list.
const [team, setTeam] = React.useState({}); const [team, setTeam] = React.useState({});
React.useEffect(() => { React.useEffect(() => {
if (props.selectedTeamId === -1) { if (props.selectedTeamId === -1) {
@ -156,7 +155,7 @@ function TeamEditor(props) {
.catch(error => showError(error)); .catch(error => showError(error));
}, [props.selectedTeamId]); }, [props.selectedTeamId]);
if (props.selectedTeamId === -1 || !team) { if (props.selectedTeamId === -1 || !team) { //returns if no team is selected for editing
return ( return (
<Paper sx={{minHeight: "30vh", width: "90vw", margin: "10px auto"}} component={Stack} direction="column" justifyContent="center"> <Paper sx={{minHeight: "30vh", width: "90vw", margin: "10px auto"}} component={Stack} direction="column" justifyContent="center">
<div align="center" > <div align="center" >
@ -176,7 +175,7 @@ function TeamEditor(props) {
event.currentTarget.select() event.currentTarget.select()
} }
function saveTeam() { function saveTeam() { //pushes new team name to api
let formData = new FormData(); let formData = new FormData();
formData.append("name", team.name); formData.append("name", team.name);
let body = new URLSearchParams(formData) let body = new URLSearchParams(formData)
@ -197,7 +196,7 @@ function TeamEditor(props) {
); );
} }
return ( return (
<Paper sx={{minHeight: "30vh", width: "90vw", margin: "10px auto"}} component={Stack} direction="column" justifyContent="center"> <Paper sx={{minHeight: "30vh", width: "90vw", margin: "10px auto"}} component={Stack} direction="column" justifyContent="center">
<div align="center"> <div align="center">
<h2><b>Edit Team:</b></h2> <h2><b>Edit Team:</b></h2>
@ -227,7 +226,6 @@ export default function TournamentTeams(props) {
} }
setTeams(data.data); setTeams(data.data);
setOriginalList(data.data) setOriginalList(data.data)
//setselectedTeamId(teams[0].id);
}) })
.catch((err) => showError(err)); .catch((err) => showError(err));
} }
@ -243,7 +241,7 @@ export default function TournamentTeams(props) {
setOpenError(true); setOpenError(true);
} }
if (!props.user.isLoggedIn) { return <LoginPage user={props.user} />; } if (!props.user.isLoggedIn) { return <LoginPage user={props.user} />; }
return ( return (
<> <>

View File

@ -1,13 +1,13 @@
import * as React from "react"; import * as React from "react";
import { BrowserRouter as Router, Link, Route, Routes, History } from "react-router-dom"; import { BrowserRouter as Router, Link, Route, Routes } from "react-router-dom";
import { AppBar, Typography, Toolbar, CssBaseline, Box, Button, IconButton, Grid, Menu, MenuItem, Container } from "@mui/material" import { AppBar, Typography, Toolbar, CssBaseline, Box, Button, IconButton, Grid, Menu, MenuItem } from "@mui/material"
import MenuIcon from '@mui/icons-material/Menu'; import MenuIcon from '@mui/icons-material/Menu';
import AccountCircleIcon from '@mui/icons-material/AccountCircle'; import AccountCircleIcon from '@mui/icons-material/AccountCircle';
import HistoryIcon from '@mui/icons-material/History'; import HistoryIcon from '@mui/icons-material/History';
import EditIcon from '@mui/icons-material/Edit'; import EditIcon from '@mui/icons-material/Edit';
import LogoutIcon from '@mui/icons-material/Logout'; import LogoutIcon from '@mui/icons-material/Logout';
import LoginIcon from '@mui/icons-material/Login'; import LoginIcon from '@mui/icons-material/Login';
import logo from "./../Asura2222.png"; import logo from "./AsuraLogo.png";
function LoggedInMenu(props) { function LoggedInMenu(props) {
const [anchorEl, setAnchorEl] = React.useState(null); const [anchorEl, setAnchorEl] = React.useState(null);
@ -42,13 +42,10 @@ function LoggedInMenu(props) {
function NotLoggedInButton() { function NotLoggedInButton() {
const login = () => {
}
return ( return (
<> <>
<Link to="/login" style={{color:"white"}}> <Link to="/login" style={{color:"white"}}>
<Button sx={{color:"white"}} onClick={login} endIcon={<LoginIcon />}> <Button sx={{color:"white"}} endIcon={<LoginIcon />}>
Login Login
</Button> </Button>
</Link> </Link>

View File

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

View File

@ -1,6 +1,5 @@
import * as React from 'react'; import * as React from 'react';
import Stack from '@mui/material/Stack'; import Stack from '@mui/material/Stack';
import Button from '@mui/material/Button';
import Snackbar from '@mui/material/Snackbar'; import Snackbar from '@mui/material/Snackbar';
import MuiAlert from '@mui/material/Alert'; import MuiAlert from '@mui/material/Alert';
@ -17,7 +16,7 @@ export default function showError(props) {
props.setOpen(false); props.setOpen(false);
}; };
if (props.message && props.message.length > 0) { if (props.message && props.message.length > 0) {
console.log(props.message); console.log(props.message); // Lets the user check the console if they want to see the error
} }
return ( return (

View File

@ -1,19 +1,25 @@
import * as React from "react"; import * as React from "react";
import { BrowserRouter as Router, Link, Route, Routes } from "react-router-dom"; import { BrowserRouter as Router, Link, Route, Routes } from "react-router-dom";
import { Typography } from '@mui/material' import { Typography, Paper, Stack } from '@mui/material'
import Appbar from './AsuraBar'
export default function NoSuchPage() { export default function NoSuchPage(props) {
return( return (
<> <>
<Typography type="h3"> <Appbar user={props.user} pageTitle={"Page not found"} />
<Paper sx={{minHeight: "30vh", width: "90vw", margin: "10px auto"}} component={Stack} direction="column" justifyContent="center">
<div align="center">
<Typography type="h2">
This page does not exist This page does not exist
</Typography> </Typography>
<Typography type="h4"> <Typography type="h3">
The page you are looking for does not exist or has been moved The page you are looking for does not exist or has been moved
</Typography> </Typography>
<Link to="/"> <Link to="/">
Return to the home page Return to the home page
</Link> </Link>
</div>
</Paper>
</> </>
) );
} }

View File

@ -1,18 +1,24 @@
import * as React from "react"; import * as React from "react";
import { BrowserRouter as Router, Link, Route, Routes } from "react-router-dom"; import { BrowserRouter as Router, Link, Route, Routes } from "react-router-dom";
import { Typography } from '@mui/material' import { Typography, Paper, Stack } from '@mui/material'
import Appbar from './AsuraBar'
export default function NoSuchPage() { export default function NoUserPage(props) {
return( return (
<> <>
<Typography type="h3"> <Appbar user={props.user} pageTitle={"Invalid User"} />
<Paper sx={{minHeight: "30vh", width: "90vw", margin: "10px auto"}} component={Stack} direction="column" justifyContent="center">
<div align="center">
<Typography type="h2">
You are not logged in You are not logged in
</Typography> </Typography>
<Typography type="h4"> <Typography type="h3">
Your account is not in the administrators list. Try again with another account here: <Link to="/login">Login</Link> Your account is not in the administrators list. Try again with another account here: <Link to="/login">Login</Link>
or or
<Link to="/"> Return to the home page</Link> <Link to="/"> Return to the home page</Link>
</Typography> </Typography>
</div>
</Paper>
</> </>
) );
} }

View File

@ -1,7 +1,7 @@
import * as React from "react"; import * as React from "react";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
import { BrowserRouter as Router, Link, Route, Routes, History } from "react-router-dom"; import { BrowserRouter as Router, Link, Route, Routes, History } from "react-router-dom";
import { Stack, Paper, Typography, Box, Button, Grid, Snackbar, IconButton } from "@mui/material" import { Stack, Paper, Button, Snackbar, IconButton } from "@mui/material"
import CloseIcon from '@mui/icons-material/Close'; import CloseIcon from '@mui/icons-material/Close';
import MuiAlert from '@mui/material/Alert'; import MuiAlert from '@mui/material/Alert';

View File

@ -1,12 +0,0 @@
import * as React from "react";
import { BrowserRouter as Router, Link, Route, Routes } from "react-router-dom";
import Button from "@mui/material/Button";
export default function SaveButton(props) {
return (
<Link to="/">
<Button variant="outlined" color="primary">Save and Exit</Button>
</Link>
);
}

View File

@ -4,10 +4,6 @@ import { createTheme } from '@mui/material/styles';
const theme = createTheme({ const theme = createTheme({
palette: { palette: {
// primary: {
// },
// secondary: {
// },
pewterblue: { pewterblue: {
main: '#8fbcbb', main: '#8fbcbb',
contrastText: '#fff', contrastText: '#fff',
@ -63,8 +59,6 @@ const theme = createTheme({
background: { background: {
default: '#f0f2f2', default: '#f0f2f2',
} }
// contrastThreshold: 5,
// tonalOffset: 0.2,
}, },
}); });

View File

@ -1,19 +1,14 @@
/*
* Flex Layout Specifics
*/
.bracket{ .bracket{
display:flex; display:flex;
flex-direction:row; flex-direction:row;
justify-content: center; justify-content: center;
} }
.round{ .round{
display:flex; display:flex;
flex-direction:column; flex-direction:column;
justify-content:center; justify-content:center;
/* width:20vw; */
list-style:none; list-style:none;
padding:0; padding:0;
/* font-size: 1.5rem; */
} }
.round .spacer{ flex-grow:1;} .round .spacer{ flex-grow:1;}
.round .spacer:first-child, .round .spacer:first-child,
@ -21,17 +16,7 @@
.round .game-spacer{ .round .game-spacer{
flex-grow:1; flex-grow:1;
} }
/*
* General Styles
*/
/* body{
font-family:sans-serif;
font-size:medium;
padding:10px;
line-height:1.4em;
} */
li.game{ li.game{
padding-left:20px; padding-left:20px;
} }
@ -54,7 +39,6 @@ li.game-spacer{
li.game-bottom{ li.game-bottom{
border-top:1px solid #aaa; border-top:1px solid #aaa;
} }
.winnerDisplay { .winnerDisplay {
display:flex; display:flex;

View File

@ -4,11 +4,9 @@ body {
height: 100%; height: 100%;
} }
body { body {
/* <yeet> */
overflow-y: auto !important; overflow-y: auto !important;
margin: 0 !important; margin: 0 !important;
padding: 0 !important; padding: 0 !important;
/* </yeet> */
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
@ -25,12 +23,10 @@ code {
.mainIcon{ .mainIcon{
border-radius: 50%; border-radius: 50%;
/* border: 5px dotted salmon; */
border: 3px solid #1ab35a; border: 3px solid #1ab35a;
background-color: white; background-color: white;
margin: 5px; margin: 5px;
float: left; float: left;
/* margin: 50% calc(2vw + 50%) 50% 50%; */
} }
a { a {

View File

@ -7,6 +7,7 @@ GOOGLE_CLIENT_ID=xxxxxxxxxxxxxxx.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=GOCSPX-xxxxxxxxx GOOGLE_CLIENT_SECRET=GOCSPX-xxxxxxxxx
GOOGLE_CALLBACK_URL=https://asura.feal.no/auth/google/callback GOOGLE_CALLBACK_URL=https://asura.feal.no/auth/google/callback
AUTH_SUCCESS_REDIRECT=https://asura.feal.no/ AUTH_SUCCESS_REDIRECT=https://asura.feal.no/
AUTH_ERROR_REDIRECT=https://asura.feal.no/nouser
DEBUG_ALLOW_ALL=false DEBUG_ALLOW_ALL=false
COOKIE_SECURE=false COOKIE_SECURE=false
COOKIE_SECRET=any random string COOKIE_SECRET=any random string

View File

@ -4,11 +4,18 @@ const session = require('express-session');
const https = require("https"); const https = require("https");
require("dotenv").config(); require("dotenv").config();
/* Asura Tournament Server Index
This node applications functions as a web server to serve the client application, as well as serving the API.
The program is divided into sections or "regions". Each region lies between two comments "// #region <title>" and "// #endregion".
*/
// Our self-written module for handling database operations // Our self-written module for handling database operations
let tmdb = require("./tmdb.js"); let tmdb = require("./tmdb.js");
// #region Express setup // #region Express setup
const app = express(); const app = express();
// Default to 3000 if no port is specified. This port is fine, as the server should be behind a reverse proxy
const port = process.env.SERVER_PORT || 3000; const port = process.env.SERVER_PORT || 3000;
app.listen(parseInt(port), () => { app.listen(parseInt(port), () => {
console.log(`Listening on port ${port}`) console.log(`Listening on port ${port}`)
@ -32,9 +39,13 @@ app.use("/api", api);
api.use(function(req, res, next) { api.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*"); res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Methods", "GET, POST, DELETE"); res.header("Access-Control-Allow-Methods", "GET, POST, DELETE");
// If your API is CORS-enabled, you can use the following line instead of the above line
// res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); // res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
next(); next();
}); });
// Log both the client requests and API requests to terminal.
// This allows for easier debugging and overview. The output includes the response status code and requested URL.
api.use(require('express-log-url')); api.use(require('express-log-url'));
app.use(require('express-log-url')); app.use(require('express-log-url'));
@ -42,11 +53,12 @@ app.use(require('express-log-url'));
// #region frontend // #region frontend
// Serve static files from the React app // Serve static files from the React app.
const indexhtmlPath = path.join(process.env.CLIENT_BUILD_DIR, "index.html"); const indexhtmlPath = path.join(process.env.CLIENT_BUILD_DIR, "index.html");
const staticPath = path.join(process.env.CLIENT_BUILD_DIR, "static"); const staticPath = path.join(process.env.CLIENT_BUILD_DIR, "static");
app.use('/', express.static(process.env.CLIENT_BUILD_DIR)); app.use('/', express.static(process.env.CLIENT_BUILD_DIR));
app.use('/login', express.static(indexhtmlPath)); app.use('/login', express.static(indexhtmlPath));
app.use('/nous', express.static(indexhtmlPath));
app.use('/history', express.static(indexhtmlPath)); app.use('/history', express.static(indexhtmlPath));
app.use('/admins', express.static(indexhtmlPath)); app.use('/admins', express.static(indexhtmlPath));
app.use('/profile', express.static(indexhtmlPath)); app.use('/profile', express.static(indexhtmlPath));
@ -56,6 +68,9 @@ app.use('/static/*', express.static(staticPath));
// #endregion // #endregion
// #region PASSPORT / OAUTH // #region PASSPORT / OAUTH
// This process of signing in with google oauth and storing the session cookie with passport and express
// is loosely based on the google documentation: https://developers.google.com/identity/sign-in/web/sign-in
// as well as this tutorial: https://www.loginradius.com/blog/engineering/google-authentication-with-nodejs-and-passportjs/
const passport = require('passport'); const passport = require('passport');
var userProfile; var userProfile;
@ -109,7 +124,7 @@ app.get('/auth/google/callback',
req.session.user = dbUser; req.session.user = dbUser;
} else { } else {
// User is "preregistered" with email only, so complete the registration // User is "preregistered" with email only, so complete the registration
// This step will register the name, img and googleId // This step will register the name and googleId
tmdb.editUser(user.email, user).catch(err => console.log(err)); tmdb.editUser(user.email, user).catch(err => console.log(err));
req.session.user = user; req.session.user = user;
@ -120,7 +135,8 @@ app.get('/auth/google/callback',
}) })
.catch(err => { .catch(err => {
// User is not in the database at all, do not give them a session. // User is not in the database at all, do not give them a session.
res.json({"status": "error", message: "Email is not in administrator list."}); // JSON alternative: res.json({"status": "error", message: "Email is not in administrator list."});
res.redirect(process.env.AUTH_ERROR_REDIRECT);
return; return;
}); });
} }
@ -128,7 +144,14 @@ app.get('/auth/google/callback',
// #endregion // #endregion
/*
API *endpoints* are given in api.get, api.post, api.delete, etc.
These are the endpoints that the client will use to interact with the server.
Functions that are not defined in this way will not be accessible to the client, but internally in the server.
Most of these endpoints do some simple validations, make an asynchrnous call to the tmdb-module, and returns the result in JSON.
*/
// #region API // #region API
api.get("/tournament/getTournaments", (req, res) => { api.get("/tournament/getTournaments", (req, res) => {
tmdb.getTournaments() tmdb.getTournaments()
@ -190,7 +213,6 @@ api.post("/tournament/:tournamentId/edit", async (req, res) => {
let prize = req.body.prize; let prize = req.body.prize;
let startDate = req.body.startDate; let startDate = req.body.startDate;
let endDate = req.body.endDate; let endDate = req.body.endDate;
console.log(startDate);
if (name == undefined || name == "" || description == undefined || description == "") { if (name == undefined || name == "" || description == undefined || description == "") {
res.json({"status": "error", "data": "name and description must be provided"}); res.json({"status": "error", "data": "name and description must be provided"});
return return
@ -206,11 +228,7 @@ api.post("/tournament/:tournamentId/edit", async (req, res) => {
res.json({"status": "error", "data": "startDate and endDate must be valid dates"}); res.json({"status": "error", "data": "startDate and endDate must be valid dates"});
return return
} }
// let today = new Date();
// if (startDate < today) {
// res.json({"status": "error", "data": "startDate cannot be in the past"});
// return
// }
if (startDate > endDate) { if (startDate > endDate) {
res.json({"status": "error", "data": "startDate cannot be after endDate"}); res.json({"status": "error", "data": "startDate cannot be after endDate"});
return return
@ -376,7 +394,6 @@ api.post("/team/:teamId/edit", async (req, res) => {
let teamId = req.params.teamId; let teamId = req.params.teamId;
let teamName = req.body.name; let teamName = req.body.name;
console.log(req.body);
if (isNaN(teamId)) { if (isNaN(teamId)) {
res.json({"status": "error", "data": "teamId must be a number"}); res.json({"status": "error", "data": "teamId must be a number"});
return return
@ -406,16 +423,13 @@ api.post("/tournament/create", async (req, res) => {
res.json({"status": "error", "data": "No data supplied"}); res.json({"status": "error", "data": "No data supplied"});
return return
} }
//Check that req is json
// if (req.get("Content-Type") != "application/json") {
console.log(req.get("Content-Type"));
let name = req.body.name; let name = req.body.name;
let description = req.body.description; let description = req.body.description;
let prize = req.body.prize; let prize = req.body.prize;
let teamLimit = req.body.teamLimit; let teamLimit = req.body.teamLimit;
let startDate = req.body.startDate; //TODO: timezones, 2 hr skips let startDate = req.body.startDate;
let endDate = req.body.endDate; let endDate = req.body.endDate;
console.log(startDate, endDate);
if (name == undefined || name == "" || description == undefined || description == "") { if (name == undefined || name == "" || description == undefined || description == "") {
res.json({"status": "error", "data": "name and description must be provided"}); res.json({"status": "error", "data": "name and description must be provided"});
return return
@ -450,7 +464,6 @@ api.post("/tournament/create", async (req, res) => {
res.json({"status": "error", "data": "endDate must be later than startDate"}); res.json({"status": "error", "data": "endDate must be later than startDate"});
return return
} }
console.log(startDate);
tmdb.createTournament(name, description, prize, startDate, endDate, teamLimit) tmdb.createTournament(name, description, prize, startDate, endDate, teamLimit)
.then(msg => res.json({"status": "OK", "data": msg})) .then(msg => res.json({"status": "OK", "data": msg}))
@ -573,7 +586,7 @@ api.get("/users/logout", (req, res) => {
}); });
// Debugging functions, disabled on purpouse // Debugging functions, disabled in production environment
// api.get("/users/getSessionUser", (req, res) => { // api.get("/users/getSessionUser", (req, res) => {
// if (req.session.user) { // if (req.session.user) {
// res.json({"status": "OK", "data": req.session.user}); // res.json({"status": "OK", "data": req.session.user});