mirror of
https://github.com/lemeow125/EquipmentTracker-Frontend.git
synced 2024-11-17 06:09:25 +08:00
Added login functionality
This commit is contained in:
parent
2aeb1ddd27
commit
508e353fdb
9 changed files with 252 additions and 10 deletions
91
package-lock.json
generated
91
package-lock.json
generated
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
67
src/Components/API/API.tsx
Normal file
67
src/Components/API/API.tsx
Normal 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;
|
||||
});
|
||||
}
|
|
@ -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={{
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
|
|
19
src/Components/Plugins/Redux/Slices/AuthSlice/AuthSlice.tsx
Normal file
19
src/Components/Plugins/Redux/Slices/AuthSlice/AuthSlice.tsx
Normal 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;
|
|
@ -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;
|
||||
|
|
20
src/Components/Types/Types.tsx
Normal file
20
src/Components/Types/Types.tsx
Normal 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;
|
||||
};
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue