aboutsummaryrefslogtreecommitdiffstats
path: root/public/projects/react-small-apps/apps/todos/src/views/TodoList
diff options
context:
space:
mode:
authorArmand Philippot <git@armandphilippot.com>2022-02-20 16:11:50 +0100
committerArmand Philippot <git@armandphilippot.com>2022-02-20 16:15:08 +0100
commit73a5c7fae9ffbe9ada721148c8c454a643aceebe (patch)
treec8fad013ed9b5dd589add87f8d45cf02bbfc6e91 /public/projects/react-small-apps/apps/todos/src/views/TodoList
parentb01239fbdcc5bbc5921f73ec0e8fee7bedd5c8e8 (diff)
chore!: restructure repo
I separated public files from the config/dev files. It improves repo readability. I also moved dotenv helper to public/inc directory and extract the Matomo tracker in the same directory.
Diffstat (limited to 'public/projects/react-small-apps/apps/todos/src/views/TodoList')
-rw-r--r--public/projects/react-small-apps/apps/todos/src/views/TodoList/TodoList.js84
-rw-r--r--public/projects/react-small-apps/apps/todos/src/views/TodoList/TodoList.scss63
-rw-r--r--public/projects/react-small-apps/apps/todos/src/views/TodoList/TodoListFilters.js47
-rw-r--r--public/projects/react-small-apps/apps/todos/src/views/TodoList/TodoListItem.js55
4 files changed, 249 insertions, 0 deletions
diff --git a/public/projects/react-small-apps/apps/todos/src/views/TodoList/TodoList.js b/public/projects/react-small-apps/apps/todos/src/views/TodoList/TodoList.js
new file mode 100644
index 0000000..c671459
--- /dev/null
+++ b/public/projects/react-small-apps/apps/todos/src/views/TodoList/TodoList.js
@@ -0,0 +1,84 @@
+import { useEffect, useState } from "react";
+import { useDispatch, useSelector } from "react-redux";
+import { Button } from "../../components/forms";
+import TodoForm from "../TodoForm/TodoForm";
+import TodoListItem from "./TodoListItem";
+import "./TodoList.scss";
+import { deleteAllTodos } from "../../store/todos/todos.slice";
+import { LocalStorage } from "../../services/LocalStorage.service";
+import TodoListFilters from "./TodoListFilters";
+
+function TodoList() {
+ const [todosList, setTodosList] = useState([]);
+ const [currentView, setCurrentView] = useState("all");
+ const dispatch = useDispatch();
+ const [isToggled, setIsToggled] = useState(false);
+ const currentUser = useSelector((state) => state.auth.currentUser);
+ const allTodos = useSelector((state) => state.todos);
+
+ useEffect(() => {
+ const userTodos = allTodos.filter((todo) => todo.userId === currentUser.id);
+
+ setTodosList(() => {
+ let list;
+
+ switch (currentView) {
+ case "completed":
+ list = userTodos.filter((todo) => todo.done);
+ break;
+ case "ongoing":
+ list = userTodos.filter((todo) => !todo.done);
+ break;
+ default:
+ list = userTodos;
+ break;
+ }
+
+ return list;
+ });
+ }, [currentView, allTodos, currentUser.id]);
+
+ useEffect(() => {
+ LocalStorage.set("todoList", allTodos);
+ });
+
+ const userTodosList = todosList.map((todo) => (
+ <TodoListItem key={todo.id} todo={todo} />
+ ));
+
+ return (
+ <div>
+ <h2>Welcome back {currentUser.username}!</h2>
+ <div className="todos-actions">
+ <Button
+ modifiers={["action"]}
+ onClickHandler={() => setIsToggled(!isToggled)}
+ >
+ New todo
+ </Button>
+ <Button
+ modifiers={["action", "delete"]}
+ onClickHandler={() => dispatch(deleteAllTodos())}
+ >
+ Delete all
+ </Button>
+ </div>
+ {isToggled ? (
+ <TodoForm userId={currentUser.id} closeForm={setIsToggled} />
+ ) : (
+ ""
+ )}
+ <TodoListFilters
+ currentView={currentView}
+ setCurrentView={setCurrentView}
+ />
+ {userTodosList.length > 0 ? (
+ <ul className="todos-list">{userTodosList}</ul>
+ ) : (
+ ""
+ )}
+ </div>
+ );
+}
+
+export default TodoList;
diff --git a/public/projects/react-small-apps/apps/todos/src/views/TodoList/TodoList.scss b/public/projects/react-small-apps/apps/todos/src/views/TodoList/TodoList.scss
new file mode 100644
index 0000000..024fe3e
--- /dev/null
+++ b/public/projects/react-small-apps/apps/todos/src/views/TodoList/TodoList.scss
@@ -0,0 +1,63 @@
+@use "../../sass/abstracts/placeholders";
+@use "../../sass/abstracts/variables" as var;
+
+.todos-actions {
+ display: flex;
+ gap: 1rem;
+}
+
+.todos-filters {
+ display: flex;
+ flex-flow: row wrap;
+ align-items: center;
+ gap: clamp(0.2rem, 1vw, 0.5rem);
+ margin-top: 1rem
+}
+
+.todos-list {
+ @extend %list-reset;
+ border: 1px solid #000;
+ margin-top: 1.5rem;
+
+ &__item {
+ display: flex;
+ flex-flow: row wrap;
+ align-items: center;
+ gap: 1rem;
+ padding: 1rem;
+
+ &:not(:first-child) {
+ border-top: 1px solid #000;
+ }
+
+ .form__label {
+ margin: 0;
+ letter-spacing: 0;
+ text-transform: none;
+ }
+
+ .todo__title {
+ background-image: linear-gradient(
+ to top,
+ transparent calc(50% - 3px),
+ var.$done-color calc(50% - 3px),
+ var.$done-color 50%,
+ transparent 50%
+ );
+ background-size: 0 100%;
+ background-repeat: no-repeat;
+ margin-right: auto;
+ transition: background-size 0.3s ease-in-out 0s;
+ }
+
+ &--done {
+ .todo__title {
+ background-size: 100% 100%;
+ }
+ }
+
+ .btn {
+ padding: 0.4rem 0.7rem;
+ }
+ }
+}
diff --git a/public/projects/react-small-apps/apps/todos/src/views/TodoList/TodoListFilters.js b/public/projects/react-small-apps/apps/todos/src/views/TodoList/TodoListFilters.js
new file mode 100644
index 0000000..338492a
--- /dev/null
+++ b/public/projects/react-small-apps/apps/todos/src/views/TodoList/TodoListFilters.js
@@ -0,0 +1,47 @@
+import { Button } from "../../components/forms";
+
+function TodoListFilters({ currentView, setCurrentView }) {
+ let allModifiers = ["filters"];
+ let ongoingModifiers = ["filters"];
+ let completedModifiers = ["filters"];
+
+ switch (currentView) {
+ case "all":
+ allModifiers.push("current");
+ break;
+ case "ongoing":
+ ongoingModifiers.push("current");
+ break;
+ case "completed":
+ completedModifiers.push("current");
+ break;
+ default:
+ break;
+ }
+
+ return (
+ <div className="todos-filters">
+ Show:
+ <Button
+ modifiers={allModifiers}
+ onClickHandler={() => setCurrentView("all")}
+ >
+ All
+ </Button>
+ <Button
+ modifiers={ongoingModifiers}
+ onClickHandler={() => setCurrentView("ongoing")}
+ >
+ Ongoing
+ </Button>
+ <Button
+ modifiers={completedModifiers}
+ onClickHandler={() => setCurrentView("completed")}
+ >
+ Completed
+ </Button>
+ </div>
+ );
+}
+
+export default TodoListFilters;
diff --git a/public/projects/react-small-apps/apps/todos/src/views/TodoList/TodoListItem.js b/public/projects/react-small-apps/apps/todos/src/views/TodoList/TodoListItem.js
new file mode 100644
index 0000000..f76c1a1
--- /dev/null
+++ b/public/projects/react-small-apps/apps/todos/src/views/TodoList/TodoListItem.js
@@ -0,0 +1,55 @@
+import { useEffect, useState } from "react";
+import { useDispatch } from "react-redux";
+import { Link } from "react-router-dom";
+import { Button, Input } from "../../components/forms";
+import { deleteTodo, toggleTodo } from "../../store/todos/todos.slice";
+import { slugify } from "../../utilities/helpers";
+
+function TodoListItem({ todo }) {
+ const { id, createdAt, title, done } = todo;
+ const [isChecked, setIsChecked] = useState(false);
+ const dispatch = useDispatch();
+
+ useEffect(() => {
+ if (done) setIsChecked(true);
+ }, [done]);
+
+ const handleTodoDone = (checkboxState) => {
+ setIsChecked(checkboxState);
+ dispatch(toggleTodo(id));
+ };
+
+ const todoSlug = slugify(title);
+
+ const classNames = `todos-list__item ${
+ isChecked ? "todos-list__item--done" : ""
+ }`;
+
+ return (
+ <li className={classNames}>
+ <span className="todo__date">
+ {new Date(createdAt).toLocaleDateString()}
+ </span>
+ <span className="todo__title">
+ <Link to={`/todo/${todoSlug}`} state={{ todoId: todo.id }}>
+ {title}
+ </Link>
+ </span>
+ <Input
+ type="checkbox"
+ label="Done?"
+ id={id}
+ value={isChecked}
+ updateValue={handleTodoDone}
+ />
+ <Button
+ modifiers={["action", "delete"]}
+ onClickHandler={() => dispatch(deleteTodo(id))}
+ >
+ Delete
+ </Button>
+ </li>
+ );
+}
+
+export default TodoListItem;