Added login functionality

This commit is contained in:
Keannu Bernasol 2023-11-19 16:10:36 +08:00
parent 2aeb1ddd27
commit 508e353fdb
9 changed files with 252 additions and 10 deletions

91
package-lock.json generated
View file

@ -15,6 +15,7 @@
"@mui/styled-engine-sc": "^6.0.0-alpha.5",
"@reduxjs/toolkit": "^1.9.7",
"@tanstack/react-query": "^5.8.3",
"axios": "^1.6.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-redux": "^8.1.3",
@ -1895,6 +1896,21 @@
"node": ">=8"
}
},
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
},
"node_modules/axios": {
"version": "1.6.2",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz",
"integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==",
"dependencies": {
"follow-redirects": "^1.15.0",
"form-data": "^4.0.0",
"proxy-from-env": "^1.1.0"
}
},
"node_modules/babel-plugin-macros": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz",
@ -2039,6 +2055,17 @@
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
},
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"dependencies": {
"delayed-stream": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@ -2126,6 +2153,14 @@
"integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
"dev": true
},
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/dir-glob": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
@ -2607,6 +2642,38 @@
"integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==",
"dev": true
},
"node_modules/follow-redirects": {
"version": "1.15.3",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz",
"integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/RubenVerborgh"
}
],
"engines": {
"node": ">=4.0"
},
"peerDependenciesMeta": {
"debug": {
"optional": true
}
}
},
"node_modules/form-data": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
@ -3016,6 +3083,25 @@
"node": ">=8.6"
}
},
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"dependencies": {
"mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
@ -3268,6 +3354,11 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
},
"node_modules/punycode": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz",

View file

@ -17,6 +17,7 @@
"@mui/styled-engine-sc": "^6.0.0-alpha.5",
"@reduxjs/toolkit": "^1.9.7",
"@tanstack/react-query": "^5.8.3",
"axios": "^1.6.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-redux": "^8.1.3",

View file

@ -0,0 +1,67 @@
import axios from "axios";
import { ActivationType, LoginType, RegisterType } from "../Types/Types";
// Product APIs
const instance = axios.create({
baseURL: "http://localhost:8000/",
});
// User APIs
export function RegisterAPI(register: RegisterType) {
return instance
.post("api/v1/accounts/users/", register)
.then(async (response) => {
console.log(response.data);
return true;
})
.catch(() => {
console.log("Registration failed");
return false;
});
}
export function LoginAPI(user: LoginType) {
return instance
.post("api/v1/accounts/jwt/create/", user)
.then(async (response) => {
localStorage.setItem("token", JSON.stringify(response.data.auth_token));
console.log("Login Success ");
return true;
})
.catch(() => {
console.log("Login Failed");
return false;
});
}
export function UserAPI() {
const token = JSON.parse(localStorage.getItem("token") || "{}");
return instance
.get("api/v1/accounts/users/me/", {
headers: {
Authorization: "Token " + token,
},
})
.then((response) => {
console.log(response.data);
return response.data;
})
.catch(() => {
console.log("Error retrieving user data");
return false;
});
}
export function ActivationAPI(activation: ActivationType) {
return instance
.post("api/v1/accounts/users/activation/", activation)
.then(async () => {
console.log("Activation Success");
return true;
})
.catch(() => {
console.log("Activation failed");
return false;
});
}

View file

