diff --git a/src/client/.env b/src/client/.env
index 02a4337..fafeb61 100644
--- a/src/client/.env
+++ b/src/client/.env
@@ -1,2 +1,3 @@
REACT_APP_API_URL=https://asura.feal.no/api
+REACT_APP_LOGIN_URL=http://localhost:3001/auth/google
BROWSER=none
diff --git a/src/client/package-lock.json b/src/client/package-lock.json
index dd036ab..bf11bdc 100644
--- a/src/client/package-lock.json
+++ b/src/client/package-lock.json
@@ -4869,6 +4869,7 @@
"dependencies": {
"anymatch": "~3.1.2",
"braces": "~3.0.2",
+ "fsevents": "~2.3.2",
"glob-parent": "~5.1.2",
"is-binary-path": "~2.1.0",
"is-glob": "~4.0.1",
@@ -6116,7 +6117,8 @@
"esprima": "^4.0.1",
"estraverse": "^5.2.0",
"esutils": "^2.0.2",
- "optionator": "^0.8.1"
+ "optionator": "^0.8.1",
+ "source-map": "~0.6.1"
},
"bin": {
"escodegen": "bin/escodegen.js",
@@ -8724,6 +8726,7 @@
"@types/node": "*",
"anymatch": "^3.0.3",
"fb-watchman": "^2.0.0",
+ "fsevents": "^2.3.2",
"graceful-fs": "^4.2.9",
"jest-regex-util": "^27.5.1",
"jest-serializer": "^27.5.1",
@@ -10029,9 +10032,14 @@
"integrity": "sha512-EoQp/Et7OSOVu0aJknJOtlXZsnr8XE8KwuzTHOLeVSEx8pVWUICc8Q0VYRHgzyjX78nMEyC/oztWFbgyhtNfDA==",
"dependencies": {
"copy-anything": "^2.0.1",
+ "errno": "^0.1.1",
"graceful-fs": "^4.1.2",
+ "image-size": "~0.5.0",
+ "make-dir": "^2.1.0",
"mime": "^1.4.1",
+ "needle": "^2.5.2",
"parse-node-version": "^1.0.1",
+ "source-map": "~0.6.0",
"tslib": "^2.3.0"
},
"bin": {
@@ -12407,6 +12415,7 @@
"eslint-webpack-plugin": "^3.1.1",
"file-loader": "^6.2.0",
"fs-extra": "^10.0.0",
+ "fsevents": "^2.3.2",
"html-webpack-plugin": "^5.5.0",
"identity-obj-proxy": "^3.0.0",
"jest": "^27.4.3",
@@ -12826,6 +12835,9 @@
"version": "2.70.1",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.70.1.tgz",
"integrity": "sha512-CRYsI5EuzLbXdxC6RnYhOuRdtz4bhejPMSWjsFLfVM/7w/85n2szZv6yExqUXsBdz5KT8eoubeyDUDjhLHEslA==",
+ "dependencies": {
+ "fsevents": "~2.3.2"
+ },
"bin": {
"rollup": "dist/bin/rollup"
},
diff --git a/src/client/public/btn_google_signing_dark.png b/src/client/public/btn_google_signing_dark.png
new file mode 100644
index 0000000..f27bb24
Binary files /dev/null and b/src/client/public/btn_google_signing_dark.png differ
diff --git a/src/client/src/AdminsOverview.js b/src/client/src/AdminsOverview.js
new file mode 100644
index 0000000..f04db42
--- /dev/null
+++ b/src/client/src/AdminsOverview.js
@@ -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 (
+
+
+
+ {/*
*/}
+
+
+
+ )
+}
+
+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(
+
+
+ {/* TODO: scroll denne menyen, eventuelt søkefelt */}
+
+
+
+ Admin
+ Actions
+
+
+
+ {props.admins.map((admin) => (
+
+
+ {admin.name}
+
+ {/* {team.members} */}
+
+ {/* */}
+
+
+
+ ))}
+
+
+
+
+ )
+}
+
+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 (
+ <>
+
+
+ >
+ );
+}
\ No newline at end of file
diff --git a/src/client/src/FrontPage.js b/src/client/src/FrontPage.js
index ebe1da4..3cfc07d 100644
--- a/src/client/src/FrontPage.js
+++ b/src/client/src/FrontPage.js
@@ -3,9 +3,9 @@ import { BrowserRouter as Router, Link, Route, Routes } from "react-router-dom";
import TournamentCreator from "./TournamentCreator.js";
import TournamentOverview from "./TournamentOverview.js";
import TournamentManager from "./TournamentManager.js";
-import TournamentAnnouncement from "./TournamentAnnouncement";
import TournamentHistory from "./TournamentHistory";
import TournamentTeams from "./TournamentTeams";
+import LoginPage from "./LoginPage";
import AppBar from './components/AsuraBar';
import { Button, Container, Typography, Box, Stack, Card, CardContent, CardMedia, Paper, Grid, Icon } from "@mui/material";
import AddCircleIcon from '@mui/icons-material/AddCircle';
@@ -56,6 +56,34 @@ function TournamentListItem(props) {
;
}
}
+
+ 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 (
+ Started!
+ )
+ } else {
+ return(
+ Starts in: {remainingDays} Days, {remainingHours} Hours, {remainingMins} Minutes and {remainingSecs} Seconds
+
+ )
+ }
+ }
+
return (
@@ -73,7 +101,7 @@ function TournamentListItem(props) {
End: {props.tournament.endTime.toLocaleString()}
- Players {props.tournament.teamCount} / {props.tournament.teamLimit}
+ Players: {props.tournament.teamCount} / {props.tournament.teamLimit}
@@ -92,6 +120,8 @@ function TournamentListItem(props) {
+
+
@@ -144,6 +174,9 @@ function Home() {
+
+ Finished tournaments are moved to the history-page
+
>
);
@@ -160,10 +193,7 @@ export default function App() {
} />
} />
} />
- }
- />
+ } />
diff --git a/src/client/src/LoginPage.js b/src/client/src/LoginPage.js
new file mode 100644
index 0000000..f723b63
--- /dev/null
+++ b/src/client/src/LoginPage.js
@@ -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 (
+ <>
+
+ Sign in with google
+
+
+
+ >
+ );
+}
\ No newline at end of file
diff --git a/src/client/src/TournamentAnnouncement.js b/src/client/src/TournamentAnnouncement.js
deleted file mode 100644
index af10100..0000000
--- a/src/client/src/TournamentAnnouncement.js
+++ /dev/null
@@ -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 (
-
- );
-}
-
-export default function TournamentAnnouncement() {
- return (
- <>
-
-
- >
- );
-}
diff --git a/src/client/src/TournamentCreator.js b/src/client/src/TournamentCreator.js
index 00a6a8b..a589981 100644
--- a/src/client/src/TournamentCreator.js
+++ b/src/client/src/TournamentCreator.js
@@ -7,6 +7,7 @@ import { Button, TextField, Stack, InputLabel, Select, Container, Slider, Paper,
import DateTimePicker from '@mui/lab/DateTimePicker';
import AdapterDateFns from '@mui/lab/AdapterDateFns';
import LocalizationProvider from '@mui/lab/LocalizationProvider';
+import { setDate } from "date-fns";
function postTournament(showError, tournamentName, tournamentDescription, tournamentStartDate, tournamentEndDate, tournamentMaxTeams) {
if (!tournamentName || tournamentName === "") {
@@ -81,8 +82,8 @@ function TournamentForm(props) {
function submitTournament(event) {
event.preventDefault();
let maxTeams = Math.pow(2, maxTeamsExponent);
- let tournamentStart = new Date(startTime).valueOf() - new Date().getTimezoneOffset() * 60000;
- let tournamentEnd = new Date(endTime).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.setSeconds(0, 0, 0)).valueOf() - new Date().getTimezoneOffset() * 60*1000;
postTournament(
props.showError,
document.getElementById("nameInput").value,
diff --git a/src/client/src/TournamentHistory.js b/src/client/src/TournamentHistory.js
index e874523..5845096 100644
--- a/src/client/src/TournamentHistory.js
+++ b/src/client/src/TournamentHistory.js
@@ -51,7 +51,7 @@ function shorten(description, maxLength) {
End: {props.tournament.endTime.toLocaleString()}
- Players {props.tournament.teamCount} / {props.tournament.teamLimit}
+ Players: {props.tournament.teamCount} / {props.tournament.teamLimit}
diff --git a/src/client/src/TournamentMatches.js b/src/client/src/TournamentMatches.js
index 43afb8a..44772c8 100644
--- a/src/client/src/TournamentMatches.js
+++ b/src/client/src/TournamentMatches.js
@@ -5,7 +5,7 @@ import Appbar from './components/Appbar';
function MatchHistory() {
return(
- `Hei`
+ `Hei på deg din gamle sei`
);
}
diff --git a/src/client/src/TournamentOverview.js b/src/client/src/TournamentOverview.js
index 732b02a..59bec17 100644
--- a/src/client/src/TournamentOverview.js
+++ b/src/client/src/TournamentOverview.js
@@ -169,16 +169,48 @@ function BracketViewer(props){
.catch(err => showError(err));
}, []);
return (
- (matches && teams) ?
- //
-
- {matches.map(tier => {
- let tierNum = tier[0].tier;
- return
- })}
-
- :
-);
+ (matches && teams) ?
+ //
+
+ {matches.map(tier => {
+ let tierNum = tier[0].tier;
+ return
+ })}
+
+ :
+ );
+}
+
+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 (
)
+ }
+}
+
+function showError(error) {
+ alert("Something went wrong. \n" + error);
+ console.error(error);
}
export default function TournamentOverview(props) {
@@ -187,7 +219,7 @@ export default function TournamentOverview(props) {
return (
<>
-
+
>
);
diff --git a/src/client/src/TournamentTeams.js b/src/client/src/TournamentTeams.js
index e063834..3d24409 100644
--- a/src/client/src/TournamentTeams.js
+++ b/src/client/src/TournamentTeams.js
@@ -43,15 +43,18 @@ function TeamCreator(props) {
return (
+
)
}
@@ -92,7 +95,7 @@ function TeamList(props) {
{/*
{team.members} */}
-
+
@@ -146,6 +149,10 @@ function TeamEditor(props) {
setTeam(newTeam);
}
+ function handleFocus(event) {
+ event.currentTarget.select()
+ }
+
function saveTeam() {
let formData = new FormData();
formData.append("name", team.name);
@@ -172,9 +179,9 @@ function TeamEditor(props) {
diff --git a/src/client/src/components/TournamentBar.js b/src/client/src/components/TournamentBar.js
index 153eb2c..b196488 100644
--- a/src/client/src/components/TournamentBar.js
+++ b/src/client/src/components/TournamentBar.js
@@ -1,7 +1,32 @@
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 } 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 = <>
+
+
+
+ >
+
+ return (
+ <>
+
+
+ >
+ );
+}
function ButtonLink(props) {
return (
@@ -14,10 +39,17 @@ function ButtonLink(props) {
export default function TournamentBar(props) {
const { tournamentId } = useParams();
return (
-
+
+
+
+
+
+
+
+
)
}
\ No newline at end of file