commit 593ab2d7d4ef972594131b125d9320c618e54691
parent 163398f68444d278eb83543ba9798a6441c8af9f
Author: Adrián Oliva <[email protected]>
Date:   Wed, 17 May 2023 21:26:50 -0600

Redux used to add ToDos

WE MADE IT!! I don't know how, but it is functional. :'3

Diffstat:
Msrc/App.jsx | 9+++------
Dsrc/ToDo-UI/NewToDo.jsx | 126-------------------------------------------------------------------------------
Dsrc/ToDo-UI/ToDoList.jsx | 69---------------------------------------------------------------------
Msrc/app/store.js | 12++++++------
Dsrc/features/counter/Counter.jsx | 30------------------------------
Dsrc/features/counter/counterSlice.js | 25-------------------------
Asrc/features/todo/ToDo.jsx | 233+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/features/todo/reducer.js | 32++++++++++++++++++++++++++++++++
8 files changed, 274 insertions(+), 262 deletions(-)

diff --git a/src/App.jsx b/src/App.jsx @@ -1,16 +1,13 @@ import React from "react"; -import { Counter } from "./features/counter/Counter"; +// import { Counter } from "./features/counter/Counter"; import { Search } from "./ToDo-UI/Search"; -import { NewToDo } from "./ToDo-UI/NewToDo"; -import { ToDoList } from "./ToDo-UI/ToDoList"; +import { Reducer } from "./features/todo/ToDo"; function App() { return ( <div> <Search /> - <NewToDo /> - <ToDoList /> - <Counter /> + <Reducer /> </div> ); } diff --git a/src/ToDo-UI/NewToDo.jsx b/src/ToDo-UI/NewToDo.jsx @@ -1,126 +0,0 @@ -import React from "react"; - -export function NewToDo() { - return ( - <> - <div className="container mt-3 mb-3"> - <button - type="button" - className="btn btn-outline-dark" - data-bs-toggle="modal" - data-bs-target="#NewToDo" - > - + New To Do - </button> - </div> - - <div - className="modal fade" - id="NewToDo" - tabIndex="-1" - role="dialog" - aria-labelledby="New To Do Window" - aria-hidden="true" - > - <div - className="modal-dialog modal-dialog-centered" - role="document" - > - <div className="modal-content"> - <div className="modal-header"> - <h5 - className="modal-title" - id="exampleModalLongTitle" - > - Add a new to do - </h5> - <button - type="button" - className="close" - data-bs-dismiss="modal" - aria-label="Close" - > - <span aria-hidden="true">&times;</span> - </button> - </div> - <div className="modal-body"> - <form> - <div className="form-floating mb-3"> - <input - className="form-control" - id="new-todo-id" - value="4" - disabled - /> - <label htmlFor="floatingInput">ID</label> - </div> - <div className="form-floating mb-3"> - <input - className="form-control" - placeholder="Text" - id="new-todo-text" - /> - <label htmlFor="floatingInput">Text</label> - </div> - <div className="input-group mb-3"> - <span className="input-group-text"> - <i className="fa-regular fa-calendar"></i> - </span> - <div className="form-floating"> - <input - className="form-control" - id="new-todo-due-date" - name="new-todo-due-date" - placeholder="Due date" - /> - <label htmlFor="floatingInput"> - Due Date - </label> - </div> - </div> - <div class="form-check mb-3"> - <input - class="form-check-input" - type="checkbox" - value="" - id="flexCheckDefault" - /> - <label - class="form-check-label" - for="flexCheckDefault" - > - Done - </label> - </div> - <div class="form-floating mb-3"> - <select - class="form-select" - id="floatingSelect" - aria-label="Floating label select example" - > - <option selected>Low</option> - <option value="1">Medium</option> - <option value="2">High</option> - </select> - <label for="floatingSelect">Priority</label> - </div> - </form> - </div> - <div className="modal-footer"> - <button - type="button" - className="btn btn-secondary" - data-bs-dismiss="modal" - > - Cancel - </button> - <button type="button" className="btn btn-primary"> - Add - </button> - </div> - </div> - </div> - </div> - </> - ); -} diff --git a/src/ToDo-UI/ToDoList.jsx b/src/ToDo-UI/ToDoList.jsx @@ -1,69 +0,0 @@ -import React from "react"; - -export function ToDoList() { - const todos = [ - { - id: 1, - text: "Finish homework.", - due_date: "2023/05/29", - done: false, - priority: "high", - creation_date: "2023/05/11", - }, - ]; - - return ( - <div className="container"> - <table className="table align-middle"> - <thead> - <tr> - <th scope="col">#</th> - <th scope="col">Name</th> - <th scope="col">Priority</th> - <th scope="col">Due Date</th> - <th scope="col">Actions</th> - </tr> - </thead> - <tbody className="table-group-divider"> - {todos.map((item) => ( - <tr> - <th scope="row"> - <div className="form-check"> - <input - className="form-check-input" - type="checkbox" - value={item.done ? "checked" : ""} - id="flexCheckChecked" - ></input> - </div> - </th> - <td>{item.text}</td> - <td>{item.priority}</td> - <td>{item.due_date}</td> - <td> - <div - className="btn-group btn-group-sm" - role="group" - aria-label="Basic example" - > - <button - type="button" - className="btn btn-outline-dark" - > - Edit - </button> - <button - type="button" - className="btn btn-outline-dark" - > - Delete - </button> - </div> - </td> - </tr> - ))} - </tbody> - </table> - </div> - ); -} diff --git a/src/app/store.js b/src/app/store.js @@ -1,8 +1,8 @@ -import { configureStore } from '@reduxjs/toolkit' -import counterReducer from '../features/counter/counterSlice' +import { configureStore } from "@reduxjs/toolkit"; +import todo_reducer from "../features/todo/reducer"; export const store = configureStore({ - reducer: { - counter: counterReducer, - }, -}) + reducer: { + todo_list: todo_reducer, + }, +}); diff --git a/src/features/counter/Counter.jsx b/src/features/counter/Counter.jsx @@ -1,30 +0,0 @@ -import React from 'react' -import { useSelector, useDispatch } from 'react-redux' -import { decrement, increment } from './counterSlice' - -export function Counter() { - const count = useSelector((state) => state.counter.value) - const dispatch = useDispatch() - - return ( - <div> - <h1>{count}</h1> - <div> - <button - aria-label="Increment value" - onClick={() => dispatch(increment())} - className='btn btn-outline-primary' - > - Increment - </button> - <button - aria-label="Decrement value" - onClick={() => dispatch(decrement())} - className='btn btn-outline-primary' - > - Decrement - </button> - </div> - </div> - ) -} diff --git a/src/features/counter/counterSlice.js b/src/features/counter/counterSlice.js @@ -1,25 +0,0 @@ -import { createSlice } from '@reduxjs/toolkit' - -export const counterSlice = createSlice({ - name: 'counter', - initialState: { - value: 0, - }, - reducers: { - increment: (state) => { - // Redux Toolkit allows us to write "mutating" logic in reducers. It - // doesn't actually mutate the state because it uses the Immer library, - // which detects changes to a "draft state" and produces a brand new - // immutable state based off those changes - state.value += 1; - }, - decrement: (state) => { - state.value -= 1; - } - }, -}) - -// Action creators are generated for each case reducer function -export const { increment, decrement } = counterSlice.actions - -export default counterSlice.reducer diff --git a/src/features/todo/ToDo.jsx b/src/features/todo/ToDo.jsx @@ -0,0 +1,233 @@ +import React, { useState } from "react"; +import { useSelector, useDispatch } from "react-redux"; +import { add_todo, select_todos } from "./reducer"; + +export function Reducer() { + const my_todos = useSelector(select_todos); + const dispatch = useDispatch(); + const [new_text, set_new_text] = useState(""); + const [new_done, set_new_done] = useState(false); + const [new_priority, set_new_priority] = useState("Low"); + + function handle_exit_modal() { + // https://stackoverflow.com/questions/27826381/clearing-form-input-fields-in-bootstrap + $("form").get(0).reset(); // Reset form + + set_new_text(""); + set_new_done(false); + set_new_priority("Low"); + + console.log("CLEANED."); + } + + return ( + <> + <div className="container mt-3 mb-3"> + <button + type="button" + className="btn btn-outline-dark" + data-bs-toggle="modal" + data-bs-target="#NewToDo" + > + + New To Do + </button> + </div> + + <div + className="modal fade" + id="NewToDo" + tabIndex="-1" + role="dialog" + aria-labelledby="New To Do Window" + aria-hidden="true" + > + <div + className="modal-dialog modal-dialog-centered" + role="document" + > + <div className="modal-content"> + <div className="modal-header"> + <h5 + className="modal-title" + id="exampleModalLongTitle" + > + Add a new to do + </h5> + <button + type="button" + className="close" + data-bs-dismiss="modal" + aria-label="Close" + onClick={handle_exit_modal} + > + <span aria-hidden="true">&times;</span> + </button> + </div> + <div className="modal-body"> + <form> + <div className="form-floating mb-3"> + <input + className="form-control" + id="new-todo-id" + value="4" + disabled + /> + <label htmlFor="floatingInput">ID</label> + </div> + <div className="form-floating mb-3"> + <input + className="form-control" + placeholder="Text" + id="new-todo-text" + onChange={(e) => + set_new_text(e.target.value) + } + /> + <label htmlFor="floatingInput">Text</label> + </div> + <div className="input-group mb-3"> + <span className="input-group-text"> + <i className="fa-regular fa-calendar"></i> + </span> + <div className="form-floating"> + <input + className="form-control" + id="new-todo-due-date" + name="new-todo-due-date" + placeholder="Due date" + /> + <label htmlFor="floatingInput"> + Due Date + </label> + </div> + </div> + <div className="form-check mb-3"> + <input + className="form-check-input" + type="checkbox" + value="" + id="flexCheckDefault" + onChange={(e) => + set_new_done(e.target.checked) + } + /> + <label + className="form-check-label" + htmlFor="flexCheckDefault" + > + Done + </label> + </div> + <div className="form-floating mb-3"> + <select + className="form-select" + id="floatingSelect" + aria-label="Floating label select example" + onChange={(e) => + set_new_priority(e.target.value) + } + > + <option value="Low" defaultValue> + Low + </option> + <option value="Medium">Medium</option> + <option value="High">High</option> + </select> + <label htmlFor="floatingSelect"> + Priority + </label> + </div> + </form> + </div> + <div className="modal-footer"> + <button + type="button" + className="btn btn-secondary" + data-bs-dismiss="modal" + onClick={handle_exit_modal} + > + Cancel + </button> + <button + type="button" + className="btn btn-primary" + onClick={() => { + dispatch( + add_todo({ + text: new_text, + // Cannot update date on change. :'( + due_date: + document.getElementById( + "new-todo-due-date" + ).value, + done: new_done, + priority: new_priority, + creation_date: + new Date().toString(), + }) + ), + handle_exit_modal(); + }} + data-bs-dismiss="modal" + > + Add + </button> + </div> + </div> + </div> + </div> + <div className="container"> + <table className="table align-middle"> + <thead> + <tr> + <th scope="col">#</th> + <th scope="col">Name</th> + <th scope="col">Priority</th> + <th scope="col">Due Date</th> + <th scope="col">Actions</th> + </tr> + </thead> + <tbody className="table-group-divider"> + {my_todos.map((item) => ( + <tr key={item.id}> + <th scope="row"> + <div className="form-check"> + <input + className="form-check-input" + type="checkbox" + checked={item.done} + id="flexCheckChecked" + ></input> + </div> + </th> + <td>{item.text}</td> + <td>{item.priority}</td> + <td>{item.due_date}</td> + <td> + <div + className="btn-group btn-group-sm" + role="group" + aria-label="Basic example" + > + <button + type="button" + className="btn btn-outline-dark" + > + Edit + </button> + <button + type="button" + className="btn btn-outline-dark" + > + Delete + </button> + </div> + </td> + </tr> + ))} + </tbody> + </table> + </div> + </> + ); +} diff --git a/src/features/todo/reducer.js b/src/features/todo/reducer.js @@ -0,0 +1,32 @@ +import { createSlice } from "@reduxjs/toolkit"; + +export const todo_slice = createSlice({ + name: "todo_list", + initialState: { + todos: [], + last_id: 0, + }, + + reducers: { + add_todo: (state, action) => { + state.todos = [ + ...state.todos, + { + id: ++state.last_id, + text: action.payload.text, + due_date: action.payload.due_date, + done: action.payload.done, + priority: action.payload.priority, + creation_date: action.payload.creation_date, + }, + ]; + console.log(state.todos); + }, + }, +}); + +export const { add_todo } = todo_slice.actions; + +export const select_todos = (state) => state.todo_list.todos; + +export default todo_slice.reducer;