Skip to content

Commit

Permalink
Merge pull request #3 from AbdusSattar-70/display-todo
Browse files Browse the repository at this point in the history
Display todo: Add TodoItem and TodoList components
  • Loading branch information
Abdus Sattar authored Feb 10, 2024
2 parents 9ea01f0 + 0c6cad7 commit a71e8b9
Show file tree
Hide file tree
Showing 13 changed files with 379 additions and 41 deletions.
2 changes: 1 addition & 1 deletion src/components/DefaultLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const DefaultLayout = () => {
<div className="flex h-screen overflow-hidden">
<div className="relative flex flex-1 flex-col overflow-y-auto overflow-x-hidden">
<main>
<div className="mx-auto max-w-screen-2xl p-4 md:p-6 2xl:p-10">
<div className="mx-auto max-w-screen-2xl px-4 md:px-6 2xl:px-4">
<Suspense fallback={<Spinner />}>
<Outlet />
</Suspense>
Expand Down
22 changes: 7 additions & 15 deletions src/components/Home.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,14 @@
import CreateTodo from "./Todo/CreateTodo";
import TodoList from "./Todo/TodoList";
import TodoManagement from "./Todo/TodoManagement";

const Home = () => {
return (
<section className="w-full">
<div className="hero hero-content">
<div className=" card w-full flex-shrink-0 bg-slate-700 shadow-2xl">
<div className="text-center">
<img
className="mx-auto w-48"
src="https://tecdn.b-cdn.net/img/Photos/new-templates/bootstrap-login-form/lotus.webp"
alt="logo"
/>
<h4 className="mb-4 mt-1 pb-1 text-xl font-semibold">
Manage Your Tasks
</h4>
</div>
<div className="card-body">
<CreateTodo />
<div className="hero hero-content max-h-screen mix-blend-hard-light">
<div className="card w-full flex-shrink-0 bg-slate-700 shadow-2xl">
<TodoManagement />
<div className="card-body max-h-96 overflow-auto pt-0">
<TodoList />
</div>
</div>
</div>
Expand Down
62 changes: 41 additions & 21 deletions src/components/Todo/CreateTodo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ import { BiSolidAddToQueue } from "react-icons/bi";
import PrioritySelector from "./PrioritySelector";
import { Priority } from "../../utils/types";

const CreateTodo = () => {
interface CreateTodoProps {
AddTodoModalRef: React.RefObject<HTMLDialogElement>;
}

const CreateTodo: React.FC<CreateTodoProps> = ({ AddTodoModalRef }) => {
const dispatch = useDispatch();
const todos = useSelector((state: RootState) => state.todos.todos);
const [newTodoText, setNewTodoText] = useState("");
Expand All @@ -28,32 +32,48 @@ const CreateTodo = () => {
dispatch(addTodo(newTodo));
setNewTodoText("");
setPriority(Priority.Low);
AddTodoModalRef.current?.close();
}
};

return (
<form
onSubmit={handleAddTodo}
className="grid grid-cols-1 gap-4 border border-blue-600 p-4 sm:grid-cols-3"
<dialog
ref={AddTodoModalRef}
className="modal text-slate-200 sm:modal-middle"
>
<div className="form-control gap-1">
<label htmlFor="addTodoInput">Add Todo</label>
<input
id="addTodoInput"
className="input input-bordered bg-slate-800"
type="text"
placeholder="Add Todo"
value={newTodoText}
onChange={(e) => setNewTodoText(e.target.value)}
/>
<div className="modal-box bg-slate-600">
<form
onSubmit={handleAddTodo}
className="mx-2 mt-2 grid grid-cols-1 gap-2 bg-slate-600 p-2 sm:grid-cols-3"
>
<div className="form-control gap-1">
<label htmlFor="addTodoInput" className="sr-only">
Add Todo
</label>
<input
id="addTodoInput"
className="input input-bordered bg-slate-800"
type="text"
placeholder="Add Todo"
value={newTodoText}
onChange={(e) => setNewTodoText(e.target.value)}
/>
</div>
<PrioritySelector value={priority} onChange={setPriority} />
<button className="btn btn-primary">
<BiSolidAddToQueue className="text-3xl" />
</button>
</form>
<div className="modal-action">
<button
className="btn btn-sm"
onClick={() => AddTodoModalRef.current?.close()}
>
Close
</button>
</div>
</div>

<PrioritySelector value={priority} onChange={setPriority} />

<button className="btn btn-primary mt-6 place-self-stretch">
<BiSolidAddToQueue className="text-3xl" />
</button>
</form>
</dialog>
);
};

Expand Down
70 changes: 70 additions & 0 deletions src/components/Todo/EditTodo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import PrioritySelector from "./PrioritySelector";
import { Priority } from "../../utils/types";
import { editTodo } from "../../redux/todoSlice";
import { RootState } from "../../redux/store";

interface EditTodoProps {
todoId: number;
editModalRef: React.RefObject<HTMLDialogElement>;
}

const EditTodo: React.FC<EditTodoProps> = ({ todoId, editModalRef }) => {
const dispatch = useDispatch();
const todos = useSelector((state: RootState) => state.todos.todos);
const selectedTodo = todos.find((todo) => todo.id === todoId);
const [updatedText, setUpdatedText] = useState(selectedTodo?.text || "");
const [priority, setPriority] = useState<Priority>(
selectedTodo?.priority || Priority.Low
);

const saveEditing = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
if (updatedText.trim() !== "") {
const updatedTodo = {
id: todoId,
text: updatedText,
priority,
};
dispatch(editTodo(updatedTodo));
editModalRef.current?.close();
}
};

return (
<dialog ref={editModalRef} className="modal text-slate-200 sm:modal-middle">
<div className="modal-box bg-slate-600">
<form
onSubmit={saveEditing}
className="mx-2 mt-2 grid grid-cols-1 gap-2 p-2"
>
<div className="form-control gap-1">
<label htmlFor="addTodoInput" className="sr-only">
Update Todo
</label>
<input
id="addTodoInput"
className="input input-bordered bg-slate-800"
type="text"
value={updatedText}
onChange={(e) => setUpdatedText(e.target.value)}
/>
</div>
<PrioritySelector value={priority} onChange={setPriority} />
<button className="btn btn-success">Update</button>
</form>
<div className="modal-action">
<button
className="btn btn-sm"
onClick={() => editModalRef.current?.close()}
>
Close
</button>
</div>
</div>
</dialog>
);
};

export default EditTodo;
38 changes: 38 additions & 0 deletions src/components/Todo/FilterBy.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { FilterTodos } from "../../utils/types";

interface FilterByProps {
filterValue: string;
setFilterValue: (value: string) => void;
}

const FilterBy: React.FC<FilterByProps> = ({ filterValue, setFilterValue }) => {
const handleTodoFilter = (event: React.ChangeEvent<HTMLSelectElement>) => {
setFilterValue(event.target.value);
};

return (
<div className="form-control gap-1">
<label htmlFor="filterTodo" className="sr-only">
Filter Todo
</label>
<select
id="filterTodo"
name="filterTodo"
value={filterValue}
onChange={handleTodoFilter}
className="select select-bordered bg-slate-800"
>
<option value="" disabled>
Filter By
</option>
{Object.values(FilterTodos).map((todo) => (
<option key={todo} value={todo}>
{todo}
</option>
))}
</select>
</div>
);
};

export default FilterBy;
4 changes: 3 additions & 1 deletion src/components/Todo/PrioritySelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ const PrioritySelector: React.FC<PrioritySelectorProps> = ({

return (
<div className="form-control gap-1">
<label htmlFor="priority">Set Priority Level</label>
<label htmlFor="priority" className="sr-only">
Set Priority Level
</label>
<select
id="priority"
name="priority"
Expand Down
75 changes: 75 additions & 0 deletions src/components/Todo/TodoItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { useRef, useState } from "react";
import { GrUpdate } from "react-icons/gr";
import { FaRegTrashCan } from "react-icons/fa6";
import { getPriorityColorClass } from "../../utils/getPriorityColor";
import EditTodo from "./EditTodo";
import { TodoType } from "../../utils/types";
import { useDispatch } from "react-redux";
import { removeTodo, toggleTodo } from "../../redux/todoSlice";
import { DELETE_CONFIRMATION } from "../../utils/constant";

interface TodoItemProps {
todo: TodoType;
}

const TodoItem: React.FC<TodoItemProps> = ({ todo }) => {
const dispatch = useDispatch();
const { id, text, priority, completed } = todo;
const [isHovered, setIsHovered] = useState(false);
const editModalRef = useRef<HTMLDialogElement | null>(null);

const handleDeleteTodo = () => {
const confirmation = window.confirm(DELETE_CONFIRMATION);
confirmation && dispatch(removeTodo(id));
};

const handleToggleTodo = () => {
dispatch(toggleTodo(id));
};

return (
<li
className={`flex flex-col items-start justify-start gap-2 border bg-slate-800 p-2 text-sm hover:bg-slate-600 sm:flex-row sm:items-center ${getPriorityColorClass(
priority
)}`}
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
>
<div className="flex items-center justify-start gap-2">
<label className="label cursor-pointer">
<input
type="checkbox"
checked={completed}
onChange={handleToggleTodo}
className={`checkbox ${
completed ? "checkbox-success opacity-50" : "checkbox-warning"
}`}
/>
</label>
<kbd className={`kbd bg-slate-600 ${completed ? "opacity-50" : ""}`}>
{priority}
</kbd>
</div>
<span className={completed ? "line-through opacity-50" : ""}>{text}</span>
{/* Actions */}
<div
className={`${
isHovered ? "flex items-center justify-center gap-2" : "hidden"
}`}
>
<button
className="btn btn-xs"
onClick={() => editModalRef.current?.showModal()}
>
<GrUpdate />
</button>
<button className="btn btn-xs" onClick={handleDeleteTodo}>
<FaRegTrashCan />
</button>
</div>
<EditTodo editModalRef={editModalRef} todoId={id} />
</li>
);
};

export default TodoItem;
26 changes: 26 additions & 0 deletions src/components/Todo/TodoList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// TodoList.tsx
import { useSelector } from "react-redux";
import { RootState } from "../../redux/store";
import TodoItem from "./TodoItem";

const TodoList = () => {
const todos = useSelector((state: RootState) => state.todos.todos);

return (
<>
<div className="mx-auto mt-8 w-full">
<ul>
{todos.length > 0 ? (
todos.map((todo) => <TodoItem key={todo.id} todo={todo} />)
) : (
<li className="border p-4 text-center text-slate-100">
No Data Found
</li>
)}
</ul>
</div>
</>
);
};

export default TodoList;
Loading

0 comments on commit a71e8b9

Please sign in to comment.