diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 68c8eb8..ee3deba 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -2106,6 +2106,11 @@ "@babel/types": "^7.3.0" } }, + "@types/component-emitter": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.10.tgz", + "integrity": "sha512-bsjleuRKWmGqajMerkzox19aGbscQX5rmmvvXl3wlIp5gMG1HgkiwPxsN5p070fBDKTNSPgojVbuY1+HWMbFhg==" + }, "@types/eslint": { "version": "7.2.6", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.2.6.tgz", @@ -3334,6 +3339,11 @@ "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==" }, + "backo2": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", + "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=" + }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", @@ -3389,6 +3399,11 @@ } } }, + "base64-arraybuffer": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz", + "integrity": "sha1-mBjHngWbE1X5fgQooBfIOOkLqBI=" + }, "base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -5250,6 +5265,38 @@ "once": "^1.4.0" } }, + "engine.io-client": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-4.1.0.tgz", + "integrity": "sha512-OUmn4m71/lW3ixICv4h3DuBRuh3ri0w3cDuepjsrINSbbqbni4Xw1shTFiKhl0v58lEtNpwJTpSKJJ3fondu5Q==", + "requires": { + "base64-arraybuffer": "0.1.4", + "component-emitter": "~1.3.0", + "debug": "~4.3.1", + "engine.io-parser": "~4.0.1", + "has-cors": "1.1.0", + "parseqs": "0.0.6", + "parseuri": "0.0.6", + "ws": "~7.4.2", + "xmlhttprequest-ssl": "~1.5.4", + "yeast": "0.1.2" + }, + "dependencies": { + "ws": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.2.tgz", + "integrity": "sha512-T4tewALS3+qsrpGI/8dqNMLIVdq/g/85U98HPMa6F0m6xTbvhXU6RCQLqPH3+SlomNV/LdY6RXEbBpMH6EOJnA==" + } + } + }, + "engine.io-parser": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-4.0.2.tgz", + "integrity": "sha512-sHfEQv6nmtJrq6TKuIz5kyEKH/qSdK56H/A+7DnAuUPWosnIZAS2NHNcPLmyjtY3cGS/MqJdZbUjW97JU72iYg==", + "requires": { + "base64-arraybuffer": "0.1.4" + } + }, "enhanced-resolve": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.3.0.tgz", @@ -6932,6 +6979,11 @@ "function-bind": "^1.1.1" } }, + "has-cors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", + "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=" + }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -9260,6 +9312,11 @@ "js-tokens": "^3.0.0 || ^4.0.0" } }, + "lottie-web": { + "version": "5.7.6", + "resolved": "https://registry.npmjs.org/lottie-web/-/lottie-web-5.7.6.tgz", + "integrity": "sha512-qn/KYMI4QQvFDhtoxs0RPkn9uZKhDB9keE5BKgbJlSRfNEZpRiDlwBE9ibYz4nPhbyE+NUlt8IRIVR7g5OSX3w==" + }, "lower-case": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", @@ -10286,6 +10343,16 @@ "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==" }, + "parseqs": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz", + "integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w==" + }, + "parseuri": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz", + "integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow==" + }, "parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -12026,6 +12093,15 @@ "resolved": "https://registry.npmjs.org/react-layout-effect/-/react-layout-effect-1.0.5.tgz", "integrity": "sha512-zdRXHuch+OBHU6bvjTelOGUCM+UDr/iCY+c0wXLEAc+G4/FlcJruD/hUOzlKH5XgO90Y/BUJPNhI/g9kl+VAsA==" }, + "react-lottie": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/react-lottie/-/react-lottie-1.2.3.tgz", + "integrity": "sha512-qLCERxUr8M+4mm1LU0Ruxw5Y5Fn/OmYkGfnA+JDM/dZb3oKwVAJCjwnjkj9TMHtzR2U6sMEUD3ZZ1RaHagM7kA==", + "requires": { + "babel-runtime": "^6.26.0", + "lottie-web": "^5.1.3" + } + }, "react-refresh": { "version": "0.8.3", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.8.3.tgz", @@ -13579,6 +13655,30 @@ } } }, + "socket.io-client": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-3.1.0.tgz", + "integrity": "sha512-T4qPOL80KnoBwkdR70zMpiR6aH6zv3ZqLNriofHqsO9wvQllNTOez0mpV4GdVqo1Y55Z+h8YOlBo7c8pOxDlHw==", + "requires": { + "@types/component-emitter": "^1.2.10", + "backo2": "~1.0.2", + "component-emitter": "~1.3.0", + "debug": "~4.3.1", + "engine.io-client": "~4.1.0", + "parseuri": "0.0.6", + "socket.io-parser": "~4.0.4" + } + }, + "socket.io-parser": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.0.4.tgz", + "integrity": "sha512-t+b0SS+IxG7Rxzda2EVvyBZbvFPBCjJoyHuE0P//7OAsN23GItzDRdWa6ALxZI/8R5ygK7jAR6t028/z+7295g==", + "requires": { + "@types/component-emitter": "^1.2.10", + "component-emitter": "~1.3.0", + "debug": "~4.3.1" + } + }, "sockjs": { "version": "0.3.20", "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.20.tgz", @@ -16369,6 +16469,11 @@ "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" }, + "xmlhttprequest-ssl": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz", + "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=" + }, "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", @@ -16423,6 +16528,11 @@ } } }, + "yeast": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", + "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=" + }, "yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index ffae583..3139c87 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -19,11 +19,13 @@ "react": "^17.0.1", "react-dom": "^17.0.1", "react-html5-camera-photo": "^1.5.4", + "react-lottie": "^1.2.3", "react-router-dom": "^5.2.0", "react-scripts": "4.0.1", "react-social": "^1.10.0", "react-swipeable-views": "^0.13.9", "react-webcam": "^5.2.2", + "socket.io-client": "^3.1.0", "web-vitals": "^0.2.4", "yup": "^0.32.8" }, diff --git a/frontend/src/AdminLogin.jsx b/frontend/src/AdminLogin.jsx deleted file mode 100644 index 8f2dc66..0000000 --- a/frontend/src/AdminLogin.jsx +++ /dev/null @@ -1,98 +0,0 @@ -import React from 'react'; -import { ArrowForward } from '@material-ui/icons'; -import { Formik, Form } from 'formik'; -import * as Yup from "yup"; -import { Grid, TextField, Container, makeStyles, CssBaseline, Button, Typography, Avatar } from '@material-ui/core'; - -import ErrorMessage from './components/ErrorMessage'; - - -const validationSchema = Yup.object().shape({ - email: Yup.string().required().email().label("Email"), - password: Yup.string().required().label("Password"), -}); - -const useStyles = makeStyles((theme) => ({ - paper: { - marginTop: theme.spacing(8), - display: 'flex', - flexDirection: 'column', - alignItems: 'center', - }, - avatar: { - backgroundColor: theme.palette.secondary.main, - color: 'white', - margin: 20, - width: theme.spacing(7), - height: theme.spacing(7), - }, - form: { - justifyContent: 'center', - width: '100%', - }, - submit: { - margin: theme.spacing(3, 0, 2), - }, - TextField: { - width: '100%', - marginTop: 10, - backgroundColor: '#fafafa', - }, -})); - -export default function AdminLogin() { - const classes = useStyles(); - - return ( - - -
- - - - - Login - - console.log(values)} - > - {({ setFieldValue, errors, touched, values }) => ( -
- - - setFieldValue("email", e.target.value)} - className={classes.TextField} - /> - - - - setFieldValue("password", e.target.value)} - className={classes.TextField} - /> - - - - -
- )} -
-
-
- ); -} diff --git a/frontend/src/App.js b/frontend/src/App.js index b5529d7..74459a0 100644 --- a/frontend/src/App.js +++ b/frontend/src/App.js @@ -1,18 +1,21 @@ -import React, { useState } from "react"; +import React from "react"; import "./App.css"; -import UserNav from "./navigation/userNavigation"; -import AdminNav from "./navigation/AdminNavigation"; -import AuthContext from "./api/authContext"; +import AppNavigation from "./navigation/userNavigation"; +import { AuthProvider } from "./api/authContext"; +import { BrowserRouter, Switch } from "react-router-dom"; function App() { - const [user, setUser] = useState(); return ( - +
- + + + + +
-
+ ); } diff --git a/frontend/src/UserRegistration.jsx b/frontend/src/UserRegistration.jsx deleted file mode 100644 index 4b8d2bf..0000000 --- a/frontend/src/UserRegistration.jsx +++ /dev/null @@ -1,113 +0,0 @@ -import React from 'react'; -import { LockOutlined } from '@material-ui/icons'; -import { Formik, Form } from 'formik'; -import * as Yup from "yup"; -import { Grid, TextField, Container, makeStyles, CssBaseline, Button, Typography } from '@material-ui/core'; - -import ErrorMessage from './components/ErrorMessage'; - -const validationSchema = Yup.object().shape({ - username: Yup.string().required().label("User name"), - email: Yup.string().required().email().label("Email"), - age: Yup.number().min(18).required().label("Age"), -}); - -const useStyles = makeStyles((theme) => ({ - paper: { - marginTop: theme.spacing(8), - display: 'flex', - flexDirection: 'column', - alignItems: 'center', - }, - avatar: { - backgroundColor: theme.palette.secondary.main, - padding: 10, - color: 'white', - borderRadius: 30, - margin: 20, - }, - form: { - justifyContent: 'center', - padding: 10, - alignItems: 'center', - }, - submit: { - margin: theme.spacing(3, 0, 2), - }, - error: { - fontSize: 14, - color: 'red', - }, - TextField: { - width: '100%', - backgroundColor: '#fafafa', - } -})); - -export default function UserRegistration() { - - const classes = useStyles(); - - return ( - - -
-
- -
- - Register - - console.log(values)} - > - {({ setFieldValue, errors, touched }) => ( -
- - - setFieldValue( "username", e.target.value)} - className={classes.TextField} - /> - - - - setFieldValue("email", e.target.value)} - className={classes.TextField} - /> - - - - setFieldValue( "age", e.target.value)} - className={classes.TextField} - /> - - - - -
- )} -
-
-
- ); -} diff --git a/frontend/src/admin/AdminMission.jsx b/frontend/src/admin/AdminMission.jsx index 820db53..33a56b6 100644 --- a/frontend/src/admin/AdminMission.jsx +++ b/frontend/src/admin/AdminMission.jsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useEffect, useState } from 'react'; import { makeStyles } from '@material-ui/core/styles'; import List from '@material-ui/core/List'; import AdminMissionListItem from './AdminMissionListItem'; @@ -6,6 +6,8 @@ import AddIcon from '@material-ui/icons/Add'; import Fab from '@material-ui/core/Fab'; import colors from '../utils/colors'; import Routes from '../utils/routes' +import { withRouter } from 'react-router-dom' +import client from '../api/client'; const useStyles = makeStyles((theme) => ({ root: { @@ -28,7 +30,15 @@ const useStyles = makeStyles((theme) => ({ function AdminMission(props) { const { history } = props; const classes = useStyles(); - const [dense, setDense] = React.useState(false); + const [dense, setDense] = useState(false); + const [data, setData] = useState([]); + useEffect(() => { + const fetchData = async () => { + const result = await client.get('api/admin/mission') + setData(result.data.Missions); + } + fetchData(); + }, []); return (
- - - - - - - - - + {data.map((mission, index) => ( + + ))}
@@ -61,4 +67,4 @@ function AdminMission(props) { ) } -export default AdminMission; \ No newline at end of file +export default withRouter(AdminMission); \ No newline at end of file diff --git a/frontend/src/admin/AdminMissionListItem.jsx b/frontend/src/admin/AdminMissionListItem.jsx index 15b3994..a455863 100644 --- a/frontend/src/admin/AdminMissionListItem.jsx +++ b/frontend/src/admin/AdminMissionListItem.jsx @@ -11,40 +11,85 @@ import LocationOnIcon from '@material-ui/icons/LocationOn'; import TextFormatIcon from '@material-ui/icons/TextFormat'; import EditIcon from '@material-ui/icons/Edit'; import colors from '../utils/colors'; +import Dialog from '@material-ui/core/Dialog'; +import DialogActions from '@material-ui/core/DialogActions'; +import DialogContent from '@material-ui/core/DialogContent'; +import DialogContentText from '@material-ui/core/DialogContentText'; +import DialogTitle from '@material-ui/core/DialogTitle'; +import Button from '@material-ui/core/Button'; +import client from '../api/client'; +import { withRouter } from 'react-router-dom'; function AdminMissionListItem(props) { const [secondary, setSecondary] = React.useState(false); - const renderType = (type) => { - if (type === "1") { - return (); + const [open, setOpen] = React.useState(false); + const { history } = props; + const handleClickOpen = () => { + setOpen(true); + }; + + const handleClose = () => { + setOpen(false); + }; + const handleDeleteConfirm = () => { + setOpen(false); + const confirmDelete = async () => { + const response = await client.delete(`api/admin/mission/delete/${props.values._id}`); + console.log(response); } - else if (type === "2") { - return (); + confirmDelete(); + } + const renderType = (type) => { + const object = { + 'Picture': , + 'Text': , + 'Picture and Location': , + 'Video': } - else - return (); + return object[type]; } return ( - {renderType(props.type)} + {renderType(props.values.Category)} - + { history.push(`edit/${props.values._id}`) }}> - + + + {"DELETE MISSION"} + + + Are you sure you want to delete this Mission ? + + + + + + + ) } -export default AdminMissionListItem; \ No newline at end of file +export default withRouter(AdminMissionListItem); \ No newline at end of file diff --git a/frontend/src/admin/Drawer.jsx b/frontend/src/admin/Drawer.jsx index ac386ba..2f68583 100644 --- a/frontend/src/admin/Drawer.jsx +++ b/frontend/src/admin/Drawer.jsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useContext } from "react"; import { Drawer as MUIDrawer, ListItem, @@ -19,6 +19,7 @@ import Avatar from '@material-ui/core/Avatar'; import { deepOrange } from '@material-ui/core/colors'; import colors from '../utils/colors'; import Routes from '../utils/routes'; +import { AuthContext } from "../api/authContext"; const useStyles = makeStyles((theme) => ({ drawer: { @@ -39,13 +40,13 @@ const useStyles = makeStyles((theme) => ({ const Drawer = props => { const { history } = props; + const authContext = useContext(AuthContext); const classes = useStyles(); const itemsList = [ { text: "Mission", icon: , onClick: () => { - props.onTitleChange('Missions') history.push(Routes.ADMIN_MISSIONS); } }, @@ -53,7 +54,6 @@ const Drawer = props => { text: "Activity", icon: , onClick: () => { - props.onTitleChange('Activity feed') history.push(Routes.ADMIN_ACTIVITY_FEED); } }, @@ -61,14 +61,16 @@ const Drawer = props => { text: "Score Board", icon: , onClick: () => { - props.onTitleChange('Score Board') history.push(Routes.ADMIN_LEADERBOARD) } }, { text: "Logout", icon: , - onClick: () => history.push("/logout") + onClick: () => { + authContext.logout(); + history.push(Routes.USER_LOGIN); + } } ]; const adminList = [ @@ -76,7 +78,6 @@ const Drawer = props => { text: "Admin List", icon: , onClick: () => { - props.onTitleChange('Admin List'); history.push(Routes.ADMIN_LIST); } }, @@ -84,8 +85,7 @@ const Drawer = props => { text: "Missions", icon: , onClick: () => { - props.onTitleChange('Missions'); - history.push(Routes.ADMIN_MISSION_EDIT); + history.push(Routes.ADMIN_MISSION_UPDATE); } } ] @@ -111,6 +111,8 @@ const Drawer = props => { ); })} + {authContext.isSuperAdmin() && ( + <>

{ ); })} + + )} ); }; diff --git a/frontend/src/admin/DrawerHeader.jsx b/frontend/src/admin/DrawerHeader.jsx new file mode 100644 index 0000000..55db995 --- /dev/null +++ b/frontend/src/admin/DrawerHeader.jsx @@ -0,0 +1,128 @@ +import React from 'react'; +import CssBaseline from '@material-ui/core/CssBaseline'; +import clsx from 'clsx'; +import AppBar from '@material-ui/core/AppBar'; +import Toolbar from '@material-ui/core/Toolbar'; +import IconButton from '@material-ui/core/IconButton'; +import Typography from '@material-ui/core/Typography'; +import Badge from '@material-ui/core/Badge'; +import MenuIcon from '@material-ui/icons/Menu'; +import NotificationsActiveIcon from '@material-ui/icons/NotificationsActive'; +import { makeStyles } from "@material-ui/core/styles"; + +const drawerWidth = 190; + +const useStyles = makeStyles((theme) => ({ + root: { + display: "flex" + }, + toolbar: { + paddingRight: 24, + }, + toolbarIcon: { + display: 'flex', + alignItems: 'center', + justifyContent: 'flex-end', + padding: '0 8px', + ...theme.mixins.toolbar, + }, + appBar: { + zIndex: theme.zIndex.drawer + 1, + transition: theme.transitions.create(['width', 'margin'], { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.leavingScreen, + }), + }, + appBarShift: { + marginLeft: drawerWidth, + width: `calc(100% - ${drawerWidth}px)`, + transition: theme.transitions.create(['width', 'margin'], { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.enteringScreen, + }), + }, + menuButton: { + marginRight: 36, + }, + menuButtonHidden: { + display: 'none', + }, + title: { + flexGrow: 1, + }, + drawerPaper: { + position: 'relative', + whiteSpace: 'nowrap', + width: drawerWidth, + transition: theme.transitions.create('width', { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.enteringScreen, + }), + }, + drawerPaperClose: { + overflowX: 'hidden', + transition: theme.transitions.create('width', { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.leavingScreen, + }), + width: theme.spacing(7), + [theme.breakpoints.up('sm')]: { + width: theme.spacing(9), + }, + }, + appBarSpacer: theme.mixins.toolbar, + content: { + flexGrow: 1, + height: '100vh', + overflow: 'auto', + }, + container: { + paddingTop: theme.spacing(4), + paddingBottom: theme.spacing(4), + }, + paper: { + padding: theme.spacing(2), + display: 'flex', + overflow: 'auto', + flexDirection: 'column', + }, + fixedHeight: { + height: 240, + }, +})); + +const DrawerHeader = (props) => { + const classes = useStyles(); + const [open, setOpen] = React.useState(true); + const handleDrawerOpen = () => { + setOpen(true); + }; + return ( +
+ + + + + + + + {props.title} + + + + + + + + +
+ ) +} + +export default DrawerHeader; \ No newline at end of file diff --git a/frontend/src/admin/EditMission.jsx b/frontend/src/admin/EditMission.jsx new file mode 100644 index 0000000..6f57168 --- /dev/null +++ b/frontend/src/admin/EditMission.jsx @@ -0,0 +1,228 @@ +import React, { useEffect, useState } from 'react'; +import { useFormik } from 'formik'; +import ArrowBackIcon from '@material-ui/icons/ArrowBack'; +import Button from '@material-ui/core/Button'; +import TextField from '@material-ui/core/TextField'; +import Select from '@material-ui/core/Select'; +import client from '../api/client'; +import { withRouter } from 'react-router-dom'; + +const dummyData = { + "id": "5ff7bac89b8b6a46c011b200", + "Category": "Picture", + "clue": "uhugyfytfytfyfyfyfug jgyfyfddtd gfgugufigi vugfyfyfy", + "answer_Type": "Picture", + "answer": ["base64string"], + "Location": { + "Lat": "12.334543656", + "Long": "34.56456756" + }, + "Other_Info": "vdhvdsvfdvfhdsvfjdvfvdvdsjvadv bjdvdsvdsv vcvdhcvjdsvcdsv", + "Feed": "true", + "ServerEvaluation": "false", + "maxPoints": "100", + "MissionName": "IESFHORIG", + "Hints": [ + { + "Content": "hfbfrbfrfrfgrfbrjbfjrbgjrbgbr", + "MaxPoints": "123" + }, { + "Content": "hfbfrbfrfrfgrfbrjbfjrbgjrbgbr", + "MaxPoints": "123" + }, { + "Content": "hfbfrbfrfrfgrfbrjbfjrbgjrbgbr", + "MaxPoints": "123" + }, + { + "Content": "hfbfrbfrfrfgrfbrjbfjrbgjrbgbr", + "MaxPoints": "123" + } + ] +}; + +const EditMission = (props) => { + const { history } = props; + const [data, setData] = useState(dummyData); + const formik = useFormik({ + initialValues: { + Category: data['Category'], + clue: data['clue'], + answer: data['answer'].join(','), + Other_Info: data['Other_Info'], + answer_Type: '', + maxPoints: data['maxPoints'], + Lat: data.Location.Lat, + Long: data.Location.Long, + MissionName: data['Category'] + }, + enableReinitialize: true, + onSubmit: async (values) => { + const { Category, clue, answer, answer_Type, Other_Info, Lat, Long, maxPoints, MissionName } = values; + const answerArray = []; + answerArray.push(answer); + const object = { + id: props.match.params.id, + Category, + clue, + answer: answerArray, + answer_Type, + Other_Info, + Location: { + Lat, + Long + }, + MissionName, + Feed: true, + ServerEvaluation: false, + maxPoints, + Hints: [ + { + Content: "hfbfrbfrfrfgrfbrjbfjrbgjrbgbr", + MaxPoints: 123 + }, { + Content: "hfbfrbfrfrfgrfbrjbfjrbgjrbgbr", + MaxPoints: 123 + }, { + Content: "hfbfrbfrfrfgrfbrjbfjrbgjrbgbr", + MaxPoints: 123 + } + ] + }; + const response = await client.patch('api/admin/mission/update', object); + console.log(response); + }, + }); + useEffect(() => { + const fetchData = async () => { + const result = await client.get(`api/mission/${props.match.params.id}`); + setData(result.data); + } + fetchData() + }, []); + return ( +
+
history.push('/admin/mission/update')}> + +
+
+

Edit

+
+ + + + + + + + + + + +
+
+ ); +}; + + +export default withRouter(EditMission); + diff --git a/frontend/src/admin/Mission.jsx b/frontend/src/admin/Mission.jsx index 7197ad6..940744e 100644 --- a/frontend/src/admin/Mission.jsx +++ b/frontend/src/admin/Mission.jsx @@ -1,34 +1,22 @@ -import React from 'react'; +import React, { useEffect, useState } from 'react'; import MissionCard from './MissionCard' +import client from '../api/client'; +import { Link } from 'react-router-dom'; const Mission = () => { + const [data, setData] = useState([]); + useEffect(() => { + const fetchData = async () => { + const result = await client.get('api/admin/mission') + setData(result.data.Missions); + } + fetchData(); + }, []); return (
- - - - - - - - - - - - - - - - - - - - - - - - - + {data.map((mission, index) => ( + + ))}
); } diff --git a/frontend/src/admin/MissionCard.jsx b/frontend/src/admin/MissionCard.jsx index c237897..bf1ec93 100644 --- a/frontend/src/admin/MissionCard.jsx +++ b/frontend/src/admin/MissionCard.jsx @@ -4,29 +4,25 @@ import LocationOnIcon from '@material-ui/icons/LocationOn'; import TextFormatIcon from '@material-ui/icons/TextFormat'; import { withRouter } from 'react-router-dom' import './admin.css' -import Routes from '../utils/routes' - const MissionCard = (props) => { const { history } = props; const renderType = (type) => { - if (type === "1") { - return (); - } - else if (type === "2") { - return (); - } - else { - return (); + const object = { + 'Picture': , + 'Text': , + 'Picture and Location': , + 'Video': } + return object[type]; } return ( -
{ history.push(Routes.ADMIN_MISSION_DETAILS) }}> +
- {renderType(props.type)} + {renderType(props.values.Category)}
-

Mission 1

+

{`Mission ${props.index}`}

); diff --git a/frontend/src/admin/MissionDetail.jsx b/frontend/src/admin/MissionDetail.jsx index 47b76c1..0845223 100644 --- a/frontend/src/admin/MissionDetail.jsx +++ b/frontend/src/admin/MissionDetail.jsx @@ -1,7 +1,8 @@ -import React from 'react'; +import React, { useEffect, useState } from 'react'; import { makeStyles } from "@material-ui/core/styles"; import MissionListItem from './MissionListItem' -import CameraAltIcon from '@material-ui/icons/CameraAlt'; +import ArrowBackIcon from '@material-ui/icons/ArrowBack'; +import client from '../api/client'; const useStyles = makeStyles((theme) => ({ root: { @@ -17,33 +18,61 @@ const useStyles = makeStyles((theme) => ({ }, })) +const dummyData = { + Location: { Lat: 23, Long: 24 }, + Category: 'Picture and Location', + clue: 'I am clueless yaaar', + answer_Type: 'Picture and Location', + answer: ['orange', 'apple', 'mango'], + Other_Info: 'I am orange' +} function MissionDetail(props) { const classes = useStyles(); + const [data, setData] = useState(dummyData); + const { history } = props; + useEffect(() => { + const fetchData = async () => { + const result = await client.get(`api/mission/${props.match.params.id}`); + console.log(result.data); + setData(result.data); + } + fetchData(); + }, [props.match.params.id]); return ( -
+
-

Mission 1

+ position: 'absolute', + left: '28%', + top: '20%', + cursor: 'pointer', + }} onClick={() => history.push('/admin')}> +
-
-
- - - - - - +
+
+

Mission

+
+
+
+ + + + + + +
diff --git a/frontend/src/admin/MissionListItem.jsx b/frontend/src/admin/MissionListItem.jsx index c29ba7e..9f43ff6 100644 --- a/frontend/src/admin/MissionListItem.jsx +++ b/frontend/src/admin/MissionListItem.jsx @@ -15,7 +15,7 @@ function MissionListItem(props) { const classes = useStyles(); return (
-

{props.title} Lorem ipsum dolor sit amet, consectetur adipis

+

{props.title} {props.value}

) } diff --git a/frontend/src/admin/NewMission.jsx b/frontend/src/admin/NewMission.jsx index 3adccfb..a5add8a 100644 --- a/frontend/src/admin/NewMission.jsx +++ b/frontend/src/admin/NewMission.jsx @@ -1,131 +1,183 @@ import React from 'react'; import { useFormik } from 'formik'; - +import ArrowBackIcon from '@material-ui/icons/ArrowBack'; import Button from '@material-ui/core/Button'; import TextField from '@material-ui/core/TextField'; import Select from '@material-ui/core/Select'; +import client from '../api/client'; +import { withRouter } from 'react-router-dom'; - -const NewMission = () => { +const NewMission = (props) => { + const { history } = props; const formik = useFormik({ initialValues: { - category: '', + Category: '', clue: '', - password: '', answer: '', - otherinfo: '', - answertype: '', + Other_Info: '', + answer_Type: '', }, - onSubmit: (values) => { - console.log(values); + onSubmit: async (values) => { + const { Category, clue, answer, answer_Type, Other_Info, Lat, Long, maxPoints, MissionName } = values; + const answerArray = []; + answerArray.push(answer); + const object = { + Category, + clue, + answer: answerArray, + answer_Type, + Other_Info, + Location: { + Lat, + Long + }, + MissionName, + Feed: true, + ServerEvaluation: false, + maxPoints, + Hints: [ + { + Content: "hfbfrbfrfrfgrfbrjbfjrbgjrbgbr", + MaxPoints: 123 + }, { + Content: "hfbfrbfrfrfgrfbrjbfjrbgjrbgbr", + MaxPoints: 123 + }, { + Content: "hfbfrbfrfrfgrfbrjbfjrbgjrbgbr", + MaxPoints: 123 + } + ] + }; + const response = await client.post('api/admin/mission/add', object); + console.log(response); }, }); return ( -
-

Add a Mission

-
- - - - - - - - - - + +
); }; -export default NewMission; +export default withRouter(NewMission); diff --git a/frontend/src/api/auth.js b/frontend/src/api/auth.js index ba82985..fffcd75 100644 --- a/frontend/src/api/auth.js +++ b/frontend/src/api/auth.js @@ -9,4 +9,4 @@ export const userMobileNoVerify = (body) => export const userLogin = (body) => client.post("/auth/login", body); export const AdminRegister = (email) => - client.post("/auth/create_admin", { emailId: email }); + client.post("/api/admin/createAdmin", { emailId: email }); diff --git a/frontend/src/api/authContext.js b/frontend/src/api/authContext.js index 4294f09..07dbeea 100644 --- a/frontend/src/api/authContext.js +++ b/frontend/src/api/authContext.js @@ -1,5 +1,67 @@ -import React from "react"; +import React, { createContext, useState } from "react"; -const AuthContext = React.createContext(); +const AuthContext = createContext(); +const { Provider } = AuthContext; -export default AuthContext; +const AuthProvider = ({ children }) => { + const token = localStorage.getItem("token"); + const userInfo = localStorage.getItem("userInfo"); + const expiresAt = localStorage.getItem("expiresAt"); + + const [authState, setAuthState] = useState({ + token, + expiresAt, + userInfo: userInfo ? JSON.parse(userInfo) : {}, + }); + + const setAuthInfo = ({ token, userInfo, expiresAt }) => { + localStorage.setItem("token", token); + localStorage.setItem("userInfo", JSON.stringify(userInfo)); + localStorage.setItem("expiresAt", expiresAt); + + setAuthState({ + token, + userInfo, + expiresAt, + }); + }; + + const logout = () => { + localStorage.removeItem("token"); + localStorage.removeItem("userInfo"); + localStorage.removeItem("expiresAt"); + setAuthState({}); + }; + + const isAuthenticated = () => { + if (!authState.token || !authState.expiresAt) { + return false; + } + return new Date().getTime() / 1000 < authState.expiresAt; + }; + + const isAdmin = () => { + const adminRoles = ["Admin", "SuperAdmin"]; + return adminRoles.includes(authState.userInfo.Role); + }; + + const isSuperAdmin = () => { + return authState.userInfo.Role === "SuperAdmin"; + }; + + return ( + setAuthInfo(authInfo), + logout, + isAuthenticated, + isAdmin, + isSuperAdmin, + }}> + {children} + + ); +}; + +export { AuthContext, AuthProvider }; diff --git a/frontend/src/api/client.js b/frontend/src/api/client.js index 4cfadfa..723c2f0 100644 --- a/frontend/src/api/client.js +++ b/frontend/src/api/client.js @@ -1,12 +1,11 @@ import apisauce from "apisauce"; -import { getToken } from "./storage"; const client = apisauce.create({ baseURL: "http://localhost:3000/", }); client.addAsyncRequestTransform(async (request) => { - const authToken = await getToken(); + const authToken = await localStorage.getItem("token"); if (!authToken) return; request.headers["token"] = authToken; }); diff --git a/frontend/src/api/scoreBoard.js b/frontend/src/api/scoreBoard.js new file mode 100644 index 0000000..7dc5e2a --- /dev/null +++ b/frontend/src/api/scoreBoard.js @@ -0,0 +1,3 @@ +import client from "./client"; + +export const scoreboard = () => client.get("/api/scoreboard"); diff --git a/frontend/src/api/storage.js b/frontend/src/api/storage.js deleted file mode 100644 index 4c7732e..0000000 --- a/frontend/src/api/storage.js +++ /dev/null @@ -1,5 +0,0 @@ -export const setToken = (token) => localStorage.setItem("token", token); - -export const getToken = () => localStorage.getItem("token"); - -export const removeToken = () => localStorage.removeItem("token"); diff --git a/frontend/src/api/team.js b/frontend/src/api/team.js new file mode 100644 index 0000000..b17108b --- /dev/null +++ b/frontend/src/api/team.js @@ -0,0 +1,6 @@ +import client from "./client"; + +export const teamRegister = (body) => client.post("/api/team/create", body); + +export const joinTeam = (teamID) => + client.get(`/api/team/join?teamid=${teamID}`); diff --git a/frontend/src/assets/animations/animation_640_kk23yswy.gif b/frontend/src/assets/animations/animation_640_kk23yswy.gif new file mode 100644 index 0000000..e343aa5 Binary files /dev/null and b/frontend/src/assets/animations/animation_640_kk23yswy.gif differ diff --git a/frontend/src/assets/animations/loader.json b/frontend/src/assets/animations/loader.json new file mode 100644 index 0000000..c7de61b --- /dev/null +++ b/frontend/src/assets/animations/loader.json @@ -0,0 +1 @@ +{"assets":[],"layers":[{"ddd":0,"ind":0,"ty":4,"nm":"形状图层 5","ks":{"o":{"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.333],"y":[0]},"n":["0p833_0p833_0p333_0"],"t":8,"s":[100],"e":[30]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.333],"y":[0]},"n":["0p833_0p833_0p333_0"],"t":24,"s":[30],"e":[100]},{"t":40}]},"r":{"k":0},"p":{"k":[187.875,77.125,0]},"a":{"k":[-76.375,-2.875,0]},"s":{"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0.333]},"n":["0p833_0p833_0p333_0","0p833_0p833_0p333_0","0p833_0p833_0p333_0p333"],"t":8,"s":[100,100,100],"e":[200,200,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0.333]},"n":["0p833_0p833_0p333_0","0p833_0p833_0p333_0","0p833_0p833_0p333_0p333"],"t":24,"s":[200,200,100],"e":[100,100,100]},{"t":40}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"k":[18,18]},"p":{"k":[0,0]},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse"},{"ty":"st","c":{"k":[1,1,1,1]},"o":{"k":100},"w":{"k":0},"lc":1,"lj":1,"ml":4,"nm":"描边 1","mn":"ADBE Vector Graphic - Stroke"},{"ty":"fl","c":{"k":[0.87,0.42,0.56,1]},"o":{"k":100},"nm":"填充 1","mn":"ADBE Vector Graphic - Fill"},{"ty":"tr","p":{"k":[-76.482,-3.482],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"变换"}],"nm":"椭圆 1","np":3,"mn":"ADBE Vector Group"}],"ip":0,"op":40,"st":0,"bm":0,"sr":1},{"ddd":0,"ind":1,"ty":4,"nm":"形状图层 4","ks":{"o":{"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.333],"y":[0]},"n":["0p833_0p833_0p333_0"],"t":6,"s":[100],"e":[30]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.333],"y":[0]},"n":["0p833_0p833_0p333_0"],"t":22,"s":[30],"e":[100]},{"t":36}]},"r":{"k":0},"p":{"k":[162.125,76.625,0]},"a":{"k":[-76.375,-2.875,0]},"s":{"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0.333]},"n":["0p833_0p833_0p333_0","0p833_0p833_0p333_0","0p833_0p833_0p333_0p333"],"t":6,"s":[100,100,100],"e":[200,200,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0.333]},"n":["0p833_0p833_0p333_0","0p833_0p833_0p333_0","0p833_0p833_0p333_0p333"],"t":22,"s":[200,200,100],"e":[100,100,100]},{"t":36}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"k":[18,18]},"p":{"k":[0,0]},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse"},{"ty":"st","c":{"k":[1,1,1,1]},"o":{"k":100},"w":{"k":0},"lc":1,"lj":1,"ml":4,"nm":"描边 1","mn":"ADBE Vector Graphic - Stroke"},{"ty":"fl","c":{"k":[0.81,0.55,0.82,1]},"o":{"k":100},"nm":"填充 1","mn":"ADBE Vector Graphic - Fill"},{"ty":"tr","p":{"k":[-76.482,-3.482],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"变换"}],"nm":"椭圆 1","np":3,"mn":"ADBE Vector Group"}],"ip":0,"op":40,"st":0,"bm":0,"sr":1},{"ddd":0,"ind":2,"ty":4,"nm":"形状图层 3","ks":{"o":{"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.333],"y":[0]},"n":["0p833_0p833_0p333_0"],"t":4,"s":[100],"e":[30]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.333],"y":[0]},"n":["0p833_0p833_0p333_0"],"t":20,"s":[30],"e":[100]},{"t":32}]},"r":{"k":0},"p":{"k":[135.625,76.625,0]},"a":{"k":[-76.375,-2.875,0]},"s":{"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0.333]},"n":["0p833_0p833_0p333_0","0p833_0p833_0p333_0","0p833_0p833_0p333_0p333"],"t":4,"s":[100,100,100],"e":[200,200,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0.333]},"n":["0p833_0p833_0p333_0","0p833_0p833_0p333_0","0p833_0p833_0p333_0p333"],"t":20,"s":[200,200,100],"e":[100,100,100]},{"t":32}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"k":[18,18]},"p":{"k":[0,0]},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse"},{"ty":"st","c":{"k":[1,1,1,1]},"o":{"k":100},"w":{"k":0},"lc":1,"lj":1,"ml":4,"nm":"描边 1","mn":"ADBE Vector Graphic - Stroke"},{"ty":"fl","c":{"k":[0.47,0.31,0.62,1]},"o":{"k":100},"nm":"填充 1","mn":"ADBE Vector Graphic - Fill"},{"ty":"tr","p":{"k":[-76.482,-3.482],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"变换"}],"nm":"椭圆 1","np":3,"mn":"ADBE Vector Group"}],"ip":0,"op":40,"st":0,"bm":0,"sr":1},{"ddd":0,"ind":3,"ty":4,"nm":"形状图层 2","ks":{"o":{"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.333],"y":[0]},"n":["0p833_0p833_0p333_0"],"t":2,"s":[100],"e":[30]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.333],"y":[0]},"n":["0p833_0p833_0p333_0"],"t":16,"s":[30],"e":[100]},{"t":28}]},"r":{"k":0},"p":{"k":[109.375,76.625,0]},"a":{"k":[-76.625,-3.125,0]},"s":{"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0.333]},"n":["0p833_0p833_0p333_0","0p833_0p833_0p333_0","0p833_0p833_0p333_0p333"],"t":2,"s":[100,100,100],"e":[200,200,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0.333]},"n":["0p833_0p833_0p333_0","0p833_0p833_0p333_0","0p833_0p833_0p333_0p333"],"t":16,"s":[200,200,100],"e":[100,100,100]},{"t":28}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"k":[18,18]},"p":{"k":[0,0]},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse"},{"ty":"st","c":{"k":[1,1,1,1]},"o":{"k":100},"w":{"k":0},"lc":1,"lj":1,"ml":4,"nm":"描边 1","mn":"ADBE Vector Graphic - Stroke"},{"ty":"fl","c":{"k":[0.54,0.81,0.89,1]},"o":{"k":100},"nm":"填充 1","mn":"ADBE Vector Graphic - Fill"},{"ty":"tr","p":{"k":[-76.482,-3.482],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"变换"}],"nm":"椭圆 1","np":3,"mn":"ADBE Vector Group"}],"ip":0,"op":40,"st":0,"bm":0,"sr":1},{"ddd":0,"ind":4,"ty":4,"nm":"形状图层 1","ks":{"o":{"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.333],"y":[0]},"n":["0p833_0p833_0p333_0"],"t":0,"s":[100],"e":[30]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.333],"y":[0]},"n":["0p833_0p833_0p333_0"],"t":12,"s":[30],"e":[100]},{"t":24}]},"r":{"k":0},"p":{"k":[82.625,76.625,0]},"a":{"k":[-76.625,-3.375,0]},"s":{"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0.333]},"n":["0p833_0p833_0p333_0","0p833_0p833_0p333_0","0p833_0p833_0p333_0p333"],"t":0,"s":[100,100,100],"e":[200,200,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0.333]},"n":["0p833_0p833_0p333_0","0p833_0p833_0p333_0","0p833_0p833_0p333_0p333"],"t":12,"s":[200,200,100],"e":[100,100,100]},{"t":24}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"k":[18,18]},"p":{"k":[0,0]},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse"},{"ty":"st","c":{"k":[1,1,1,1]},"o":{"k":100},"w":{"k":0},"lc":1,"lj":1,"ml":4,"nm":"描边 1","mn":"ADBE Vector Graphic - Stroke"},{"ty":"fl","c":{"k":[0.34,0.45,0.78,1]},"o":{"k":100},"nm":"填充 1","mn":"ADBE Vector Graphic - Fill"},{"ty":"tr","p":{"k":[-76.482,-3.482],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"变换"}],"nm":"椭圆 1","np":3,"mn":"ADBE Vector Group"}],"ip":0,"op":40,"st":0,"bm":0,"sr":1}],"v":"4.5.4","ddd":0,"ip":0,"op":40,"fr":24,"w":280,"h":160} \ No newline at end of file diff --git a/frontend/src/assets/animations/success_animation.json b/frontend/src/assets/animations/success_animation.json new file mode 100644 index 0000000..7fea758 --- /dev/null +++ b/frontend/src/assets/animations/success_animation.json @@ -0,0 +1 @@ +{"v":"5.0.4","fr":30,"ip":0,"op":90,"w":50,"h":50,"nm":"Success_tick","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"tick","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"n":["0p667_1_0p333_0"],"t":69,"s":[0],"e":[100]},{"t":78}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[23.75,25.125,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-13,1],[-4.625,10.5],[14.5,-9.25]],"c":false},"ix":2},"nm":"Tracé 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"nm":"Contour 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformer "}],"nm":"Forme 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":28,"s":[0],"e":[100]},{"t":49}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Raccorder les tracés 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":69,"op":90,"st":13,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"circle","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"n":["0p667_1_0p333_0"],"t":69,"s":[0],"e":[100]},{"t":78}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[24.259,25.634,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[43.732,43.732],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Tracé d'ellipse 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.286274509804,0.721568627451,0.282352941176,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fond 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0.741,-0.634],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformer "}],"nm":"Ellipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":13,"s":[0],"e":[100]},{"t":33}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Raccorder les tracés 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":69,"op":90,"st":13,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Calque de forme 7","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[23.75,25.125,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-13,1],[-4.625,10.5],[14.5,-9.25]],"c":false},"ix":2},"nm":"Tracé 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.153552650003,0.565732230392,0.149839333927,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"nm":"Contour 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformer "}],"nm":"Forme 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"n":["0p667_1_0p333_0"],"t":48,"s":[0],"e":[100]},{"t":68}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Raccorder les tracés 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":47,"op":70,"st":15,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Calque de forme 6","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[23.75,25.125,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-13,1],[-4.625,10.5],[14.5,-9.25]],"c":false},"ix":2},"nm":"Tracé 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.277887920305,0.759528186275,0.273548799403,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"nm":"Contour 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformer "}],"nm":"Forme 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"n":["0p667_1_0p333_0"],"t":43,"s":[0],"e":[100]},{"t":63}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Raccorder les tracés 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":42,"op":68,"st":10,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Calque de forme 5","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[23.75,25.125,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-13,1],[-4.625,10.5],[14.5,-9.25]],"c":false},"ix":2},"nm":"Tracé 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.390696387197,0.889353553922,0.386203960344,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"nm":"Contour 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformer "}],"nm":"Forme 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"n":["0p667_1_0p333_0"],"t":38,"s":[0],"e":[100]},{"t":58}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Raccorder les tracés 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":37,"op":67,"st":5,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"Calque de forme 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[23.75,25.125,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-13,1],[-4.625,10.5],[14.5,-9.25]],"c":false},"ix":2},"nm":"Tracé 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.542909450157,0.90612745098,0.539637187883,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"nm":"Contour 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformer "}],"nm":"Forme 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"n":["0p667_1_0p333_0"],"t":32,"s":[0],"e":[100]},{"t":52}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Raccorder les tracés 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":31,"op":65,"st":-1,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"Calque de forme 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[24.259,25.634,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[43.732,43.732],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Tracé d'ellipse 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.286274509804,0.721568627451,0.282352941176,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"nm":"Contour 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0.741,-0.634],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformer "}],"nm":"Ellipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"n":["0p667_1_0p333_0"],"t":0,"s":[0],"e":[100]},{"t":30}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Raccorder les tracés 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":150,"st":0,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/frontend/src/components/LoadingPage.jsx b/frontend/src/components/LoadingPage.jsx new file mode 100644 index 0000000..011b071 --- /dev/null +++ b/frontend/src/components/LoadingPage.jsx @@ -0,0 +1,33 @@ +import { makeStyles } from '@material-ui/core'; +import React from 'react'; +import Lottie from 'react-lottie' +import animationData from '../assets/animations/loader.json'; + +const useStyles = makeStyles(() => ({ + animation: { + justifyContent: 'center', + alignItems: 'center', + } +})) + +function LoadingPage(props) { + const styles = useStyles(); + const defaultOptions = { + loop: true, + autoplay: true, + animationData: animationData, + rendererSettings: { + preserveAspectRatio: 'xMidYMid slice' + } + }; + return ( +
+ +
+ ); +} + +export default LoadingPage; \ No newline at end of file diff --git a/frontend/src/components/MessageBot.jsx b/frontend/src/components/MessageBot.jsx new file mode 100644 index 0000000..ef4a35e --- /dev/null +++ b/frontend/src/components/MessageBot.jsx @@ -0,0 +1,18 @@ +import React from 'react'; +import useScript from '../hooks/useScript'; + +function MessageBot(props) { + useScript( + "https://embed.tawk.to/5ffc4538c31c9117cb6d70dc/1eromsq55", + "user", + { + key: "crossorigin", + value: "*", + } + ); + return ( +
+ ); +} + +export default MessageBot; \ No newline at end of file diff --git a/frontend/src/components/SuccessAnimation.jsx b/frontend/src/components/SuccessAnimation.jsx new file mode 100644 index 0000000..2b07818 --- /dev/null +++ b/frontend/src/components/SuccessAnimation.jsx @@ -0,0 +1,23 @@ +import React from 'react'; +import Lottie from 'react-lottie' +import animationData from '../assets/animations/success_animation.json'; + +function SuccessAnimation(props) { + const defaultOptions = { + loop: false, + autoplay: true, + animationData: animationData, + rendererSettings: { + preserveAspectRatio: 'xMidYMid slice' + } + }; + return ( + + ); +} + +export default SuccessAnimation; \ No newline at end of file diff --git a/frontend/src/hooks/useAuth.js b/frontend/src/hooks/useAuth.js deleted file mode 100644 index c35a321..0000000 --- a/frontend/src/hooks/useAuth.js +++ /dev/null @@ -1,22 +0,0 @@ -import { useContext } from "react"; -import jwtDecode from "jwt-decode"; -import AuthContext from "../api/authContext"; -import { setToken, removeToken } from "../api/storage"; - -export default function useAuth() { - const { user, setUser } = useContext(AuthContext); - - const logIn = (authToken) => { - const user = jwtDecode(authToken); - setUser(user); - console.log(user); - setToken(authToken); - }; - - const logOut = () => { - setUser(null); - removeToken(); - }; - - return { user, logIn, logOut }; -} diff --git a/frontend/src/hooks/useScript.js b/frontend/src/hooks/useScript.js index 89affbe..8fd0be4 100644 --- a/frontend/src/hooks/useScript.js +++ b/frontend/src/hooks/useScript.js @@ -9,9 +9,9 @@ const useScript = (url, classElement, attribute) => { script.setAttribute(attribute.key, attribute.value); document.getElementsByClassName(classElement)[0].appendChild(script); - return () => { - document.getElementsByClassName(classElement)[0].removeChild(script); - }; + // return () => { + // document.getElementsByClassName(classElement)[0].removeChild(script); + // }; }, [attribute.key, attribute.value, classElement, url]); }; diff --git a/frontend/src/index.js b/frontend/src/index.js index fb35d14..a054b14 100644 --- a/frontend/src/index.js +++ b/frontend/src/index.js @@ -3,7 +3,9 @@ import ReactDOM from "react-dom"; // import AdminMembers from "./AdminMembers"; // import AdminRegistration from "./AdminRegistration"; import App from "./App"; -import PaymentPage from "./user/PaymentPage"; +// import LoadingPage from "./components/LoadingPage"; +// import SuccessAnimation from "./components/SuccessAnimation"; +// import PaymentPage from "./user/PaymentPage"; // import ProfilePage from "./user/ProfilePage"; // import UserLogin from "./UserLogin"; // import GameIntro from "./user/GameIntro"; @@ -14,4 +16,4 @@ import PaymentPage from "./user/PaymentPage"; // import AdminRegistration from "./AdminRegistration"; // import Template from "./user/Template"; -ReactDOM.render(, document.getElementById("root")); +ReactDOM.render(, document.getElementById("root")); diff --git a/frontend/src/navigation/AdminNavigation.jsx b/frontend/src/navigation/AdminNavigation.jsx index 72e4dad..8bfe621 100644 --- a/frontend/src/navigation/AdminNavigation.jsx +++ b/frontend/src/navigation/AdminNavigation.jsx @@ -1,11 +1,7 @@ -import React from "react"; -import { BrowserRouter as Router, Route, Switch } from "react-router-dom"; -import CssBaseline from '@material-ui/core/CssBaseline'; -import clsx from 'clsx'; -import AppBar from '@material-ui/core/AppBar'; -import Toolbar from '@material-ui/core/Toolbar'; -import IconButton from '@material-ui/core/IconButton'; +import React, { useContext } from "react"; +import { BrowserRouter as Router, Redirect, Route, Switch } from "react-router-dom"; import Drawer from "../admin/Drawer"; +import DrawerHeader from '../admin/DrawerHeader'; import Mission from '../admin/Mission' import Activity from '../admin/Activity.jsx' import ScoreBoard from '../admin/ScoreBoard' @@ -14,147 +10,96 @@ import AdminMission from '../admin/AdminMission'; import Notification from '../admin/Notifications'; import MissionDetail from '../admin/MissionDetail' import NewMission from '../admin/NewMission'; -import { makeStyles } from "@material-ui/core/styles"; -import Typography from '@material-ui/core/Typography'; -import Badge from '@material-ui/core/Badge'; -import MenuIcon from '@material-ui/icons/Menu'; -import { ChatBubble } from '@material-ui/icons'; -import NotificationsActiveIcon from '@material-ui/icons/NotificationsActive'; +import EditMission from '../admin/EditMission'; import Routes from "../utils/routes"; +import UserLogin from "../user/UserLogin"; +import GameIntro from "../user/GameIntro"; +import { AuthContext } from "../api/authContext"; -const drawerWidth = 190; - -const useStyles = makeStyles((theme) => ({ - root: { - display: "flex" - }, - toolbar: { - paddingRight: 24, - }, - toolbarIcon: { - display: 'flex', - alignItems: 'center', - justifyContent: 'flex-end', - padding: '0 8px', - ...theme.mixins.toolbar, - }, - appBar: { - zIndex: theme.zIndex.drawer + 1, - transition: theme.transitions.create(['width', 'margin'], { - easing: theme.transitions.easing.sharp, - duration: theme.transitions.duration.leavingScreen, - }), - }, - appBarShift: { - marginLeft: drawerWidth, - width: `calc(100% - ${drawerWidth}px)`, - transition: theme.transitions.create(['width', 'margin'], { - easing: theme.transitions.easing.sharp, - duration: theme.transitions.duration.enteringScreen, - }), - }, - menuButton: { - marginRight: 36, - }, - menuButtonHidden: { - display: 'none', - }, - title: { - flexGrow: 1, - }, - drawerPaper: { - position: 'relative', - whiteSpace: 'nowrap', - width: drawerWidth, - transition: theme.transitions.create('width', { - easing: theme.transitions.easing.sharp, - duration: theme.transitions.duration.enteringScreen, - }), - }, - drawerPaperClose: { - overflowX: 'hidden', - transition: theme.transitions.create('width', { - easing: theme.transitions.easing.sharp, - duration: theme.transitions.duration.leavingScreen, - }), - width: theme.spacing(7), - [theme.breakpoints.up('sm')]: { - width: theme.spacing(9), - }, - }, - appBarSpacer: theme.mixins.toolbar, - content: { - flexGrow: 1, - height: '100vh', - overflow: 'auto', - }, - container: { - paddingTop: theme.spacing(4), - paddingBottom: theme.spacing(4), - }, - paper: { - padding: theme.spacing(2), - display: 'flex', - overflow: 'auto', - flexDirection: 'column', - }, - fixedHeight: { - height: 240, - }, -})); +const AdminRoute = ({ children, ...rest }) => { + const auth = useContext(AuthContext); + return ( + + auth.isAuthenticated() && auth.isAdmin() ? ( + <>{children} + ) : ( + + ) + } + > + ); +}; export default function AdminNav() { - const classes = useStyles(); - const [open, setOpen] = React.useState(true); - const [title, setTitle] = React.useState('Missions'); - const handleDrawerOpen = () => { - setOpen(true); - }; - const onTitleChange = (title) => { - setTitle(title); - } + const auth = useContext(AuthContext); return ( - -
- - - - - - - - {title} - - - - - - - - - - - - - - - - } /> - } /> - } /> - } /> - } /> - } /> - - - + <> + + + + + + +
+ +
+
+ + + + + + + + + + + + + + + + ( + auth.isAuthenticated() && auth.isAdmin() ? ( +
+ + + +
) : () + )} /> + + + + + + + + + + + + + + + + ( + auth.isAuthenticated() && auth.isAdmin() ? ( +
+ + +
- + ) : () + )} /> +
+ ); } \ No newline at end of file diff --git a/frontend/src/navigation/UserNavigation.jsx b/frontend/src/navigation/UserNavigation.jsx index 4712be6..95e9b9f 100644 --- a/frontend/src/navigation/UserNavigation.jsx +++ b/frontend/src/navigation/UserNavigation.jsx @@ -1,7 +1,6 @@ import React, { useContext } from "react"; -import { BrowserRouter as Router, Switch, Route, Redirect } from 'react-router-dom' +import { Switch, Route, Redirect } from 'react-router-dom' import Home from "../user/Home"; -import UserRegistration from "../user/UserRegistration"; import TopNav from "../user/TopNav"; import NavBar from "../user/NavBar"; import { Container } from "@material-ui/core"; @@ -16,99 +15,110 @@ import TextClues from '../user/TextClues'; import LocationClues from '../user/LocationClues'; import Capture from '../user/Photogragh'; -import VerificationEmail from "../user/VerificationEmail"; -import UserLogin from "../user/UserLogin"; import ClueTabs from "../user/ClueTabs"; import Routes from "../utils/routes"; import ProfilePage from "../user/ProfilePage"; import JoinTeam from "../user/JoinTeam"; -import AuthContext from "../api/authContext"; -import { getToken } from "../api/storage"; -import useScript from "../hooks/useScript"; +import UserRegistration from "../user/UserRegistration"; +import UserLogin from "../user/UserLogin"; +import VerificationEmail from "../user/VerificationEmail"; +import { AuthContext } from "../api/authContext"; +import AdminNav from "./AdminNavigation"; +import MessageBot from "../components/MessageBot"; + +const UserAuthenticatedRoute = ({ children, ...rest }) => { + const authContext = useContext(AuthContext); + return ( + + authContext.isAuthenticated() ? ( + <> + {children} + + + ) : ( + + ) + } + > + ); +}; function UserNav() { - useScript( - "https://embed.tawk.to/5ffc4538c31c9117cb6d70dc/1eromsq55", - "user", - { - key: "crossorigin", - value: "*" - } - ); - const authContext = useContext(AuthContext); - console.log(authContext) - return ( - -
- - - - - - {getToken() ? ( + return ( + <> + + + + + + + + + + ( <> - - - - - - - - -
- -
-
- - -
- -
- -
- - - - - - - - -
- - - -
- - -
- -
- -
-
- - -
- - - -
- - - - - - + + - ) : } -
-
-
+ )} /> + + + + + + + + + + + + +
+ +
+
+ + +
+ +
+
+ + +
+ + + +
+ + +
+ +
+ +
+
+ + +
+ + + +
+ + + + + + + + + ) - } export default UserNav; diff --git a/frontend/src/navigation/userNavigation.jsx b/frontend/src/navigation/userNavigation.jsx index 4712be6..95e9b9f 100644 --- a/frontend/src/navigation/userNavigation.jsx +++ b/frontend/src/navigation/userNavigation.jsx @@ -1,7 +1,6 @@ import React, { useContext } from "react"; -import { BrowserRouter as Router, Switch, Route, Redirect } from 'react-router-dom' +import { Switch, Route, Redirect } from 'react-router-dom' import Home from "../user/Home"; -import UserRegistration from "../user/UserRegistration"; import TopNav from "../user/TopNav"; import NavBar from "../user/NavBar"; import { Container } from "@material-ui/core"; @@ -16,99 +15,110 @@ import TextClues from '../user/TextClues'; import LocationClues from '../user/LocationClues'; import Capture from '../user/Photogragh'; -import VerificationEmail from "../user/VerificationEmail"; -import UserLogin from "../user/UserLogin"; import ClueTabs from "../user/ClueTabs"; import Routes from "../utils/routes"; import ProfilePage from "../user/ProfilePage"; import JoinTeam from "../user/JoinTeam"; -import AuthContext from "../api/authContext"; -import { getToken } from "../api/storage"; -import useScript from "../hooks/useScript"; +import UserRegistration from "../user/UserRegistration"; +import UserLogin from "../user/UserLogin"; +import VerificationEmail from "../user/VerificationEmail"; +import { AuthContext } from "../api/authContext"; +import AdminNav from "./AdminNavigation"; +import MessageBot from "../components/MessageBot"; + +const UserAuthenticatedRoute = ({ children, ...rest }) => { + const authContext = useContext(AuthContext); + return ( + + authContext.isAuthenticated() ? ( + <> + {children} + + + ) : ( + + ) + } + > + ); +}; function UserNav() { - useScript( - "https://embed.tawk.to/5ffc4538c31c9117cb6d70dc/1eromsq55", - "user", - { - key: "crossorigin", - value: "*" - } - ); - const authContext = useContext(AuthContext); - console.log(authContext) - return ( - -
- - - - - - {getToken() ? ( + return ( + <> + + + + + + + + + + ( <> - - - - - - - - -
- -
-
- - -
- -
- -
- - - - - - - - -
- - - -
- - -
- -
- -
-
- - -
- - - -
- - - - - - + + - ) : } -
-
-
+ )} /> + + + + + + + + + + + + +
+ +
+
+ + +
+ +
+
+ + +
+ + + +
+ + +
+ +
+ +
+
+ + +
+ + + +
+ + + + + + + + + ) - } export default UserNav; diff --git a/frontend/src/user/CreateTeam.jsx b/frontend/src/user/CreateTeam.jsx index 7d5ab90..76e4e0c 100644 --- a/frontend/src/user/CreateTeam.jsx +++ b/frontend/src/user/CreateTeam.jsx @@ -1,10 +1,14 @@ -import React from 'react'; +import React, { useContext } from 'react'; import { Formik, Form } from 'formik'; import * as Yup from "yup"; import { TextField, Container, makeStyles, CssBaseline, Button, Typography } from '@material-ui/core'; import ErrorMessage from '../components/ErrorMessage'; import team from '../assets/animations/team.gif'; +import { teamRegister } from '../api/team'; +import { useHistory } from 'react-router'; +import Routes from '../utils/routes'; +import { AuthContext } from '../api/authContext'; const validationSchema = Yup.object().shape({ teamName: Yup.string().required().label("Team Name"), @@ -37,6 +41,23 @@ const useStyles = makeStyles((theme) => ({ function CreateTeam(props) { const classes = useStyles(); + const History = useHistory(); + const handleSubmit = async({ teamName }, { resetForm }) => { + const response = await teamRegister({ + teamName: teamName + }); + if(!response.ok){ + console.log(response.status ,response.originalError, response.problem); + return; + }; + console.log(response.data); + const userInfo = JSON.parse(localStorage.getItem("userInfo")); + localStorage.setItem("userInfo", JSON.stringify({...userInfo, teamID: response.data.TeamId})); + resetForm(); + setTimeout(() => { + History.push(Routes.USER_PROFILE); + }, 500); + } return ( @@ -49,7 +70,7 @@ function CreateTeam(props) { console.log(values)} + onSubmit={handleSubmit} > {({ setFieldValue, errors, touched }) => (
@@ -63,7 +84,7 @@ function CreateTeam(props) { /> )} diff --git a/frontend/src/user/GameIntro.jsx b/frontend/src/user/GameIntro.jsx index 61da8fb..a219ea9 100644 --- a/frontend/src/user/GameIntro.jsx +++ b/frontend/src/user/GameIntro.jsx @@ -1,8 +1,10 @@ -import React from 'react'; +import React, { useContext } from 'react'; import { makeStyles } from '@material-ui/core/styles'; import Button from '@material-ui/core/Button'; import { Typography } from '@material-ui/core'; import Routes from '../utils/routes'; +import {AuthContext} from '../api/authContext'; +import { useHistory } from 'react-router'; const useStyles = makeStyles((theme) => ({ root: { @@ -14,8 +16,16 @@ const useStyles = makeStyles((theme) => ({ })); export default function GameIntro() { + const auth = useContext(AuthContext); const classes = useStyles(); - + const history = useHistory(); + const handleLogin = () => { + if(auth.isAuthenticated()){ + auth.isAdmin() ? history.push(Routes.ADMIN_MISSIONS) : history.push(Routes.HOME); + }else{ + history.push(Routes.USER_LOGIN); + } + } return (
@@ -25,7 +35,7 @@ export default function GameIntro() { Register
-
diff --git a/frontend/src/user/JoinTeam.jsx b/frontend/src/user/JoinTeam.jsx index 549b9ee..c968aa0 100644 --- a/frontend/src/user/JoinTeam.jsx +++ b/frontend/src/user/JoinTeam.jsx @@ -6,6 +6,7 @@ import ErrorMessage from '../components/ErrorMessage'; import * as Yup from "yup"; import { userMobileNoVerify } from '../api/auth'; import Routes from '../utils/routes'; +import { joinTeam } from '../api/team'; const validationSchema = Yup.object().shape({ teamId: Yup.string().required().label("Team ID"), @@ -40,18 +41,15 @@ const useStyles = makeStyles((theme) => ({ function JoinTeam(props) { const styles = useStyles(); const handleSubmit = async({teamId}, { resetForm }) => { - const body = { - teamId:teamId - } - const response = await userMobileNoVerify(body); + const response = await joinTeam(teamId); if(!response.ok){ - console.log(response.problem); - console.log(response.data); - return; + console.log(response.problem); + console.log(response); + return; } console.log(response.data); resetForm(); - props.history.push(Routes.USER_PROFILE); + // props.history.push(Routes.USER_PROFILE); } return ( diff --git a/frontend/src/user/Leaderbaord.jsx b/frontend/src/user/Leaderbaord.jsx index 1a00ce6..647aec9 100644 --- a/frontend/src/user/Leaderbaord.jsx +++ b/frontend/src/user/Leaderbaord.jsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useEffect, useState } from 'react'; import PropTypes from 'prop-types'; import { makeStyles, useTheme } from '@material-ui/core/styles'; import Table from '@material-ui/core/Table'; @@ -14,6 +14,7 @@ import KeyboardArrowLeft from '@material-ui/icons/KeyboardArrowLeft'; import KeyboardArrowRight from '@material-ui/icons/KeyboardArrowRight'; import LastPageIcon from '@material-ui/icons/LastPage'; import { Container, TableHead } from '@material-ui/core'; +import { scoreboard } from '../api/scoreBoard'; const useStyles1 = makeStyles((theme) => ({ root: { @@ -79,26 +80,6 @@ TablePaginationActions.propTypes = { rowsPerPage: PropTypes.number.isRequired, }; -function createData(name, points) { - return { name, points }; -} - -const rows = [ - createData('Cupcake', 300), - createData('Donut', 400), - createData('Eclair', 200), - createData('Frozen yoghurt', 150), - createData('Gingerbread', 350), - createData('Honeycomb', 400), - createData('Ice cream sandwich', 230), - createData('Jelly Bean', 370), - createData('KitKat', 510), - createData('Lollipop', 390), - createData('Marshmallow', 310), - createData('Nougat', 360), - createData('Oreo', 430), -].sort((a, b) => (a.points > b.points ? -1 : 1)); - const useStyles2 = makeStyles(theme => ({ root: { width: "100%", @@ -120,8 +101,20 @@ export default function CustomPaginationActionsTable() { const classes = useStyles2(); const [page, setPage] = React.useState(0); const [rowsPerPage, setRowsPerPage] = React.useState(10); + const [ scores, setScores ] = useState([]) + const fetchScore = async() => { + const response = await scoreboard(); + if(!response.ok){ + console.log(response.status, response.originalError, response.problem); + return; + } + setScores(response.data); + } + useEffect(() => { + fetchScore(); + },[]) - const emptyRows = rowsPerPage - Math.min(rowsPerPage, rows.length - page * rowsPerPage); + const emptyRows = rowsPerPage - Math.min(rowsPerPage, scores.length - page * rowsPerPage); const handleChangePage = (event, newPage) => { setPage(newPage); @@ -141,21 +134,21 @@ export default function CustomPaginationActionsTable() { Team Name - + points {(rowsPerPage > 0 - ? rows.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage) - : rows + ? scores.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage) + : scores ).map((row) => ( - {row.name} + {row.teamName} - + {row.points} @@ -163,7 +156,7 @@ export default function CustomPaginationActionsTable() { {emptyRows > 0 && ( - + )} @@ -172,7 +165,7 @@ export default function CustomPaginationActionsTable() { ({ root: { @@ -31,8 +30,13 @@ const useStyles = makeStyles((theme) => ({ })) function ProfilePage(props) { - const { logOut } = useAuth(); + const authContext = useContext(AuthContext) const classes = useStyles(); + const history = useHistory(); + const handleLogout = () => { + authContext.logout(); + history.push(Routes.USER_LOGIN); + } return (
@@ -87,11 +91,11 @@ function ProfilePage(props) { - + - + diff --git a/frontend/src/user/UserLogin.jsx b/frontend/src/user/UserLogin.jsx index aa8f90d..e848b69 100644 --- a/frontend/src/user/UserLogin.jsx +++ b/frontend/src/user/UserLogin.jsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useContext, useState } from 'react'; import { ArrowForward } from '@material-ui/icons'; import { Formik, Form } from 'formik'; import * as Yup from "yup"; @@ -8,9 +8,11 @@ import ErrorMessage from '../components/ErrorMessage'; import { userLogin } from '../api/auth'; import AlertMessage from '../components/AlertMessage'; import Routes from '../utils/routes'; -import { withRouter } from 'react-router'; -import useAuth from '../hooks/useAuth'; - +import { useHistory } from 'react-router'; +import { AuthContext } from '../api/authContext'; +import jwtDecode from 'jwt-decode'; +import LoadingPage from '../components/LoadingPage'; +import SuccessAnimation from '../components/SuccessAnimation'; const validationSchema = Yup.object().shape({ email: Yup.string().required().email().label("Email"), @@ -47,9 +49,13 @@ const useStyles = makeStyles((theme) => ({ export default function UserLogin(props) { const [info, setInfo] = useState(''); - const { logIn } = useAuth(); + const [loading, setLoading] = useState(false); + const [successLogin, setSuccessLogin] = useState(false); + const authContext = useContext(AuthContext); const classes = useStyles(); + const History = useHistory(); const handleSubmit = async({ email, password },{ resetForm }) => { + setLoading(true); const body = { emailId:email, password:password @@ -60,15 +66,28 @@ export default function UserLogin(props) { console.log(response.data); return; } - logIn(response.data.token); + setLoading(false); + setSuccessLogin(true); + const {exp} = await jwtDecode(response.data.token) + const data = { + token: response.data.token, + expiresAt: exp, + userInfo: response.data.user + } + await authContext.setAuthState(data); resetForm(); - response.data.user.Role === "Player" ? props.history.push(Routes.HOME) : props.history.push(Routes.ADMIN_MISSIONS); + const adminRoles = ["Admin", "SuperAdmin"]; + setTimeout(() => { + adminRoles.includes(data.userInfo.Role) ? History.push(Routes.ADMIN_MISSIONS) : History.push(Routes.HOME); + }, 2000); } return ( + {loading && } + {successLogin && } {info && } -
+ {(!loading && !successLogin) &&
@@ -114,9 +133,7 @@ export default function UserLogin(props) { )} -
+
}
); } - -const UserLoginRoute = withRouter(UserLogin); \ No newline at end of file diff --git a/frontend/src/user/UserProfile.jsx b/frontend/src/user/UserProfile.jsx deleted file mode 100644 index 6330eb5..0000000 --- a/frontend/src/user/UserProfile.jsx +++ /dev/null @@ -1,50 +0,0 @@ -import React from 'react'; -import Box from '@material-ui/core/Box'; -import AccountBoxTwoToneIcon from '@material-ui/icons/AccountBoxTwoTone'; -import { makeStyles } from '@material-ui/core/styles'; -import{ Link } from '@material-ui/core'; -import CssBaseline from '@material-ui/core/CssBaseline'; - -function Profile(props) { - const classes= useStyles(); - return ( -
- - -
-

User Name

-

Email :

-

Team :

-
- My posts - Logout - -
- - ); -} - -const useStyles= makeStyles( - { - box:{ - position: 'absolute', left: '50%', top: '50%', - transform: 'translate(-50%, -50%)' - }, - icon:{ - fontSize:70, - color:"navy" - }, - link:{ - fontSize:16, - fontStyle:"italic", - padding:15 - }, - post:{ - fontSize:16, - fontStyle:"italic", - padding:15 - } - } -) - -export default Profile; \ No newline at end of file diff --git a/frontend/src/user/UserRegistration.jsx b/frontend/src/user/UserRegistration.jsx index e072e16..a6970a2 100644 --- a/frontend/src/user/UserRegistration.jsx +++ b/frontend/src/user/UserRegistration.jsx @@ -7,8 +7,9 @@ import { Grid, TextField, Container, makeStyles, CssBaseline, Button, Typography import ErrorMessage from '../components/ErrorMessage'; import { userRegister } from '../api/auth'; import AlertMessage from '../components/AlertMessage'; -import { withRouter } from 'react-router-dom'; +import { useHistory, withRouter } from 'react-router-dom'; import Routes from '../utils/routes'; +import LoadingPage from '../components/LoadingPage'; const validationSchema = Yup.object().shape({ @@ -52,9 +53,12 @@ const useStyles = makeStyles((theme) => ({ export default function UserRegistration(props) { const [info, setInfo] = useState(''); + const [loading, setLoading] = useState(false); const classes = useStyles(); + const history = useHistory(); const handleSubmit = async ({ username, email, phoneNo, password },{ resetForm }) => { + setLoading(true); const body = { name:username, emailId:email, @@ -64,21 +68,23 @@ export default function UserRegistration(props) { const response = await userRegister(body); if(!response.ok){ console.log(response.problem); - console.log(response.data.message); setInfo(response.data.message); + setLoading(false); return; } - console.log(response.data.message); - setInfo(response.data.message); resetForm(); - props.history.push(`${Routes.USER_VERIFY}?mobileNo=${phoneNo}`); + setTimeout(() => { + setLoading(false); + history.push(`${Routes.USER_VERIFY}?mobileNo=${phoneNo}`); + }, 500); } return ( + {loading && } {info && } -
+ {!loading &&
@@ -148,7 +154,7 @@ export default function UserRegistration(props) { )} -
+
}
); } diff --git a/frontend/src/user/VerificationEmail.jsx b/frontend/src/user/VerificationEmail.jsx index 5fb31c7..dfe64e6 100644 --- a/frontend/src/user/VerificationEmail.jsx +++ b/frontend/src/user/VerificationEmail.jsx @@ -1,12 +1,15 @@ -import { Button, Container, Grid, Link, makeStyles, TextField, Typography } from '@material-ui/core'; +import { Button, Container, CssBaseline, Grid, Link, makeStyles, TextField, Typography } from '@material-ui/core'; import { MessageOutlined } from '@material-ui/icons'; import { Form, Formik } from 'formik'; -import React from 'react'; +import React, { useContext, useState } from 'react'; import ErrorMessage from '../components/ErrorMessage'; import * as Yup from "yup"; import { userMobileNoVerify } from '../api/auth'; import Routes from '../utils/routes'; -import useAuth from '../hooks/useAuth'; +import jwtDecode from 'jwt-decode'; +import { AuthContext } from '../api/authContext'; +import LoadingPage from '../components/LoadingPage'; +import SuccessAnimation from '../components/SuccessAnimation'; const queryString = require('query-string'); const validationSchema = Yup.object().shape({ @@ -41,26 +44,43 @@ const useStyles = makeStyles((theme) => ({ function VerificationEmail(props) { const styles = useStyles(); - const { logIn } = useAuth(); + const [loading, setLoading] = useState(false); + const [successVerify, setSuccessVerify] = useState(false); + const auth = useContext(AuthContext); const parsed = queryString.parse(props.location.search); const handleSubmit = async({otp}, { resetForm }) => { + setLoading(true); const body = { mobileNo:parsed.mobileNo, otp:otp } const response = await userMobileNoVerify(body); if(!response.ok){ - console.log(response.problem); - console.log(response.data); - return; + console.log(response.problem); + console.log(response.data); + setLoading(false); + return; } - console.log(response.data); - logIn(response.data.token); + setLoading(false); + setSuccessVerify(true); + const {exp} = await jwtDecode(response.data.token) + const data = { + token: response.data.token, + expiresAt: exp, + userInfo: response.data.result + } + await auth.setAuthState(data); resetForm(); - props.history.push(Routes.HOME); + setTimeout(() => { + props.history.push(Routes.HOME); + }, 2000); } return ( - + + + {loading && } + {successVerify && } + {(!loading && !successVerify) &&
@@ -103,6 +123,7 @@ function VerificationEmail(props) { > resend OTP +
}
); } diff --git a/frontend/src/utils/routes.js b/frontend/src/utils/routes.js index cb590d7..24fddfe 100644 --- a/frontend/src/utils/routes.js +++ b/frontend/src/utils/routes.js @@ -14,13 +14,14 @@ const Routes = { ADMIN_LOGIN: "/admin/login", ADMIN_ADD: "/admin/add-admin", ADMIN_MISSIONS: "/admin", - ADMIN_MISSION_DETAILS: "/admin/missiondetail", + ADMIN_MISSION_DETAILS: "/admin/missiondetail/:id", ADMIN_LEADERBOARD: "/admin/scoreboard", ADMIN_LIST: "/admin/list", - ADMIN_MISSION_EDIT: "/admin/mission/edit", + ADMIN_MISSION_UPDATE: "/admin/mission/update", ADMIN_NOTIFICATION: "/admin/notification", ADMIN_NEW_MISSION: "/admin/mission/new", ADMIN_ACTIVITY_FEED: "/admin/activity", + ADMIN_EDIT_MISSION: "/admin/mission/edit/:id", }; export default Routes; diff --git a/frontend/src/utils/socketio.js b/frontend/src/utils/socketio.js new file mode 100644 index 0000000..2886b53 --- /dev/null +++ b/frontend/src/utils/socketio.js @@ -0,0 +1,15 @@ +import { io } from "socket.io-client"; + +const socket = io("http://localhost:3000/"); + +socket.on("connect", () => { + console.log(socket.id); +}); + +const listener = (...args) => { + console.log(args); +}; + +const teamID = "abcd123"; + +socket.on(`Notification ${teamID}`, listener);