3. Create Frontend
Frontend configuration
First delete all the CSS and JSX preconfigurations from React. We gonna make our own.
Install Tailwind in React:
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
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: [], }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
Make in
.srcfolder a new folder named components.Create a file named
table.jsxin the components folder.Take a code snippet called
rafceto 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.
axiosis used for making HTTP requests,Reactand theuseStatehook are imported from the React library for managing component state, and specific icons are imported fromreact-icons/mdfor UI elements.
Table Component Declaration
const Table = ({ todos, isLoading, setTodos }) => {
- This is the declaration of the
Tablefunctional component. It receivestodos,isLoading, andsetTodosas props.todosis the list of todo items,isLoadingis a boolean indicating if data is being fetched, andsetTodosis a function to update the list of todos.
useState Hook for Edit Text
const [editText, setEditText] = useState({
'body': ''
})
- The
useStatehook initializeseditTextstate to store the current edit value. The state is an object with abodyproperty, 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);
}
}
handleDeleteis an asynchronous function that makes a DELETE request to a specified endpoint to delete a todo item. After a successful deletion, it updates thetodosstate 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);
}
}
handleEditis an asynchronous function used to send a PATCH request to update a todo item. It updates thetodosstate 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
editTextstate 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
})
}
handleClickcallshandleEditwith the currenteditTextand then resetseditText.handleCheckboxtoggles the completed status of a todo item and callshandleEdit.
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.
Modal for Editing Todos
<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.
Reactand theuseStatehook are imported from the React library for state management within the component. Theaxiosmodule is imported for making HTTP requests.
TodoForm Component Declaration
const TodoForm = () => {
- Here we have the declaration of the
TodoFormfunctional 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
useStatehook is used to initialize thenewTodostate, which is an object with abodyproperty. Initially,bodyis 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
newTodostate 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);
}
}
postTodois an asynchronous function that makes a POST request usingaxiosto add a new todo item to a server. After a successful POST request, it updates a todo list (seemingly managed by asetTodosfunction, which is not defined within this component) and then calls afetchDatafunction (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
onChangeevent of the input is linked to thehandleChangefunction, and theonClickevent of the button is linked to thepostTodofunction.
Exporting the Component
export default TodoForm