3. Create Frontend

Frontend configuration

First delete all the CSS and JSX preconfigurations from React. We gonna make our own.

Install Tailwind in React:

  1. npm install -D tailwindcss postcss autoprefixer

  2. npx tailwindcss init -p

  3. Go to tailwind.config.js and add the paths:

    /** @type {import('tailwindcss').Config} */
    export default {
      content: ["./index.html", "./src/**/*.{js,jsx,ts,tsx}",],
      theme: {
        extend: {},
      },
      plugins: [],
    }
    
  4. Add tailwindcss styles to index.css:

@import 'tailwindcss/base';
@import 'tailwindcss/components';
@import 'tailwindcss/utilities';

Important! -> Restart now the server to see changes!


Perfect now let’s try something to see if its work:

import { useState } from 'react'
import './App.css'

function App() {

  return (
    <div className='bg-indigo-100 py-8 min-h-screen'>
      <nav className='pt-8'>
        <h1 className='text-5xl text-center'>To Do Liste</h1>
      </nav>
    </div>

  )
}

export default App

You should be able to see something in the browser after running npm run dev in the terminal!


Create the Table

  1. Make in .src folder a new folder named components.

  2. Create a file named table.jsx in the components folder.

  3. Take a code snippet called rafce to create a boilerplate and input following code:

Let’s break down the provided React code step-by-step, discussing each relevant part along with its corresponding code:

Import Statements

import axios from 'axios'
import React, { useState } from 'react'
import { MdOutlineDeleteOutline, MdEditNote, MdOutlineCheckBox, MdOutlineCheckBoxOutlineBlank } from 'react-icons/md'
  • Here, various modules and dependencies are imported. axios is used for making HTTP requests, React and the useState hook are imported from the React library for managing component state, and specific icons are imported from react-icons/md for UI elements.

Table Component Declaration