@ -1,15 +1,28 @@
import { useState } from "react";
import { useEffect, useState } from "react";
import styles, { colors } from "../../styles";
import MenuIcon from "@mui/icons-material/Menu";
import SidebarModal from "../SidebarModal/SidebarModal";
import { Drawer } from "@mui/material";
import { useSelector } from "react-redux";
import { RootState } from "../Plugins/Redux/Store/Store";
import { useNavigate } from "react-router-dom";
export interface props {
label: React.ReactNode;
label: string;
}
export default function Header(props: props) {
const [SidebarOpen, SetSidebarOpen] = useState(false);
const authenticated = useSelector((state: RootState) => state.auth.value);
const navigate = useNavigate();
useEffect(() => {
if (!authenticated) {
navigate("/");
console.log("Not logged in. Redirecting to landing page");
}
}, [authenticated, navigate]);
return (
<div
style={{

View file

@ -10,14 +10,19 @@ import LoginIcon from "@mui/icons-material/Login";
import Checkbox from "@mui/material/Checkbox";
import Button from "../Buttons/Button";
import { useNavigate } from "react-router-dom";
import { LoginAPI } from "../API/API";
import { useDispatch } from "react-redux";
import { Toggle_Login } from "../Plugins/Redux/Slices/AuthSlice/AuthSlice";
export default function LoginModal() {
const navigate = useNavigate();
const [showPassword, setShowPassword] = useState(false);
const [remember, setRemember] = useState(true);
const [error, setError] = useState("");
const [user, setUser] = useState({
username: "",
password: "",
});
const dispatch = useDispatch();
return (
<>
<div
@ -41,9 +46,10 @@ export default function LoginModal() {
id="outlined-helperText"
label="Username"
style={styles.input_form}
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
setUser({ ...user, username: e.target.value })
}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setUser({ ...user, username: e.target.value });
setError("");
}}
value={user.username}
placeholder={"Enter username"}
/>
@ -56,7 +62,10 @@ export default function LoginModal() {
<InputAdornment position="end">
<IconButton
aria-label="toggle password visibility"
onClick={() => setShowPassword(!showPassword)}
onClick={() => {
setShowPassword(!showPassword);
setError("");
}}
edge="end"
>
{showPassword ? <VisibilityOff /> : <Visibility />}
@ -112,11 +121,18 @@ export default function LoginModal() {
marginBottom: 8,
}}
/>
<p style={{ ...styles.text_dark, ...styles.text_S }}>{error}</p>
<Button
type={"dark"}
label={"Login"}
onClick={() => {
navigate("/dashboard");
onClick={async () => {
const status = await LoginAPI(user);
if (status === true) {
await dispatch(Toggle_Login());
navigate("/dashboard");
} else {
setError("Invalid login");
}
}}
/>
</>

View file

@ -0,0 +1,19 @@
/* eslint-disable react-refresh/only-export-components */
import { createSlice } from "@reduxjs/toolkit";
export const AuthSlice = createSlice({
name: "Auth",
initialState: {
value: false,
},
reducers: {
Toggle_Login: (state) => {
state.value = !state.value;
},
},
});
// Action creators are generated for each case reducer function
export const { Toggle_Login } = AuthSlice.actions;
export default AuthSlice.reducer;

View file

@ -1,7 +1,10 @@
import { configureStore } from "@reduxjs/toolkit";
import AuthReducer from "../Slices/AuthSlice/AuthSlice";
const store = configureStore({
reducer: {},
reducer: {
auth: AuthReducer,
},
});
export default store;

View file

@ -0,0 +1,20 @@
export type RegisterType = {
email: string;
username: string;
password: string;
};
export type LoginType = {
username: string;
password: string;
};
export type ActivationType = {
uid: string;
token: string;
};
export type AddEquipmentType = {
name: string;
remarks: string;
};

View file

@ -3,12 +3,24 @@ import styles from "../../styles";
import citc_logo from "../../assets/citc_logo.jpg";
import Popup from "reactjs-popup";
import "reactjs-popup/dist/index.css";
import { useState } from "react";
import { useEffect, useState } from "react";
import LoginModal from "../../Components/LoginModal/LoginModal";
import RegisterModal from "../../Components/RegisterModal/RegisterModal";
import { useSelector } from "react-redux";
import { useNavigate } from "react-router-dom";
import { RootState } from "../../Components/Plugins/Redux/Store/Store";
export default function LandingPage() {
const [LoginModalOpen, SetLoginModalOpen] = useState(false);
const [RegisterModalOpen, SetRegisterModalOpen] = useState(false);
const authenticated = useSelector((state: RootState) => state.auth.value);
const navigate = useNavigate();
useEffect(() => {
if (authenticated) {
navigate("/dashboard");
console.log("Already logged in. Redirecting to dashboard page");
}
}, [authenticated, navigate]);
return (
<div style={styles.background}>
<div