Merge branch 'client' into 'client-kristofferTournament'
# Conflicts: # src/client/src/TournamentCreator.js # src/client/src/TournamentOverview.js
This commit is contained in:
commit
7865e97c28
|
@ -1,2 +1,3 @@
|
||||||
REACT_APP_API_URL=https://asura.feal.no/api
|
REACT_APP_API_URL=https://asura.feal.no/api
|
||||||
|
REACT_APP_LOGIN_URL=http://localhost:3001/auth/google
|
||||||
BROWSER=none
|
BROWSER=none
|
||||||
|
|
|
@ -4869,6 +4869,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"anymatch": "~3.1.2",
|
"anymatch": "~3.1.2",
|
||||||
"braces": "~3.0.2",
|
"braces": "~3.0.2",
|
||||||
|
"fsevents": "~2.3.2",
|
||||||
"glob-parent": "~5.1.2",
|
"glob-parent": "~5.1.2",
|
||||||
"is-binary-path": "~2.1.0",
|
"is-binary-path": "~2.1.0",
|
||||||
"is-glob": "~4.0.1",
|
"is-glob": "~4.0.1",
|
||||||
|
@ -6116,7 +6117,8 @@
|
||||||
"esprima": "^4.0.1",
|
"esprima": "^4.0.1",
|
||||||
"estraverse": "^5.2.0",
|
"estraverse": "^5.2.0",
|
||||||
"esutils": "^2.0.2",
|
"esutils": "^2.0.2",
|
||||||
"optionator": "^0.8.1"
|
"optionator": "^0.8.1",
|
||||||
|
"source-map": "~0.6.1"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"escodegen": "bin/escodegen.js",
|
"escodegen": "bin/escodegen.js",
|
||||||
|
@ -8724,6 +8726,7 @@
|
||||||
"@types/node": "*",
|
"@types/node": "*",
|
||||||
"anymatch": "^3.0.3",
|
"anymatch": "^3.0.3",
|
||||||
"fb-watchman": "^2.0.0",
|
"fb-watchman": "^2.0.0",
|
||||||
|
"fsevents": "^2.3.2",
|
||||||
"graceful-fs": "^4.2.9",
|
"graceful-fs": "^4.2.9",
|
||||||
"jest-regex-util": "^27.5.1",
|
"jest-regex-util": "^27.5.1",
|
||||||
"jest-serializer": "^27.5.1",
|
"jest-serializer": "^27.5.1",
|
||||||
|
@ -10029,9 +10032,14 @@
|
||||||
"integrity": "sha512-EoQp/Et7OSOVu0aJknJOtlXZsnr8XE8KwuzTHOLeVSEx8pVWUICc8Q0VYRHgzyjX78nMEyC/oztWFbgyhtNfDA==",
|
"integrity": "sha512-EoQp/Et7OSOVu0aJknJOtlXZsnr8XE8KwuzTHOLeVSEx8pVWUICc8Q0VYRHgzyjX78nMEyC/oztWFbgyhtNfDA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"copy-anything": "^2.0.1",
|
"copy-anything": "^2.0.1",
|
||||||
|
"errno": "^0.1.1",
|
||||||
"graceful-fs": "^4.1.2",
|
"graceful-fs": "^4.1.2",
|
||||||
|
"image-size": "~0.5.0",
|
||||||
|
"make-dir": "^2.1.0",
|
||||||
"mime": "^1.4.1",
|
"mime": "^1.4.1",
|
||||||
|
"needle": "^2.5.2",
|
||||||
"parse-node-version": "^1.0.1",
|
"parse-node-version": "^1.0.1",
|
||||||
|
"source-map": "~0.6.0",
|
||||||
"tslib": "^2.3.0"
|
"tslib": "^2.3.0"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
|
@ -12407,6 +12415,7 @@
|
||||||
"eslint-webpack-plugin": "^3.1.1",
|
"eslint-webpack-plugin": "^3.1.1",
|
||||||
"file-loader": "^6.2.0",
|
"file-loader": "^6.2.0",
|
||||||
"fs-extra": "^10.0.0",
|
"fs-extra": "^10.0.0",
|
||||||
|
"fsevents": "^2.3.2",
|
||||||
"html-webpack-plugin": "^5.5.0",
|
"html-webpack-plugin": "^5.5.0",
|
||||||
"identity-obj-proxy": "^3.0.0",
|
"identity-obj-proxy": "^3.0.0",
|
||||||
"jest": "^27.4.3",
|
"jest": "^27.4.3",
|
||||||
|
@ -12826,6 +12835,9 @@
|
||||||
"version": "2.70.1",
|
"version": "2.70.1",
|
||||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.70.1.tgz",
|
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.70.1.tgz",
|
||||||
"integrity": "sha512-CRYsI5EuzLbXdxC6RnYhOuRdtz4bhejPMSWjsFLfVM/7w/85n2szZv6yExqUXsBdz5KT8eoubeyDUDjhLHEslA==",
|
"integrity": "sha512-CRYsI5EuzLbXdxC6RnYhOuRdtz4bhejPMSWjsFLfVM/7w/85n2szZv6yExqUXsBdz5KT8eoubeyDUDjhLHEslA==",
|
||||||
|
"dependencies": {
|
||||||
|
"fsevents": "~2.3.2"
|
||||||
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"rollup": "dist/bin/rollup"
|
"rollup": "dist/bin/rollup"
|
||||||
},
|
},
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 7.8 KiB |
|
@ -0,0 +1,134 @@
|
||||||
|
import * as React from "react";
|
||||||
|
import { BrowserRouter as Router, Link, Route, Routes, useParams } from "react-router-dom";
|
||||||
|
import Appbar from "./components/AsuraBar";
|
||||||
|
import ErrorSnackbar from "./components/ErrorSnackbar";
|
||||||
|
|
||||||
|
import {Button, Box, TextField, Stack, InputLabel, Paper, TableContainer, Table, TableBody, TableHead, TableCell, TableRow, Typography} 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 showError(error) {
|
||||||
|
alert("Something went wrong. \n" + error);
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
function AdminCreator(props){
|
||||||
|
function postCreate(){
|
||||||
|
let adminEmail = document.getElementById("adminEmailInput").value;
|
||||||
|
if (!adminEmail) {
|
||||||
|
showError("Admin email is required");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let formData = new FormData();
|
||||||
|
formData.append("email", adminEmail)
|
||||||
|
let body = new URLSearchParams(formData)
|
||||||
|
|
||||||
|
fetch(process.env.REACT_APP_API_URL + `/admins/create`, {
|
||||||
|
method: "POST",
|
||||||
|
body: body
|
||||||
|
})
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.status !== "OK") {
|
||||||
|
showError(data.data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
document.getElementById("adminEmailInput").value = "";
|
||||||
|
props.onAdminCreated();
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Paper sx={{width: "90vw", margin: "10px auto", padding: "15px"}} component={Stack} direction="column">
|
||||||
|
<div align="center">
|
||||||
|
<TextField id="adminEmailInput" sx={{ width: "70%" }} label="Admin Email" variant="outlined" />
|
||||||
|
{/* <Button variant="contained" color="primary" onClick={postCreate}>Create Team</Button> */}
|
||||||
|
<Button variant="contained" color="success" onClick={postCreate} sx={{width: "20%", marginLeft: "5px"}}>
|
||||||
|
<Box sx={{padding: "10px"}}>
|
||||||
|
Create Admin
|
||||||
|
</Box>
|
||||||
|
<AddCircleIcon />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</Paper>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function AdminList(props){
|
||||||
|
const deleteAdmin = adminId => {
|
||||||
|
fetch(process.env.REACT_APP_API_URL + `/admins/${adminId}`, {method: "DELETE"})
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(data => {
|
||||||
|
if(data.status !== "OK"){
|
||||||
|
showError(data.data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
props.setAdmins(props.admins.filter(admin => admin.id !== adminId));
|
||||||
|
})
|
||||||
|
.catch(error => showError(error));
|
||||||
|
}
|
||||||
|
|
||||||
|
return(
|
||||||
|
<Paper sx={{minHeight: "30vh", width:"90vw", margin:"10px auto"}} component={Stack} direction="column" justifycontent="center">
|
||||||
|
<div align="center">
|
||||||
|
{/* TODO: scroll denne menyen, eventuelt søkefelt */}
|
||||||
|
<Table aria-label="simple table">
|
||||||
|
<TableHead>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell>Admin</TableCell>
|
||||||
|
<TableCell align="center">Actions</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
</TableHead>
|
||||||
|
<TableBody>
|
||||||
|
{props.admins.map((admin) => (
|
||||||
|
<TableRow key={admin.id}>
|
||||||
|
<TableCell component="th" scope="row"> <b>
|
||||||
|
{admin.name}
|
||||||
|
</b></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={() => {deleteAdmin(admin.id)}} endIcon={<DeleteIcon />}>Delete</Button>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</div>
|
||||||
|
</Paper>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Admins(props) {
|
||||||
|
const [admins, setAdmins] = React.useState([]);
|
||||||
|
const { adminId } = useParams();
|
||||||
|
|
||||||
|
function getAdmins() {
|
||||||
|
fetch(process.env.REACT_APP_API_URL + `/admins/getAdmins`)
|
||||||
|
.then((res) => res.json())
|
||||||
|
.then((data) =>{
|
||||||
|
if(data.status !== "OK") {
|
||||||
|
showError(data.data);
|
||||||
|
}
|
||||||
|
setAdmins(data.data);
|
||||||
|
})
|
||||||
|
.catch((err) => showError(err));
|
||||||
|
}
|
||||||
|
React.useEffect(() => {
|
||||||
|
getAdmins()
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Appbar pageTitle="Admins" />
|
||||||
|
<div className="admins">
|
||||||
|
<AdminCreator />
|
||||||
|
<AdminList />
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
|
@ -3,9 +3,9 @@ import { BrowserRouter as Router, Link, Route, Routes } from "react-router-dom";
|
||||||
import TournamentCreator from "./TournamentCreator.js";
|
import TournamentCreator from "./TournamentCreator.js";
|
||||||
import TournamentOverview from "./TournamentOverview.js";
|
import TournamentOverview from "./TournamentOverview.js";
|
||||||
import TournamentManager from "./TournamentManager.js";
|
import TournamentManager from "./TournamentManager.js";
|
||||||
import TournamentAnnouncement from "./TournamentAnnouncement";
|
|
||||||
import TournamentHistory from "./TournamentHistory";
|
import TournamentHistory from "./TournamentHistory";
|
||||||
import TournamentTeams from "./TournamentTeams";
|
import TournamentTeams from "./TournamentTeams";
|
||||||
|
import LoginPage from "./LoginPage";
|
||||||
import AppBar from './components/AsuraBar';
|
import AppBar from './components/AsuraBar';
|
||||||
import { Button, Container, Typography, Box, Stack, Card, CardContent, CardMedia, Paper, Grid, Icon } from "@mui/material";
|
import { Button, Container, Typography, Box, Stack, Card, CardContent, CardMedia, Paper, Grid, Icon } from "@mui/material";
|
||||||
import AddCircleIcon from '@mui/icons-material/AddCircle';
|
import AddCircleIcon from '@mui/icons-material/AddCircle';
|
||||||
|
@ -56,6 +56,34 @@ function TournamentListItem(props) {
|
||||||
</Box>;
|
</Box>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function Countdown() {
|
||||||
|
const [remainingTime, setremainingTime] = React.useState(Math.abs(props.tournament.startTime - new Date()))
|
||||||
|
React.useEffect(() => {
|
||||||
|
const interval = setInterval(() =>
|
||||||
|
setremainingTime(Math.abs(props.tournament.startTime - new Date()))
|
||||||
|
, 1000);
|
||||||
|
return () => {
|
||||||
|
clearInterval(interval);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
let remainingDays = Math.floor(remainingTime / (1000 * 60 * 60 * 24));
|
||||||
|
let remainingHours = Math.floor(remainingTime / (1000 * 60 * 60)) - remainingDays * 24
|
||||||
|
let remainingMins = Math.floor(remainingTime / (1000 * 60)) - (remainingDays * 24 * 60 + remainingHours * 60)
|
||||||
|
let remainingSecs = Math.floor(remainingTime / 1000) - (remainingDays * 24 * 60 * 60 + remainingHours * 60 * 60 + remainingMins * 60)
|
||||||
|
if (props.tournament.startTime < new Date()) {
|
||||||
|
return (<Box>
|
||||||
|
<Typography variant="body"> Started! </Typography>
|
||||||
|
</Box>)
|
||||||
|
} else {
|
||||||
|
return(<Box>
|
||||||
|
<Typography variant="body"> Starts in: {remainingDays} Days, {remainingHours} Hours, {remainingMins} Minutes and {remainingSecs} Seconds </Typography>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Paper elevation={8} >
|
<Paper elevation={8} >
|
||||||
<Card>
|
<Card>
|
||||||
|
@ -73,7 +101,7 @@ function TournamentListItem(props) {
|
||||||
<Typography variant="body"> End: {props.tournament.endTime.toLocaleString()} </Typography>
|
<Typography variant="body"> End: {props.tournament.endTime.toLocaleString()} </Typography>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Typography variant="h5" color="text.primary" gutterBottom> Players {props.tournament.teamCount} / {props.tournament.teamLimit} </Typography>
|
<Typography variant="h5" color="text.primary" gutterBottom> Players: {props.tournament.teamCount} / {props.tournament.teamLimit} </Typography>
|
||||||
<Description />
|
<Description />
|
||||||
|
|
||||||
<Box sx={{flexGrow: 1, marginTop: "20px"}}>
|
<Box sx={{flexGrow: 1, marginTop: "20px"}}>
|
||||||
|
@ -92,6 +120,8 @@ function TournamentListItem(props) {
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
<Countdown />
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
@ -144,6 +174,9 @@ function Home() {
|
||||||
<CreateButton />
|
<CreateButton />
|
||||||
</Box>
|
</Box>
|
||||||
<TournamentList />
|
<TournamentList />
|
||||||
|
<Typography variant="h5" color="#555555">
|
||||||
|
Finished tournaments are moved to the <Link to="/history">history-page</Link>
|
||||||
|
</Typography>
|
||||||
</Container>
|
</Container>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -160,10 +193,7 @@ export default function App() {
|
||||||
<Route path="/tournament/:tournamentId/manage" element={<TournamentManager />} />
|
<Route path="/tournament/:tournamentId/manage" element={<TournamentManager />} />
|
||||||
<Route path="/tournament/:tournamentId/teams" element={<TournamentTeams />} />
|
<Route path="/tournament/:tournamentId/teams" element={<TournamentTeams />} />
|
||||||
<Route path="/history" element={<TournamentHistory />} />
|
<Route path="/history" element={<TournamentHistory />} />
|
||||||
<Route
|
<Route path="/login" element={<LoginPage />} />
|
||||||
path="/tournament/manage/announcement"
|
|
||||||
element={<TournamentAnnouncement />}
|
|
||||||
/>
|
|
||||||
</Routes>
|
</Routes>
|
||||||
</Router>
|
</Router>
|
||||||
</React.StrictMode>
|
</React.StrictMode>
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
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';
|
||||||
|
|
||||||
|
|
||||||
|
export default function LoginPage() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<AppBar pageTitle="Sign in" />
|
||||||
|
<h2>Sign in with google</h2>
|
||||||
|
<a href={process.env.REACT_APP_LOGIN_URL}>
|
||||||
|
<img src="/btn_google_signing_dark.png" alt="Sign in with google" />
|
||||||
|
</a>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,34 +0,0 @@
|
||||||
import * as React from "react";
|
|
||||||
import { BrowserRouter as Router, Link, Route, Routes } from "react-router-dom";
|
|
||||||
import Appbar from './components/AsuraBar';
|
|
||||||
|
|
||||||
function Announcement() {
|
|
||||||
return (
|
|
||||||
<form>
|
|
||||||
<label for="recipients">Recipients:</label>
|
|
||||||
<select id="recipients">
|
|
||||||
<option value="all">All</option>
|
|
||||||
<option value="mail1@gmail.com">Person 1</option>
|
|
||||||
<option value="mail2@gmail.com">Person 2</option>
|
|
||||||
<option value="mail3@gmail.com">Person 3</option>
|
|
||||||
</select>
|
|
||||||
<br />
|
|
||||||
<label for="subject">Subject:</label>
|
|
||||||
<input type="text" id="subject"></input>
|
|
||||||
<br />
|
|
||||||
<input type="text" id="contents"></input>
|
|
||||||
<Link to="/tournament/manage">
|
|
||||||
<input type="submit"></input>
|
|
||||||
</Link>
|
|
||||||
</form>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function TournamentAnnouncement() {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Appbar />
|
|
||||||
<Announcement />
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -7,6 +7,7 @@ import { Button, TextField, Stack, InputLabel, Select, Container, Slider, Paper,
|
||||||
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';
|
||||||
|
import { setDate } from "date-fns";
|
||||||
|
|
||||||
function postTournament(showError, tournamentName, tournamentDescription, tournamentStartDate, tournamentEndDate, tournamentMaxTeams) {
|
function postTournament(showError, tournamentName, tournamentDescription, tournamentStartDate, tournamentEndDate, tournamentMaxTeams) {
|
||||||
if (!tournamentName || tournamentName === "") {
|
if (!tournamentName || tournamentName === "") {
|
||||||
|
@ -81,8 +82,8 @@ function TournamentForm(props) {
|
||||||
function submitTournament(event) {
|
function submitTournament(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
let maxTeams = Math.pow(2, maxTeamsExponent);
|
let maxTeams = Math.pow(2, maxTeamsExponent);
|
||||||
let tournamentStart = new Date(startTime).valueOf() - new Date().getTimezoneOffset() * 60000;
|
let tournamentStart = new Date(startTime.setSeconds(0, 0, 0)).valueOf() - new Date().getTimezoneOffset() * 60*1000;
|
||||||
let tournamentEnd = new Date(endTime).valueOf() - new Date().getTimezoneOffset() * 60000;
|
let tournamentEnd = new Date(endTime.setSeconds(0, 0, 0)).valueOf() - new Date().getTimezoneOffset() * 60*1000;
|
||||||
postTournament(
|
postTournament(
|
||||||
props.showError,
|
props.showError,
|
||||||
document.getElementById("nameInput").value,
|
document.getElementById("nameInput").value,
|
||||||
|
|
|
@ -51,7 +51,7 @@ function shorten(description, maxLength) {
|
||||||
<Typography variant="body"> End: {props.tournament.endTime.toLocaleString()} </Typography>
|
<Typography variant="body"> End: {props.tournament.endTime.toLocaleString()} </Typography>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Typography variant="h5" color="text.primary" gutterBottom> Players {props.tournament.teamCount} / {props.tournament.teamLimit} </Typography>
|
<Typography variant="h5" color="text.primary" gutterBottom> Players: {props.tournament.teamCount} / {props.tournament.teamLimit} </Typography>
|
||||||
<Description />
|
<Description />
|
||||||
|
|
||||||
<Box sx={{flexGrow: 1, marginTop: "20px"}}>
|
<Box sx={{flexGrow: 1, marginTop: "20px"}}>
|
||||||
|
|
|
@ -5,7 +5,7 @@ import Appbar from './components/Appbar';
|
||||||
function MatchHistory() {
|
function MatchHistory() {
|
||||||
|
|
||||||
return(
|
return(
|
||||||
`Hei`
|
`Hei på deg din gamle sei`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -178,7 +178,39 @@ function BracketViewer(props){
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
: <Box sx={{display:'flex', justifyContent:'center', alignItems:'center', position:'relative', marginTop:'5%'}}><CircularProgress size={"20vw"}/></Box>
|
: <Box sx={{display:'flex', justifyContent:'center', alignItems:'center', position:'relative', marginTop:'5%'}}><CircularProgress size={"20vw"}/></Box>
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function RemovableBar(props) {
|
||||||
|
const [endTime, setendTime] = React.useState(null);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
fetch(process.env.REACT_APP_API_URL + `/tournament/${props.tournamentId}`)
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.status !== "OK") {
|
||||||
|
// Do your error thing
|
||||||
|
console.error(data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let endTime = data.data.endTime;
|
||||||
|
setendTime(endTime);
|
||||||
|
})
|
||||||
|
.catch(err => showError(err));
|
||||||
|
})
|
||||||
|
let today = new Date()
|
||||||
|
let yesterday = today.setDate(today.getDate() - 1)
|
||||||
|
let isComplete = new Date(endTime) < yesterday
|
||||||
|
if (isComplete) {
|
||||||
|
return (null)
|
||||||
|
} else {
|
||||||
|
return (<TournamentBar pageTitle="View Tournament" />)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function showError(error) {
|
||||||
|
alert("Something went wrong. \n" + error);
|
||||||
|
console.error(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function TournamentOverview(props) {
|
export default function TournamentOverview(props) {
|
||||||
|
@ -187,7 +219,7 @@ export default function TournamentOverview(props) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Appbar pageTitle="View Tournament" />
|
<Appbar pageTitle="View Tournament" />
|
||||||
<TournamentBar pageTitle="View Tournament" />
|
<RemovableBar tournamentId={tournamentId} />
|
||||||
<BracketViewer tournamentId={tournamentId} className="bracketViewer" />
|
<BracketViewer tournamentId={tournamentId} className="bracketViewer" />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
@ -43,15 +43,18 @@ function TeamCreator(props) {
|
||||||
return (
|
return (
|
||||||
<Paper sx={{width: "90vw", margin: "10px auto", padding: "15px"}} component={Stack} direction="column">
|
<Paper sx={{width: "90vw", margin: "10px auto", padding: "15px"}} component={Stack} direction="column">
|
||||||
<div align="center">
|
<div align="center">
|
||||||
|
<form>
|
||||||
<TextField id="teamNameInput" sx={{ width: "70%" }} label="Team Name" variant="outlined" />
|
<TextField id="teamNameInput" sx={{ width: "70%" }} label="Team Name" variant="outlined" />
|
||||||
{/* <Button variant="contained" color="primary" onClick={postCreate}>Create Team</Button> */}
|
{/* <Button variant="contained" color="primary" onClick={postCreate}>Create Team</Button> */}
|
||||||
<Button variant="contained" color="success" onClick={postCreate} sx={{width: "20%", marginLeft: "5px"}}>
|
<Button type="submit" variant="contained" color="success" onClick={postCreate} sx={{width: "20%", marginLeft: "5px"}}>
|
||||||
<Box sx={{padding: "10px"}}>
|
<Box sx={{padding: "10px"}}>
|
||||||
Create Team
|
Create Team
|
||||||
</Box>
|
</Box>
|
||||||
<AddCircleIcon />
|
<AddCircleIcon />
|
||||||
</Button>
|
</Button>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</Paper>
|
</Paper>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -92,7 +95,7 @@ function TeamList(props) {
|
||||||
</b></TableCell>
|
</b></TableCell>
|
||||||
{/* <TableCell align="right">{team.members}</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="primary" onClick={() => {props.setSelectedTeamId(team.id); window.scrollTo(0, document.body.scrollHeight)}} endIcon={<EditIcon />}>Edit</Button>
|
||||||
<Button variant="contained" sx={{margin: "auto 5px"}} color="error" onClick={() => {deleteTeam(team.id)}} endIcon={<DeleteIcon />}>Delete</Button>
|
<Button variant="contained" sx={{margin: "auto 5px"}} color="error" onClick={() => {deleteTeam(team.id)}} endIcon={<DeleteIcon />}>Delete</Button>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
|
@ -146,6 +149,10 @@ function TeamEditor(props) {
|
||||||
setTeam(newTeam);
|
setTeam(newTeam);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleFocus(event) {
|
||||||
|
event.currentTarget.select()
|
||||||
|
}
|
||||||
|
|
||||||
function saveTeam() {
|
function saveTeam() {
|
||||||
let formData = new FormData();
|
let formData = new FormData();
|
||||||
formData.append("name", team.name);
|
formData.append("name", team.name);
|
||||||
|
@ -172,9 +179,9 @@ function TeamEditor(props) {
|
||||||
<div align="center">
|
<div align="center">
|
||||||
<h2><b>Edit Team:</b></h2>
|
<h2><b>Edit Team:</b></h2>
|
||||||
<form>
|
<form>
|
||||||
<TextField id="teamNameInput" label="Team Name" value={team.name || ""} onChange={nameInputChanged} sx={{width: "80%"}} />
|
<TextField id="newTeamNameInput" label="Team Name" value={team.name || ""} onChange={nameInputChanged} onFocus={handleFocus} sx={{width: "80%"}} />
|
||||||
{/* <PlayerList players={players} setPlayers={setPlayers} /> */}
|
{/* <PlayerList players={players} setPlayers={setPlayers} /> */}
|
||||||
<Button variant="contained" sx={{margin: "auto 5px"}} color="primary" onClick={saveTeam}>Save</Button>
|
<Button type="submit" variant="contained" sx={{margin: "auto 5px"}} color="primary" onClick={saveTeam}>Save</Button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|
|
@ -1,7 +1,32 @@
|
||||||
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 } from "@mui/material"
|
import { Stack, Paper, Typography, Box, Button, Grid, Snackbar, IconButton } from "@mui/material"
|
||||||
|
import CloseIcon from '@mui/icons-material/Close';
|
||||||
|
|
||||||
|
function ClipboardButton(props) {
|
||||||
|
const [open, setOpen] = React.useState(false);
|
||||||
|
function copyString() {
|
||||||
|
navigator.clipboard.writeText(props.clipboardContent || "");
|
||||||
|
setOpen(true);
|
||||||
|
}
|
||||||
|
const handleClose = (event, reason) => {
|
||||||
|
if (reason === 'clickaway') { return }
|
||||||
|
setOpen(false);
|
||||||
|
};
|
||||||
|
const closeAction = <>
|
||||||
|
<IconButton size="small" aria-label="close" color="inherit" onClick={handleClose}>
|
||||||
|
<CloseIcon fontSize="small" />
|
||||||
|
</IconButton>
|
||||||
|
</>
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Button onClick={copyString} variant="outlined" color="primary" sx={{margin: "auto 5px"}} >Copy {props.name}</Button>
|
||||||
|
<Snackbar open={open} autoHideDuration={1500} onClose={handleClose} message={props.name + " copied to clipboard"} action={closeAction} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function ButtonLink(props) {
|
function ButtonLink(props) {
|
||||||
return (
|
return (
|
||||||
|
@ -14,10 +39,17 @@ function ButtonLink(props) {
|
||||||
export default function TournamentBar(props) {
|
export default function TournamentBar(props) {
|
||||||
const { tournamentId } = useParams();
|
const { tournamentId } = useParams();
|
||||||
return (
|
return (
|
||||||
<Paper sx={{width: "90vw", margin: "10px auto"}} component={Stack} direction="row" justifyContent="center">
|
<Paper sx={{width: "90vw", margin: "1.5% auto"}} component={Stack} direction="column" justifyContent="center" alignItems="center">
|
||||||
|
<Stack direction="row" paddingTop={'0.5%'}>
|
||||||
<ButtonLink targetPath="" tournamentId={tournamentId} activeTitle={props.pageTitle} title="View Tournament" />
|
<ButtonLink targetPath="" tournamentId={tournamentId} activeTitle={props.pageTitle} title="View Tournament" />
|
||||||
<ButtonLink targetPath="/manage" tournamentId={tournamentId} activeTitle={props.pageTitle} title="Edit Tournament" />
|
<ButtonLink targetPath="/manage" tournamentId={tournamentId} activeTitle={props.pageTitle} title="Edit Tournament" />
|
||||||
<ButtonLink targetPath="/teams" tournamentId={tournamentId} activeTitle={props.pageTitle} title="Manage Teams" />
|
<ButtonLink targetPath="/teams" tournamentId={tournamentId} activeTitle={props.pageTitle} title="Manage Teams" />
|
||||||
|
</Stack>
|
||||||
|
<Stack direction="row" paddingBottom={'0.5%'}>
|
||||||
|
<ClipboardButton clipboardContent={"https://discord.gg/asura"} name="Discord Invite Link" />
|
||||||
|
<ClipboardButton clipboardContent={"https://asura.feal.no/tournament/" + tournamentId} name="Tournament Link" />
|
||||||
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|
||||||
)
|
)
|
||||||
}
|
}
|
Loading…
Reference in New Issue