const Table = ({ todos, isLoading, setTodos }) => {
  • This is the declaration of the Table functional component. It receives todos, isLoading, and setTodos as props. todos is the list of todo items, isLoading is a boolean indicating if data is being fetched, and setTodos is a function to update the list of todos.

useState Hook for Edit Text

const [editText, setEditText] = useState({
  'body': ''
})
  • The useState hook initializes editText state to store the current edit value. The state is an object with a body property, initially an empty string.

handleDelete Function

const handleDelete = async (id) => {
  try {
    await axios.delete(`http://127.0.0.1:8000/api/todo/${id}/`)
    const newList = todos.filter(todo => todo.id !== id)
    setTodos(newList)
  } catch (error) {
    console.log(error);
  }
}
  • handleDelete is an asynchronous function that makes a DELETE request to a specified endpoint to delete a todo item. After a successful deletion, it updates the todos state by filtering out the deleted item.

handleEdit Function

const handleEdit = async (id, value) => {
  try {
    const response = await axios.patch(`http://127.0.0.1:8000/api/todo/${id}/`, value)
    console.log(response.data);
    const newTodos = todos.map(todo => todo.id === id ? response.data : todo)
    setTodos(newTodos)
  } catch (error) {
    console.log(error);
  }
}
  • handleEdit is an asynchronous function used to send a PATCH request to update a todo item. It updates the todos state with the new data returned from the API.

handleChange Function

const handleChange = (e) => {
  console.log(e.target.value);
  setEditText(prev => ({
    ...prev,
    'body': e.target.value
  }))
  console.log(editText);
}
  • This function handles changes in the edit text input. It updates the editText state with the new value.

handleClick and handleCheckbox Functions

const handleClick = () => {
  handleEdit(editText.id, editText)
  setEditText({
    'body': ""
  })
}

const handleCheckbox = (id, value) => {
  console.log(value.completed);
  handleEdit(id, {
    'completed': !value
  })
}
  • handleClick calls handleEdit with the current editText and then resets editText. handleCheckbox toggles the completed status of a todo item and calls handleEdit.

JSX for Rendering the Table

```javascript
return (
    <div>
      <table className='w-11/12 max-w-4xl'>
        <thead className='border-b-2 border-black'>
          <tr>
            <th className='p-3 text-sm font-semibold tracking-wide text-left'>Checkbox</th>
            <th className='p-3 text-sm font-semibold tracking-wide text-left'>To Do</th>
            <th className='p-3 text-sm font-semibold tracking-wide text-left'>Status</th>
            <th className='p-3 text-sm font-semibold tracking-wide text-left'>Date Created</th>
            <th className='p-3 text-sm font-semibold tracking-wide text-left'>Actions</th>
          </tr>
        </thead>
        <tbody>
          {isLoading ? <div>Is Loading </div> :
            <> {todos.map((todoItem, index) =>
            (
              <tr key={todoItem.id} className='border-b border-black'>
                <td className='p-3'>
                  <span onClick={() => handleCheckbox(todoItem.id, todoItem.completed)}
                    className='inline-block cursor-pointer'>{todoItem.completed === true ? <MdOutlineCheckBox /> :
                      <MdOutlineCheckBoxOutlineBlank />}
                  </span>
                </td>
                <td className='p-3 text-sm ' title={todoItem.id}>{todoItem.body}</td>
                <td className='p-3 text-sm text-center'>
                  <span className={`p-1.5 text-xs font-medium tracking-wider rounded-md ${todoItem.completed ? 'bg-green-300' : 'bg-red-300'}`}>
                    {todoItem.completed ? 'Done' : 'Incomplete'}
                  </span>
                </td>
                <td className='p-3 text-sm font-medium'>{new Date(todoItem.created).toLocaleString()}</td>
                <td className='p-3 text-sm font-medium grid grid-flow-col items-center mt-5 '>
                  <span><label htmlFor="my-modal" ><MdEditNote onClick={() => setEditText(todoItem)} className=' text-xl cursor-pointer' /></label></span>
                  <span className=' text-xl cursor-pointer'><MdOutlineDeleteOutline onClick={() => handleDelete(todoItem.id)} /></span>

                </td>
              </tr>
            )
            )}</>}
```
  • The JSX returns a table element displaying the todo items. It includes checkboxes, todo text, status, creation date, and action icons for editing and deleting. It also conditionally renders content based on isLoading.
   <input type="checkbox" id="my-modal" className="modal-toggle" />
   <div className="modal">
     <div className="modal-box">
       <h3 className="font-bold text-lg">Edit Todo</h3>
       <input type="text" value={editText.body} onChange={handleChange} placeholder="Type here" className="input input-bordered w-full mt-8" />
       <div className="modal-action">
         <label htmlFor="my-modal" onClick={handleClick} className="btn btn-primary">Edit</label>
         <label htmlFor="my-modal" className="btn">Close</label>
       </div>
     </div>
   </div>
 </div>
  • A modal is included in the JSX for editing todo items. It contains an input field for the todo text and buttons for saving or closing the modal.

Install react Icons

I used react icons, it’s free and easy whit the node package manager Link:

npm install react-icons --save

the example usage looks like this:

import { FaBeer } from "react-icons/fa";

function Question() {
  return (
    <h3>
      Lets go for a <FaBeer />?
    </h3>
  );
}

Install Daisy-UI

First we will install a Tailwind Plugin for better styling - Daisy UI -  Link:

npm i -D daisyui@latest

After install add Daisy UI to your tailwind.config.js as following:

module.exports = {
  //...
  plugins: [require("daisyui")],
}

Create the TodoForm

Full code

import React, {useState} from 'react'
import axios from 'axios'

const TodoForm = () => {

  const [newTodo, setNewTodo] = useState({
    'body': ''
  })

  const handleChange = (e) => {
      setNewTodo(prev => ({
        ...prev,
        'body': e.target.value
      }))
    console.log(newTodo);
  }

  const postTodo = async () => {
    try {
      await axios.post('http://127.0.0.1:8000/api/todo/', newTodo)
      setTodos(prevTodos => [...prevTodos, newTodo])
      fetchData()
    }catch(error) {
      console.log(error);
    }

  }
    

  return (

    <div className='pt-10 ml-10'>
        <input type='text' placeholder="Add todo" className="input input-bordered"
          onChange={handleChange} value={newTodo.body}
        />
        <button className="ml-2 btn btn-primary" onClick={postTodo}>Add todo</button>
    </div>
  )
}

export default TodoForm

** Explanation **

Import Statements

import React, { useState } from 'react'
import axios from 'axios'
  • This code imports the necessary modules. React and the useState hook are imported from the React library for state management within the component. The axios module is imported for making HTTP requests.

TodoForm Component Declaration

const TodoForm = () => {
  • Here we have the declaration of the TodoForm functional component. This component does not receive any props and is used to create new todo items.

useState Hook for New Todo

const [newTodo, setNewTodo] = useState({
  'body': ''
})
  • The useState hook is used to initialize the newTodo state, which is an object with a body property. Initially, body is set to an empty string. This state will hold the data for the new todo item to be added.

handleChange Function

const handleChange = (e) => {
    setNewTodo(prev => ({
      ...prev,
      'body': e.target.value
    }))
  console.log(newTodo);
}
  • This function is an event handler for the input field. It updates the newTodo state with the current value of the input. The function logs the updated state to the console after setting it.

postTodo Function

const postTodo = async () => {
  try {
    await axios.post('http://127.0.0.1:8000/api/todo/', newTodo)
    setTodos(prevTodos => [...prevTodos, newTodo])
    fetchData()
  } catch(error) {
    console.log(error);
  }
}
  • postTodo is an asynchronous function that makes a POST request using axios to add a new todo item to a server. After a successful POST request, it updates a todo list (seemingly managed by a setTodos function, which is not defined within this component) and then calls a fetchData function (also not defined within this component) to refresh the todo list.

JSX for Rendering the Form

return (
  <div className='pt-10 ml-10'>
      <input type='text' placeholder="Add todo" className="input input-bordered"
        onChange={handleChange} value={newTodo.body}
      />
      <button className="ml-2 btn btn-primary" onClick={postTodo}>Add todo</button>
  </div>
)
  • The component returns JSX code for rendering the form. It consists of an input field for entering a new todo item and a button to submit the new todo. The onChange event of the input is linked to the handleChange function, and the onClick event of the button is linked to the postTodo function.

Exporting the Component

export default TodoForm