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": {
"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",
@ -6117,8 +6116,7 @@
"esprima": "^4.0.1",
"estraverse": "^5.2.0",
"esutils": "^2.0.2",
"optionator": "^0.8.1",
"source-map": "~0.6.1"
"optionator": "^0.8.1"
},
"bin": {
"escodegen": "bin/escodegen.js",
@ -8726,7 +8724,6 @@
"@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",
@ -10032,14 +10029,9 @@
"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": {
@ -12415,7 +12407,6 @@
"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",
@ -12835,9 +12826,6 @@
"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"
},

View File

@ -114,7 +114,7 @@ function TournamentListItem(props) {
<Typography variant="h5" color="text.primary" gutterBottom> Particpants: {props.tournament.teamCount} / {props.tournament.teamLimit} </Typography>
<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 />
<Box sx={{flexGrow: 1, marginTop: "20px"}}>
@ -205,36 +205,36 @@ let showError = (message) => {};
export default function App() {
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 = () => {
// setUser({
// name: "TEST USERTEST",
// isManager: true,
// isLoggedIn: true,
// email: "testesen@gmail.com",
// asuraId: "123456789",
// googleId: "234"
// 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 = () => {
setUser({
name: "TEST USERTEST",
isManager: true,
isLoggedIn: true,
email: "testesen@gmail.com",
asuraId: "123456789",
googleId: "234"
});
}
React.useEffect(() => {
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>
<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"}}>
<Grid container spacing={4} justifyContent="center" wrap="wrap">
@ -135,13 +135,13 @@ function shorten(description, maxLength) {
}
}
return <>
<TextField type="text" id="searchInput" label="Search" placeholder="Tournament Name" InputLabelProps={{shrink: true}} onChange={search}/>
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) {
@ -149,12 +149,7 @@ export default function TournamentHistory(props) {
return (
<>
<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 />
</Container>
</>
);
}

View File

@ -91,7 +91,7 @@ function Match(props){
{team1Name}
</Typography>
{ 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}>
{ 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}
</Typography>
{ 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 &&
<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">
{props.team.name}
</Typography>
<EmojiEventsIcon alt="A trohpy" />
<EmojiEventsIcon alt="A trophy" />
</div>
)
}

View File

@ -71,17 +71,49 @@ 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()
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 (
<Paper sx={{minHeight: "30vh", width: "90vw", margin: "10px auto"}} component={Stack} direction="column" justifyContent="center">
<div align="center" >
<h2><b>Teams:</b></h2>
{/* TODO: scroll denne menyen, eventuelt søkefelt */}
{/* 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>Team Name</TableCell>
<TableCell><h2>Team Name</h2></TableCell>
{/* <TableCell align="right">Team Members</TableCell> */}
<TableCell align="center">Actions</TableCell>
<TableCell align="center"><h2>Actions</h2></TableCell>
</TableRow>
</TableHead>
<TableBody>
@ -91,7 +123,6 @@ function TeamList(props) {
<TableCell component="th" scope="row"> <b>
{team.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); 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>
@ -184,6 +215,7 @@ let showError = (message) => {};
export default function TournamentTeams(props) {
const [teams, setTeams] = React.useState([]);
const [selectedTeamId, setSelectedTeamId] = React.useState(-1);
const [originalList, setOriginalList] = React.useState([])
const { tournamentId } = useParams();
function getTeams() {
@ -194,6 +226,7 @@ export default function TournamentTeams(props) {
showError(data.data);
}
setTeams(data.data);
setOriginalList(data.data)
//setselectedTeamId(teams[0].id);
})
.catch((err) => showError(err));
@ -218,7 +251,7 @@ export default function TournamentTeams(props) {
<TournamentBar pageTitle="Manage Teams" />
<div className="tournamentTeams">
<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} />
</div>
<ErrorSnackbar message={errorMessage} open={openError} setOpen={setOpenError} />

View File

@ -25,11 +25,11 @@ function LoggedInMenu(props) {
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}>
<MenuIcon />
</IconButton>
<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>
{ props.user.isManager &&
<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>
{ props.pageTitle !== "Asura Tournaments" &&
<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
</Typography>
</Link>

View File

@ -48,13 +48,13 @@ function ButtonLink(props) {
export default function TournamentBar(props) {
const { tournamentId } = useParams();
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}>
<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="/teams" tournamentId={tournamentId} activeTitle={props.pageTitle} title="Manage Teams" />
</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://asura.feal.no/tournament/" + tournamentId} name="Tournament Link" />
</Stack>

View File

@ -43,14 +43,16 @@ app.use(require('express-log-url'));
// #region frontend
// Serve static files from the React app
app.use('/', express.static(path.join(__dirname, 'clientbuild')));
app.use('/login', express.static(path.join(__dirname, 'clientbuild', 'index.html')));
app.use('/history', express.static(path.join(__dirname, 'clientbuild', 'index.html')));
app.use('/admins', express.static(path.join(__dirname, 'clientbuild', 'index.html')));
app.use('/profile', express.static(path.join(__dirname, 'clientbuild', 'index.html')));
app.use('/tournament/*', express.static(path.join(__dirname, 'clientbuild', 'index.html')));
app.use('/static', express.static(path.join(__dirname, 'clientbuild/static')));
app.use('/static/*', express.static(path.join(__dirname, 'clientbuild/static')));
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('/history', express.static(indexhtmlPath));
app.use('/admins', express.static(indexhtmlPath));
app.use('/profile', express.static(indexhtmlPath));
app.use('/tournament/*', express.static(indexhtmlPath));
app.use('/static', express.static(staticPath));
app.use('/static/*', express.static(staticPath));
// #endregion
// #region PASSPORT / OAUTH