diff --git a/package-lock.json b/package-lock.json index e6a160a..1a1791e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,11 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-scripts": "5.0.1", + "uuid4": "^2.0.3", "web-vitals": "^2.1.4" + }, + "devDependencies": { + "tailwindcss": "^3.4.1" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -17332,6 +17336,11 @@ "uuid": "dist/bin/uuid" } }, + "node_modules/uuid4": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/uuid4/-/uuid4-2.0.3.tgz", + "integrity": "sha512-CTpAkEVXMNJl2ojgtpLXHgz23dh8z81u6/HEPiQFOvBc/c2pde6TVHmH4uwY0d/GLF3tb7+VDAj4+2eJaQSdZQ==" + }, "node_modules/v8-to-istanbul": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz", diff --git a/package.json b/package.json index 88a269e..37f020b 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-scripts": "5.0.1", + "uuid4": "^2.0.3", "web-vitals": "^2.1.4" }, "scripts": { @@ -34,5 +35,8 @@ "last 1 firefox version", "last 1 safari version" ] + }, + "devDependencies": { + "tailwindcss": "^3.4.1" } } diff --git a/src/App.css b/src/App.css index 13088c2..5e5f622 100644 --- a/src/App.css +++ b/src/App.css @@ -1,4 +1,4 @@ -:root { +/* :root { --peach: #ffe6e6; --pink: #e1afd1; --lightPurple: #ad88c6; @@ -46,4 +46,4 @@ to { transform: rotate(360deg); } -} +} */ \ No newline at end of file diff --git a/src/App.js b/src/App.js index 0ff7d8a..a74fc28 100644 --- a/src/App.js +++ b/src/App.js @@ -1,12 +1,17 @@ import "./App.css"; +import todoData from "./todoData"; +import TodoList from "./components/TodoList"; +import LandingPage from "./components/LandingPage"; + // TODO: Import the todoData and pass it as a prop to the TodoList component function App() { return ( -
-
- {/* Call the TodoList Component Here */} -
+
+ +
+ +
); } diff --git a/src/assets/todo.jpeg b/src/assets/todo.jpeg new file mode 100644 index 0000000..71f5044 Binary files /dev/null and b/src/assets/todo.jpeg differ diff --git a/src/assets/todoo.avif b/src/assets/todoo.avif new file mode 100644 index 0000000..630b634 Binary files /dev/null and b/src/assets/todoo.avif differ diff --git a/src/components/AddTask.js b/src/components/AddTask.js new file mode 100644 index 0000000..9eace56 --- /dev/null +++ b/src/components/AddTask.js @@ -0,0 +1,160 @@ +import React, { useState } from 'react'; +import uuid4 from 'uuid4'; +import { users } from "../usersData" + +function AddTask({ todoList, setTodoList, listType, setFilteredList }) { + let defaultDate = new Date() + defaultDate.setDate(defaultDate.getDate() + 3) + + const [isOpen, setIsOpen] = useState(false); + const [todoTitle, setTitle] = useState(""); + const [todoLevel, setLevel] = useState(""); + const [todoUser, setUser] = useState(null); + const [todoStart, setStart] = useState(defaultDate); + const [todoEnd, setEnd] = useState(defaultDate); + const [todoDescription, setDescription] = useState(""); + + + const addTask = () => { + setIsOpen(!isOpen); + }; + + const handleSubmit = (event) => { + event.preventDefault(); + + const startDate = new Date(todoStart); + const endDate = new Date(todoEnd); + + // Check if end date is before start date + if (endDate < startDate) { + alert("End date must be after start date"); + return; + } + const newTask = { + id: uuid4(), + title: todoTitle, + level: todoLevel, + user: todoUser, + startDate: todoStart, + endDate: todoEnd, + description: todoDescription, + done: false + }; + const updatedTodoList = [...todoList, newTask]; // Update todoList with the new task + setTodoList(updatedTodoList); // Update todoList state + + // Update filteredList using the updated todoList + if (listType === "completed") { + setFilteredList(updatedTodoList.filter(todo => todo.done)); + } else if (listType === "active") { + setFilteredList(updatedTodoList.filter(todo => !todo.done)); + } else { + setFilteredList(updatedTodoList); + } + + // Reset values and close modal + addTask(); + setTitle(""); + setLevel(""); + setStart(defaultDate); + setEnd(defaultDate); + setDescription(""); + + }; + const handleUser = (event) => { + const userId = event.target.value; + const user = users.find(user => user.id === Number(userId)); + setUser(user); + } + + + return ( + <> + {/* Modal toggle */} + + + {/* Main modal */} + {isOpen && ( +
+
+ {/* Modal content */} +
+ {/* Modal header */} +
+

+ Add New Task +

+ +
+ {/* Modal body */} +
+ +
+
+ + { + setTitle(event.target.value); + }} value={todoTitle} className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5" placeholder="Type task name" required /> +
+
+ + +
+
+ + +
+
+ + { + setStart(event.target.value); + }} value={todoStart} className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5" /> +
+
+ + { + setEnd(event.target.value); + }} value={todoEnd} className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5" /> +
+
+ + +
+
+ +
+
+
+
+ )} + + ); +} + +export default AddTask; diff --git a/src/components/EditTask.js b/src/components/EditTask.js new file mode 100644 index 0000000..e24d590 --- /dev/null +++ b/src/components/EditTask.js @@ -0,0 +1,126 @@ +import React from 'react'; +import { useState } from 'react'; +import { users } from '../usersData'; + +function EditTask({ todo, setIsEditing, handleCancel, handleEditList }) { + const [editedTitle, setEditTitle] = useState(todo.title); + const [editedLevel, setEditLevel] = useState(todo.level); + const [editedUser, setEditUser] = useState(todo.user); + const [editedStart, setEditStart] = useState(todo.startDate); + const [editedEnd, setEditEnd] = useState(todo.endDate); + const [editedDescription, setEditDescription] = useState(todo.description); + + // Implement your edit functionality here + const handleSave = () => { + if (editedTitle === "") return; + + const updatedTask = { + ...todo, + title: editedTitle, + level: editedLevel ? editedLevel : "", + user: editedUser ? editedUser : "Not assigned", + startDate: editedStart ? editedStart : "", + endDate: editedEnd ? editedEnd : "", + description: editedDescription ? editedDescription : "", + done: todo.done + }; + handleEditList(updatedTask); + setIsEditing(false); + } + + const handleEditUser = (event) => { + const userId = event.target.value; + const user = users.find(user => user.id === Number(userId)); + setEditUser(user); + } + return ( + <> + {/* Main modal */} +
+
+ {/* Modal content */} +
+ {/* Modal header */} +
+

+ Edit Task +

+ +
+ {/* Modal body */} +
+
+
+
+
+ + { + setEditTitle(event.target.value); + }} value={editedTitle} className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5" placeholder="Type task name" required /> +
+
+ + +
+
+ + +
+
+ + { + setEditStart(event.target.value); + }} value={editedStart} className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5" /> +
+
+ + { + setEditEnd(event.target.value); + }} value={editedEnd} className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5" /> +
+
+ + +
+
+
+ + +
+
+
+ +
+
+
+
+ + ); +} + +export default EditTask; diff --git a/src/components/LandingPage.js b/src/components/LandingPage.js new file mode 100644 index 0000000..0b5d453 --- /dev/null +++ b/src/components/LandingPage.js @@ -0,0 +1,24 @@ +import React from 'react'; +import backgroundImage from '../assets/todoo.avif'; + +function LandingPage() { + return ( +
+ + ); +} + +export default LandingPage; diff --git a/src/components/Task.js b/src/components/Task.js new file mode 100644 index 0000000..6cd605a --- /dev/null +++ b/src/components/Task.js @@ -0,0 +1,100 @@ +import { useState } from 'react'; +import EditTask from './EditTask'; + +function Task({ todo, taskView, handleView, handleEditList }) { + const [isEditing, setIsEditing] = useState(false); + + const handleEdit = () => { + setIsEditing(true); + }; + + const handleCancel = () => { + setIsEditing(false); + }; + + return ( + <> + {/* Main modal */} + {taskView && ( +
+
+ {/* Modal header */} +
+

+ {todo.title} +

+ +
+ {/* Modal body */} +
+
+ {isEditing ? ( + + ) : ( + + )} +
+
+
+ +

{todo.title}

+
+
+ +

{todo.level}

+
+
+ +

{todo.user ? todo.user.name : "Not Assigned"}

+
+
+ +

{todo.startDate ? todo.startDate : "--:--:--"}

+ +
+
+ +

{todo.endDate ? todo.startDate : "--:--:--"}

+
+
+ +

{todo.description}

+
+
+ +
+
+
+ ) + } + + ); + +} +export default Task; \ No newline at end of file diff --git a/src/components/TodoItem.js b/src/components/TodoItem.js new file mode 100644 index 0000000..5570239 --- /dev/null +++ b/src/components/TodoItem.js @@ -0,0 +1,51 @@ +import React, { useState } from "react"; +import Task from "./Task"; + +function TodoItem({ todoId, todo, handleDelete, handleEditList }) { + const [isDone, setDone] = useState(todo.done); + const [taskView, setTaskView] = useState(false); + + const handleDone = () => { + setDone((prevDone) => !prevDone); // Use functional state update for consistency + handleEditList({ ...todo, done: !isDone }); + }; + + const handleView = () => { + setTaskView((prevView) => !prevView); // Functional state update + }; + + return ( +
  • +
    + + + {todo.title} + +
    + handleDelete(todoId)} + className="text-sm text-red-500 hover:underline" + > + Delete + + {taskView && ( + + )} +
  • + ); +} + +export default TodoItem; diff --git a/src/components/TodoList.js b/src/components/TodoList.js new file mode 100644 index 0000000..93663b2 --- /dev/null +++ b/src/components/TodoList.js @@ -0,0 +1,110 @@ +import { useState, useEffect } from "react"; +import TodoItem from "./TodoItem"; +import AddTask from "./AddTask"; + +function TodoList({ todoData }) { + const options = ["All", "Active", "Completed"]; + + const [todoList, setTodoList] = useState(todoData); + const [listType, setType] = useState("All"); + const [filteredList, setFilteredList] = useState(todoList); + const [searchQuery, setSearchQuery] = useState(""); + + const handleSearch = (event) => { + const query = event.target.value; + setSearchQuery(query); + + const searchList = todoList.filter((todo) => { + return todo.title.toLowerCase().indexOf(query.toLowerCase()) !== -1; + }); + + setFilteredList(searchList); + }; + + const handleDelete = (todoId) => { + console.log(todoId); + const updatedList = todoList.filter((item) => item.id !== todoId); + console.log(updatedList); + setTodoList(updatedList); + + if (listType === "Completed") { + setFilteredList(updatedList.filter(todo => todo.done)); + } else if (listType === "Active") { + setFilteredList(updatedList.filter(todo => !todo.done)); + } else { + setFilteredList(updatedList); + } + }; + + const handleEditList = (updatedTask) => { + console.log(todoList); + console.log(updatedTask); + setTodoList((todoList) => + todoList.map((todo) => + todo.id === updatedTask.id ? updatedTask : todo + ) + ); + console.log(todoList); + + }; + + const handleSelect = (type) => { + setType(type); + if (type === "All") { + setFilteredList(todoList); + } else if (type === "Completed") { + setFilteredList(todoList.filter(todo => todo.done)); + } else if (type === "Active") { + setFilteredList(todoList.filter(todo => !todo.done)); + } + } + + return ( +
    +
    +
    +

    T O D O

    + +
    + +
      + {options.map((type, index) => ( +
    • handleSelect(type)} + className={`cursor-pointer text-gray-600 hover:text-gray-900 ${listType === type ? "font-bold text-blue-600" : "" + }`} + > + {type} +
    • + ))} +
    + { + filteredList.map((todo) => ( + + )) + } +
    +
    + ); +} + +export default TodoList; diff --git a/src/index.css b/src/index.css index ec2585e..a1ef556 100644 --- a/src/index.css +++ b/src/index.css @@ -1,4 +1,8 @@ -body { +@tailwind base; +@tailwind components; +@tailwind utilities; + +/* body { margin: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', @@ -10,4 +14,4 @@ body { code { font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace; -} +} */ \ No newline at end of file diff --git a/src/todoData.js b/src/todoData.js index d12d26f..386eed3 100644 --- a/src/todoData.js +++ b/src/todoData.js @@ -2,7 +2,7 @@ const todoData = [ { id: 1, title: "Go bungee jumping", - done: false, + done: true, }, { id: 2, @@ -17,12 +17,12 @@ const todoData = [ { id: 4, title: "Write a book", - done: false, + done: true, }, { id: 5, title: "Travel to every continent", - done: false, + done: true, }, { id: 6, diff --git a/src/usersData.js b/src/usersData.js new file mode 100644 index 0000000..9587c45 --- /dev/null +++ b/src/usersData.js @@ -0,0 +1,22 @@ +export const users = [ + { + id: 1, + name: "Alex" + }, + { + id: 2, + name: "John" + }, + { + id: 3, + name: "Jessica" + }, + { + id: 4, + name: "Marco" + }, + { + id: 5, + name: "Rebecca" + } +] \ No newline at end of file diff --git a/tailwind.config.js b/tailwind.config.js new file mode 100644 index 0000000..e5b64f3 --- /dev/null +++ b/tailwind.config.js @@ -0,0 +1,9 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: ["./src/**/*.{html,js}"], + theme: { + extend: {}, + }, + plugins: [], +} +