login and profile page implemented
This commit is contained in:
parent
9af9818f9a
commit
e50b99641c
2
.env
2
.env
@ -1 +1 @@
|
||||
VITE_API_URL=http://127.0.0.1:8000
|
||||
VITE_API_URL=https://gallery.steve-dekart.xyz
|
||||
@ -1,18 +1,18 @@
|
||||
import {
|
||||
type LoginPayload,
|
||||
type LoginResponse,
|
||||
type MeResponse,
|
||||
type User,
|
||||
} from "../components/shared/types/AuthTypes";
|
||||
import axiosInstance from "./axiosInstance";
|
||||
|
||||
export const loginRequest = async (data: LoginPayload) => {
|
||||
const res = await axiosInstance.post<LoginResponse>("/api/auth/login", data);
|
||||
const res = await axiosInstance.post<LoginResponse>("/api/auth/login/", data);
|
||||
return res.data;
|
||||
};
|
||||
|
||||
export const fetchMe = async () => {
|
||||
const res = await axiosInstance.get<MeResponse>("/api/auth/me");
|
||||
return res.data.user;
|
||||
const res = await axiosInstance.get<User>("/api/auth/me/");
|
||||
return res.data;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@ -10,7 +10,7 @@ const axiosInstance = axios.create({
|
||||
axiosInstance.interceptors.request.use(
|
||||
(config) => {
|
||||
const token = localStorage.getItem("access_token");
|
||||
if (token) config.headers.Authorization = `Bearer ${token}`;
|
||||
if (token) config.headers.Authorization = `Token ${token}`;
|
||||
|
||||
return config;
|
||||
},
|
||||
|
||||
@ -11,10 +11,13 @@ import MenuItem from "@mui/material/MenuItem";
|
||||
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { Container } from "@mui/material";
|
||||
import CameraIcon from '@mui/icons-material/Camera';
|
||||
import CameraIcon from "@mui/icons-material/Camera";
|
||||
import appColors from "../../utils/colors";
|
||||
import { useAuth } from "../../context/AuthContext";
|
||||
|
||||
function Header() {
|
||||
const navigate = useNavigate();
|
||||
const { user } = useAuth();
|
||||
|
||||
const [anchorElNav, setAnchorElNav] = React.useState<null | HTMLElement>(
|
||||
null
|
||||
@ -31,7 +34,7 @@ function Header() {
|
||||
const goToAdmin = () => navigate("/admin");
|
||||
|
||||
return (
|
||||
<AppBar sx={{ backgroundColor: "#008080" }} position="static">
|
||||
<AppBar sx={{ backgroundColor: appColors.primary }} position="static">
|
||||
<Container>
|
||||
<Toolbar disableGutters>
|
||||
<CameraIcon sx={{ display: { xs: "none", md: "flex" }, mr: 1 }} />
|
||||
@ -74,7 +77,7 @@ function Header() {
|
||||
onClose={handleCloseNavMenu}
|
||||
sx={{ display: { xs: "block", md: "none" } }}
|
||||
>
|
||||
|
||||
{!user && (
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
handleCloseNavMenu();
|
||||
@ -83,9 +86,10 @@ function Header() {
|
||||
>
|
||||
<Typography textAlign="center">Login</Typography>
|
||||
</MenuItem>
|
||||
)}
|
||||
|
||||
|
||||
<span>
|
||||
<span>
|
||||
{user && (
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
handleCloseNavMenu();
|
||||
@ -94,20 +98,19 @@ function Header() {
|
||||
>
|
||||
<Typography textAlign="center">Profile</Typography>
|
||||
</MenuItem>
|
||||
)}
|
||||
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
handleCloseNavMenu();
|
||||
goToAdmin();
|
||||
}}
|
||||
>
|
||||
<Typography textAlign="center">
|
||||
Admin Dashboard
|
||||
</Typography>
|
||||
</MenuItem>
|
||||
|
||||
</span>
|
||||
|
||||
{user && user.is_superuser && (
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
handleCloseNavMenu();
|
||||
goToAdmin();
|
||||
}}
|
||||
>
|
||||
<Typography textAlign="center">Admin Dashboard</Typography>
|
||||
</MenuItem>
|
||||
)}
|
||||
</span>
|
||||
</Menu>
|
||||
</Box>
|
||||
|
||||
@ -132,33 +135,32 @@ function Header() {
|
||||
</Typography>
|
||||
|
||||
<Box sx={{ flexGrow: 1, display: { xs: "none", md: "flex" } }}>
|
||||
|
||||
{!user && (
|
||||
<Button
|
||||
onClick={() => navigate("/login")}
|
||||
sx={{ my: 2, color: "white", display: "block" }}
|
||||
>
|
||||
Login
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{user && (
|
||||
<Button
|
||||
onClick={() => navigate("/profile")}
|
||||
sx={{ my: 2, color: "white", display: "block" }}
|
||||
>
|
||||
Profile
|
||||
</Button>
|
||||
)}
|
||||
|
||||
|
||||
<>
|
||||
<Button
|
||||
onClick={() => navigate("/profile")}
|
||||
sx={{ my: 2, color: "white", display: "block" }}
|
||||
>
|
||||
Profile
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
onClick={goToAdmin}
|
||||
sx={{ my: 2, color: "white", display: "block" }}
|
||||
>
|
||||
Admin Dashboard
|
||||
</Button>
|
||||
|
||||
</>
|
||||
|
||||
{user?.is_superuser && (
|
||||
<Button
|
||||
onClick={goToAdmin}
|
||||
sx={{ my: 2, color: "white", display: "block" }}
|
||||
>
|
||||
Admin Dashboard
|
||||
</Button>
|
||||
)}
|
||||
</Box>
|
||||
</Toolbar>
|
||||
</Container>
|
||||
|
||||
@ -18,7 +18,7 @@ export interface LoginResponse {
|
||||
}
|
||||
|
||||
export interface LoginPayload {
|
||||
email: string;
|
||||
username: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
|
||||
65
src/pages/LoginPage/LoginPage.tsx
Normal file
65
src/pages/LoginPage/LoginPage.tsx
Normal file
@ -0,0 +1,65 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { useAuth } from "../../context/AuthContext";
|
||||
|
||||
import { Box, Button, Container, TextField, Typography } from "@mui/material";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import appColors from "../../utils/colors";
|
||||
|
||||
|
||||
const LoginPage = () => {
|
||||
const { login, user } = useAuth();
|
||||
const [username, setUsername] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
if (user) {
|
||||
navigate("/");
|
||||
}
|
||||
}, [user, navigate]);
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
await login({ username, password });
|
||||
};
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Box sx={{ padding: 4, maxWidth: 360, margin: "auto" }}>
|
||||
<Typography variant="h4" mb={3} textAlign="center">
|
||||
Login
|
||||
</Typography>
|
||||
<form onSubmit={handleSubmit}>
|
||||
<TextField
|
||||
label="Username"
|
||||
type="text"
|
||||
value={username}
|
||||
onChange={(e) => setUsername(e.target.value)}
|
||||
fullWidth
|
||||
margin="normal"
|
||||
required
|
||||
/>
|
||||
<TextField
|
||||
label="Password"
|
||||
type="password"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
fullWidth
|
||||
margin="normal"
|
||||
required
|
||||
/>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="contained"
|
||||
fullWidth
|
||||
sx={{ mt: "10px", backgroundColor: appColors.primary }}
|
||||
>
|
||||
Login
|
||||
</Button>
|
||||
</form>
|
||||
</Box>
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
||||
export default LoginPage;
|
||||
57
src/pages/ProfilePage/ProfilePage.tsx
Normal file
57
src/pages/ProfilePage/ProfilePage.tsx
Normal file
@ -0,0 +1,57 @@
|
||||
import { Box, Button, Container, Typography } from "@mui/material";
|
||||
import { useAuth } from "../../context/AuthContext";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import appColors from "../../utils/colors";
|
||||
import { useEffect } from "react";
|
||||
import { toast } from "react-toastify";
|
||||
|
||||
const ProfilePage = () => {
|
||||
const { user, logout, isLoading } = useAuth();
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
if (!isLoading && !user) {
|
||||
toast.error("You must be logged in to view your profile");
|
||||
navigate("/login");
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Box sx={{ padding: 4, maxWidth: 360 }}>
|
||||
<Typography variant="h4" mb={2}>
|
||||
Profile
|
||||
</Typography>
|
||||
<Typography variant="body1" mb={2}>
|
||||
Username: {user?.username}
|
||||
</Typography>
|
||||
<Typography variant="body1" mb={2}>
|
||||
E-mail: {user?.email}
|
||||
</Typography>
|
||||
<Typography variant="body1" mb={2}>
|
||||
Index: {user?.school_index}
|
||||
</Typography>
|
||||
<Typography variant="body1" mb={2}>
|
||||
Status: {user?.is_superuser ? "Admin" : "User"}
|
||||
</Typography>
|
||||
<Button
|
||||
sx={{ backgroundColor: appColors.primary }}
|
||||
variant="contained"
|
||||
onClick={logout}
|
||||
>
|
||||
Logout
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
sx={{ marginLeft: "10px", backgroundColor: appColors.secondary }}
|
||||
variant="contained"
|
||||
onClick={() => navigate("/admin")}
|
||||
>
|
||||
Admin Dashboard
|
||||
</Button>
|
||||
</Box>
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
||||
export default ProfilePage;
|
||||
@ -1,6 +1,8 @@
|
||||
import { createBrowserRouter } from "react-router-dom";
|
||||
import MainLayout from "../MainLayout/MainLayout";
|
||||
import { Container } from "@mui/material";
|
||||
import LoginPage from "../pages/LoginPage/LoginPage";
|
||||
import ProfilePage from "../pages/ProfilePage/ProfilePage";
|
||||
|
||||
const router = createBrowserRouter([
|
||||
{
|
||||
@ -10,6 +12,14 @@ const router = createBrowserRouter([
|
||||
{
|
||||
index: true,
|
||||
element: <Container>index page</Container>
|
||||
},
|
||||
{
|
||||
path: "/login",
|
||||
element: <LoginPage/>
|
||||
},
|
||||
{
|
||||
path: "/profile",
|
||||
element: <ProfilePage/>
|
||||
}
|
||||
]}
|
||||
])
|
||||
|
||||
8
src/utils/colors.ts
Normal file
8
src/utils/colors.ts
Normal file
@ -0,0 +1,8 @@
|
||||
export const appColors = {
|
||||
secondary: '#B3CDE0',
|
||||
bg: '#FFFFFF',
|
||||
primary: '#011F4B',
|
||||
tertiary: '#EDF2FA',
|
||||
};
|
||||
|
||||
export default appColors;
|
||||
Loading…
x
Reference in New Issue
Block a user