diff --git a/.eslintrc b/.eslintrc index a2ceebe..d227306 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,3 +1,7 @@ { - "extends": ["next/babel", "next/core-web-vitals"] -} + "extends": ["next", "next/core-web-vitals"], + "rules": { + // Other rules + "@next/next/no-img-element": "off" + } +} \ No newline at end of file diff --git a/components/AddTask.js b/components/AddTask.js index 9652adb..8e2ce00 100644 --- a/components/AddTask.js +++ b/components/AddTask.js @@ -1,17 +1,61 @@ -export default function AddTask() { - const addTask = () => { +import { useState } from 'react' +import { useAuth } from '../context/auth' +import 'react-toastify/dist/ReactToastify.css'; +import { ToastContainer, toast } from 'react-toastify'; +import axios from '../utils/axios' + + + +export default function AddTask({getTasks}) { + const [Task, setTask] = useState('') + const {token} =useAuth() + const registerFieldsAreValid = (Task ) => { + if ( Task === '' ) { + toast.error('Title cannot be empty',{position: 'bottom-right'}) + return false + } + return true + } + const addTask = (e) => { /** * @todo Complete this function. * @todo 1. Send the request to add the task to the backend server. * @todo 2. Add the task in the dom. */ - } + e.preventDefault() + if ( + registerFieldsAreValid(Task) + ) { + const dataForApiRequest = { + title: Task + } + + axios.post( + 'todo/create/',dataForApiRequest,{ + headers: { + Authorization: 'Token ' + token, + },} + + ) + .then(function () { + toast.success('Task created successfully',{position: 'bottom-right'}) + getTasks() + setTask('') + }) + .catch(function (err) { + toast.error('Error!!',{position: 'bottom-right'}) + }) + } + } + return (
setTask(e.target.value)} + value={Task} /> +
) } diff --git a/components/LoginForm.js b/components/LoginForm.js index fa28f9e..55d82f3 100644 --- a/components/LoginForm.js +++ b/components/LoginForm.js @@ -1,15 +1,73 @@ +import React, { useState } from 'react' +import axios from '../utils/axios' +import 'react-toastify/dist/ReactToastify.css'; +import { ToastContainer, toast } from 'react-toastify'; +import { useAuth } from '../context/auth' +import { useRouter } from 'next/router' + + export default function RegisterForm() { - const login = () => { + /*** * @todo Complete this function. * @todo 1. Write code for form validation. * @todo 2. Fetch the auth token from backend and login the user. * @todo 3. Set the token in the context (See context/auth.js) */ - } + const { setToken ,setProfileName} = useAuth() + const router = useRouter() + + const [password, setPassword] = useState('') + const [username, setUsername] = useState('') + + const registerFieldsAreValid = ( + username, + password + ) => { + if ( + username === '' || + password === '' + ) { + toast.error('Please fill all the fields',{position: 'bottom-right'}) + return false + } + return true + } + + const login = (e) => { + e.preventDefault() + + if ( + registerFieldsAreValid(username, password) + ) { + console.log('Please wait...') + + const dataForApiRequest = { + username: username, + password: password, + } + + axios.post( + 'auth/login/', + dataForApiRequest, + ) + .then(function ({ data, status }) { + toast.success('Login Success',{position: 'bottom-right'}) + setToken(data.token) + router.push('/') + }) + .catch(function (err) { + console.log( + toast.error('Invalid username or password',{position: 'bottom-right'}) + ) + }) + } + } + return ( -
+
+

Login

@@ -19,6 +77,8 @@ export default function RegisterForm() { name='inputUsername' id='inputUsername' placeholder='Username' + onChange={(e) => setUsername(e.target.value)} + value={username} /> setPassword(e.target.value)} />
-
+
) + } ) diff --git a/components/RegisterForm.js b/components/RegisterForm.js index a6ef2e3..6a91452 100644 --- a/components/RegisterForm.js +++ b/components/RegisterForm.js @@ -1,5 +1,7 @@ import React, { useState } from 'react' import axios from '../utils/axios' +import 'react-toastify/dist/ReactToastify.css'; +import { ToastContainer, toast } from 'react-toastify'; import { useAuth } from '../context/auth' import { useRouter } from 'next/router' @@ -27,11 +29,11 @@ export default function Register() { username === '' || password === '' ) { - console.log('Please fill all the fields correctly.') + toast.error('Please fill all the fields',{position: 'bottom-right'}) return false } if (!/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/.test(email)) { - console.log('Please enter a valid email address.') + toast.error('Enter a valid email address',{position: 'bottom-right'}) return false } return true @@ -57,19 +59,19 @@ export default function Register() { dataForApiRequest, ) .then(function ({ data, status }) { + toast.success('Registration Success',{position: 'bottom-right'}) setToken(data.token) router.push('/') }) .catch(function (err) { - console.log( - 'An account using same email or username is already created' - ) + toast.error('An account using same email or username is already created',{position: 'bottom-right'}) }) } } return (
+

Register

diff --git a/components/TodoListItem.js b/components/TodoListItem.js index 7965f3b..f16192b 100644 --- a/components/TodoListItem.js +++ b/components/TodoListItem.js @@ -1,11 +1,28 @@ /* eslint-disable @next/next/no-img-element */ +import { useState } from 'react' +import { useAuth } from '../context/auth' +import 'react-toastify/dist/ReactToastify.css'; +import { ToastContainer, toast } from 'react-toastify'; +import axios from '../utils/axios' -export default function TodoListItem() { +export default function TodoListItem({todo,getTasks}) { + const {token}=useAuth() + const [hideid,sethideid]=useState('') + const [updatetask,setupdatetask] = useState('') + const registerFieldsAreValid = (Task ) => { + if ( Task === '' ) { + toast.error('Title cannot be empty',{position: 'bottom-right'}) + return false + } + return true + } const editTask = (id) => { /** * @todo Complete this function. * @todo 1. Update the dom accordingly */ + sethideid(id) + setupdatetask('') } const deleteTask = (id) => { @@ -14,42 +31,74 @@ export default function TodoListItem() { * @todo 1. Send the request to delete the task to the backend server. * @todo 2. Remove the task from the dom. */ - } + axios + .delete(`todo/${id}/`, { + headers: { + Authorization: 'Token ' + token, + }, + }) + .then(()=>{toast.success("Task deleted successfully",{position: 'bottom-right'}); + getTasks() + }) + .catch(err => console.error(err)); + } const updateTask = (id) => { /** * @todo Complete this function. * @todo 1. Send the request to update the task to the backend server. * @todo 2. Update the task in the dom. - */ + */ + if (registerFieldsAreValid(updatetask)) { + console.log('Please wait...') + + const dataForApiRequest = { + title: updatetask, + } + axios + .put( + `todo/${id}/`,dataForApiRequest,{ + headers: { + 'Authorization': 'Token ' + token, + 'Content-Type': "application/json", + }, + }) + .then((response)=> {toast.success("Task Edited successfully!",{position: 'bottom-right'});sethideid('');getTasks()}) + .catch(err => console.error(err)) + } } - return ( <> -
  • + + { !todo.length && (
    No Task Found !!
    )} + { + todo.map((task)=> + (
  • setupdatetask(e.target.value)} /> -
    +
    -
    - Sample Task 1 +
    + {task.title}
    - +
  • + )) + } + ) } + diff --git a/context/auth.js b/context/auth.js index 646b10e..36d7ef4 100644 --- a/context/auth.js +++ b/context/auth.js @@ -2,23 +2,25 @@ import { useEffect, useState, useContext, createContext } from 'react' import { useCookies } from 'react-cookie' import axios from '../utils/axios' import { useRouter } from 'next/router' +import {Auth} from '../middlewares/auth_required' const AuthContext = createContext({}) export const AuthProvider = ({ children }) => { const router = useRouter() + const [profileName, setProfileName] = useState('') const [avatarImage, setAvatarImage] = useState('#') - const [cookies, setCookies, removeCookies] = useCookies(['auth']) + const [cookies, setCookies, removeCookies] = useCookies(['token']) const token = cookies.token - const setToken = (newToken) => setCookies('token', newToken, { path: '/' }) + const setToken = (newToken) => { setCookies('token', newToken, { path: '/' });} const deleteToken = () => removeCookies('token') const logout = () => { deleteToken() router.push('/login') } - + useEffect(() => { if (token) { axios @@ -38,10 +40,12 @@ export const AuthProvider = ({ children }) => { .catch((error) => { console.log('Some error occurred') }) + } - }, [setAvatarImage, setProfileName, token]) +}, [setAvatarImage, setProfileName,token]) return ( + { + if(!token) + return true + return false +} \ No newline at end of file diff --git a/middlewares/no_auth_required.js b/middlewares/no_auth_required.js index 82558d4..ea95af3 100644 --- a/middlewares/no_auth_required.js +++ b/middlewares/no_auth_required.js @@ -1,3 +1,8 @@ /*** * @todo Redirect the user to main page if token is present. - */ \ No newline at end of file + */ + export const noAuth=(token)=>{ + if(token) + return true + return false +} \ No newline at end of file diff --git a/package.json b/package.json index 98b7437..cfc0a22 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "react": "17.0.2", "react-cookie": "^4.0.3", "react-dom": "17.0.2", + "react-toastify": "^9.0.5", "tailwindcss": "^2.2.2" }, "devDependencies": { diff --git a/pages/index.js b/pages/index.js index c3c9134..491db95 100644 --- a/pages/index.js +++ b/pages/index.js @@ -3,28 +3,68 @@ import AddTask from '../components/AddTask' import { useEffect, useState } from 'react' import axios from '../utils/axios' import { useAuth } from '../context/auth' +import { useRouter } from 'next/router' +import {Auth} from '../middlewares/auth_required' export default function Home() { + const router=useRouter() const { token } = useAuth() const [tasks, setTasks] = useState([]) - + const [Search,setSearch] = useState('') + useEffect(() => { + if(Auth(token)) + {router.push('/login');return;} + getTasks() + }, [Search]) function getTasks() { /*** * @todo Fetch the tasks created by the user. * @todo Set the tasks state and display them in the using TodoListItem component * The user token can be accessed from the context using useAuth() from /context/auth.js */ + axios + .get('todo/', { + headers: { + Authorization: 'Token ' + token, + }, + }) + .then((response) => { + const todos=[] + let data=response.data; + data.forEach(todo => { + if((todo.title).search(Search) != -1) + todos.push(todo); + }) + setTasks(todos); + }) + .catch((error) => { + console.log('Some error occurred') + }) } - + return (
    - + + {setSearch(e.target.value);}} + value={Search} + /> +
      Available Tasks - +
    diff --git a/pages/login.js b/pages/login.js index 5f2bbe0..eb7d8c2 100644 --- a/pages/login.js +++ b/pages/login.js @@ -1,6 +1,16 @@ import LoginForm from '../components/LoginForm' +import { useEffect } from 'react' +import { useAuth } from '../context/auth' +import { useRouter } from 'next/router' +import {noAuth} from '../middlewares/no_auth_required' export default function Login() { + const {token} = useAuth() + const router= useRouter() + useEffect(() =>{ + if(noAuth(token)) + {router.push('/')} + },[]) return (
    diff --git a/pages/register.js b/pages/register.js index 456003a..6018a65 100644 --- a/pages/register.js +++ b/pages/register.js @@ -1,6 +1,16 @@ import RegisterForm from '../components/RegisterForm' +import { useEffect } from 'react' +import { useAuth } from '../context/auth' +import { useRouter } from 'next/router' +import {noAuth} from '../middlewares/no_auth_required' export default function Register() { + const {token} = useAuth() + const router= useRouter() + useEffect(() =>{ + if(noAuth(token)) + {router.push('/')} + },[]) return (
    diff --git a/yarn.lock b/yarn.lock index 11dbcb7..92f8067 100644 --- a/yarn.lock +++ b/yarn.lock @@ -720,6 +720,11 @@ classnames@2.2.6: resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce" integrity sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q== +clsx@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.1.1.tgz#98b3134f9abbdf23b2663491ace13c5c03a73188" + integrity sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA== + color-convert@^1.9.0, color-convert@^1.9.1: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" @@ -2803,6 +2808,13 @@ react-refresh@0.8.3: resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.8.3.tgz#721d4657672d400c5e3c75d063c4a85fb2d5d68f" integrity sha512-X8jZHc7nCMjaCqoU+V2I0cOhNW+QMBwSUkeXnTi8IPe6zaRWfn60ZzvFDZqWPfmSJfjub7dDW1SP0jaHWLu/hg== +react-toastify@^9.0.5: + version "9.0.5" + resolved "https://registry.yarnpkg.com/react-toastify/-/react-toastify-9.0.5.tgz#310d7bcfcf3887c0e8461ac6068fe1abb8d720e1" + integrity sha512-dszPCeQINY+Nm6HmsiAXT/7wsazPqv0S/RuhIYLAW+fTKcd3T1iRjZG0XqrN9nvAzqaE5J6uxMaiBrOevxjY8g== + dependencies: + clsx "^1.1.1" + react@17.0.2: version "17.0.2" resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037"