Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Todo Task listing UI #8

Merged
merged 3 commits into from
Dec 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions app/tasks/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
"use client";
import { useEffect, useState } from "react";
import { TaskHeader } from "@/components/TaskHeader";
import { TaskList } from "@/components/TaskList";
import tasksData from "@/data/taskData.json";
import { Task } from "@/app/types/tasks";

const Tasks = () => {
const [tasks, setTasks] = useState<Task[]>([]);

useEffect(() => {
setTasks(tasksData);
}, []);

const todoTasks = tasks.filter((task) => task.status === "todo");
const inProgressTasks = tasks.filter((task) => task.status === "in-progress");

return (
<div
data-testid="tasks-container"
className="md:w-5/6 lg:w-3/4 flex flex-col mx-auto"
>
<section data-testid="todo-section">
<TaskHeader title="To Do" />
<TaskList tasks={todoTasks} />
</section>

<section data-testid="in-progress-section">
<TaskHeader title="In Progress" icon="/assets/InProgressEllipse.svg" />
<TaskList tasks={inProgressTasks} />
</section>
</div>
);
};

export default Tasks;
8 changes: 0 additions & 8 deletions app/test/home.test.tsx

This file was deleted.

8 changes: 8 additions & 0 deletions app/types/task.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export type Task = {
id: number;
title: string;
assignee: string;
dueDate: string;
status: string;
profile?: string;
};
66 changes: 66 additions & 0 deletions components/TaskCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { Task } from "@/app/types/tasks";
import { DateFormats, DateUtil } from "@/utils/dateUtil";
import Image from "next/image";

interface TaskCardProps {
task: Task;
className?: string;
}

const getStatusImagePath = (status: string): string => {
switch (status.toLowerCase()) {
case "in-progress":
return "/assets/InProgressEllipse.svg";
case "todo":
default:
return "/assets/ToDoEllipse.svg";
}
};

export const TaskCard = ({ task, className }: TaskCardProps) => {
const statusImagePath = getStatusImagePath(task.status);

const formattedDueDate = new DateUtil(task.dueDate).format(
DateFormats.D_MMM_YYYY
);

return (
<div
className={`flex justify-between items-center px-6 py-3 mt-1 mx-4 border border-[#D0D5DD] rounded-lg ${className}`}
data-testid={`task-${task.id}`}
>
<div className="flex items-center justify-between">
<h3 className="text-sm sm:text-base font-medium text-[#74777B] mr-2">
#{task.id}
</h3>
<Image
src={statusImagePath}
alt="task-status-icon"
width={20}
height={20}
/>
<h2 className="text-sm sm:text-base ml-2 font-medium">{task.title}</h2>
</div>

<div className="flex items-center space-x-6">
<div className="flex text-[#74787E] items-center justify-center space-x-2">
<div className="hidden md:flex px-2 py-[2px] rounded-full border border-[#4541C6] bg-[#F5F5FF] text-xs">
{task.assignee}
</div>
<div className="px-2 py-[2px] rounded-full border border-[#4541C6] bg-[#F5F5FF] text-xs">
{formattedDueDate}
</div>
</div>

<div className="md:hidden">
<Image
src={task.profile || "/assets/user.png"}
alt="assignee-profile"
width={20}
height={20}
/>
</div>
</div>
</div>
);
};
38 changes: 38 additions & 0 deletions components/TaskHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import Image from "next/image";

interface TaskSectionProps {
className?: string;
icon?: string;
title: string;
onCreateTask?: () => void;
}

export const TaskHeader = ({
className,
icon,
title,
onCreateTask,
}: TaskSectionProps) => {
return (
<div
data-testid={`header-${title.toLowerCase().replace(" ", "-")}`}
className={`flex justify-between items-center px-6 py-3 bg-[#F5F5FF] mt-6 mb-2 mx-4 rounded-lg ${className}`}
>
<div className="flex items-center">
<Image
src={icon || "/assets/ToDoEllipse.svg"}
alt="header-icon"
width={20}
height={20}
/>
<h2 className="text-[#4541C6] text-base sm:text-lg ml-2 font-medium">
{title}
</h2>
</div>

<button onClick={onCreateTask}>
<Image src="/assets/plus.svg" alt="plusIcon" width={20} height={20} />
</button>
</div>
);
};
30 changes: 30 additions & 0 deletions components/TaskList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Task } from "@/app/types/tasks";
import { TaskCard } from "./TaskCard";

interface TaskListProps {
tasks: Task[];
}

export const TaskList = ({ tasks }: TaskListProps) => {
if (tasks.length === 0) {
return (
<div
className="text-gray-500 text-center py-8"
data-testid="empty-task-list"
>
No tasks available in this section
</div>
);
}
return (
<div data-testid={`task-list`}>
{tasks.map((task) => (
<TaskCard
key={task.id}
task={task}
className="transition-transform hover:scale-[1.01]"
/>
))}
</div>
);
};
34 changes: 34 additions & 0 deletions data/taskData.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
[
{
"id": 1,
"title": "Todo Listing",
"assignee": "Vaibhav Singh",
"dueDate": "2023-12-15",
"status": "todo",
"profile": "/assets/user.png"
},
{
"id": 2,
"title": "Prepare Design Doc",
"assignee": "Random 1",
"dueDate": "2023-12-10",
"status": "todo",
"profile": "/assets/user.png"
},
{
"id": 3,
"title": "Code Todo Frontend",
"assignee": "Random 2",
"dueDate": "2023-12-10",
"status": "in-progress",
"profile": "/assets/user.png"
},
{
"id": 4,
"title": "Code Todo Backend",
"assignee": "Random 3",
"dueDate": "2023-12-10",
"status": "in-progress",
"profile": "/assets/user.png"
}
]
4 changes: 4 additions & 0 deletions public/assets/InProgressEllipse.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions public/assets/ToDoEllipse.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions public/assets/plus.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/assets/user.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.