New terminal at highest level
Npm create vite@latest
Install package
Project name : frontend
React framework
Javascript +swc
Cd frontend
Npm install
npm run dev
This will load the default site
Then install the dependencies we will use
npm install @emotion/react @emotion/styled @hookform/resolvers @mui/icons-material @mui/material axios react react-dom react-hook-form react-router-dom yup
npm install -D @types/react @types/react-dom @vitejs/plugin-react-swc eslint eslint-plugin-react eslint-plugin-react-hooks eslint-plugin-react-refresh vite
You can add tailwind css if you like
Clean up index.css in src so it looks like this
:root {
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
}
html, body{
height: 100%;
margin: 0;
padding: 0;
}
Main.jsx should look like this
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.jsx'
import './index.css'
import {BrowserRouter as Router} from 'react-router-dom'
ReactDOM.createRoot(document.getElementById('root')).render(
)
App.jsx should look like this
import { useState } from 'react'
import './App.css'
import Home from './components/Home'
import Register from './components/Register'
import Login from './components/Login'
import About from './components/About'
import Navbar from './components/Navbar'
import {Routes, Route, useLocation} from 'react-router-dom'
import ProtectedRoute from './components/ProtectedRoutes'
import PasswordResetRequest from './components/PasswordResetRequest'
import PasswordReset from './components/PasswordReset'
function App() {
const location = useLocation()
const noNavbar = location.pathname === "/register" || location.pathname === "/" || location.pathname.includes("password")
return (
<>
{
noNavbar ?
}/>
}/>
}/>
}/>
:
}>
}/>
}/>
}
/>
}
>
)
}
export default App
If your not using tailwind at the moment app.css could look like this
.myBackground{
width:100vw;
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background: rgb(205,0,118);
background: linear-gradient(137deg, rgba(205,0,118,1) 0%, rgba(85,35,185,1) 68%);
}
.whiteBox{
background-color: white;
padding: 20px;
min-width: 300px;
height: 70%;
width: 25%;
border: 1px solid #333;
}
.itemBox{
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 80px;
}
.title{
font-size: 30px;
color: #333;
}
.myForm,
.myButton{
width: 100%;
}
.myButton{
background: rgb(66, 8, 160) !important;
}
Also make sure your .eslintrc.cjs looks like this
module.exports = {
root: true,
env: { browser: true, es2020: true },
extends: [
'eslint:recommended',
'plugin:react/recommended',
'plugin:react/jsx-runtime',
'plugin:react-hooks/recommended',
],
ignorePatterns: ['dist', '.eslintrc.cjs'],
parserOptions: { ecmaVersion: 'latest', sourceType: 'module' },
settings: { react: { version: '18.2' } },
plugins: ['react-refresh'],
rules: {
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
}
Then in your src folder create a new folder components
We are going to create our pages first
About.jsx
const About = () =>{
return(
This is the about page
)
}
export default About
Home.jsx
import AxiosInstance from './AxiosInstance'
import {React, useEffect, useMemo, useState} from 'react'
import {Box} from '@mui/material'
const Home = () =>{
const [myData, setMyData] = useState()
const [loading,setLoading] = useState(true)
const GetData = () => {
AxiosInstance.get(`users/`).then((res) =>{
setMyData(res.data)
console.log(res.data)
setLoading(false)
})
}
useEffect(() =>{
GetData();
},[])
return(
{ loading ? Loading data... :
{myData.map((item, index) => (
ID: {item.id}
Email: {item.email}
)
)}
}
)
}
export default Home
Login.jsx
import '../App.css'
import {React, useState} from 'react'
import { Box } from '@mui/material'
import MyTextField from './forms/MyTextField'
import MyPassField from './forms/MyPassField'
import MyButton from './forms/MyButton'
import {Link} from 'react-router-dom'
import {useForm} from 'react-hook-form'
import AxiosInstance from './AxiosInstance'
import { useNavigate } from 'react-router-dom'
import MyMessage from './Message'
const Login = () =>{
const navigate = useNavigate()
const {handleSubmit, control} = useForm()
const [ShowMessage, setShowMessage] = useState(false)
const submission = (data) => {
AxiosInstance.post(`login/`,{
email: data.email,
password: data.password,
})
.then((response) => {
console.log(response)
localStorage.setItem('Token', response.data.token)
navigate(`/home`)
})
.catch((error) => {
setShowMessage(true)
console.error('Error during login', error)
})
}
return(
{ShowMessage ? : null}
Login for Auth App
No account yet? Please register!
Password forgotten? Click here
)
}
export default Login
Register.jsx
import '../App.css'
import { Box } from '@mui/material'
import MyTextField from './forms/MyTextField'
import MyPassField from './forms/MyPassField'
import MyButton from './forms/MyButton'
import {Link} from 'react-router-dom'
import {useForm} from 'react-hook-form'
import AxiosInstance from './AxiosInstance'
import { useNavigate } from 'react-router-dom'
import {yupResolver} from "@hookform/resolvers/yup"
import * as yup from "yup"
const Register = () =>{
const navigate = useNavigate()
const schema = yup
.object({
email: yup.string().email('Field expects an email adress').required('Email is a required field'),
password: yup.string()
.required('Password is a required field')
.min(8,'Password must be at least 8 characters')
.matches(/[A-Z]/,'Password must contain at least one uppercase letter')
.matches(/[a-z]/,'Password must contain at least one lower case letter')
.matches(/[0-9]/,'Password must contain at least one number')
.matches(/[!@#$%^&*(),.?":;{}|<>+]/, 'Password must contain at least one special character'),
password2: yup.string().required('Password confirmation is a required field')
.oneOf([yup.ref('password'),null], 'Passwords must match')
})
const {handleSubmit, control} = useForm({resolver: yupResolver(schema)})
const submission = (data) => {
AxiosInstance.post(`register/`,{
email: data.email,
password: data.password,
})
.then(() => {
navigate(`/`)
}
)
}
return(
User registration
Already registered? Please login!
)
}
export default Register
Now were going to make our page components
Message.jsx
import { Box } from "@mui/material"
const MyMessage = ({text, color}) =>{
return(
{text}
)
}
export default MyMessage
Navbar.jsx
import * as React from 'react';
import Box from '@mui/material/Box';
import Drawer from '@mui/material/Drawer';
import AppBar from '@mui/material/AppBar';
import CssBaseline from '@mui/material/CssBaseline';
import Toolbar from '@mui/material/Toolbar';
import List from '@mui/material/List';
import Typography from '@mui/material/Typography';
import ListItem from '@mui/material/ListItem';
import ListItemButton from '@mui/material/ListItemButton';
import ListItemIcon from '@mui/material/ListItemIcon';
import ListItemText from '@mui/material/ListItemText';
import InboxIcon from '@mui/icons-material/MoveToInbox';
import HomeIcon from '@mui/icons-material/Home';
import InfoIcon from '@mui/icons-material/Info';
import {Link, useLocation} from 'react-router-dom'
import LogoutIcon from '@mui/icons-material/Logout';
import AxiosInstance from './AxiosInstance';
import { useNavigate } from 'react-router-dom';
const drawerWidth = 240;
export default function Navbar(props) {
const {content} = props
const location = useLocation()
const path = location.pathname
const navigate = useNavigate()
const logoutUser = () =>{
AxiosInstance.post(`logoutall/`,{
})
.then( () => {
localStorage.removeItem("Token")
navigate('/')
}
)
}
return (
theme.zIndex.drawer + 1 }}>
Clipped drawer
{content}
);
}
Most importantly is our axios instance that connects our frontend and backend
import axios from 'axios'
const baseUrl = 'http://127.0.0.1:8000/'
const AxiosInstance = axios.create({
baseURL: baseUrl,
timeout: 5000,
headers:{
"Content-Type":"application/json",
accept: "application/json"
}
})
AxiosInstance.interceptors.request.use(
(config) => {
const token = localStorage.getItem('Token')
if(token){
config.headers.Authorization = `Token ${token}`
}
else{
config.headers.Authorization = ``
}
return config;
}
)
AxiosInstance.interceptors.response.use(
(response) => {
return response
},
(error) => {
if(error.response && error.response.status === 401){
localStorage.removeItem('Token')
}
}
)
export default AxiosInstance;
Then our PasswordReset page
import '../App.css'
import {React, useState} from 'react'
import { Box } from '@mui/material'
import MyTextField from './forms/MyTextField'
import MyPassField from './forms/MyPassField'
import MyButton from './forms/MyButton'
import {useParams } from 'react-router-dom'
import {useForm} from 'react-hook-form'
import AxiosInstance from './AxiosInstance'
import { useNavigate } from 'react-router-dom'
import MyMessage from './Message'
const PasswordReset = () =>{
const navigate = useNavigate()
const {handleSubmit, control} = useForm()
const {token} = useParams()
console.log(token)
const [ShowMessage, setShowMessage] = useState(false)
const submission = (data) => {
AxiosInstance.post(`api/password_reset/confirm/`,{
password: data.password,
token: token,
})
.then((response) => {
setShowMessage(true)
setTimeout(() =>{
navigate('/')
}, 6000 )
})
}
return(
{ShowMessage ? : null}
Reset password
)
}
export default PasswordReset
Password reset request
import '../App.css'
import {React, useState} from 'react'
import { Box } from '@mui/material'
import MyTextField from './forms/MyTextField'
import MyPassField from './forms/MyPassField'
import MyButton from './forms/MyButton'
import {Link} from 'react-router-dom'
import {useForm} from 'react-hook-form'
import AxiosInstance from './AxiosInstance'
import { useNavigate } from 'react-router-dom'
import MyMessage from './Message'
const PasswordResetRequest = () =>{
const navigate = useNavigate()
const {handleSubmit, control} = useForm()
const [ShowMessage, setShowMessage] = useState(false)
const submission = (data) => {
AxiosInstance.post(`api/password_reset/`,{
email: data.email,
})
.then((response) => {
setShowMessage(true)
})
}
return(
{ShowMessage ? : null}
Request password reset
)
}
export default PasswordResetRequest
Protected routes
import {Outlet, Navigate} from 'react-router-dom'
const ProtectedRoute = () => {
const token = localStorage.getItem('Token')
return(
token ? :
)
}
export default ProtectedRoute
In the components folder create a new folder called forms
Create a file called MyButton.jsx
import * as React from 'react';
import Button from '@mui/material/Button';
export default function MyButton(props) {
const {label,type} = props
return (
{label}
MyPassField.jsx
import * as React from 'react';
import IconButton from '@mui/material/IconButton';
import OutlinedInput from '@mui/material/OutlinedInput';
import InputLabel from '@mui/material/InputLabel';
import InputAdornment from '@mui/material/InputAdornment';
import FormControl from '@mui/material/FormControl';
import Visibility from '@mui/icons-material/Visibility';
import VisibilityOff from '@mui/icons-material/VisibilityOff';
import { FormHelperText } from '@mui/material';
import {Controller} from 'react-hook-form'
export default function MyPassField(props) {
const [showPassword, setShowPassword] = React.useState(false);
const {label,name, control} = props
const handleClickShowPassword = () => setShowPassword((show) => !show);
const handleMouseDownPassword = (event) => {
event.preventDefault();
};
return (
(
{label}
{showPassword ? : }
}
label={label}
/>
{error?.message}
)
}
/>
);
}
MyTextField.jsx
import * as React from 'react';
import '../../App.css'
import TextField from '@mui/material/TextField';
import {Controller} from 'react-hook-form'
export default function MyTextField(props) {
const {label, name, control} = props
return (
(
)
}
/>
);
}
And there you have it django react log in
To run it use python manage.py runserver on the backend and rpm run dev on frontend
);
}
MyPassField.jsx
import * as React from 'react';
import IconButton from '@mui/material/IconButton';
import OutlinedInput from '@mui/material/OutlinedInput';
import InputLabel from '@mui/material/InputLabel';
import InputAdornment from '@mui/material/InputAdornment';
import FormControl from '@mui/material/FormControl';
import Visibility from '@mui/icons-material/Visibility';
import VisibilityOff from '@mui/icons-material/VisibilityOff';
import { FormHelperText } from '@mui/material';
import {Controller} from 'react-hook-form'
export default function MyPassField(props) {
const [showPassword, setShowPassword] = React.useState(false);
const {label,name, control} = props
const handleClickShowPassword = () => setShowPassword((show) => !show);
const handleMouseDownPassword = (event) => {
event.preventDefault();
};
return (
name = {name}
control = {control}
render = {({
field:{onChange, value},
fieldState : {error},
formState,
}) =>(
{label}
{showPassword ? : }
}
label={label}
/>
{error?.message}
)
}
/>
);
}
MyTextField.jsx
import * as React from 'react';
import '../../App.css'
import TextField from '@mui/material/TextField';
import {Controller} from 'react-hook-form'
export default function MyTextField(props) {
const {label, name, control} = props
return (
(
)
}
/>
);
}
And there you have it django react log in
To run it use python manage.py runserver on the backend and rpm run dev on frontend