Skip to content
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
504 changes: 43 additions & 461 deletions src/app/(protected)/(root)/_components/InProgressTaskItem.tsx

Large diffs are not rendered by default.

34 changes: 34 additions & 0 deletions src/app/(protected)/(root)/_components/NoTaskScreen.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import Image from "next/image";
import { memo } from "react";

interface NoTaskScreenProps {
firstText: string;
secondText: string;
thirdText: string;
}

const NoTaskScreen = ({
firstText,
secondText,
thirdText,
}: NoTaskScreenProps) => {
return (
<div className="mb-[40px]">
<div className="flex flex-col items-center justify-center">
<Image
src="/icons/home/xman.svg"
alt="Character"
width={80}
height={80}
className="mb-[48px] mt-[60px]"
priority
/>
<h2 className="t3 text-center text-text-strong">{firstText}</h2>
<h2 className="t3 mb-2 text-center text-text-strong">{secondText}</h2>
<p className="b3 text-center text-text-alternative">{thirdText}</p>
</div>
</div>
);
};

export default memo(NoTaskScreen);
9 changes: 5 additions & 4 deletions src/app/(protected)/(root)/_components/TaskItem.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"use client";

import { useResetAlerts } from "@/hooks/useTasks";
import type { TaskStatus } from "@/types/task";
import {
calculateRemainingTime,
Expand All @@ -9,7 +10,7 @@ import {
import Image from "next/image";
import { useRouter } from "next/navigation";
import type React from "react";
import { useCallback, useEffect, useState } from "react";
import { memo, useCallback, useEffect, useState } from "react";

type TaskItemProps = {
title: string;
Expand All @@ -21,7 +22,6 @@ type TaskItemProps = {
onPreviewStart?: (taskId?: number) => void;
ignoredAlerts?: number;
timeRequired: string;
resetAlerts?: (taskId: number) => void;
dueDatetime?: string;
status?: TaskStatus;
};
Expand All @@ -36,7 +36,6 @@ const TaskItem: React.FC<TaskItemProps> = ({
onDelete,
onPreviewStart = () => {},
ignoredAlerts = 0,
resetAlerts = () => {},
dueDatetime,
status,
}) => {
Expand All @@ -46,6 +45,8 @@ const TaskItem: React.FC<TaskItemProps> = ({
const [remainingTime, setRemainingTime] = useState<string>("");
const [isUrgent, setIsUrgent] = useState<boolean>(false);

const resetAlerts = useResetAlerts();

// 진행 중인 태스크인지 확인
const isInProgress = status === "inProgress";

Expand Down Expand Up @@ -281,4 +282,4 @@ const TaskItem: React.FC<TaskItemProps> = ({
);
};

export default TaskItem;
export default memo(TaskItem);
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import type { Task } from "@/types/task";
import Image from "next/image";
import React from "react";
import AllTasksTab from "./allTasksTab/AllTasksTab";

interface AllTaskTabWrapperProps {
isAllEmpty: boolean;
inProgressTasks: Task[];
todayTasks: Task[];
weeklyTasks: Task[];
futureTasks: Task[];
onTaskClick: (task: Task) => void;
onDeleteTask: (taskId: number) => void;
}

const AllTaskTabWrapper = ({
isAllEmpty,
inProgressTasks,
todayTasks,
weeklyTasks,
futureTasks,
onTaskClick,
onDeleteTask,
}: AllTaskTabWrapperProps) => {
return isAllEmpty ? (
<div className="mt-[130px]">
<div className="flex h-full flex-col items-center justify-center px-4 text-center">
<div className="mb-[40px]">
<Image
src="/icons/home/rocket.svg"
alt="Rocket"
width={142}
height={80}
className="mx-auto"
priority
/>
</div>
<h2 className="t3 mb-[8px] text-text-strong">
이번주 할일이 없어요.
<br />
마감할 일을 추가해볼까요?
</h2>
<p className="b3 text-text-alternative">
미루지 않도록 알림을 보내 챙겨드릴게요.
</p>
</div>
</div>
) : (
<AllTasksTab
inProgressTasks={inProgressTasks}
todayTasks={todayTasks}
weeklyTasks={weeklyTasks}
futureTasks={futureTasks}
onTaskClick={onTaskClick}
onDeleteTask={onDeleteTask}
/>
);
};

export default AllTaskTabWrapper;
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// TODO(prgmr99): Drawer 적용해야 함

import type { Task } from "@/types/task";
import { memo } from "react";

interface ExpiredTaskDrawerProps {
expiredTask: Task;
handleGoToReflection: (taskId: number) => void;
handleCloseExpiredSheet: () => void;
}

const ExpiredTaskDrawer = ({
expiredTask,
handleGoToReflection,
handleCloseExpiredSheet,
}: ExpiredTaskDrawerProps) => {
const { id, title, dueDate, dueDay, dueTime } = expiredTask;

return (
<div className="fixed inset-0 z-50 flex items-end justify-center bg-black bg-opacity-60">
<div className="flex w-full flex-col items-center rounded-t-[28px] bg-component-gray-secondary p-4 pt-10">
<h2 className="t3 text-center text-text-strong">{title}</h2>
<p className="t3 mb-2 text-center text-text-strong">
작업이 끝났어요. 짧게 돌아볼까요?
</p>
<div className="flex w-full justify-between">
<p className="b3 mb-7 text-text-neutral">마감일 </p>
<p className="b3 mb-7 text-text-neutral">
{new Date(dueDate).toLocaleDateString("ko-KR", {
month: "long",
day: "numeric",
})}
({dueDay}), {dueTime}
</p>
</div>
<button
type="button"
className="l2 mb-3 w-full rounded-[16px] bg-component-accent-primary py-4 text-white"
onClick={() => handleGoToReflection(id)}
>
회고하기
</button>

<button
type="button"
className="l2 w-full py-4 text-text-neutral"
onClick={handleCloseExpiredSheet}
>
닫기
</button>
</div>
</div>
);
};

export default memo(ExpiredTaskDrawer);
32 changes: 32 additions & 0 deletions src/app/(protected)/(root)/_components/footer/Footer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { memo, useEffect, useState } from "react";
import TaskAddButton from "./taskAddButton/TaskAddButton";

const Footer = ({ onClick }: { onClick: () => void }) => {
const [showTooltip, setShowTooltip] = useState(true);

useEffect(() => {
const hasVisited = localStorage.getItem("hasVisitedBefore");

if (hasVisited) {
setShowTooltip(false);
} else {
localStorage.setItem("hasVisitedBefore", "true");
}
}, []);

return (
<footer className="fixed bottom-0 left-0 right-0 z-10">
<div
className="pointer-events-none absolute bottom-0 left-0 right-0 h-40"
style={{
background:
"linear-gradient(to bottom, rgba(15, 17, 20, 0) 0%, rgba(15, 17, 20, 1) 100%)",
}}
/>

<TaskAddButton showTooltip={showTooltip} onClick={onClick} />
</footer>
);
};

export default memo(Footer);
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { Button } from "@/components/ui/button";
import Image from "next/image";
import React, { memo } from "react";

const TaskAddButton = ({
showTooltip,
onClick,
}: { showTooltip: boolean; onClick: () => void }) => {
return (
<div className="relative flex justify-end p-5 pb-[47px]">
{showTooltip && (
<div className="b3 absolute bottom-[130px] right-4 rounded-[12px] bg-component-accent-primary px-4 py-3 text-text-strong shadow-lg">
지금 바로 할 일을 추가해보세요!
<div
className="absolute h-0 w-0"
style={{
bottom: "-11px",
right: "3rem",
transform: "translateX(50%)",
borderStyle: "solid",
borderWidth: "12px 7px 0 7px",
borderColor: "#6B6BE1 transparent transparent transparent",
}}
/>
</div>
)}
<Button
variant="point"
size="md"
className="l2 flex h-[52px] w-[130px] items-center gap-2 rounded-full py-[16.5px] text-text-inverse"
onClick={onClick}
>
<Image
src="/icons/home/plus.svg"
alt="할일 추가"
width={16}
height={16}
priority
/>
할일 추가
</Button>
</div>
);
};

export default memo(TaskAddButton);
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import useTaskFiltering from "@/hooks/useTaskFilter";
import { useHomeData, useResetAlerts, useStartTask } from "@/hooks/useTasks";
import type { Task } from "@/types/task";
import { useMemo } from "react";
import NoTaskScreen from "../NoTaskScreen";
import TaskItem from "../TaskItem";
import AllTaskButton from "./allTaskButton/AllTaskButton";

interface HasAllTasksOnlyScreenProps {
handleTaskClick: (task: Task) => void;
handleDeleteTask: (taskId: number) => void;
}

const HasAllTasksOnlyScreen = ({
handleTaskClick,
handleDeleteTask,
}: HasAllTasksOnlyScreenProps) => {
const resetAlerts = useResetAlerts();

const { mutate: startTaskMutation } = useStartTask();

const { data: homeData } = useHomeData();

const { allTasks } = useTaskFiltering(homeData);

/**
* * 마감이 임박한 순으로 정렬된 전체 할 일 (최대 2개)
*/
const topAllTasks = useMemo(() => {
return [...allTasks]
.sort(
(a, b) =>
new Date(a.dueDatetime).getTime() - new Date(b.dueDatetime).getTime(),
)
.slice(0, 2);
}, [allTasks]);

return (
<div className="mt-4">
<NoTaskScreen
firstText="이번주 마감할 일이 없어요."
secondText="급한 할일부터 시작해볼까요?"
thirdText="미루지 말고 여유있게 시작해보세요"
/>

<div className="mb-4">
{topAllTasks.map((task) => (
<TaskItem
key={task.id}
taskId={task.id}
onClick={() => handleTaskClick(task)}
onDelete={() => handleDeleteTask(task.id)}
onPreviewStart={(taskId) => taskId && startTaskMutation(taskId)}
{...task}
/>
))}
</div>

<AllTaskButton />
</div>
);
};

export default HasAllTasksOnlyScreen;
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import Image from "next/image";
import Link from "next/link";
import { memo } from "react";

const AllTaskButton = () => {
return (
<Link href="/?tab=all">
<div>
<button
type="button"
className="flex w-full items-center justify-between px-4 py-4"
>
<span className="s2 text-text-neutral">전체 할일 더보기</span>
<Image
src="/icons/home/arrow-right.svg"
alt="Arrow Right"
width={24}
height={24}
priority
/>
</button>
</div>
</Link>
);
};

export default memo(AllTaskButton);
Loading