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",
"version": "1.0.0",
"version": "1.1.0",
"description": "DCST1008 Project - Server - Asura Tournament Management System",
"author": "felixalb, kristoju, jonajha, krisleri",
"private": true,

View File

@ -9,7 +9,7 @@
name="description"
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
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"
},
{
"src": "logo192.png",
"src": "favicon.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
"sizes": "100x100"
}
],
"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 ErrorSnackbar from "./components/ErrorSnackbar";
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 { Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle } 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, DialogTitle } from "@mui/material";
import AddCircleIcon from '@mui/icons-material/AddCircle';
import DeleteIcon from '@mui/icons-material/Delete';
import EditIcon from '@mui/icons-material/Edit';
import PropTypes from 'prop-types'
function AdminCreator(props){
@ -43,7 +42,6 @@ function AdminCreator(props){
<div align="center">
<form>
<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%']}}>
<Box sx={{padding: "10px"}}>
Create Admin
@ -64,7 +62,6 @@ function UserList(props){
.then(data => {
if(data.status !== "OK"){
showError(data.data);
console.log("UWU")
return;
}
props.onUserUpdated();
@ -115,7 +112,6 @@ function UserList(props){
</b>
</TableCell>
<TableCell>{user.email}</TableCell>
{/* TODO Drop down menu for selecting rank */}
<TableCell>
<FormControl variant="standard">
<Select onChange={updateRank(user.asuraId)} value={user.isManager ? "manager" : "admin"} aria-label="rank" id="rankSelect">
@ -124,9 +120,7 @@ function UserList(props){
</Select>
</FormControl>
</TableCell>
{/* <TableCell align="right">{team.members}</TableCell> */}
<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>
</TableCell>
</TableRow>
@ -139,7 +133,6 @@ function UserList(props){
}
function ConfirmationDialogRaw(props) {
const { userId } = useParams();
const { onClose, value: valueProp, open, ...other } = props;
const [value, setValue] = React.useState(valueProp);

View File

@ -12,8 +12,10 @@ import Appbar from './components/AsuraBar';
import SuccessSnackbar from "./components/SuccessSnackbar";
import ErrorSnackbar from "./components/ErrorSnackbar";
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 KeyboardDoubleArrowDownIcon from '@mui/icons-material/KeyboardDoubleArrowDown';
import KeyboardDoubleArrowUpIcon from '@mui/icons-material/KeyboardDoubleArrowUp';
@ -164,7 +166,6 @@ function TournamentList(props) {
currenttournaments.push(tournaments[i])
}
}
// tournaments.filter((tournament) => today - tournament.endTime < 24*60*60*1000)
setTournamentList(currenttournaments);
})
.catch((err) => console.log(err.message));
@ -224,17 +225,6 @@ export default function App() {
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(() => {
fetchUser();
@ -269,6 +259,8 @@ export default function App() {
<Route path="/login" element={<LoginPage user={user} />} />
<Route path="/profile" element={<ProfilePage user={user} />} />
<Route path="/admins" element={<AdminsOverview user={user} />} />
<Route path="/nouser" element={<NoUserPage user={user} />} />
<Route path="*"element={<NoSuchPage user={user} />} />
</Routes>
</Router>

View File

@ -1,13 +1,12 @@
import * as React from "react";
import { BrowserRouter as Router, Link, Route, Routes } from "react-router-dom";
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) {
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 = "/";
return;
}

View File

@ -2,8 +2,8 @@ import * as React from "react";
import { BrowserRouter as Router, Link, Route, Routes } from "react-router-dom";
import Appbar from "./components/AsuraBar";
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) {
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 ErrorSnackbar from "./components/ErrorSnackbar";
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 AdapterDateFns from '@mui/lab/AdapterDateFns';
import LocalizationProvider from '@mui/lab/LocalizationProvider';
@ -57,7 +57,7 @@ function postTournament(tournamentName, tournamentDescription, tournamentStartDa
.then(response => response.json())
.then(data => {
if (data.status === "OK") {
alert("Tournament created successfully");
console.log("Tournament created successfully");
let tournamentId = data.data.tournamentId;
if (tournamentId) {
window.location.href = "/tournament/" + tournamentId;
@ -106,9 +106,7 @@ function TournamentForm(props) {
<>
<form>
<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}}/>
{/* <InputLabel htmlFor="descriptionInput">Description: </InputLabel */}
<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}}/>
<Box flexGrow={1}>
@ -129,8 +127,6 @@ function TournamentForm(props) {
/>
</LocalizationProvider>
</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>
</Box>
<InputLabel id="max-teams-label">Maximum number of teams</InputLabel>
@ -146,7 +142,6 @@ function TournamentForm(props) {
</Grid>
</Box>
{/* go brrrr */}
<br /><br />
<Button type="submit" variant="contained" onClick={submitTournament} color="primary">Create Tournament!</Button>

View File

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

View File

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

View File

@ -2,15 +2,12 @@ import * as React from "react";
import { Link } from "react-router-dom";
import Appbar from './components/AsuraBar';
import TournamentBar from "./components/TournamentBar";
import ErrorSnackbar from "./components/ErrorSnackbar";
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 EmojiEventsIcon from '@mui/icons-material/EmojiEvents';
import DoDisturbIcon from '@mui/icons-material/DoDisturb';
import BackspaceIcon from '@mui/icons-material/Backspace';
import AddCircleIcon from '@mui/icons-material/AddCircle';
import { fontSize } from "@mui/system";
function TournamentTier(props){
let roundTypes = ["finals", "semifinals", "quarterfinals", "eighthfinals", "sixteenthfinals", "thirtysecondfinals"];
@ -54,7 +51,7 @@ function Match(props){
.then(response => response.json())
.then(data => {
if (data.status === "OK") {
//Refresh when winner is set successfully
// Refresh when winner is set successfully
props.onwinnerchange();
} else {
showError(data.data)
@ -84,7 +81,7 @@ function Match(props){
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`}>
<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']}}>
@ -104,7 +101,7 @@ function Match(props){
</Stack>
</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`}>
<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']}}>
@ -144,8 +141,6 @@ function WinnerDisplay(props) {
})
.catch(error => showError(error));
};
if (!props.team) {
// Winner is not yet chosen
@ -179,8 +174,7 @@ function BracketViewer(props){
.then(res => res.json())
.then(data => {
if (data.status !== "OK") {
// Do your error thing
console.error(data);
showError(data);
return;
}
let allMatches = data.data;
@ -203,7 +197,7 @@ function BracketViewer(props){
.then(res => res.json())
.then(data=>{
if(data.status !== "OK"){
console.error(data)
showError(data)
return;
}
let teams = data.data;
@ -229,7 +223,6 @@ function BracketViewer(props){
return (
(props.tournament && matches && teams) ?
// <div sx={{width: "100vw", height: "80vh", overflow: "scroll"}} className="bracket">
<>
<div className="bracket">
{matches.map(tierMatches => {
@ -244,19 +237,14 @@ function BracketViewer(props){
);
}
let showError = (message) => {};
function showError(error) {
console.error(error);
}
export default function TournamentOverview(props) {
const { tournamentId } = useParams();
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(() => {
fetch(process.env.REACT_APP_API_URL + `/tournament/${tournamentId}`)
.then(res => res.json())

View File

@ -4,13 +4,13 @@ import Appbar from "./components/AsuraBar";
import TournamentBar from "./components/TournamentBar";
import ErrorSnackbar from "./components/ErrorSnackbar";
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 DeleteIcon from '@mui/icons-material/Delete';
import EditIcon from '@mui/icons-material/Edit';
function TeamCreator(props) {
function postCreate() {
function postCreate() { // Posts new team to api when the form is submitted
let teamName = document.getElementById("teamNameInput").value;
if (!teamName) {
showError("Team name is required");
@ -43,7 +43,6 @@ function TeamCreator(props) {
<div align="center">
<form>
<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%']}}>
<Box sx={{padding: "10px"}}>
Create Team
@ -71,48 +70,47 @@ function TeamList(props) {
.catch(error => showError(error));
}
function search() {
let searchBase = []
let searchResult = []
let originalList = props.originalList
originalList.map((tournament) => searchBase.push(tournament.name))
let input = document.getElementById("searchInput")
let inputUpperCase = input.value.toUpperCase()
for (let i = 0; i < searchBase.length; i++) {
let tournamentName = searchBase[i].toUpperCase()
function search() {
// Update search criteria and re-render the list
let searchBase = [];
let searchResult = [];
let originalList = props.originalList; // Stores the original list of teams before searching
originalList.map((tournament) => searchBase.push(tournament.name));
let input = document.getElementById("searchInput");
let inputUpperCase = input.value.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) {
searchResult.push(tournamentName)
searchResult.push(tournamentName);
}
}
let searchedList = []
let searchedList = [];
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++) {
if (name.toUpperCase() == searchResult[j]) {
searchedList.push(originalList[i])
if (name.toUpperCase() === searchResult[j]) {
searchedList.push(originalList[i]);
}
}
}
if (input.value == "") {
props.setTeams(originalList)
if (input.value === "") {
props.setTeams(originalList);
} else {
props.setTeams(searchedList)
props.setTeams(searchedList);
}
}
return (
<Paper sx={{minHeight: "30vh", width: "90vw", margin: "10px auto"}} component={Stack} direction="column" justifyContent="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}/>
<Table aria-label="simple table">
<TableHead>
<TableRow>
<TableCell><h2>Team Name</h2></TableCell>
{/* <TableCell align="right">Team Members</TableCell> */}
<TableCell align="center"><h2>Actions</h2></TableCell>
</TableRow>
</TableHead>
@ -138,6 +136,7 @@ function TeamList(props) {
}
function TeamEditor(props) {
// Component that returns a team name editor if a team is selected in the list.
const [team, setTeam] = React.useState({});
React.useEffect(() => {
if (props.selectedTeamId === -1) {
@ -156,7 +155,7 @@ function TeamEditor(props) {
.catch(error => showError(error));
}, [props.selectedTeamId]);
if (props.selectedTeamId === -1 || !team) {
if (props.selectedTeamId === -1 || !team) { //returns if no team is selected for editing
return (
<Paper sx={{minHeight: "30vh", width: "90vw", margin: "10px auto"}} component={Stack} direction="column" justifyContent="center">
<div align="center" >
@ -176,7 +175,7 @@ function TeamEditor(props) {
event.currentTarget.select()
}
function saveTeam() {
function saveTeam() { //pushes new team name to api
let formData = new FormData();
formData.append("name", team.name);
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">
<div align="center">
<h2><b>Edit Team:</b></h2>
@ -227,7 +226,6 @@ export default function TournamentTeams(props) {
}
setTeams(data.data);
setOriginalList(data.data)
//setselectedTeamId(teams[0].id);
})
.catch((err) => showError(err));
}
@ -243,7 +241,7 @@ export default function TournamentTeams(props) {
setOpenError(true);
}
if (!props.user.isLoggedIn) { return <LoginPage user={props.user} />; }
if (!props.user.isLoggedIn) { return <LoginPage user={props.user} />; }
return (
<>

View File

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

View File

@ -1,19 +1,25 @@
import * as React from "react";
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() {
return(
export default function NoSuchPage(props) {
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
</Typography>
<Typography type="h4">
<Typography type="h3">
The page you are looking for does not exist or has been moved
</Typography>
<Link to="/">
Return to the home page
</Link>
</div>
</Paper>
</>
)
);
}

View File

@ -1,18 +1,24 @@
import * as React from "react";
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() {
return(
export default function NoUserPage(props) {
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
</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>
or
<Link to="/"> Return to the home page</Link>
</Typography>
</div>
</Paper>
</>
)
);
}

View File

@ -1,7 +1,7 @@
import * as React from "react";
import { useParams } 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 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({
palette: {
// primary: {
// },
// secondary: {
// },
pewterblue: {
main: '#8fbcbb',
contrastText: '#fff',
@ -63,8 +59,6 @@ const theme = createTheme({
background: {
default: '#f0f2f2',
}
// contrastThreshold: 5,
// tonalOffset: 0.2,
},
});

View File

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

View File

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

View File

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

View File

@ -4,11 +4,18 @@ const session = require('express-session');
const https = require("https");
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
let tmdb = require("./tmdb.js");
// #region Express setup
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;
app.listen(parseInt(port), () => {
console.log(`Listening on port ${port}`)
@ -32,9 +39,13 @@ app.use("/api", api);
api.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
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");
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'));
app.use(require('express-log-url'));
@ -42,11 +53,12 @@ app.use(require('express-log-url'));
// #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 staticPath = path.join(process.env.CLIENT_BUILD_DIR, "static");
app.use('/', express.static(process.env.CLIENT_BUILD_DIR));
app.use('/login', express.static(indexhtmlPath));
app.use('/nous', express.static(indexhtmlPath));
app.use('/history', express.static(indexhtmlPath));
app.use('/admins', express.static(indexhtmlPath));
app.use('/profile', express.static(indexhtmlPath));
@ -56,6 +68,9 @@ app.use('/static/*', express.static(staticPath));
// #endregion
// #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');
var userProfile;
@ -109,7 +124,7 @@ app.get('/auth/google/callback',
req.session.user = dbUser;
} else {
// 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));
req.session.user = user;
@ -120,7 +135,8 @@ app.get('/auth/google/callback',
})
.catch(err => {
// 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;
});
}
@ -128,7 +144,14 @@ app.get('/auth/google/callback',
// #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
api.get("/tournament/getTournaments", (req, res) => {
tmdb.getTournaments()
@ -190,7 +213,6 @@ api.post("/tournament/:tournamentId/edit", async (req, res) => {
let prize = req.body.prize;
let startDate = req.body.startDate;
let endDate = req.body.endDate;
console.log(startDate);
if (name == undefined || name == "" || description == undefined || description == "") {
res.json({"status": "error", "data": "name and description must be provided"});
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"});
return
}
// let today = new Date();
// if (startDate < today) {
// res.json({"status": "error", "data": "startDate cannot be in the past"});
// return
// }
if (startDate > endDate) {
res.json({"status": "error", "data": "startDate cannot be after endDate"});
return
@ -376,7 +394,6 @@ api.post("/team/:teamId/edit", async (req, res) => {
let teamId = req.params.teamId;
let teamName = req.body.name;
console.log(req.body);
if (isNaN(teamId)) {
res.json({"status": "error", "data": "teamId must be a number"});
return
@ -406,16 +423,13 @@ api.post("/tournament/create", async (req, res) => {
res.json({"status": "error", "data": "No data supplied"});
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 description = req.body.description;
let prize = req.body.prize;
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;
console.log(startDate, endDate);
if (name == undefined || name == "" || description == undefined || description == "") {
res.json({"status": "error", "data": "name and description must be provided"});
return
@ -450,7 +464,6 @@ api.post("/tournament/create", async (req, res) => {
res.json({"status": "error", "data": "endDate must be later than startDate"});
return
}
console.log(startDate);
tmdb.createTournament(name, description, prize, startDate, endDate, teamLimit)
.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) => {
// if (req.session.user) {
// res.json({"status": "OK", "data": req.session.user});