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
.src
folder a new folder named components.Create a file named
table.jsx
in the components folder.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 theuseState
hook are imported from the React library for managing component state, and specific icons are imported fromreact-icons/md
for UI elements.
Table Component Declaration
const Table = ({ todos, isLoading, setTodos }) => {
- This is the declaration of the
Table
functional component. It receivestodos
,isLoading
, andsetTodos
as props.todos
is the list of todo items,isLoading
is a boolean indicating if data is being fetched, andsetTodos
is a function to update the list of todos.
useState Hook for Edit Text
const [editText, setEditText] = useState({
'body': ''
})
- The
useState
hook initializeseditText
state to store the current edit value. The state is an object with abody
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 thetodos
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 thetodos
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
callshandleEdit
with the currenteditText
and then resetseditText
.handleCheckbox
toggles 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.
React
and theuseState
hook are imported from the React library for state management within the component. Theaxios
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 thenewTodo
state, which is an object with abody
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 usingaxios
to add a new todo item to a server. After a successful POST request, it updates a todo list (seemingly managed by asetTodos
function, which is not defined within this component) and then calls afetchData
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 thehandleChange
function, and theonClick
event of the button is linked to thepostTodo
function.
Exporting the Component
export default TodoForm