I am unable to toggle my completed todo on the backend as well as on the frontend, If once completed set to true is not returning back to false
import { useContext, useEffect, useState } from "react";
import AuthContext from "../context/AuthContext";
const TodoApp = () => {
// const [clicked , setClicked] = useState(false)
const {
authTokens,
fetchTasks,
tasks,
user,
createTask,
updateTask,
deleteTask,
setButtonClicked,
setTasks,
buttonClicked,
updateComplete,
} = useContext(AuthContext);
// console.log('authTokens in TodoApp:', authTokens);
useEffect(() => {
console.log("buttonClicked:", buttonClicked);
if (user) {
fetchTasks();
}
// console.log("rendered");
// console.log(fetchTasks());
}, [user]);
const handleButtonClick = async (id) => {
await updateComplete(id);
};
return (
<>
<form
onSubmit={(e) => {
e.preventDefault();
createTask(e.target.taskTitle.value);
e.target.taskTitle.value = "";
}}
className="bg-white md:w-[60vw] w-[90vw] md:h-24 h-16 md:p-4 p-2 flex gap-2 shadow-lg shadow-indigo-500/50 "
>
<input
type="text"
name="taskTitle"
placeholder="Username"
className="input input-bordered w-[72%] md:h-16 h-11 text-black flex-[3_1_0] "
/>
<button
className=" btn btn-success md:h-16 h-6 w-[25%] text-3xl"
type="submit"
>
+
</button>
</form>
<br />
<div className="h-[calc(100vh-200px)] w-[100vw] mt-2 overflow-y-auto overflow-x-hidden">
{user && user ? (
tasks.map((ele) => (
<div key={ele.id} className=" h-24 w-[100vw] p-1">
<form
onSubmit={(e) => e.preventDefault()}
className=" m-auto border h-16 w-[90vw] md:w-[70vw] md:text-2xl rounded p-3 font-serif relative overflow-hidden"
>
{ele.completed ? (
<strike className="w-[50vw] text-black" key={ele.id}>
{ele.title}
</strike>
) : (
<div className="w-[50vw] text-black" key={ele.id}>
{ele.title}
</div>
)}
<div className="text-xl w-22 absolute right-2 top-2">
<button
className={`fa-regular fa-circle-check border rounded-s-2xl p-2 md:text-2xl ${
ele.completed ? "bg-green-500" : ""
}`}
type="button" // Use type="button" instead of "submit"
onClick={() => handleButtonClick(ele.id)}
></button>
<button
className="fa-solid fa-pen-to-square border p-2 md:text-2xl"
type="submit"
onClick={() => {
// Prompt the user to enter the new title
const newTitle = prompt(
"Enter the updated title:",
ele.title
);
if (newTitle) {
// Only update if the user provides a new title
updateTask(ele.id, { title: newTitle });
}
}}
></button>
<button
className="fa-solid fa-trash border rounded-e-2xl p-2 md:text-2xl"
type="submit"
onClick={() => deleteTask(ele.id)}
></button>
</div>
</form>
</div>
))
) : (
<div>Add Todo</div>
)}
</div>
</>
);
};
export default TodoApp;
My AuthContext.jsx
import {createContext , useState , useEffect} from 'react';
import jwt_decode from 'jwt-decode';
import { useNavigate } from 'react-router-dom'
import jwtDecode from 'jwt-decode';
import Axios from 'axios';
const AuthContext = createContext();
export default AuthContext;
// eslint-disable-next-line react/prop-types
export const AuthProvider =({children}) => {
const [tasks, setTasks] = useState([]);
const [errors, setErrors] = useState();
const [buttonClicked, setButtonClicked] = useState(false);
const [authTokens , setAuthTokens] = useState(() => localStorage.getItem('authTokens') ? JSON.parse(localStorage.getItem('authTokens')) : {})
const [user , setUser] = useState(() => localStorage.getItem('authTokens') ? jwt_decode(localStorage.getItem('authTokens')) : null)
const [loading , setLoading] = useState(false);
let navigate = useNavigate();
let loginUser = async (e) => {
e.preventDefault()
let response = await fetch('http://127.0.0.1:8000/api/token/', {
method: 'POST',
headers:{
'Content-Type' : 'application/json'
},
body:JSON.stringify({'username': e.target.username.value, 'password' : e.target.password.value })
})
let data = await response.json()
if (response.status === 200) {
setAuthTokens(data)
setUser(jwt_decode(data.access))
localStorage.setItem('authTokens', JSON.stringify(data))
navigate('/')
}else{
alert('something went wrong!')
}
}
let logoutUser = () => {
setAuthTokens(null)
setUser(null)
localStorage.removeItem('authTokens')
navigate('/login')
}
let updateToken = async () =>{
console.log("Update Token updated");
let response = await fetch('http://127.0.0.1:8000/api/token/refresh/', {
method: 'POST',
headers:{
'Content-Type' : 'application/json'
},
body:JSON.stringify({'refresh': authTokens.refresh})
})
let data = await response.json()
if (response.status === 200){
setAuthTokens(data)
setUser(jwt_decode(data.access))
localStorage.setItem('authTokens', JSON.stringify(data))
}else{
logoutUser();
}
}
const registerUser = async (formData) => {
try {
console.log('Registration request data:', formData);
const response = await fetch('http://127.0.0.1:8000/api/register/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(formData),
});
const data = await response.json();
if (data && response.ok) {
// Registration successful
// Perform any necessary actions, e.g., redirect the user to a success page or show a success message
console.log('Registration successful!');
navigate('/'); // You can redirect to a success page
} else {
// Registration failed due to client-side validation error or server error
// You can handle the error, e.g., show an error message
console.log('Registration failed due to error');
console.log('Error response data:', data.error); // Log the full error response
setErrors(data.error)
// Check if the response contains a detailed error message
if (data && data.error) {
console.log(data);
setErrors(data)
} else {
console.log(data);
setErrors(data)
// alert('Registration failed. Something went wrong!');
}
}
} catch (error) {
console.error('Error occurred during registration:', error);
setErrors('Something went wrong!');
}
};
// State management for TODO App
const fetchTasks = async () => {
// console.log(`Bearer ${authTokens.access}`);
try {
const response = await Axios.get('http://127.0.0.1:8000/todo/task-list/' , {
headers: {
Authorization: `Bearer ${authTokens.access}`,
// Authorization: `Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNjkwNTU2Mjg3LCJpYXQiOjE2OTA1NTU5ODcsImp0aSI6ImQ0NzFiMzRmNzIwZjRjYzViOTIzNGE3YzVhZWQ3ZTQyIiwidXNlcl9pZCI6MSwidXNlcm5hbWUiOiJ6dWxxYXIifQ.pUPPTNxNanTB0qw0ZDOROQ2CrgDRkh5nT6-5oeWaCc8`,
}
});
setTasks(response.data);
// return response.data
} catch (error) {
console.error('Error fetching tasks:', error);
}
};
const createTask = async (title) => {
try {
const response = await Axios.post('http://127.0.0.1:8000/todo/task-create/', {
title: title,
completed: false,
buttonClicked: false,
},
{
headers: {
Authorization: `Bearer ${authTokens.access}`,
}
}
)
setTasks([...tasks, response.data]);
} catch (error) {
console.error('Error creating task:', error);
}
};
const updateTask = async (id, updatedTask) => {
try {
await Axios.post(`http://127.0.0.1:8000/todo/task-update/${id}/`,
updatedTask,
{
headers: {
Authorization: `Bearer ${authTokens.access}`,
}
});
const updatedTasks = tasks.map((task) =>
task.id === id ? { ...task, ...updatedTask } : task
);
setTasks(updatedTasks);
} catch (error) {
console.error('Error updating task:', error);
}
};
const updateComplete = async (id) => {
try {
const taskToUpdate = tasks.find((task) => task.id === id);
const updatedTask = {
...taskToUpdate,
completed: !taskToUpdate.buttonClicked, // Toggle the completed property based on the buttonClicked state
};
await Axios.post(
`http://127.0.0.1:8000/todo/task-update/${id}/`,
updatedTask,
{
headers: {
Authorization: `Bearer ${authTokens.access}`,
},
}
);
// Update the local state of tasks with the updated buttonClicked value
const updatedTasks = tasks.map((task) => (task.id === id ? updatedTask : task));
setTasks(updatedTasks);
} catch (error) {
console.error('Error updating task:', error);
// Handle the error, e.g., show an error message to the user.
}
};
// Since the task is updated on the backend, you don't need to do anything here.
const deleteTask = async (id) => {
try {
await Axios.delete(`http://127.0.0.1:8000/todo/task-delete/${id}/`);
const updatedTasks = tasks.filter((task) => task.id !== id);
setTasks(updatedTasks);
} catch (error) {
console.error('Error deleting task:', error);
}
};
let contextData = {
user : user,
loginUser : loginUser,
logoutUser : logoutUser,
updateToken : updateToken,
registerUser : registerUser,
tasks : tasks,
setTasks : setTasks,
fetchTasks : fetchTasks,
createTask : createTask,
updateTask : updateTask,
updateComplete : updateComplete,
deleteTask : deleteTask,
errors : errors,
setButtonClicked : setButtonClicked,
buttonClicked : buttonClicked,
authTokens :authTokens,
}
useEffect(() => {
// console.log('authTokens:', authTokens);
if (authTokens) {
setUser(jwtDecode(authTokens.access))
}
setLoading(false);
}, [authTokens , loading])
return(
<AuthContext.Provider value={contextData}>
{loading ?<div>Loading...</div> : children}
</AuthContext.Provider>
)
}
My Views.py
from rest_framework.response import Response
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated
from .serializers import TaskSerializer
from .models import Task
# Create your views here.
@api_view(['GET'])
def ApiOverView(request):
api_urls = {
'List' : '/task-list/',
'DetailView' : '/detail-view/',
'Create' : '/task-create/',
'Update' : '/task-update/',
'Delete' : '/task-delete/'
}
return Response(api_urls)
@api_view(['GET'])
def TaskList(request):
user = request.user
print('user get: ' + str(request.user))
tasks = Task.objects.filter(user = user)
serializer = TaskSerializer(tasks, many = True)
return Response(serializer.data)
@api_view(['GET'])
def DetailView(request , pk):
tasks = Task.objects.get(id = pk)
serializer = TaskSerializer(tasks, many = False)
return Response(serializer.data)
@api_view(['POST'])
def TaskCreate(request):
data = request.data
data['user'] = request.user.id
serializer = TaskSerializer(data = data)
try:
if serializer.is_valid(raise_exception=True):
serializer.save()
# print('Post Created ' + serializer.data)
except Exception as e:
print(e)
return Response(serializer.data , status=201)
@api_view(['POST'])
def TaskUpdate(request , pk):
data = request.data
data['user'] = request.user.id
task = Task.objects.get(id = pk)
task.completed = data['completed']
task.save()
serializer = TaskSerializer(instance=task ,data=request.data)
try:
if serializer.is_valid(raise_exception=True):
serializer.save()
print('Post updated ' + serializer.data)
except Exception as e:
print(e)
# return Response({'message': 'Post updated', 'data': data}, status=202)
return Response(serializer.data , status=202)
@api_view(['DELETE'])
def TaskDelete(request , pk):
print("id " , pk)
data = request.data
data['user'] = request.user.id
task = Task.objects.get(id = pk)
task.delete()
return Response('Item deleted')
Please give the solution
I am expecting that once the user click on the handleButtonClick the completed field will toggle both in the Backend and the Frontend