Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
0b1b20c
feat(web): Today 페이지 스크롤 레이아웃 및 날짜 헤더 컨테이너 추가 (#141)
ehye1 Jul 11, 2026
268a56c
refactor(web): TodayTodoCardContainer isPlaying 상태를 prop 기반 파생값으로 변경 …
ehye1 Jul 11, 2026
5ed648c
chore(web): 스크롤 확인용 todo 목데이터 추가 (#141)
ehye1 Jul 11, 2026
7191daf
style(web): 투두 리스트 스크롤 오른쪽 패딩 추가 (#141)
ehye1 Jul 12, 2026
bab2e47
refactor(web): TodayDayHeaderContainer 리네임 및 DateInformation 공통 컴포넌트 …
ehye1 Jul 12, 2026
5242acd
file(web): 투두 모달 컴포넌트·훅 공통 경로로 이동 (#141)
ehye1 Jul 13, 2026
6998c9d
feat(web): 오늘 화면 투두카드 인터랙션 구현 (#141)
ehye1 Jul 13, 2026
7a664fe
refactor(web): 투두 생성 모달 공통 컴포넌트 적용 (#141)
ehye1 Jul 13, 2026
2e51673
fix(web): OAuth 콜백 페이지 수정 (#141)
ehye1 Jul 13, 2026
d3d34b9
fix(ui): Dropdown 컴포넌트 수정 (#141)
ehye1 Jul 13, 2026
4862d81
refactor(web): TodayTodoCard 툴바 비활성화 및 카드 클릭 구조로 전환 (#141)
ehye1 Jul 13, 2026
1e10160
refactor(web): TodayTodoListContainer 상태 로직을 useTodayTodoList 훅으로 분리 …
ehye1 Jul 13, 2026
1e465d2
fix(web): HomeDayHeaderContainer getToday 임포트 경로 수정 (#141)
ehye1 Jul 13, 2026
279d5f7
fix(web): HomeDayHeaderContainer parseDateKey 임포트 경로 수정 (#141)
ehye1 Jul 13, 2026
7912b5b
fix(web): TodayTodoCard 툴바 아이콘 색상 및 카드 상호작용 리팩토링 (#141)
ehye1 Jul 13, 2026
504afc4
fix(web): CI lint/type 에러 수정 (#141)
ehye1 Jul 13, 2026
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
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { TodayBadge } from "@repo/timo-design-system/ui";
import { cn } from "@repo/timo-design-system/utils";

interface HomeDateInformationProps {
interface DateInformationProps {
date: string;
dayOfWeek: string;
isHoliday: boolean;
Expand All @@ -10,14 +10,14 @@ interface HomeDateInformationProps {
completedCount: number;
}

export const HomeDateInformation = ({
export const DateInformation = ({
date,
dayOfWeek,
isHoliday,
isToday,
totalCount,
completedCount,
}: HomeDateInformationProps) => {
}: DateInformationProps) => {
return (
<div className="flex w-full flex-col gap-3">
<div className="flex w-full flex-col gap-0.5">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { useTranslations } from "next-intl";

import type { ApiDayOfWeek } from "@/app/[locale]/(main)/(with-time-sidebar)/home/_types/home-view-type";

import { HomeDateInformation } from "@/app/[locale]/(main)/(with-time-sidebar)/home/_components/todo-card/HomeDateInformation";
import { DateInformation } from "@/app/[locale]/(main)/(with-time-sidebar)/_components/DateInformation";
import { CreateTodoModalContainer } from "@/app/[locale]/(main)/(with-time-sidebar)/home/_containers/todo-modal/CreateTodoModalContainer";
import { convertDateToDateText } from "@/app/[locale]/(main)/(with-time-sidebar)/home/_utils/date";
import { getToday, parseDateKey } from "@/utils/date";
Expand All @@ -31,7 +31,7 @@ export const HomeDayHeaderContainer = ({

return (
<div className="flex flex-col gap-3 pb-2">
<HomeDateInformation
<DateInformation
date={convertDateToDateText(date)}
dayOfWeek={tCommon(`weekday.${dayOfWeek}`)}
isHoliday={isHoliday}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import { AddTaskButton } from "@repo/timo-design-system/ui";
import { useTranslations } from "next-intl";
import { overlay } from "overlay-kit";

import { CreateTodoModalContent } from "@/app/[locale]/(main)/(with-time-sidebar)/home/_containers/todo-modal/CreateTodoModalContent";
import { useCreateTodoSubmit } from "@/app/[locale]/(main)/(with-time-sidebar)/home/_hooks/todo-modal/use-create-todo-submit";
import { AnimatedToast } from "@/components/toast/AnimatedToast";
import { CreateTodoModalContent } from "@/components/todo-modal/CreateTodoModalContent";

export interface CreateTodoModalContainerProps {
defaultDate?: Date;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
"use client";

import { useTranslations } from "next-intl";

import type { TodoMock } from "@/app/[locale]/(main)/(with-time-sidebar)/today/_mocks/today-todo-mock";

import { DateInformation } from "@/app/[locale]/(main)/(with-time-sidebar)/_components/DateInformation";
import { convertDateToDateText } from "@/app/[locale]/(main)/(with-time-sidebar)/home/_utils/date";
import { CreateTodoModalContainer } from "@/app/[locale]/(main)/(with-time-sidebar)/today/_containers/todo-modal/CreateTodoModalContainer";
import { getToday } from "@/utils/date";
import { getDayOfWeekKey } from "@/utils/get-day-of-week-key";

interface TodayDateHeaderContainerProps {
completedCount: number;
totalCount: number;
onCreateTodo: (todo: TodoMock) => void;
}

export const TodayDateHeaderContainer = ({
completedCount,
totalCount,
onCreateTodo,
}: TodayDateHeaderContainerProps) => {
const tCommon = useTranslations("Common");

const today = getToday();
const date = convertDateToDateText(today);
const dayKey = getDayOfWeekKey(today);
const dayOfWeek = tCommon(`weekday.${dayKey}`);

return (
<div className="flex flex-col gap-3 pb-2">
<DateInformation
date={date}
dayOfWeek={dayOfWeek}
isHoliday={false}
isToday={true}
totalCount={totalCount}
completedCount={completedCount}
/>
<CreateTodoModalContainer defaultDate={today} onCreate={onCreateTodo} />
</div>
);
};
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use client";

import { useEffect, useState } from "react";
import { useState } from "react";

import type { ReactNode } from "react";

Expand All @@ -17,35 +17,29 @@ export interface TodayTodoCardContainerProps {
toolbar: TodayTodoCardToolbar;
timerStatus: "RUNNING" | "PAUSED" | "STOPPED";
icon?: ReactNode;
onIconClick?: () => void;
onCardClick?: () => void;
onCheck?: () => void;
onPlay?: () => void;
onDelete?: () => void;
onSubTodoCheck?: (id: number) => void;
}

export const TodayTodoCardContainer = ({
title,
isDone: initialIsDone,
icon,
onIconClick,
subTodos: initialSubTodos,
toolbar,
timerStatus,
onCardClick,
onCheck,
onPlay,
onDelete,
onSubTodoCheck,
}: TodayTodoCardContainerProps) => {
const [isHovered, setIsHovered] = useState(false);
const [isPlaying, setIsPlaying] = useState(timerStatus === "RUNNING");
const [isDone, setIsDone] = useState(initialIsDone);
const [subTodos, setSubTodos] = useState(initialSubTodos);

useEffect(() => {
setIsPlaying(timerStatus === "RUNNING");
}, [timerStatus]);

const isPlaying = timerStatus === "RUNNING";
const isDimmed = isDone && !isHovered;

const handleCheck = () => {
Expand All @@ -54,7 +48,6 @@ export const TodayTodoCardContainer = ({
if (next) {
setSubTodos((prev) => prev.map((s) => ({ ...s, isDone: true })));
if (isPlaying) {
setIsPlaying(false);
onPlay?.();
}
}
Expand All @@ -63,7 +56,6 @@ export const TodayTodoCardContainer = ({

const handlePlay = () => {
if (!isDone) {
setIsPlaying((prev) => !prev);
onPlay?.();
}
};
Expand All @@ -82,12 +74,11 @@ export const TodayTodoCardContainer = ({
isDimmed={isDimmed}
isPlaying={isPlaying}
icon={icon}
onIconClick={onIconClick}
subTodos={subTodos}
toolbar={toolbar}
onCardClick={onCardClick}
onCheck={handleCheck}
onPlay={handlePlay}
onDelete={onDelete ?? (() => {})}
onSubTodoCheck={handleSubTodoCheck}
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
"use client";

import { useState } from "react";

import type { TodoMock } from "@/app/[locale]/(main)/(with-time-sidebar)/today/_mocks/today-todo-mock";

import { TodayDateHeaderContainer } from "@/app/[locale]/(main)/(with-time-sidebar)/today/_containers/TodayDateHeaderContainer";
import { TodayTodoCardContainer } from "@/app/[locale]/(main)/(with-time-sidebar)/today/_containers/TodayTodoCardContainer";
import { todayTodoMocks } from "@/app/[locale]/(main)/(with-time-sidebar)/today/_mocks/today-todo-mock";
import { useTodayTodoList } from "@/app/[locale]/(main)/(with-time-sidebar)/today/_hooks/useTodayTodoList";
import { convertDurationToTimeText } from "@/utils/convert-duration-to-time-text";
import { formatDate } from "@/utils/date";

Expand All @@ -17,84 +14,51 @@ const PRIORITY_MAP = {
} as const;

export const TodayTodoListContainer = () => {
const [todos, setTodos] = useState<TodoMock[]>(todayTodoMocks);
const runningTodoId =
todos.find((t) => t.timerStatus === "RUNNING")?.todoId ?? null;

const handlePlay = (todoId: number) => {
// TODO: API
setTodos((prev) =>
prev.map((t) => ({
...t,
timerStatus:
t.todoId === todoId && t.timerStatus !== "RUNNING"
? "RUNNING"
: "STOPPED",
})),
);
};

const handleCheck = (todoId: number) => {
// TODO: API
setTodos((prev) =>
prev.map((t) =>
t.todoId === todoId
? { ...t, completed: !t.completed, timerStatus: "STOPPED" }
: t,
),
);
};

const handleDelete = (todoId: number) => {
// TODO: API
setTodos((prev) => prev.filter((t) => t.todoId !== todoId));
};
const {
todos,
runningTodoId,
handlePlay,
handleCheck,
handleAddTodo,
handleSubTodoCheck,
} = useTodayTodoList();

const handleSubTodoCheck = (todoId: number, subtaskId: number) => {
// TODO: API
setTodos((prev) =>
prev.map((t) =>
t.todoId === todoId
? {
...t,
subtasks: t.subtasks.map((s) =>
s.subtaskId === subtaskId
? { ...s, completed: !s.completed }
: s,
),
}
: t,
),
);
};
const completedCount = todos.filter((todo) => todo.completed).length;

return (
<div className="flex flex-col gap-2">
{todos.map((todo) => (
<TodayTodoCardContainer
key={todo.todoId}
title={todo.title}
isDone={todo.completed}
timerStatus={runningTodoId === todo.todoId ? "RUNNING" : "STOPPED"}
toolbar={{
date: formatDate(todo.date),
time: convertDurationToTimeText(todo.durationSeconds),
priority: PRIORITY_MAP[todo.priority],
tag: todo.tag?.name,
hasMemo: todo.hasMemo,
hasRepeat: todo.isRepeated,
}}
subTodos={todo.subtasks.map((s) => ({
id: s.subtaskId,
text: s.content,
isDone: s.completed,
}))}
onPlay={() => handlePlay(todo.todoId)}
onCheck={() => handleCheck(todo.todoId)}
onDelete={() => handleDelete(todo.todoId)}
onSubTodoCheck={(id) => handleSubTodoCheck(todo.todoId, id)}
/>
))}
<div className="flex h-full flex-col gap-2">
<TodayDateHeaderContainer
completedCount={completedCount}
totalCount={todos.length}
onCreateTodo={handleAddTodo}
/>
<div className="flex min-h-0 flex-1 flex-col gap-2 overflow-y-auto pr-1 pb-4">
{todos.map((todo) => (
<TodayTodoCardContainer
key={todo.todoId}
title={todo.title}
isDone={todo.completed}
timerStatus={runningTodoId === todo.todoId ? "RUNNING" : "STOPPED"}
toolbar={{
date: formatDate(todo.date),
dateValue: new Date(todo.date),
time: convertDurationToTimeText(todo.durationSeconds),
priority: PRIORITY_MAP[todo.priority],
tag: todo.tag?.name,
hasMemo: todo.hasMemo,
hasRepeat: todo.isRepeated,
}}
subTodos={todo.subtasks.map((s) => ({
id: s.subtaskId,
text: s.content,
isDone: s.completed,
}))}
onPlay={() => handlePlay(todo.todoId)}
onCheck={() => handleCheck(todo.todoId)}
onSubTodoCheck={(id) => handleSubTodoCheck(todo.todoId, id)}
/>
))}
</div>
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
"use client";

import { AddTaskButton } from "@repo/timo-design-system/ui";
import { useTranslations } from "next-intl";
import { overlay } from "overlay-kit";

import type { TodoMock } from "@/app/[locale]/(main)/(with-time-sidebar)/today/_mocks/today-todo-mock";

import { useCreateTodoSubmit } from "@/app/[locale]/(main)/(with-time-sidebar)/today/_hooks/todo-modal/use-create-todo-submit";
import { CreateTodoModalContent } from "@/components/todo-modal/CreateTodoModalContent";

export interface CreateTodoModalContainerProps {
defaultDate?: Date;
onCreate: (todo: TodoMock) => void;
}

export const CreateTodoModalContainer = ({
defaultDate,
onCreate,
}: CreateTodoModalContainerProps) => {
const t = useTranslations("Home");
const { handleSubmit } = useCreateTodoSubmit({ onCreate });

const handleAddClick = () => {
overlay.open(({ isOpen, close, unmount }) => (
<CreateTodoModalContent
isOpen={isOpen}
onClose={close}
onExited={unmount}
defaultDate={defaultDate}
onSubmit={handleSubmit}
/>
));
};

return (
<AddTaskButton variant="big" text={t("addTask")} onClick={handleAddClick} />
);
};
Loading
Loading