Teaching Assistant feedback + deployment

This commit is contained in:
Felix Albrigtsen 2022-04-27 10:22:29 +02:00
parent 29f463f248
commit e14db4b382
8 changed files with 91 additions and 73 deletions

View File

@ -4869,7 +4869,6 @@
"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",
@ -6117,8 +6116,7 @@
"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",
@ -8726,7 +8724,6 @@
"@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",
@ -10032,14 +10029,9 @@
"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": {
@ -12415,7 +12407,6 @@
"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",
@ -12835,9 +12826,6 @@
"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"
}, },

View File

@ -114,7 +114,7 @@ function TournamentListItem(props) {
<Typography variant="h5" color="text.primary" gutterBottom> Particpants: {props.tournament.teamCount} / {props.tournament.teamLimit} </Typography> <Typography variant="h5" color="text.primary" gutterBottom> Particpants: {props.tournament.teamCount} / {props.tournament.teamLimit} </Typography>
<Description /> <Description />
<Typography variant="body" color="text.primary"><EmojiEventsIcon alt="A trohpy" color="gold"/> Prize: {props.tournament.prize} </Typography> <Typography variant="body" color="text.primary"><EmojiEventsIcon alt="A trophy" color="gold"/> Prize: {props.tournament.prize} </Typography>
<Countdown /> <Countdown />
<Box sx={{flexGrow: 1, marginTop: "20px"}}> <Box sx={{flexGrow: 1, marginTop: "20px"}}>
@ -205,36 +205,36 @@ let showError = (message) => {};
export default function App() { export default function App() {
const [user, setUser] = React.useState({}); const [user, setUser] = React.useState({});
let fetchUser = () => {
fetch(process.env.REACT_APP_API_URL + `/users/getSavedUser`)
.then(res => res.json())
.then(data => {
if (data.status !== "OK") {
setUser({ isManager: false, isLoggedIn: false });
console.log(data.data); // "No user logged in"
return;
}
let u = data.data;
u.isLoggedIn = true;
console.log("User is logged in")
setUser(u);
})
.catch((err) => {
showError(err.message);
setUser({ isManager: false, isLoggedIn: false });
});
}
// // Debug mode, allow all:
// let fetchUser = () => { // let fetchUser = () => {
// setUser({ // fetch(process.env.REACT_APP_API_URL + `/users/getSavedUser`)
// name: "TEST USERTEST", // .then(res => res.json())
// isManager: true, // .then(data => {
// isLoggedIn: true, // if (data.status !== "OK") {
// email: "testesen@gmail.com", // setUser({ isManager: false, isLoggedIn: false });
// asuraId: "123456789", // console.log(data.data); // "No user logged in"
// googleId: "234" // return;
// }
// let u = data.data;
// u.isLoggedIn = true;
// console.log("User is logged in")
// setUser(u);
// })
// .catch((err) => {
// showError(err.message);
// 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();

View File

@ -56,7 +56,7 @@ function shorten(description, maxLength) {
<Typography variant="h5" color="text.primary" gutterBottom> Participants: {props.tournament.teamCount} / {props.tournament.teamLimit} </Typography> <Typography variant="h5" color="text.primary" gutterBottom> Participants: {props.tournament.teamCount} / {props.tournament.teamLimit} </Typography>
<Description /> <Description />
<Typography variant="body" color="text.primary"><EmojiEventsIcon alt="A trohpy" color="gold" align="vertical-center"/> Prize: {props.tournament.prize} </Typography> <Typography variant="body" color="text.primary"><EmojiEventsIcon alt="A trophy" color="gold" align="vertical-center"/> Prize: {props.tournament.prize} </Typography>
<Box sx={{flexGrow: 1, marginTop: "20px"}}> <Box sx={{flexGrow: 1, marginTop: "20px"}}>
<Grid container spacing={4} justifyContent="center" wrap="wrap"> <Grid container spacing={4} justifyContent="center" wrap="wrap">
@ -135,13 +135,13 @@ function shorten(description, maxLength) {
} }
} }
return <> return <Container sx={{minHeight: "30vh", width: "90vw", padding: "20px 20px", alignContent:'center'}}>
<TextField type="text" id="searchInput" label="Search" placeholder="Tournament Name" InputLabelProps={{shrink: true}} onChange={search}/> <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"}}> <Stack spacing={3} sx={{margin: "10px auto"}}>
{tournamentList && tournamentList.map((tournamentObject) => <TournamentListItem key={tournamentObject.id.toString()} tournament={tournamentObject} />)} {tournamentList && tournamentList.map((tournamentObject) => <TournamentListItem key={tournamentObject.id.toString()} tournament={tournamentObject} />)}
</Stack> </Stack>
</>; </Container>;
} }
export default function TournamentHistory(props) { export default function TournamentHistory(props) {
@ -149,12 +149,7 @@ export default function TournamentHistory(props) {
return ( return (
<> <>
<Appbar user={props.user} pageTitle="Tournament History" /> <Appbar user={props.user} pageTitle="Tournament History" />
<Container sx={{minHeight: "30vh", width: "90vw", padding: "20px 20px"}} component={Container} direction="column" align="center">
<Box component={Stack} direction="row" align="center" justifyContent="center" alignItems="center" sx={{flexGrow: 1, margin:'2.5% 0'}}>
<Typography sx={{fontSize:['1.5rem','2rem','2rem']}}>Past Tournaments</Typography>
</Box>
<TournamentList /> <TournamentList />
</Container>
</> </>
); );
} }

View File

@ -91,7 +91,7 @@ function Match(props){
{team1Name} {team1Name}
</Typography> </Typography>
{ props.match.winnerId && (props.match.team1Id === props.match.winnerId) && { props.match.winnerId && (props.match.team1Id === props.match.winnerId) &&
<EmojiEventsIcon alt="A trohpy" sx={{width:['0.75em','1em','1.25em'], height:['0.75em','1em','1.25em']}} /> <EmojiEventsIcon alt="A trophy" sx={{width:['0.75em','1em','1.25em'], height:['0.75em','1em','1.25em']}} />
} }
<Box component={Stack} direction={'row'} spacing={-1.25}> <Box component={Stack} direction={'row'} spacing={-1.25}>
{ props.match.team1Id !== null && !props.tournament.hasEnded && props.match.tier !== Math.log2(props.tournament.teamLimit) - 1 && props.match.winnerId === null && props.user.isLoggedIn && { props.match.team1Id !== null && !props.tournament.hasEnded && props.match.tier !== Math.log2(props.tournament.teamLimit) - 1 && props.match.winnerId === null && props.user.isLoggedIn &&
@ -111,7 +111,7 @@ function Match(props){
{team2Name} {team2Name}
</Typography> </Typography>
{ props.match.winnerId && (props.match.team2Id === props.match.winnerId) && { props.match.winnerId && (props.match.team2Id === props.match.winnerId) &&
<EmojiEventsIcon alt="A trohpy" sx={{width:['0.75em','1em','1.25em'], height:['0.75em','1em','1.25em']}} /> <EmojiEventsIcon alt="A trophy" sx={{width:['0.75em','1em','1.25em'], height:['0.75em','1em','1.25em']}} />
} }
{ props.match.team2Id !== null && !props.tournament.hasEnded && props.match.tier !== Math.log2(props.tournament.teamLimit) - 1 && props.match.winnerId === null && props.user.isLoggedIn && { props.match.team2Id !== null && !props.tournament.hasEnded && props.match.tier !== Math.log2(props.tournament.teamLimit) - 1 && props.match.winnerId === null && props.user.isLoggedIn &&
<IconButton color="error" aria-label="remove winner" component="span" onClick={curryUnsetContestant(props.match.team2Id)}><BackspaceIcon sx={{width:['0.75em','1em','1.25em'], height:['0.75em','1em','1.25em']}} /></IconButton> <IconButton color="error" aria-label="remove winner" component="span" onClick={curryUnsetContestant(props.match.team2Id)}><BackspaceIcon sx={{width:['0.75em','1em','1.25em'], height:['0.75em','1em','1.25em']}} /></IconButton>
@ -162,7 +162,7 @@ function WinnerDisplay(props) {
<Typography sx={{fontSize:['1em','1em','1.5em','2em']}} className="winner"> <Typography sx={{fontSize:['1em','1em','1.5em','2em']}} className="winner">
{props.team.name} {props.team.name}
</Typography> </Typography>
<EmojiEventsIcon alt="A trohpy" /> <EmojiEventsIcon alt="A trophy" />
</div> </div>
) )
} }

View File

@ -71,17 +71,49 @@ function TeamList(props) {
.catch(error => showError(error)); .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()
if(tournamentName.indexOf(inputUpperCase) >= 0) {
searchResult.push(tournamentName)
}
}
let searchedList = []
for (let i = 0; i < originalList.length; i++) {
let name = originalList[i].name
for (let j = 0; j < searchResult.length; j++) {
if (name.toUpperCase() == searchResult[j]) {
searchedList.push(originalList[i])
}
}
}
if (input.value == "") {
props.setTeams(originalList)
} else {
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" >
<h2><b>Teams:</b></h2> {/* Make a horizontal stack */}
{/* TODO: scroll denne menyen, eventuelt søkefelt */} <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>Team Name</TableCell> <TableCell><h2>Team Name</h2></TableCell>
{/* <TableCell align="right">Team Members</TableCell> */} {/* <TableCell align="right">Team Members</TableCell> */}
<TableCell align="center">Actions</TableCell> <TableCell align="center"><h2>Actions</h2></TableCell>
</TableRow> </TableRow>
</TableHead> </TableHead>
<TableBody> <TableBody>
@ -91,7 +123,6 @@ function TeamList(props) {
<TableCell component="th" scope="row"> <b> <TableCell component="th" scope="row"> <b>
{team.name} {team.name}
</b></TableCell> </b></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); window.scrollTo(0, document.body.scrollHeight)}} 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>
@ -184,6 +215,7 @@ let showError = (message) => {};
export default function TournamentTeams(props) { export default function TournamentTeams(props) {
const [teams, setTeams] = React.useState([]); const [teams, setTeams] = React.useState([]);
const [selectedTeamId, setSelectedTeamId] = React.useState(-1); const [selectedTeamId, setSelectedTeamId] = React.useState(-1);
const [originalList, setOriginalList] = React.useState([])
const { tournamentId } = useParams(); const { tournamentId } = useParams();
function getTeams() { function getTeams() {
@ -194,6 +226,7 @@ export default function TournamentTeams(props) {
showError(data.data); showError(data.data);
} }
setTeams(data.data); setTeams(data.data);
setOriginalList(data.data)
//setselectedTeamId(teams[0].id); //setselectedTeamId(teams[0].id);
}) })
.catch((err) => showError(err)); .catch((err) => showError(err));
@ -218,7 +251,7 @@ export default function TournamentTeams(props) {
<TournamentBar pageTitle="Manage Teams" /> <TournamentBar pageTitle="Manage Teams" />
<div className="tournamentTeams"> <div className="tournamentTeams">
<TeamCreator tournamentId={tournamentId} teams={teams} onTeamCreated={getTeams} /> <TeamCreator tournamentId={tournamentId} teams={teams} onTeamCreated={getTeams} />
<TeamList teams={teams} setTeams={setTeams} selectedTeamId={selectedTeamId} setSelectedTeamId={setSelectedTeamId} /> <TeamList teams={teams} setTeams={setTeams} selectedTeamId={selectedTeamId} setSelectedTeamId={setSelectedTeamId} originalList={originalList} setOriginalList={setOriginalList} />
<TeamEditor teams={teams} setTeams={setTeams} selectedTeamId={selectedTeamId} setSelectedTeamId={setSelectedTeamId} /> <TeamEditor teams={teams} setTeams={setTeams} selectedTeamId={selectedTeamId} setSelectedTeamId={setSelectedTeamId} />
</div> </div>
<ErrorSnackbar message={errorMessage} open={openError} setOpen={setOpenError} /> <ErrorSnackbar message={errorMessage} open={openError} setOpen={setOpenError} />

View File

@ -25,11 +25,11 @@ function LoggedInMenu(props) {
return ( return (
<> <>
<Link to="/profile"><Button sx={{color:'white', marginTop:'5px'}} startIcon={<AccountCircleIcon />}>{props.user.name}</Button></Link>
<IconButton size="large" edge="start" color="inherit" aria-label="menu" onClick={handleClick}> <IconButton size="large" edge="start" color="inherit" aria-label="menu" onClick={handleClick}>
<MenuIcon /> <MenuIcon />
</IconButton> </IconButton>
<Menu anchorEl={anchorEl} open={open} onClose={handleClose} MenuListProps={{'aria-labelledby': 'basic-button',}} sx={{position:"absolute"}}> <Menu anchorEl={anchorEl} open={open} onClose={handleClose} MenuListProps={{'aria-labelledby': 'basic-button',}} sx={{position:"absolute"}}>
<Link to="/profile" style={{color:"black"}}><MenuItem onClick={handleClose}><Button startIcon={<AccountCircleIcon />}>{props.user.name}</Button></MenuItem></Link>
<Link to="/history" style={{color:"black"}}><MenuItem onClick={handleClose}><Button startIcon={<HistoryIcon />}>History</Button></MenuItem></Link> <Link to="/history" style={{color:"black"}}><MenuItem onClick={handleClose}><Button startIcon={<HistoryIcon />}>History</Button></MenuItem></Link>
{ props.user.isManager && { props.user.isManager &&
<Link to="/admins" style={{color:"black"}}><MenuItem onClick={handleClose}><Button startIcon={<EditIcon />} >Admins</Button></MenuItem></Link> <Link to="/admins" style={{color:"black"}}><MenuItem onClick={handleClose}><Button startIcon={<EditIcon />} >Admins</Button></MenuItem></Link>
@ -71,7 +71,7 @@ export default function Appbar(props) {
</Link> </Link>
{ props.pageTitle !== "Asura Tournaments" && { props.pageTitle !== "Asura Tournaments" &&
<Link to="/" style={{color:"white"}}> <Link to="/" style={{color:"white"}}>
<Typography component="div" align="center" sx={{fontSize:['1em','1em','1.5em','2em']}}> <Typography component="div" align="center" sx={{fontSize:['1em','1em','1.5em']}}>
Home Home
</Typography> </Typography>
</Link> </Link>

View File

@ -48,13 +48,13 @@ 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",], fontSize:['1rem','1rem','1.5rem','2rem'], margin: "1.5% auto"}} component={Stack} direction="column" justifyContent="center" alignItems="center"> <Paper sx={{width: ["90vw",], fontSize:['1rem','1rem','1.5rem'], margin: "1.5% auto"}} component={Stack} direction="column" justifyContent="center" alignItems="center">
<Stack direction="row" paddingTop={'0.5%'} sx={{fontSize:['1rem','1rem','1.5rem','2rem'], margin:'1.5%'}} spacing={2}> <Stack direction="row" paddingTop={'0.5%'} sx={{fontSize:['1rem','1rem','1.5rem','2rem'], margin:'1.5%'}} spacing={2}>
<ButtonLink targetPath="" tournamentId={tournamentId} activeTitle={props.pageTitle} title="View Tournament" viewTournament={props.viewTournament}/> <ButtonLink targetPath="" tournamentId={tournamentId} activeTitle={props.pageTitle} title="View Tournament" viewTournament={props.viewTournament}/>
<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>
<Stack direction="row" paddingBottom={'0.5%'}> <Stack direction="row" paddingBottom={'0.5%'} component={Stack}>
<ClipboardButton clipboardContent={"https://discord.gg/asura"} name="Discord Invite Link" /> <ClipboardButton clipboardContent={"https://discord.gg/asura"} name="Discord Invite Link" />
<ClipboardButton clipboardContent={"https://asura.feal.no/tournament/" + tournamentId} name="Tournament Link" /> <ClipboardButton clipboardContent={"https://asura.feal.no/tournament/" + tournamentId} name="Tournament Link" />
</Stack> </Stack>

View File

@ -43,14 +43,16 @@ app.use(require('express-log-url'));
// #region frontend // #region frontend
// Serve static files from the React app // Serve static files from the React app
app.use('/', express.static(path.join(__dirname, 'clientbuild'))); const indexhtmlPath = path.join(process.env.CLIENT_BUILD_DIR, "index.html");
app.use('/login', express.static(path.join(__dirname, 'clientbuild', 'index.html'))); const staticPath = path.join(process.env.CLIENT_BUILD_DIR, "static");
app.use('/history', express.static(path.join(__dirname, 'clientbuild', 'index.html'))); app.use('/', express.static(process.env.CLIENT_BUILD_DIR));
app.use('/admins', express.static(path.join(__dirname, 'clientbuild', 'index.html'))); app.use('/login', express.static(indexhtmlPath));
app.use('/profile', express.static(path.join(__dirname, 'clientbuild', 'index.html'))); app.use('/history', express.static(indexhtmlPath));
app.use('/tournament/*', express.static(path.join(__dirname, 'clientbuild', 'index.html'))); app.use('/admins', express.static(indexhtmlPath));
app.use('/static', express.static(path.join(__dirname, 'clientbuild/static'))); app.use('/profile', express.static(indexhtmlPath));
app.use('/static/*', express.static(path.join(__dirname, 'clientbuild/static'))); app.use('/tournament/*', express.static(indexhtmlPath));
app.use('/static', express.static(staticPath));
app.use('/static/*', express.static(staticPath));
// #endregion // #endregion
// #region PASSPORT / OAUTH // #region PASSPORT / OAUTH