diff --git a/index.html b/index.html index f7ac4e4..fcd932a 100644 --- a/index.html +++ b/index.html @@ -4,6 +4,11 @@ + + + + + Todo diff --git a/package.json b/package.json index caf6289..8faa9b3 100644 --- a/package.json +++ b/package.json @@ -10,8 +10,11 @@ "preview": "vite preview" }, "dependencies": { + "lottie-react": "^2.4.1", + "moment": "^2.30.1", "react": "^19.0.0", - "react-dom": "^19.0.0" + "react-dom": "^19.0.0", + "zustand": "^5.0.5" }, "devDependencies": { "@eslint/js": "^9.21.0", diff --git a/src/App.css b/src/App.css new file mode 100644 index 0000000..630835d --- /dev/null +++ b/src/App.css @@ -0,0 +1,22 @@ +.main { + padding: 20px 100px; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} + +.innerMain { + border: 1px solid black; + width: 400px; +} + +h1 { + color: black; + display: flex; + justify-content: center; + align-items: center; + font-size: 70px; + font-family: "Rampart One", sans-serif; + font-weight: 400; +} diff --git a/src/App.jsx b/src/App.jsx index 5427540..ee7863c 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,5 +1,17 @@ +import NewTask from "./components/NewTask"; +import StatusBar from "./components/StatusBar"; +import TaskList from "./components/TaskList"; +import "./App.css"; + export const App = () => { return ( -

React Boilerplate

- ) -} +
+
+

ToDo App

+ + + +
+
+ ); +}; diff --git a/src/components/NewTask.css b/src/components/NewTask.css new file mode 100644 index 0000000..cd02d59 --- /dev/null +++ b/src/components/NewTask.css @@ -0,0 +1,10 @@ +.toDoBox { + border: 1px solid black; + background-color: #eee; + box-shadow: 0.25rem 0.25rem rgba(12, 23, 14, 0.388); + display: flex; + flex-direction: column; + padding: 1rem; + margin: 1rem; + gap: 10px; +} diff --git a/src/components/NewTask.jsx b/src/components/NewTask.jsx new file mode 100644 index 0000000..df4eef4 --- /dev/null +++ b/src/components/NewTask.jsx @@ -0,0 +1,34 @@ +import React, { useState } from "react"; +import { useNoteStore } from "../store"; +import "./NewTask.css"; + +const NewTask = () => { + const [toDo, setToDo] = useState(""); + + const { addNewTask } = useNoteStore(); + + const addToDo = () => { + if (toDo) { + addNewTask(toDo); + } + setToDo(""); + }; + + return ( +
ev.preventDefault()}> + + setToDo(ev.target.value)} + /> + +
+ ); +}; + +export default NewTask; diff --git a/src/components/StatusBar.css b/src/components/StatusBar.css new file mode 100644 index 0000000..4a1f91b --- /dev/null +++ b/src/components/StatusBar.css @@ -0,0 +1,17 @@ +.statusBar { + border: 1px solid black; + background-color: #b9b8b4; + box-shadow: 0.25rem 0.25rem rgba(12, 23, 14, 0.388); + display: flex; + flex-direction: row; + padding: 1rem; + margin: 1rem; + gap: 10px; + justify-content: space-around; +} + +.actionButton { + border-radius: 5px; + padding: 2px; + background-color: #2febe5; +} diff --git a/src/components/StatusBar.jsx b/src/components/StatusBar.jsx new file mode 100644 index 0000000..4abcb16 --- /dev/null +++ b/src/components/StatusBar.jsx @@ -0,0 +1,22 @@ +import React from "react"; +import { useNoteStore } from "../store"; + +import "./StatusBar.css"; + +const StatusBar = () => { + const { todos, checkAllTasks, removeAllDoneTasks } = useNoteStore(); + + return ( +
+ {todos.length} tasks + + +
+ ); +}; + +export default StatusBar; diff --git a/src/components/Task.css b/src/components/Task.css new file mode 100644 index 0000000..f5569e8 --- /dev/null +++ b/src/components/Task.css @@ -0,0 +1,33 @@ +.taskBox { + border: 1px solid black; + background-color: #74cd07; + box-shadow: 0.25rem 0.25rem rgba(12, 23, 14, 0.388); + display: flex; + flex-direction: column; + gap: 10px; + justify-content: space-around; +} + +.taskDone { + background-color: #c5c3c1; +} + +.taskDetails { + background-color: rgba(112, 82, 41, 0.363); + display: flex; + flex-direction: row; + justify-content: space-between; +} + +.taskMain { + display: flex; + flex-direction: row; + justify-content: space-between; + padding: 0.1rem 0.2rem; +} + +.removeButton { + border-radius: 5px; + padding: 2px; + background-color: #b0d790; +} diff --git a/src/components/Task.jsx b/src/components/Task.jsx new file mode 100644 index 0000000..07e86b9 --- /dev/null +++ b/src/components/Task.jsx @@ -0,0 +1,41 @@ +import React from "react"; +import { useNoteStore } from "../store"; +import moment from "moment"; + +import "./Task.css"; + +const Task = ({ todo }) => { + const { toggleChecked, removeTask } = useNoteStore(); + + const toggle = (todo) => { + toggleChecked(todo.text, todo.date); + }; + + return ( +
+
+ {todo.text} + +
+
+ {moment(todo.date).fromNow()} + +
+
+ ); +}; + +export default Task; diff --git a/src/components/TaskList.css b/src/components/TaskList.css new file mode 100644 index 0000000..4f8eab8 --- /dev/null +++ b/src/components/TaskList.css @@ -0,0 +1,10 @@ +.taskList { + border: 1px solid black; + background-color: #f1daa9; + display: flex; + flex-direction: column; + padding: 1rem; + margin: 1rem; + gap: 10px; + justify-content: space-around; +} diff --git a/src/components/TaskList.jsx b/src/components/TaskList.jsx new file mode 100644 index 0000000..dbebea1 --- /dev/null +++ b/src/components/TaskList.jsx @@ -0,0 +1,17 @@ +import React from "react"; +import Task from "./Task"; +import { useNoteStore } from "../store"; +import "./TaskList.css"; + +const TaskList = () => { + const { todos } = useNoteStore(); + return ( +
+ {todos.map((todo) => ( + + ))} +
+ ); +}; + +export default TaskList; diff --git a/src/index.css b/src/index.css index f7c0aef..11430b3 100644 --- a/src/index.css +++ b/src/index.css @@ -1,3 +1,9 @@ :root { - font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; + font-family: "Handlee", Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; +} + +* { + margin: 0; + padding: 0; + box-sizing: border-box; } diff --git a/src/store.jsx b/src/store.jsx new file mode 100644 index 0000000..4325709 --- /dev/null +++ b/src/store.jsx @@ -0,0 +1,83 @@ +import moment from "moment"; +import { create } from "zustand"; +import { persist } from "zustand/middleware"; + +export const useNoteStore = create( persist( (set) => { + return { + todos: [ + { + text: "test", + date: moment.now(), + checked: false, + }, + { + text: "test2", + date: moment.now(), + checked: true, + }, + ], + addNewTask: (newTodo) => + set((state) => { + const newTodos = [...state.todos]; + newTodos.push({ text: newTodo, date: moment.now(), checked: false }); + return { todos: newTodos }; + }), + removeTask: (todotext, date) => + set((state) => { + const newTodos = []; + for (const index in state.todos) { + const todo = state.todos[index]; + if (todotext === todo.text && date === todo.date) { + // skip + } else { + newTodos.push(todo); + } + } + return { todos: newTodos }; + }), + toggleChecked: (todotext, date) => + set((state) => { + const newTodos = []; + for (const index in state.todos) { + const todo = state.todos[index]; + if (todotext === todo.text && date === todo.date) { + newTodos.push({ + text: todo.text, + date: todo.date, + checked: !todo.checked, + }); + } else { + newTodos.push(todo); + } + } + return { todos: newTodos }; + }), + checkAllTasks: () => + set((state) => { + const newTodos = []; + for (const index in state.todos) { + const todo = state.todos[index]; + newTodos.push({ + text: todo.text, + date: todo.date, + checked: true, + }); + } + return { todos: newTodos }; + }), + removeAllDoneTasks: () => + set((state) => { + const newTodos = []; + for (const index in state.todos) { + const todo = state.todos[index]; + if (todo.checked === false) { + newTodos.push(todo); + } + } + return { todos: newTodos }; + }), + }; +}, { + name: 'todo-tasks' +} +));