Commit 3e985a07 authored by Adithya Kahawanugoda's avatar Adithya Kahawanugoda

Merge branch 'IT19105208-gui' into 'master'

initial frontend structure created

See merge request !3
parents 74ac94dd caf6332f
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.env
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Getting Started with Create React App
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
## Available Scripts
In the project directory, you can run:
### `npm start`
Runs the app in the development mode.\
Open [http://localhost:3000](http://localhost:3000) to view it in your browser.
The page will reload when you make changes.\
You may also see any lint errors in the console.
### `npm test`
Launches the test runner in the interactive watch mode.\
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
### `npm run build`
Builds the app for production to the `build` folder.\
It correctly bundles React in production mode and optimizes the build for the best performance.
The build is minified and the filenames include the hashes.\
Your app is ready to be deployed!
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
### `npm run eject`
**Note: this is a one-way operation. Once you `eject`, you can't go back!**
If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own.
You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it.
## Learn More
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
To learn React, check out the [React documentation](https://reactjs.org/).
### Code Splitting
This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting)
### Analyzing the Bundle Size
This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size)
### Making a Progressive Web App
This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app)
### Advanced Configuration
This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration)
### Deployment
This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment)
### `npm run build` fails to minify
This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify)
This diff is collapsed.
{
"name": "web-app-frontend",
"version": "0.1.0",
"private": true,
"dependencies": {
"@emotion/react": "^11.10.0",
"@emotion/styled": "^11.10.0",
"@mui/icons-material": "^5.8.4",
"@mui/material": "^5.9.3",
"@mui/x-date-pickers": "^5.0.0-beta.3",
"@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^13.3.0",
"@testing-library/user-event": "^13.5.0",
"axios": "^0.27.2",
"date-fns": "^2.29.1",
"formik": "^2.2.9",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.3.0",
"react-scripts": "5.0.1",
"react-spinners": "^0.13.4",
"validator": "^13.7.0",
"web-vitals": "^2.1.4",
"yup": "^0.32.11"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<title>ICAAT</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:
* {
padding: 0;
margin: 0;
box-sizing: border-box;
scroll-behavior: smooth;
cursor: default;
}
import React from "react";
import "./App.css";
import { BrowserRouter as BRouter, Routes, Route } from "react-router-dom";
import { Home, Admin, Student } from "./pages/index";
const App = () => {
return (
<BRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/admin" element={<Admin />} />
<Route path="/activities" element={<Student />} />
</Routes>
</BRouter>
);
};
export default App;
.activity-container-angry-grid {
display: grid;
grid-template-rows: 1fr 1fr 1fr;
grid-template-columns: 1fr 1fr 1fr 1fr 1fr;
gap: 0px;
height: 100%;
padding: 20px;
}
#activity-container-item-0 {
grid-row-start: 1;
grid-column-start: 1;
grid-row-end: 4;
grid-column-end: 2;
margin-right: 10px;
border: solid gainsboro;
border-radius: 5px;
padding: 5px;
}
#activity-container-item-1 {
grid-row-start: 1;
grid-column-start: 2;
grid-row-end: 4;
grid-column-end: 6;
border: solid gainsboro;
border-radius: 5px;
padding: 5px;
}
.activity-container-navigator-item {
font-family: var(--font-family);
font-weight: 400;
font-size: 1em;
color: white;
background-color: teal;
border-radius: 5px;
margin: 5px 0px 5px 0px;
text-align: center;
padding-top: 5px;
padding-bottom: 5px;
}
.activity-container-navigator-item:hover {
opacity: 0.8;
}
import React, { useState, useEffect } from "react";
import "./ActivityContainer.css";
import { PictureConcept, Arithmetic } from "../index";
const ActivityContainer = () => {
const [currentActivityNo, setCurrentActivityNo] = useState(1);
const nextActivityHandler = () => {
setCurrentActivityNo(currentActivityNo + 1);
};
return (
<div className="activity-container-angry-grid">
<div id="activity-container-item-0">
<div
className="activity-container-navigator-item"
onClick={() => {
setCurrentActivityNo(1);
}}
>
Picture Concept
</div>
<div
className="activity-container-navigator-item"
onClick={() => {
setCurrentActivityNo(2);
}}
>
Arithmetic
</div>
<div
className="activity-container-navigator-item"
onClick={() => {
setCurrentActivityNo(3);
}}
>
Activity Name
</div>
<div
className="activity-container-navigator-item"
onClick={() => {
setCurrentActivityNo(4);
}}
>
Activity Name
</div>
</div>
<div id="activity-container-item-1">
{currentActivityNo === 1 && (
<PictureConcept nextActivity={nextActivityHandler} />
)}
{currentActivityNo === 2 && (
<Arithmetic nextActivity={nextActivityHandler} />
)}
</div>
</div>
);
};
export default ActivityContainer;
import React from "react";
const Arithmetic = ({ nextActivity }) => {
return (
<div>
Arithmetic
<button
onClick={() => {
nextActivity();
}}
>
Next
</button>
</div>
);
};
export default Arithmetic;
import React from "react";
const ImageRow = () => {
return <div>Images Row Containing different pictures</div>;
};
export default ImageRow;
import React from "react";
import { ImageRow } from "../../../index";
const PictureConcept = ({ nextActivity }) => {
return (
<div>
<ImageRow />
<button
onClick={() => {
nextActivity();
}}
>
Next
</button>
</div>
);
};
export default PictureConcept;
.container-card-styles {
display: block;
background-color: white;
border-radius: 15px;
height: 100%;
width: 100%;
/* padding: 25px; */
}
import React from "react";
import "./ContainerCard.css";
const ContainerCard = ({ children }) => {
return <div className="container-card-styles">{children}</div>;
};
export default ContainerCard;
.pagination {
display: flex;
justify-content: flex-end;
padding-left: 1rem;
margin-top: 1rem;
margin-bottom: 2rem;
}
.pagination button {
margin-right: 0.5rem;
cursor: pointer;
padding: 7px 15px;
}
.pagination button:disabled {
background-color: #333;
color: #fff;
cursor: not-allowed;
}
import React from "react";
import "./Pagination.css";
const Pagination = ({ page, pages, changePage }) => {
let midPagination;
if (pages <= 5) {
midPagination = [...Array(pages)].map((_, idx) => (
<button
key={idx + 1}
onClick={() => changePage(idx + 1)}
disabled={page === idx + 1}
>
{idx + 1}
</button>
));
} else {
const startValue = Math.floor((page - 1) / 5) * 5;
midPagination = (
<>
{[...Array(5)].map((_, idx) => (
<button
key={startValue + idx + 1}
disabled={page === startValue + idx + 1}
onClick={() => changePage(startValue + idx + 1)}
>
{startValue + idx + 1}
</button>
))}
<button>...</button>
<button onClick={() => changePage(pages)}>{pages}</button>
</>
);
if (page > 5) {
if (pages - page >= 5) {
midPagination = (
<>
<button onClick={() => changePage(1)}>1</button>
<button>...</button>
<button onClick={() => changePage(startValue)}>{startValue}</button>
{[...Array(5)].map((_, idx) => (
<button
key={startValue + idx + 1}
disabled={page === startValue + idx + 1}
onClick={() => changePage(startValue + idx + 1)}
>
{startValue + idx + 1}
</button>
))}
<button>...</button>
<button onClick={() => changePage(pages)}>{pages}</button>
</>
);
} else {
let amountLeft = pages - page + 5;
midPagination = (
<>
<button onClick={() => changePage(1)}>1</button>
<button>...</button>
<button onClick={() => changePage(startValue)}>{startValue}</button>
{[...Array(amountLeft)].map((_, idx) => (
<button
key={startValue + idx + 1}
disabled={page === startValue + idx + 1}
style={
pages < startValue + idx + 1 ? { display: "none" } : null
}
onClick={() => changePage(startValue + idx + 1)}
>
{startValue + idx + 1}
</button>
))}
</>
);
}
}
}
return (
pages > 1 && (
<div className="pagination">
<button
className="pagination__prev"
onClick={() => changePage((page) => page - 1)}
disabled={page === 1}
>
&#171;
</button>
{midPagination}
<button
className="pagination__next"
onClick={() => changePage((page) => page + 1)}
disabled={page === pages}
>
&#187;
</button>
</div>
)
);
};
export default Pagination;
export { default as Login } from "./login/Login";
export { default as ProfileUpdate } from "./profileUpdate/ProfileUpdate";
export { default as Search } from "./search/Search";
export { default as ResultsModal } from "./search/ResultsModal";
export { default as SignUp } from "./signup/SignUp";
export { default as Users } from "./users/Users";
export { default as ContainerCard } from "./common/ContainerCard";
export { default as Pagination } from "./common/Pagination";
export { default as ActivityContainer } from "./activities/ActivityContainer";
export { default as PictureConcept } from "./activities/reasoningIqEval/pictureConcept/PictureConcept";
export { default as Arithmetic } from "./activities/reasoningIqEval/arithmetic/Arithmetic";
export { default as ImageRow } from "./activities/reasoningIqEval/pictureConcept/ImageRow";
.login-component-email-container {
width: 60%;
margin-bottom: 10px;
}
.login-component-password-container {
width: 60%;
margin-bottom: 20px;
}
.login-component-login-btn {
width: fit-content;
}
.login-component-login-btn-text {
font-family: var(--font-family);
font-weight: 400;
font-size: 1em;
}
.login-component-error-text {
padding: 10px;
font-family: var(--font-family);
font-weight: 400;
font-size: 1em;
color: red;
}
import React, { useState, useEffect } from "react";
import "./Login.css";
import TextField from "@mui/material/TextField";
import Button from "@mui/material/Button";
import { teal } from "@mui/material/colors";
import ArrowForwardIosRoundedIcon from "@mui/icons-material/ArrowForwardIosRounded";
import BarLoader from "react-spinners/BarLoader";
import validator from "validator";
import { useNavigate } from "react-router-dom";
import axios from "axios";
const Login = ({ sectionNavigator }) => {
const navigate = useNavigate();
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [errorText, setErrorText] = useState("");
const [isValid, setIsValid] = useState(true);
const [isSubmit, setIsSubmit] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const [loginData, setLoginData] = useState(null);
useEffect(() => {
localStorage.removeItem("authToken");
localStorage.removeItem("accountType");
const loginSuccessHandler = () => {
if (loginData?.token && loginData?.userAccountType) {
localStorage.setItem("authToken", loginData.token);
localStorage.setItem("accountType", loginData.userAccountType);
}
if (loginData?.userStatus === false) {
sectionNavigator(2);
} else if (loginData?.userStatus === true) {
if (loginData.userAccountType === "ADMIN") {
navigate("/admin");
} else if (loginData.userAccountType === "STUDENT") {
navigate("/student");
} else {
navigate("/");
}
}
setIsLoading(false);
setIsSubmit(false);
};
if (loginData) {
loginSuccessHandler();
}
}, [loginData, navigate, sectionNavigator]);
const emailInputHandler = (inputText) => {
setIsValid(true);
setEmail(inputText);
if (!validator.isEmail(email)) {
setIsValid(false);
}
};
const passwordInputHandler = (inputText) => {
setPassword(inputText);
};
const loginHandler = async (e) => {
e.preventDefault();
if (isValid && email.length > 0 && password.length > 5) {
setIsLoading(true);
setIsSubmit(true);
await axios
.post("http://localhost:5000/api/auth/login", { email, password })
.then((res) => {
setLoginData(res.data);
})
.catch((error) => {
setErrorText(error.response.data.msg);
setEmail("");
setPassword("");
setIsValid(true);
setIsSubmit(false);
setIsLoading(false);
});
}
};
return (
<div>
<div className="login-component-email-container">
<TextField
variant="outlined"
label="E-mail"
id="field1"
fullWidth
size="small"
type="text"
InputLabelProps={{
style: { fontWeight: 700, fontSize: "1em", autoComplete: "false" },
}}
onChange={(e) => {
emailInputHandler(e.target.value);
}}
onBlur={() => {
if (email.length === 0) {
setIsValid(true);
}
}}
value={email}
/>
<div>{!isValid && <p>Email is not valid</p>}</div>
</div>
<div className="login-component-password-container">
<TextField
variant="outlined"
label="Password"
id="field2"
fullWidth
size="small"
type="password"
autoComplete="new-password"
InputLabelProps={{
style: { fontWeight: 700, fontSize: "1em", autoComplete: "false" },
}}
onChange={(e) => {
passwordInputHandler(e.target.value);
}}
value={password}
/>
<div>
{password && password.length < 6 && (
<p>Password should contain at least 6 characters</p>
)}
</div>
</div>
<div className="login-component-login-btn">
<Button
variant="contained"
size="medium"
style={{ backgroundColor: teal[500] }}
startIcon={<ArrowForwardIosRoundedIcon />}
onClick={loginHandler}
disabled={isSubmit}
>
<div className="login-component-login-btn-text">Login</div>
</Button>
{isLoading && <BarLoader width={"100%"} />}
</div>
{errorText && (
<div className="login-component-error-text">{errorText}</div>
)}
</div>
);
};
export default Login;
.profile-update-container-angry-grid {
display: grid;
grid-template-rows: 1fr 1fr 1fr 1fr;
grid-template-columns: 1fr 1fr 1fr 1fr;
gap: 0px;
height: fit-content;
padding-left: 10px;
padding-right: 10px;
}
#profile-update-container-item-0 {
grid-row-start: 1;
grid-column-start: 1;
grid-row-end: 2;
grid-column-end: 3;
height: 7vh;
}
#profile-update-container-item-1 {
grid-row-start: 1;
grid-column-start: 3;
grid-row-end: 2;
grid-column-end: 5;
}
#profile-update-container-item-2 {
grid-row-start: 2;
grid-column-start: 1;
grid-row-end: 3;
grid-column-end: 3;
}
#profile-update-container-item-3 {
grid-row-start: 2;
grid-column-start: 3;
grid-row-end: 3;
grid-column-end: 5;
}
#profile-update-container-item-4 {
grid-row-start: 3;
grid-column-start: 1;
grid-row-end: 4;
grid-column-end: 3;
}
#profile-update-container-item-5 {
grid-row-start: 3;
grid-column-start: 3;
grid-row-end: 4;
grid-column-end: 5;
}
#profile-update-container-item-6 {
grid-row-start: 4;
grid-column-start: 1;
grid-row-end: 5;
grid-column-end: 3;
}
#profile-update-container-item-7 {
grid-row-start: 4;
grid-column-start: 3;
grid-row-end: 5;
grid-column-end: 5;
}
.profile-update-container-input-field {
width: 80%;
}
.profile-update-container-input-field-error-text {
padding-top: 5px;
font-family: var(--font-family);
font-weight: 500;
font-size: 0.8em;
color: red;
}
.profile-update-container-submit-btn {
padding-left: 10px;
padding-top: 15px;
padding-bottom: 10px;
width: fit-content;
}
.profile-update-container-submit-btn-text {
font-family: var(--font-family);
font-weight: 400;
font-size: 1em;
}
.search-result-list-item {
font-family: var(--font-family);
font-weight: 500;
font-size: 0.9em;
border: solid black;
padding-left: 10px;
padding-right: 10px;
display: flex;
justify-content: space-between;
margin-top: 10px;
margin-bottom: 10px;
}
.search-result-list-item:hover {
background-color: rgb(218, 218, 218);
}
.search-result-list-item-column1 {
width: 10%;
}
.search-result-list-item-column2 {
width: 45%;
}
.search-result-list-item-column3 {
width: 45%;
}
.search-result-selected-user-details {
font-family: var(--font-family);
font-weight: 500;
font-size: 0.9em;
}
.search-result-selected-user-details-field {
margin-top: 10px;
margin-bottom: 10px;
}
import React, { useState } from "react";
import Box from "@mui/material/Box";
import Modal from "@mui/material/Modal";
import Button from "@mui/material/Button";
import { teal } from "@mui/material/colors";
import "./ResultsModal.css";
const style = {
position: "absolute",
top: "50%",
left: "50%",
transform: "translate(-50%, -50%)",
width: 800,
bgcolor: "background.paper",
border: "2px solid #000",
boxShadow: 24,
pt: 2,
px: 4,
pb: 3,
};
const ResultsModal = ({ users, resultsHandler }) => {
const [open, setOpen] = React.useState(false);
const [selectedUser, setSelectedUser] = useState(null);
const handleOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
const [openChild, setOpenChild] = useState(false);
const handleChildModalOpen = () => {
setOpenChild(true);
};
const handleChildModalClose = () => {
setSelectedUser(null);
setOpenChild(false);
};
return (
<div>
<Button
variant="contained"
size="medium"
style={{ backgroundColor: teal[500], marginTop: "10px" }}
onClick={handleOpen}
>
View Results
</Button>
<Modal
open={open}
onClose={() => {
resultsHandler();
handleClose();
}}
aria-labelledby="parent-modal-title"
aria-describedby="parent-modal-description"
>
<Box sx={{ ...style, width: 800 }}>
<h2 id="parent-modal-title">Search Results</h2>
<div>
{users?.map((value, index) => {
return (
<div
className="search-result-list-item"
key={index}
onClick={() => {
setSelectedUser(value);
handleChildModalOpen(true);
}}
>
<div className="search-result-list-item-column1">
ID: {value?.id}
</div>
<div className="search-result-list-item-column2">
Name: {value?.firstName} {value?.lastName}
</div>
<div className="search-result-list-item-column3">
Email: {value?.email}
</div>
</div>
);
})}
</div>
{/* <ChildModal userData={selectedUser}/> */}
{selectedUser && (
<Modal
hideBackdrop
open={openChild}
onClose={handleChildModalClose}
aria-labelledby="child-modal-title"
aria-describedby="child-modal-description"
>
<Box sx={{ ...style, width: 400 }}>
<h2 id="child-modal-title">User Details</h2>
<div className="search-result-selected-user-details">
<div className="search-result-selected-user-details-field">
ID: {selectedUser?.id}
</div>
<div className="search-result-selected-user-details-field">
Name: {selectedUser?.firstName} {selectedUser?.lastName}
</div>
<div className="search-result-selected-user-details-field">
Email: {selectedUser?.email}
</div>
<div className="search-result-selected-user-details-field">
Mobile: {selectedUser?.mobile}
</div>
<div className="search-result-selected-user-details-field">
Account Type: {selectedUser?.accountType}
</div>
<div className="search-result-selected-user-details-field">
Account Status: {selectedUser?.status?.toString()}
</div>
</div>
<Button
variant="contained"
size="medium"
style={{ backgroundColor: teal[500] }}
onClick={handleChildModalClose}
>
Close
</Button>
</Box>
</Modal>
)}
</Box>
</Modal>
</div>
);
};
export default ResultsModal;
.search-container {
border: solid black;
border-radius: 8px;
width: 40%;
/* padding: 5px 5px 5px 20px; */
font-family: var(--font-family);
font-weight: 500;
font-size: 1em;
}
import React, { useState } from "react";
import "./Search.css";
import TextField from "@mui/material/TextField";
import InputAdornment from "@mui/material/InputAdornment";
import ManageSearchRoundedIcon from "@mui/icons-material/ManageSearchRounded";
import { teal, red } from "@mui/material/colors";
import BarLoader from "react-spinners/BarLoader";
import { ResultsModal } from "../index";
import axios from "axios";
const Search = () => {
const [search, setSearch] = useState("");
const [isLoading, setIsLoading] = useState(false);
const [results, setResults] = useState(null);
const [isHover, setIsHover] = useState(false);
const searchHandler = async () => {
setIsLoading(true);
const config = {
headers: {
Authorization: `Bearer ${localStorage.getItem("authToken")}`,
},
};
await axios
.get(`http://localhost:5000/api/user/search?term=${search}`, config)
.then((res) => {
setResults(res.data.user);
setIsLoading(false);
})
.catch((err) => {
console.log(err);
setIsLoading(false);
});
};
const resultsHandler = () => {
setResults(null);
};
const hoverInHandler = () => {
setIsHover(true);
};
const hoverOutHandler = () => {
setIsHover(false);
};
return (
<>
<TextField
variant="outlined"
label="Search"
placeholder="Search by name, email or id.."
id="field1"
fullWidth
size="small"
type="text"
InputLabelProps={{
style: { fontWeight: 700, fontSize: "1em", autoComplete: "false" },
}}
InputProps={{
endAdornment: (
<InputAdornment position="end">
<ManageSearchRoundedIcon
onClick={searchHandler}
style={{
color: isHover ? red[500] : teal[500],
fontSize: "30px",
}}
onMouseEnter={hoverInHandler}
onMouseLeave={hoverOutHandler}
/>
</InputAdornment>
),
}}
onChange={(e) => {
setSearch(e.target.value);
}}
value={search}
/>
{isLoading && <BarLoader width={"100%"} />}
{!isLoading && results && (
<ResultsModal users={results} resultsHandler={resultsHandler} />
)}
</>
);
};
export default Search;
.signup-component-email-container {
width: 60%;
margin-bottom: 20px;
}
.signup-component-register-btn {
width: fit-content;
}
.signup-component-register-btn-text {
font-family: var(--font-family);
font-weight: 400;
font-size: 1em;
}
import React, { useState, useEffect } from "react";
import "./SignUp.css";
import TextField from "@mui/material/TextField";
import Button from "@mui/material/Button";
import { teal } from "@mui/material/colors";
import ArrowForwardIosRoundedIcon from "@mui/icons-material/ArrowForwardIosRounded";
import Modal from "@mui/material/Modal";
import Box from "@mui/material/Box";
import Typography from "@mui/material/Typography";
import BarLoader from "react-spinners/BarLoader";
import validator from "validator";
import axios from "axios";
const SignUp = ({ tabNavigator }) => {
const [email, setEmail] = useState("");
const [notification, setNotification] = useState("");
const [isValid, setIsValid] = useState(true);
const [isSubmit, setIsSubmit] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const [open, setOpen] = useState(false);
useEffect(() => {
const partialRegHandler = () => {
setOpen(true);
setIsLoading(false);
};
if (notification) {
partialRegHandler();
}
}, [notification]);
const emailInputHandler = (inputText) => {
setIsValid(true);
setEmail(inputText);
if (!validator.isEmail(email)) {
setIsValid(false);
}
};
const registrationHandler = async (e) => {
e.preventDefault();
if (isValid && email.length > 0) {
setIsLoading(true);
setIsSubmit(true);
await axios
.post("http://localhost:5000/api/auth/register", { email })
.then((res) => {
console.log(res);
setNotification(res.data.msg);
})
.catch((error) => {
console.log(error);
setIsLoading(false);
});
}
};
const modalStyles = {
position: "absolute",
top: "50%",
left: "50%",
transform: "translate(-50%, -50%)",
width: 400,
bgcolor: "background.paper",
border: "2px solid #000",
boxShadow: 24,
p: 4,
};
return (
<div>
<div className="signup-component-email-container">
<TextField
variant="outlined"
label="E-mail"
id="email"
fullWidth
size="small"
type="text"
InputLabelProps={{ style: { fontWeight: 700, fontSize: "1em" } }}
onChange={(e) => {
emailInputHandler(e.target.value);
}}
onBlur={() => {
if (email.length === 0) {
setIsValid(true);
}
}}
value={email}
/>
<div>{!isValid && <p>Email is not valid</p>}</div>
</div>
<div className="signup-component-register-btn">
<Button
variant="contained"
size="medium"
style={{ backgroundColor: teal[500] }}
startIcon={<ArrowForwardIosRoundedIcon />}
onClick={registrationHandler}
disabled={isSubmit}
>
<div className="signup-component-register-btn-text">Register</div>
</Button>
{isLoading && <BarLoader width={"100%"} />}
{!isLoading && notification && (
<Modal
open={open}
onClose={() => {
setOpen(false);
tabNavigator(2);
}}
aria-labelledby="modal-modal-title"
aria-describedby="modal-modal-description"
>
<Box sx={modalStyles}>
<Typography id="modal-modal-title" variant="h6" component="h2">
Important!
</Typography>
<Typography id="modal-modal-description" sx={{ mt: 2 }}>
{notification}
</Typography>
<Typography id="modal-modal-description" sx={{ mt: 2 }}>
A temporary password has been sent to the provided email. Please
use if for the first-time login.
</Typography>
</Box>
</Modal>
)}
</div>
</div>
);
};
export default SignUp;
.user-list-container {
padding: 20px;
}
.user-list-item-search {
margin-bottom: 30px;
width: 50%;
}
.user-list-container-heading {
color: teal;
font-family: var(--font-family);
font-weight: 500;
font-size: 2em;
}
.user-list-item-container {
padding-top: 20px;
padding-left: 10px;
}
.user-list-item-headings {
border-bottom: solid black;
padding-left: 10px;
padding-right: 10px;
display: flex;
justify-content: space-between;
width: 80%;
margin-bottom: 20px;
font-family: var(--font-family);
font-weight: 500;
font-size: 1.3em;
}
.user-list-item-container-boundary {
height: 15vh;
}
.user-list-item-boundary {
border-bottom: solid rgba(0, 0, 0, 0.247);
padding-left: 15px;
padding-right: 15px;
display: flex;
justify-content: space-between;
width: 80%;
margin-bottom: 10px;
font-family: var(--font-family);
font-weight: 300;
font-size: 1em;
}
.user-list-pagination {
margin-top: 30px;
width: 80%;
}
import React, { useState, useEffect } from "react";
import "./Users.css";
import { Pagination, Search } from "../index";
import Modal from "@mui/material/Modal";
import Box from "@mui/material/Box";
import Typography from "@mui/material/Typography";
import axios from "axios";
const Users = () => {
const [usersList, setUsersList] = useState([]);
const [isLoading, setIsLoading] = useState(false);
const [page, setPage] = useState(1);
const [pages, setPages] = useState(1);
const [selectedUser, setSelectedUser] = useState(null);
const [open, setOpen] = useState(false);
useEffect(() => {
const getUsers = async () => {
setIsLoading(true);
const config = {
headers: {
Authorization: `Bearer ${localStorage.getItem("authToken")}`,
},
};
await axios
.get(`http://localhost:5000/api/user/getAll?page=${page}`, config)
.then((res) => {
setPages(res.data.pages);
setUsersList(res.data.users);
setIsLoading(false);
})
.catch((error) => {
console.log(error);
});
};
if (
localStorage.getItem("authToken") &&
localStorage.getItem("accountType") === "ADMIN"
) {
getUsers();
}
}, [page]);
const modalStyles = {
position: "absolute",
top: "50%",
left: "50%",
transform: "translate(-50%, -50%)",
width: 400,
bgcolor: "background.paper",
border: "2px solid #000",
boxShadow: 24,
p: 4,
};
return (
<div className="user-list-container">
<div className="user-list-container-heading">User list</div>
<div className="user-list-item-container">
<div className="user-list-item-search">
<Search />
</div>
<div className="user-list-item-headings">
<div>User ID</div>
<div>Full Name</div>
<div>Account Type</div>
</div>
<div className="user-list-item-container-boundary">
{usersList.length > 0 &&
usersList.map((user, index) => {
return (
<div
className="user-list-item-boundary"
key={index}
onClick={() => {
setSelectedUser(user);
setOpen(true);
}}
>
<div>{user.id}</div>
<div>
{user.firstName
? `${user.firstName} ${user.lastName}`
: "NOT UPDATED YET"}
</div>
<div>{user.accountType}</div>
</div>
);
})}
</div>
<div className="user-list-pagination">
<Pagination page={page} pages={pages} changePage={setPage} />
</div>
</div>
{!isLoading && selectedUser && (
<Modal
open={open}
onClose={() => {
setOpen(false);
setSelectedUser(null);
}}
aria-labelledby="modal-modal-title"
aria-describedby="modal-modal-description"
>
<Box sx={modalStyles}>
<Typography id="modal-modal-title" variant="h6" component="h2">
User Information
</Typography>
<Typography id="modal-modal-description" sx={{ mt: 2 }}>
ID: {selectedUser?.id}
</Typography>
<Typography id="modal-modal-description" sx={{ mt: 2 }}>
Name:{" "}
{selectedUser?.firstName
? `${selectedUser?.firstName} ${selectedUser?.lastName}`
: "NOT UPDATED YET"}
</Typography>
<Typography id="modal-modal-description" sx={{ mt: 2 }}>
Email: {selectedUser?.email}
</Typography>
<Typography id="modal-modal-description" sx={{ mt: 2 }}>
Mobile:{" "}
{selectedUser?.mobile
? `${selectedUser.mobile}`
: "NOT UPDATED YET"}
</Typography>
<Typography id="modal-modal-description" sx={{ mt: 2 }}>
DOB:{" "}
{selectedUser?.dateOfBirth
? `${selectedUser?.dateOfBirth}`
: "NOT UPDATED YET"}
</Typography>
<Typography id="modal-modal-description" sx={{ mt: 2 }}>
Account Status: {selectedUser?.status.toString()}
</Typography>
<Typography id="modal-modal-description" sx={{ mt: 2 }}>
Account Type: {selectedUser?.accountType}
</Typography>
</Box>
</Modal>
)}
</div>
);
};
export default Users;
@import url("https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,200;0,400;0,500;0,600;0,700;0,800;1,300;1,700&display=swap");
/* added regular font weights - 200,400,500,600,700,800 */
/* added italic font weights - 300,700 */
:root {
--font-family: "Poppins", sans-serif;
/* ff 3.6+ */
background: -moz-radial-gradient(
circle at 5% 5%,
rgba(44, 83, 100, 1) 0%,
rgba(32, 58, 67, 1) 33%,
rgba(15, 32, 39, 1) 100%
);
/* safari 5.1+,chrome 10+ */
background: -webkit-radial-gradient(
circle at 5% 5%,
rgba(44, 83, 100, 1) 0%,
rgba(32, 58, 67, 1) 33%,
rgba(15, 32, 39, 1) 100%
);
/* opera 11.10+ */
background: -o-radial-gradient(
circle at 5% 5%,
rgba(44, 83, 100, 1) 0%,
rgba(32, 58, 67, 1) 33%,
rgba(15, 32, 39, 1) 100%
);
/* ie 10+ */
background: -ms-radial-gradient(
circle at 5% 5%,
rgba(44, 83, 100, 1) 0%,
rgba(32, 58, 67, 1) 33%,
rgba(15, 32, 39, 1) 100%
);
/* global 92%+ browsers support */
background: radial-gradient(
circle at 5% 5%,
rgba(44, 83, 100, 1) 0%,
rgba(32, 58, 67, 1) 33%,
rgba(15, 32, 39, 1) 100%
);
}
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
.admin-page-angry-grid {
display: grid;
grid-template-rows: 1fr 1fr 1fr 1fr;
grid-template-columns: 1fr 1fr 1fr 1fr 1fr;
gap: 0px;
height: 100%;
}
#admin-page-item-0 {
grid-row-start: 1;
grid-column-start: 1;
grid-row-end: 2;
grid-column-end: 6;
display: flex;
justify-content: space-between;
padding-left: 5vw;
padding-right: 5vw;
padding-top: 5vh;
font-family: var(--font-family);
font-weight: 500;
font-size: 2em;
color: white;
}
#admin-page-item-1 {
grid-row-start: 2;
grid-column-start: 2;
grid-row-end: 4;
grid-column-end: 5;
height: 50vh;
}
#admin-page-item-2 {
grid-row-start: 4;
grid-column-start: 1;
grid-row-end: 5;
grid-column-end: 6;
}
#admin-page-item-3 {
grid-row-start: 2;
grid-column-start: 5;
grid-row-end: 4;
grid-column-end: 6;
}
#admin-page-item-4 {
grid-row-start: 2;
grid-column-start: 1;
grid-row-end: 4;
grid-column-end: 2;
}
.admin-page-header-options:hover {
color: teal;
}
import React from "react";
import "./Admin.css";
import { ContainerCard, Users } from "../../components/index";
import { useNavigate } from "react-router-dom";
const Admin = () => {
const navigate = useNavigate();
const logoutHandler = () => {
localStorage.removeItem("authToken");
localStorage.removeItem("accountType");
navigate("/");
};
return (
<>
<div className="admin-page-angry-grid">
<div id="admin-page-item-0">
<div className="admin-page-header">ADMIN VIEW</div>
<div className="admin-page-header-options" onClick={logoutHandler}>
LOG OUT
</div>
</div>
<div id="admin-page-item-1">
<ContainerCard>
<Users />
</ContainerCard>
</div>
<div id="admin-page-item-2"></div>
<div id="admin-page-item-3"></div>
<div id="admin-page-item-4"></div>
</div>
</>
);
};
export default Admin;
.home-page-angry-grid {
display: grid;
grid-template-rows: 1fr 1fr 1fr 1fr;
grid-template-columns: 1fr 1fr 1fr 1fr;
gap: 0px;
height: 100%;
}
#home-page-item-0 {
grid-row-start: 1;
grid-column-start: 1;
grid-row-end: 2;
grid-column-end: 5;
}
#home-page-item-1 {
grid-row-start: 2;
grid-column-start: 2;
grid-row-end: 4;
grid-column-end: 4;
height: 50vh;
}
#home-page-item-2 {
grid-row-start: 4;
grid-column-start: 1;
grid-row-end: 5;
grid-column-end: 5;
}
#home-page-item-3 {
grid-row-start: 2;
grid-column-start: 4;
grid-row-end: 4;
grid-column-end: 5;
}
#home-page-item-4 {
grid-row-start: 2;
grid-column-start: 1;
grid-row-end: 4;
grid-column-end: 2;
}
.home-container-angry-grid {
display: grid;
grid-template-rows: 1fr 1fr 1fr 1fr;
grid-template-columns: 1fr 1fr 1fr 1fr;
gap: 0px;
height: 100%;
}
#home-container-item-0 {
grid-row-start: 1;
grid-column-start: 1;
grid-row-end: 2;
grid-column-end: 3;
}
#home-container-item-1 {
grid-row-start: 2;
grid-column-start: 1;
grid-row-end: 5;
grid-column-end: 5;
padding: 25px;
}
#home-container-item-2 {
grid-row-start: 1;
grid-column-start: 3;
grid-row-end: 2;
grid-column-end: 5;
}
.home-container-tab {
padding-left: 25px;
padding-top: 15px;
padding-bottom: 10px;
border-radius: 15px;
}
.home-container-tab:hover {
background-color: rgb(240, 240, 240);
}
.home-container-tab-selected {
background-color: rgb(218, 218, 218);
}
.home-container-tab-heading {
font-family: var(--font-family);
font-weight: 600;
font-size: 2em;
}
.home-container-back-btn {
padding-left: 25px;
padding-top: 15px;
padding-bottom: 10px;
width: fit-content;
}
.home-container-back-btn-text {
font-family: var(--font-family);
font-weight: 400;
font-size: 1em;
}
.home-container-update-profile-text {
margin-left: 20px;
margin-bottom: 10px;
color: teal;
font-family: var(--font-family);
font-weight: 500;
font-size: 1em;
}
import React, { useState } from "react";
import {
ContainerCard,
Login,
SignUp,
ProfileUpdate,
} from "../../components/index";
import "./Home.css";
import Button from "@mui/material/Button";
import { teal } from "@mui/material/colors";
import ArrowBackIosRoundedIcon from "@mui/icons-material/ArrowBackIosRounded";
const Home = () => {
const [currentSection, setCurrentSection] = useState(1); //set to 1
const [currentTab, setCurrentTab] = useState(1); //set to 1
// tab 1 - signup
// tab 2 - login
// both in section 1
const currentTabHandler = (tabNo) => {
if (currentSection === 1) {
if (tabNo === 1 || tabNo === 2) {
setCurrentTab(tabNo);
}
}
};
// section 1 - login & signup
// section 2 - profile update & password reset
// section 3 - account type selection
const currentSectionHandler = (sectionNo) => {
if (currentTab === 2) {
if (sectionNo === 1 || sectionNo === 2 || sectionNo === 3) {
setCurrentSection(sectionNo);
}
}
};
return (
<>
<div className="home-page-angry-grid">
<div id="home-page-item-0"></div>
<div id="home-page-item-1">
{currentSection === 1 && (
<ContainerCard>
<div className="home-container-angry-grid">
<div id="home-container-item-0">
<div
className={`${
currentTab === 1 && "home-container-tab-selected"
} home-container-tab`}
onClick={() => {
currentTabHandler(1);
}}
>
<div className="home-container-tab-heading"> SignUp</div>
</div>
</div>
<div id="home-container-item-1">
{currentTab === 1 && (
<div>
<SignUp tabNavigator={currentTabHandler} />
</div>
)}
{currentTab === 2 && (
<div>
<Login sectionNavigator={currentSectionHandler} />
</div>
)}
</div>
<div id="home-container-item-2">
<div
className={`${
currentTab === 2 && "home-container-tab-selected"
} home-container-tab`}
onClick={() => {
currentTabHandler(2);
}}
>
<div className="home-container-tab-heading"> Login</div>
</div>
</div>
</div>
</ContainerCard>
)}
{currentSection === 2 && (
<ContainerCard>
<div
className="home-container-back-btn"
onClick={() => {
currentSectionHandler(1);
}}
>
<Button
variant="contained"
size="medium"
style={{ backgroundColor: teal[500] }}
endIcon={<ArrowBackIosRoundedIcon />}
>
<div className="home-container-back-btn-text">Back</div>
</Button>
</div>
<p className="home-container-update-profile-text">
Please fill following fields to complete your registration
</p>
<ProfileUpdate sectionNavigator={currentSectionHandler} />
</ContainerCard>
)}
</div>
<div id="home-page-item-2"></div>
<div id="home-page-item-3"></div>
<div id="home-page-item-4"></div>
</div>
</>
);
};
export default Home;
export { default as Admin } from "./admin/Admin";
export { default as Home } from "./home/Home";
export { default as Student } from "./student/Student";
.student-page-angry-grid {
display: grid;
grid-template-rows: 1fr 1fr 1fr 1fr 1fr;
grid-template-columns: 1fr 1fr 1fr 1fr 1fr;
gap: 0px;
height: 100%;
}
.student-page-header-options:hover {
color: teal;
}
#student-page-item-0 {
grid-row-start: 1;
grid-column-start: 1;
grid-row-end: 2;
grid-column-end: 6;
display: flex;
justify-content: space-between;
padding-left: 5vw;
padding-right: 5vw;
padding-top: 5vh;
font-family: var(--font-family);
font-weight: 500;
font-size: 2em;
color: white;
}
#student-page-item-1 {
grid-row-start: 5;
grid-column-start: 1;
grid-row-end: 6;
grid-column-end: 6;
}
#student-page-item-2 {
grid-row-start: 2;
grid-column-start: 2;
grid-row-end: 5;
grid-column-end: 5;
height: 60vh;
margin-top: 10px;
}
#item-3 {
grid-row-start: 2;
grid-column-start: 1;
grid-row-end: 5;
grid-column-end: 2;
}
#student-page-item-4 {
grid-row-start: 2;
grid-column-start: 5;
grid-row-end: 5;
grid-column-end: 6;
}
import React from "react";
import "./Student.css";
import { ActivityContainer, ContainerCard } from "../../components/index";
import { useNavigate } from "react-router-dom";
const Student = () => {
const navigate = useNavigate();
const logoutHandler = () => {
localStorage.removeItem("authToken");
localStorage.removeItem("accountType");
navigate("/");
};
return (
<>
<div className="student-page-angry-grid">
<div id="student-page-item-0">
<div className="student-page-header">ICAAT</div>
<div className="student-page-header-options" onClick={logoutHandler}>
LOG OUT
</div>
</div>
<div id="student-page-item-1"></div>
<div id="student-page-item-2">
<ContainerCard>
<ActivityContainer />
</ContainerCard>
</div>
<div id="student-page-item-3"></div>
<div id="student-page-item-4"></div>
</div>
</>
);
};
export default Student;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment