From e2327b6abb1ded66fbb96e8aa96b38ae1f0bf033 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9E=84=EC=A7=80=EC=88=98?= Date: Fri, 19 Sep 2025 17:50:49 +0900 Subject: [PATCH 1/8] =?UTF-8?q?design:=20=EB=94=94=EC=9E=90=EC=9D=B8=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/page/todo/myTodo/MyTodo.css.ts | 30 ++--- .../TodoCheckSection/TodoCheckSection.tsx | 122 +++++++++--------- 2 files changed, 74 insertions(+), 78 deletions(-) diff --git a/src/page/todo/myTodo/MyTodo.css.ts b/src/page/todo/myTodo/MyTodo.css.ts index 8e0c1d37..63c96fab 100644 --- a/src/page/todo/myTodo/MyTodo.css.ts +++ b/src/page/todo/myTodo/MyTodo.css.ts @@ -32,7 +32,7 @@ export const contentWrapper = style({ alignItems: 'flex-start', width: '100%', maxWidth: '128rem', - gap: '8.6rem', // 헤더-데이트피커 간격 86px + gap: '8.6rem', }); export const datePickerSection = style({ @@ -124,19 +124,10 @@ export const checkMainContainer = style({ flexDirection: 'column', alignItems: 'flex-start', height: '67.2rem', - padding: '2.6rem 5rem 2.6rem', - gap: '1rem', + padding: '2.6rem', alignSelf: 'stretch', }); -export const mainContentSection = style({ - display: 'flex', - alignItems: 'flex-start', - gap: '1.9rem', - width: '100%', - height: '100%', -}); - export const todoCheckArea = style({ display: 'flex', flexDirection: 'column', @@ -147,6 +138,7 @@ export const todoCheckArea = style({ height: '100%', flexShrink: 0, paddingRight: '1.9rem', + boxSizing: 'border-box', }); export const selectorChipsContainer = style({ @@ -161,22 +153,25 @@ export const todoCheckContainer = style({ display: 'flex', flexDirection: 'column', alignItems: 'flex-start', - width: '57.1rem', + width: '100%', height: '53.8rem', gap: '2.4rem', alignSelf: 'stretch', overflowY: 'auto', + boxSizing: 'border-box', }); export const noScrollTodoCheckContainer = style({ display: 'flex', flexDirection: 'column', alignItems: 'flex-start', + width: '100%', height: '53.8rem', gap: '2.4rem', alignSelf: 'stretch', - overflowY: 'hidden', + overflow: 'hidden', paddingRight: '1.9rem', + boxSizing: 'border-box', }); export const todoCheckLine = style({ @@ -204,7 +199,7 @@ export const todoText = styleVariants({ export const emptyTodoBox = style({ display: 'flex', - width: '55.2rem', + width: '100%', height: '53.8rem', padding: '25.1rem 9.1rem', justifyContent: 'center', @@ -223,8 +218,11 @@ export const emptyTodoText = style({ export const mandalartWithTodoSection = style({ display: 'flex', - alignItems: 'center', - justifyContent: 'space-between', + alignItems: 'flex-start', + justifyContent: 'flex-start', + gap: '3.7rem', + width: '100%', + height: '100%', flex: 1, }); diff --git a/src/page/todo/myTodo/component/TodoCheckSection/TodoCheckSection.tsx b/src/page/todo/myTodo/component/TodoCheckSection/TodoCheckSection.tsx index 33951a1b..db8b3b09 100644 --- a/src/page/todo/myTodo/component/TodoCheckSection/TodoCheckSection.tsx +++ b/src/page/todo/myTodo/component/TodoCheckSection/TodoCheckSection.tsx @@ -104,69 +104,67 @@ const TodoCheckSection = ({
-
-
- ({ - id: idx < 4 ? idx : idx + 1, - title: goal.title, - position: goal.position, - subGoals: goal.subGoals ?? [], - }), - ) - : [], - }} - onGoalClick={(position) => { - const coreGoal = coreGoalsData?.data?.coreGoals.find( - (goal) => goal.position === position, - ); - const parentId = coreGoal?.id; - onMandalartClick(selectedParentId === parentId ? undefined : parentId); - }} - /> -
-
- {CYCLE_LIST.map((cycle) => ( - - ))} -
- -
- {localSubGoals.length === 0 ? ( -
- 해당하는 할 일이 없어요 +
+ ({ + id: idx < 4 ? idx : idx + 1, + title: goal.title, + position: goal.position, + subGoals: goal.subGoals ?? [], + }), + ) + : [], + }} + onGoalClick={(position) => { + const coreGoal = coreGoalsData?.data?.coreGoals.find( + (goal) => goal.position === position, + ); + const parentId = coreGoal?.id; + onMandalartClick(selectedParentId === parentId ? undefined : parentId); + }} + /> +
+
+ {CYCLE_LIST.map((cycle) => ( + + ))} +
+ +
+ {localSubGoals.length === 0 ? ( +
+ 해당하는 할 일이 없어요 +
+ ) : ( + localSubGoals.map((todo) => ( +
+ +
- ) : ( - localSubGoals.map((todo) => ( -
- - -
- )) - )} -
+ )) + )}
From ce6bd4e9121ada1758af768b7e4dce6655d5c87e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9E=84=EC=A7=80=EC=88=98?= Date: Fri, 19 Sep 2025 18:13:37 +0900 Subject: [PATCH 2/8] =?UTF-8?q?feat:=20=EC=8B=A4=EC=A0=9C=20=EB=82=A0?= =?UTF-8?q?=EC=A7=9C=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/page/todo/myTodo/hook/useMyTodo.ts | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/page/todo/myTodo/hook/useMyTodo.ts b/src/page/todo/myTodo/hook/useMyTodo.ts index 3e450c07..2f3a752b 100644 --- a/src/page/todo/myTodo/hook/useMyTodo.ts +++ b/src/page/todo/myTodo/hook/useMyTodo.ts @@ -3,7 +3,7 @@ import { useState, useEffect } from 'react'; import type { CycleType } from '../constant/mock'; import type { TodoItemTypes } from '@/page/todo/myTodo/component/TodoBox/TodoBox.types'; -import { createDate, formatDateDot } from '@/common/util/format'; +import { formatDateDot } from '@/common/util/format'; import { useGetRecommendation } from '@/api/domain/myTodo/hook/useGetRecommendation'; import { usePostRecommendation } from '@/api/domain/myTodo/hook/usePostRecommendation'; import { useDeleteRecommendation } from '@/api/domain/myTodo/hook/useDeleteRecommendation'; @@ -32,11 +32,12 @@ interface UseMyTodoProps { initialMyTodos?: TodoItemTypes[]; } -const MIN_DATE = createDate(2025, 1, 1); -const MAX_DATE = createDate(2025, 1, 31); +export const useMyTodo = ({ initialDate }: UseMyTodoProps = {}) => { + const defaultDate = initialDate ?? new Date(); + const startOfMonth = new Date(defaultDate.getFullYear(), defaultDate.getMonth(), 1); + const endOfMonth = new Date(defaultDate.getFullYear(), defaultDate.getMonth() + 1, 0); -export const useMyTodo = ({ initialDate = createDate(2025, 7, 18) }: UseMyTodoProps = {}) => { - const [currentDate, setCurrentDate] = useState(initialDate); + const [currentDate, setCurrentDate] = useState(defaultDate); const [selectedCycle, setSelectedCycle] = useState(); const [selectedParentId, setSelectedParentId] = useState(); const [todos, setTodos] = useState([]); @@ -65,10 +66,13 @@ export const useMyTodo = ({ initialDate = createDate(2025, 7, 18) }: UseMyTodoPr setTodos(mockSubGoals); }, []); - const hasPreviousDate = currentDate > MIN_DATE; - const hasNextDate = currentDate < MAX_DATE; + const hasPreviousDate = currentDate > startOfMonth; + const hasNextDate = currentDate < endOfMonth; const handleDateChange = (newDate: Date) => { + if (newDate < startOfMonth || newDate > endOfMonth) { + return; + } setCurrentDate(newDate); }; From 08d34056a1d2fc1cb753930ef83b3188c294c971 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9E=84=EC=A7=80=EC=88=98?= Date: Sat, 20 Sep 2025 00:27:36 +0900 Subject: [PATCH 3/8] =?UTF-8?q?refactor:=20=EC=BD=94=EB=93=9C=20=EB=A6=AC?= =?UTF-8?q?=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/page/todo/myTodo/MyTodo.css.ts | 1 + .../TodoCheckSection/TodoCheckSection.tsx | 79 +++++++++---------- 2 files changed, 40 insertions(+), 40 deletions(-) diff --git a/src/page/todo/myTodo/MyTodo.css.ts b/src/page/todo/myTodo/MyTodo.css.ts index 63c96fab..715a5c96 100644 --- a/src/page/todo/myTodo/MyTodo.css.ts +++ b/src/page/todo/myTodo/MyTodo.css.ts @@ -208,6 +208,7 @@ export const emptyTodoBox = style({ flexShrink: 0, borderRadius: '0.8px', background: colors.grey4, + boxSizing: 'border-box', }); export const emptyTodoText = style({ diff --git a/src/page/todo/myTodo/component/TodoCheckSection/TodoCheckSection.tsx b/src/page/todo/myTodo/component/TodoCheckSection/TodoCheckSection.tsx index d2a08519..1529a523 100644 --- a/src/page/todo/myTodo/component/TodoCheckSection/TodoCheckSection.tsx +++ b/src/page/todo/myTodo/component/TodoCheckSection/TodoCheckSection.tsx @@ -35,6 +35,7 @@ const TodoCheckSection = ({ selectedCycle, mandalartData, onCycleClick, + onTodoClick, onMandalartClick, selectedParentId, }: TodoCheckSectionProps) => { @@ -49,37 +50,37 @@ const TodoCheckSection = ({ const [localSubGoals, setLocalSubGoals] = useState([]); useEffect(() => { - const subGoals: TodoItemTypes[] = (subGoalResponse?.data?.subGoals ?? []).map((goal) => ({ + const apiSubGoals = (subGoalResponse?.data?.subGoals ?? []).map((goal) => ({ id: goal.id, - title: goal.title, + content: goal.title, cycle: goal.cycle, isCompleted: goal.isCompleted, - content: goal.title, })); - setLocalSubGoals(subGoals); + + setLocalSubGoals(apiSubGoals); }, [subGoalResponse]); + const updateLocalSubGoalCompletion = (id: TodoItemTypes['id'], nextValue: boolean) => { + setLocalSubGoals((prev) => + prev.map((goal) => (goal.id === id ? { ...goal, isCompleted: nextValue } : goal)), + ); + }; + const checkSubGoalMutation = useCheckSubGoal(); const uncheckSubGoalMutation = useUncheckSubGoal(); const handleTodoClick = (item: TodoItemTypes) => { const today = new Date().toISOString().split('T')[0]; - const originalCompleted = item.isCompleted; + const originalCompleted = Boolean(item.isCompleted); + const nextCompleted = !originalCompleted; - setLocalSubGoals((prev) => - prev.map((goal) => - goal.id === item.id ? { ...goal, isCompleted: !goal.isCompleted } : goal, - ), - ); + updateLocalSubGoalCompletion(item.id, nextCompleted); + onTodoClick({ ...item, isCompleted: nextCompleted }); - if (originalCompleted === true) { + if (originalCompleted) { uncheckSubGoalMutation.mutate(Number(item.id), { onError: () => { - setLocalSubGoals((prev) => - prev.map((goal) => - goal.id === item.id ? { ...goal, isCompleted: originalCompleted } : goal, - ), - ); + updateLocalSubGoalCompletion(item.id, originalCompleted); }, }); } else { @@ -90,17 +91,32 @@ const TodoCheckSection = ({ }, { onError: () => { - setLocalSubGoals((prev) => - prev.map((goal) => - goal.id === item.id ? { ...goal, isCompleted: originalCompleted } : goal, - ), - ); + updateLocalSubGoalCompletion(item.id, originalCompleted); }, }, ); } }; + const hasTodos = localSubGoals.length > 0; + const todoContainerClass = hasTodos + ? styles.todoCheckContainer + : styles.noScrollTodoCheckContainer; + + const renderEmptyState = () => ( +
+ 해당하는 할 일이 없어요 +
+ ); + + const renderTodoItems = () => + localSubGoals.map((todo) => ( +
+ + +
+ )); + return (
@@ -151,25 +167,8 @@ const TodoCheckSection = ({ ))}
-
- {localSubGoals.length === 0 ? ( -
- 해당하는 할 일이 없어요 -
- ) : ( - localSubGoals.map((todo) => ( -
- - -
- )) - )} +
+ {hasTodos ? renderTodoItems() : renderEmptyState()}
From 5265d182a4d44ed263b86bcefbcdb1dac2cd3aa7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9E=84=EC=A7=80=EC=88=98?= Date: Sat, 20 Sep 2025 00:59:04 +0900 Subject: [PATCH 4/8] =?UTF-8?q?feat:=20=EB=A0=88=EC=9D=B4=EC=95=84?= =?UTF-8?q?=EC=9B=83=20=ED=86=A0=ED=81=B0=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/page/todo/myTodo/MyTodo.css.ts | 352 ++++++++++++++++------------- 1 file changed, 190 insertions(+), 162 deletions(-) diff --git a/src/page/todo/myTodo/MyTodo.css.ts b/src/page/todo/myTodo/MyTodo.css.ts index 715a5c96..d734f8da 100644 --- a/src/page/todo/myTodo/MyTodo.css.ts +++ b/src/page/todo/myTodo/MyTodo.css.ts @@ -1,6 +1,7 @@ import { style, styleVariants } from '@vanilla-extract/css'; import { colors, fonts } from '@/style/token'; +import { layout } from '@/style/token/layout.css.ts'; export const myTodoBg = style({ position: 'fixed', @@ -12,167 +13,188 @@ export const myTodoBg = style({ zIndex: 0, }); -export const myTodoContainer = style({ - position: 'relative', - zIndex: 1, - maxWidth: '128rem', - margin: '0 auto', - minHeight: '100vh', - display: 'flex', - flexDirection: 'column', - alignItems: 'center', - width: '100%', - marginTop: '8.6rem', - marginBottom: '10rem', -}); +export const myTodoContainer = style([ + layout.flexColumn, + { + position: 'relative', + zIndex: 1, + maxWidth: '128rem', + margin: '0 auto', + minHeight: '100vh', + alignItems: 'center', + width: '100%', + marginTop: '8.6rem', + marginBottom: '10rem', + }, +]); -export const contentWrapper = style({ - display: 'flex', - flexDirection: 'column', - alignItems: 'flex-start', - width: '100%', - maxWidth: '128rem', - gap: '8.6rem', -}); +export const contentWrapper = style([ + layout.flexColumn, + { + alignItems: 'flex-start', + width: '100%', + maxWidth: '128rem', + gap: '8.6rem', + }, +]); -export const datePickerSection = style({ - width: '100%', - display: 'flex', - justifyContent: 'center', -}); +export const datePickerSection = style([ + layout.rowCenter, + { + width: '100%', + }, +]); -export const mainContentWrapper = style({ - display: 'flex', - flexDirection: 'column', - alignItems: 'flex-start', - width: '100%', - gap: '11.8rem', -}); +export const mainContentWrapper = style([ + layout.flexColumn, + { + alignItems: 'flex-start', + width: '100%', + gap: '11.8rem', + }, +]); -export const recommendSection = style({ - display: 'flex', - flexDirection: 'column', - alignItems: 'flex-start', - width: '100%', - gap: '2.8rem', -}); +export const recommendSection = style([ + layout.flexColumn, + { + alignItems: 'flex-start', + width: '100%', + gap: '2.8rem', + }, +]); -export const recommendTextWrapper = style({ - display: 'flex', - flexDirection: 'column', - alignItems: 'flex-start', - gap: '0.4rem', -}); +export const recommendTextWrapper = style([ + layout.flexColumn, + { + alignItems: 'flex-start', + gap: '0.4rem', + }, +]); -export const recommendTitle = style({ - ...fonts.title01, - color: colors.white01, - margin: 0, -}); +export const recommendTitle = style([ + fonts.title01, + { + color: colors.white01, + margin: 0, + }, +]); -export const recommendSubtitle = style({ - ...fonts.subtitle06, - color: colors.grey6, - margin: 0, - alignSelf: 'stretch', -}); +export const recommendSubtitle = style([ + fonts.subtitle06, + { + color: colors.grey6, + margin: 0, + alignSelf: 'stretch', + }, +]); -export const recommendBoxWrapper = style({ - background: colors.grey05_32, - borderRadius: '12px', - display: 'flex', - padding: '1.5rem', - flexDirection: 'row', - justifyContent: 'center', - alignItems: 'center', - gap: '1.6rem', - alignSelf: 'stretch', -}); +export const recommendBoxWrapper = style([ + layout.rowCenter, + { + background: colors.grey05_32, + borderRadius: '12px', + padding: '1.5rem', + gap: '1.6rem', + alignSelf: 'stretch', + }, +]); -export const checkSection = style({ - display: 'flex', - flexDirection: 'column', - alignItems: 'flex-start', - width: '100%', - gap: '2.8rem', -}); +export const checkSection = style([ + layout.flexColumn, + { + alignItems: 'flex-start', + width: '100%', + gap: '2.8rem', + }, +]); -export const checkTextWrapper = style({ - display: 'flex', - flexDirection: 'column', - alignItems: 'flex-start', - gap: '0.4rem', -}); +export const checkTextWrapper = style([ + layout.flexColumn, + { + alignItems: 'flex-start', + gap: '0.4rem', + }, +]); -export const checkTitle = style({ - ...fonts.title01, - color: colors.white01, - margin: 0, -}); +export const checkTitle = style([ + fonts.title01, + { + color: colors.white01, + margin: 0, + }, +]); -export const checkSubtitle = style({ - ...fonts.subtitle06, - color: colors.grey6, - margin: 0, - alignSelf: 'stretch', -}); +export const checkSubtitle = style([ + fonts.subtitle06, + { + color: colors.grey6, + margin: 0, + alignSelf: 'stretch', + }, +]); -export const checkMainContainer = style({ - background: colors.grey05_32, - borderRadius: '12px', - display: 'flex', - flexDirection: 'column', - alignItems: 'flex-start', - height: '67.2rem', - padding: '2.6rem', - alignSelf: 'stretch', -}); +export const checkMainContainer = style([ + layout.flexColumn, + { + background: colors.grey05_32, + borderRadius: '12px', + alignItems: 'flex-start', + height: '67.2rem', + padding: '2.6rem', + width: '106rem', + minWidth: '106rem', + alignSelf: 'center', + }, +]); -export const todoCheckArea = style({ - display: 'flex', - flexDirection: 'column', - alignItems: 'flex-start', - width: '55.2rem', - minWidth: '55.2rem', - gap: '2.6rem', - height: '100%', - flexShrink: 0, - paddingRight: '1.9rem', - boxSizing: 'border-box', -}); +export const todoCheckArea = style([ + layout.flexColumn, + { + alignItems: 'flex-start', + width: '55.2rem', + minWidth: '55.2rem', + gap: '2.6rem', + height: '100%', + flexShrink: 0, + paddingRight: '1.9rem', + boxSizing: 'border-box', + }, +]); -export const selectorChipsContainer = style({ - display: 'flex', - justifyContent: 'space-between', - alignItems: 'center', - alignSelf: 'stretch', - gap: '2.6rem', -}); +export const selectorChipsContainer = style([ + layout.rowBetween, + { + alignSelf: 'stretch', + gap: '2.6rem', + }, +]); -export const todoCheckContainer = style({ - display: 'flex', - flexDirection: 'column', - alignItems: 'flex-start', - width: '100%', - height: '53.8rem', - gap: '2.4rem', - alignSelf: 'stretch', - overflowY: 'auto', - boxSizing: 'border-box', -}); +export const todoCheckContainer = style([ + layout.flexColumn, + { + alignItems: 'flex-start', + width: '100%', + height: '53.8rem', + gap: '2.4rem', + alignSelf: 'stretch', + overflowY: 'auto', + boxSizing: 'border-box', + }, +]); -export const noScrollTodoCheckContainer = style({ - display: 'flex', - flexDirection: 'column', - alignItems: 'flex-start', - width: '100%', - height: '53.8rem', - gap: '2.4rem', - alignSelf: 'stretch', - overflow: 'hidden', - paddingRight: '1.9rem', - boxSizing: 'border-box', -}); +export const noScrollTodoCheckContainer = style([ + layout.flexColumn, + { + alignItems: 'flex-start', + width: '100%', + height: '53.8rem', + gap: '2.4rem', + alignSelf: 'stretch', + overflow: 'hidden', + paddingRight: '1.9rem', + boxSizing: 'border-box', + }, +]); export const todoCheckLine = style({ display: 'flex', @@ -182,19 +204,23 @@ export const todoCheckLine = style({ }); export const todoText = styleVariants({ - recommend: { - width: '33.3rem', - whiteSpace: 'nowrap', - overflow: 'hidden', - textOverflow: 'ellipsis', - color: colors.grey10, - ...fonts.subtitle05, - }, - todo: { - color: colors.grey10, - textAlign: 'center', - ...fonts.subtitle02, - }, + recommend: [ + fonts.subtitle05, + { + width: '33.3rem', + whiteSpace: 'nowrap', + overflow: 'hidden', + textOverflow: 'ellipsis', + color: colors.grey10, + }, + ], + todo: [ + fonts.subtitle02, + { + color: colors.grey10, + textAlign: 'center', + }, + ], }); export const emptyTodoBox = style({ @@ -211,11 +237,13 @@ export const emptyTodoBox = style({ boxSizing: 'border-box', }); -export const emptyTodoText = style({ - color: colors.grey10, - textAlign: 'center', - ...fonts.subtitle02, -}); +export const emptyTodoText = style([ + fonts.subtitle02, + { + color: colors.grey10, + textAlign: 'center', + }, +]); export const mandalartWithTodoSection = style({ display: 'flex', From 1be5ccb16f6921113e042eb470961b6f70f6a21f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9E=84=EC=A7=80=EC=88=98?= Date: Mon, 22 Sep 2025 13:38:47 +0900 Subject: [PATCH 5/8] =?UTF-8?q?fix:=20=EB=94=94=EC=9E=90=EC=9D=B8=20?= =?UTF-8?q?=EC=98=A4=EB=A5=98=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/page/todo/myTodo/MyTodo.css.ts | 16 +++++++--------- .../myTodo/component/CycleChip/CycleChip.css.ts | 5 +++++ .../todo/myTodo/component/TodoBox/TodoBox.css.ts | 6 +++++- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/page/todo/myTodo/MyTodo.css.ts b/src/page/todo/myTodo/MyTodo.css.ts index d734f8da..5240a2fe 100644 --- a/src/page/todo/myTodo/MyTodo.css.ts +++ b/src/page/todo/myTodo/MyTodo.css.ts @@ -141,8 +141,6 @@ export const checkMainContainer = style([ alignItems: 'flex-start', height: '67.2rem', padding: '2.6rem', - width: '106rem', - minWidth: '106rem', alignSelf: 'center', }, ]); @@ -151,12 +149,11 @@ export const todoCheckArea = style([ layout.flexColumn, { alignItems: 'flex-start', - width: '55.2rem', - minWidth: '55.2rem', + width: '57.1rem', + minWidth: '57.1rem', gap: '2.6rem', height: '100%', flexShrink: 0, - paddingRight: '1.9rem', boxSizing: 'border-box', }, ]); @@ -164,8 +161,8 @@ export const todoCheckArea = style([ export const selectorChipsContainer = style([ layout.rowBetween, { + width: '55.2rem', alignSelf: 'stretch', - gap: '2.6rem', }, ]); @@ -173,11 +170,13 @@ export const todoCheckContainer = style([ layout.flexColumn, { alignItems: 'flex-start', - width: '100%', + width: '57.1rem', height: '53.8rem', gap: '2.4rem', alignSelf: 'stretch', overflowY: 'auto', + overflowX: 'hidden', + paddingRight: '1.9rem', boxSizing: 'border-box', }, ]); @@ -186,11 +185,10 @@ export const noScrollTodoCheckContainer = style([ layout.flexColumn, { alignItems: 'flex-start', - width: '100%', + width: '57.1rem', height: '53.8rem', gap: '2.4rem', alignSelf: 'stretch', - overflow: 'hidden', paddingRight: '1.9rem', boxSizing: 'border-box', }, diff --git a/src/page/todo/myTodo/component/CycleChip/CycleChip.css.ts b/src/page/todo/myTodo/component/CycleChip/CycleChip.css.ts index 4ab1ceee..831d1017 100644 --- a/src/page/todo/myTodo/component/CycleChip/CycleChip.css.ts +++ b/src/page/todo/myTodo/component/CycleChip/CycleChip.css.ts @@ -36,8 +36,13 @@ export const selectorChip = styleVariants({ }); export const displayChip = style({ + boxSizing: 'border-box', width: '10.6rem', padding: '1.4rem 2rem', background: colors.grey4, color: colors.grey10, + flex: '0 0 10.6rem', + selectors: { + '&&': { width: '10.6rem' }, + }, }); diff --git a/src/page/todo/myTodo/component/TodoBox/TodoBox.css.ts b/src/page/todo/myTodo/component/TodoBox/TodoBox.css.ts index d85250f7..057690e7 100644 --- a/src/page/todo/myTodo/component/TodoBox/TodoBox.css.ts +++ b/src/page/todo/myTodo/component/TodoBox/TodoBox.css.ts @@ -21,9 +21,13 @@ export const todoBoxContainer = styleVariants({ alignItems: 'center', background: colors.grey4, borderRadius: '8px', + boxSizing: 'border-box', width: '43.6rem', + flex: '0 0 43.6rem', + selectors: { + '&&': { width: '43.6rem' }, + }, padding: '1.4rem 2rem', - marginRight: '1.9rem', }, }); From 944f6cc11b708daed4b400dd87d618ad1a32bf8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9E=84=EC=A7=80=EC=88=98?= Date: Mon, 22 Sep 2025 13:45:58 +0900 Subject: [PATCH 6/8] =?UTF-8?q?feat:=20=EB=A1=9C=EB=94=A9=EC=8A=A4?= =?UTF-8?q?=ED=94=BC=EB=84=88=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/page/todo/myTodo/MyTodo.css.ts | 28 ++++++++++++++++- .../TodoCheckSection/TodoCheckSection.tsx | 30 ++++++++++++------- 2 files changed, 47 insertions(+), 11 deletions(-) diff --git a/src/page/todo/myTodo/MyTodo.css.ts b/src/page/todo/myTodo/MyTodo.css.ts index 5240a2fe..4ae20c0c 100644 --- a/src/page/todo/myTodo/MyTodo.css.ts +++ b/src/page/todo/myTodo/MyTodo.css.ts @@ -1,4 +1,4 @@ -import { style, styleVariants } from '@vanilla-extract/css'; +import { keyframes, style, styleVariants } from '@vanilla-extract/css'; import { colors, fonts } from '@/style/token'; import { layout } from '@/style/token/layout.css.ts'; @@ -174,6 +174,7 @@ export const todoCheckContainer = style([ height: '53.8rem', gap: '2.4rem', alignSelf: 'stretch', + position: 'relative', overflowY: 'auto', overflowX: 'hidden', paddingRight: '1.9rem', @@ -201,6 +202,31 @@ export const todoCheckLine = style({ width: '100%', }); +const spin = keyframes({ + '0%': { transform: 'rotate(0deg)' }, + '100%': { transform: 'rotate(360deg)' }, +}); + +export const todoLoadingOverlay = style({ + position: 'absolute', + inset: 0, + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + background: 'rgba(18, 18, 18, 0.60)', + backdropFilter: 'blur(1px)', + zIndex: 1, +}); + +export const todoLoadingSpinner = style({ + width: '4rem', + height: '4rem', + borderRadius: '50%', + border: `0.4rem solid ${colors.grey3}`, + borderTopColor: colors.grey10, + animation: `${spin} 0.8s linear infinite`, +}); + export const todoText = styleVariants({ recommend: [ fonts.subtitle05, diff --git a/src/page/todo/myTodo/component/TodoCheckSection/TodoCheckSection.tsx b/src/page/todo/myTodo/component/TodoCheckSection/TodoCheckSection.tsx index 1529a523..e5d19e29 100644 --- a/src/page/todo/myTodo/component/TodoCheckSection/TodoCheckSection.tsx +++ b/src/page/todo/myTodo/component/TodoCheckSection/TodoCheckSection.tsx @@ -41,16 +41,20 @@ const TodoCheckSection = ({ }: TodoCheckSectionProps) => { const mandalartId = useMandalartId(); const { data: coreGoalsData } = useGetMandalCoreGoals(mandalartId); - const { data: subGoalResponse } = useGetMandalartSubGoals( - mandalartId, - selectedParentId, - selectedCycle, - ); + const { + data: subGoalResponse, + isLoading: isSubGoalsLoading, + isFetching: isSubGoalsFetching, + } = useGetMandalartSubGoals(mandalartId, selectedParentId, selectedCycle); const [localSubGoals, setLocalSubGoals] = useState([]); useEffect(() => { - const apiSubGoals = (subGoalResponse?.data?.subGoals ?? []).map((goal) => ({ + if (!subGoalResponse?.data) { + return; + } + + const apiSubGoals = (subGoalResponse.data.subGoals ?? []).map((goal) => ({ id: goal.id, content: goal.title, cycle: goal.cycle, @@ -98,10 +102,11 @@ const TodoCheckSection = ({ } }; + const isLoadingSubGoals = isSubGoalsLoading || isSubGoalsFetching; const hasTodos = localSubGoals.length > 0; - const todoContainerClass = hasTodos - ? styles.todoCheckContainer - : styles.noScrollTodoCheckContainer; + const showSpinner = isLoadingSubGoals; + const todoContainerClass = + showSpinner || hasTodos ? styles.todoCheckContainer : styles.noScrollTodoCheckContainer; const renderEmptyState = () => (
@@ -168,7 +173,12 @@ const TodoCheckSection = ({
- {hasTodos ? renderTodoItems() : renderEmptyState()} + {showSpinner && ( +
+
+
+ )} + {hasTodos ? renderTodoItems() : !showSpinner && renderEmptyState()}
From 0d3c1b162fadbdb114e26127f9df5cb245de19b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9E=84=EC=A7=80=EC=88=98?= Date: Mon, 22 Sep 2025 13:57:11 +0900 Subject: [PATCH 7/8] =?UTF-8?q?fix:=20=ED=95=98=EC=9C=84=EB=AA=A9=ED=91=9C?= =?UTF-8?q?=20=EC=A1=B0=ED=9A=8C=20=EC=98=A4=EB=A5=98=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../TodoCheckSection/TodoCheckSection.tsx | 29 +++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/src/page/todo/myTodo/component/TodoCheckSection/TodoCheckSection.tsx b/src/page/todo/myTodo/component/TodoCheckSection/TodoCheckSection.tsx index e5d19e29..72977f03 100644 --- a/src/page/todo/myTodo/component/TodoCheckSection/TodoCheckSection.tsx +++ b/src/page/todo/myTodo/component/TodoCheckSection/TodoCheckSection.tsx @@ -138,25 +138,30 @@ const TodoCheckSection = ({ position: 4, title: mandalartData.title || mandalartData.mainGoal, subGoals: Array.isArray(coreGoalsData?.data?.coreGoals) - ? coreGoalsData.data.coreGoals.map( - ( - goal: { title: string; position: number; subGoals?: unknown[] }, - idx: number, - ) => ({ - id: idx < 4 ? idx : idx + 1, + ? [...coreGoalsData.data.coreGoals] + .sort((a, b) => a.position - b.position) + .map((goal) => ({ + id: goal.id, title: goal.title, position: goal.position, - subGoals: goal.subGoals ?? [], - }), - ) + subGoals: [], + })) : [], }} - onGoalClick={(position) => { + onGoalClick={(position, goalId) => { const coreGoal = coreGoalsData?.data?.coreGoals.find( (goal) => goal.position === position, ); - const parentId = coreGoal?.id; - onMandalartClick(selectedParentId === parentId ? undefined : parentId); + const resolvedParentId = goalId && goalId > 0 ? goalId : coreGoal?.id; + + if (!resolvedParentId) { + onMandalartClick(undefined); + return; + } + + onMandalartClick( + selectedParentId === resolvedParentId ? undefined : resolvedParentId, + ); }} />
From f8f322146782f3b80c6a2a50449a04e55ae963c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9E=84=EC=A7=80=EC=88=98?= Date: Mon, 22 Sep 2025 14:19:33 +0900 Subject: [PATCH 8/8] =?UTF-8?q?fix:=20=EC=88=98=EC=A0=95=ED=95=98=EA=B8=B0?= =?UTF-8?q?=20=EB=B7=B0=20=EC=98=A4=EB=A5=98=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/page/edit/component/Content/Content.tsx | 62 ++++++++++++--------- 1 file changed, 37 insertions(+), 25 deletions(-) diff --git a/src/page/edit/component/Content/Content.tsx b/src/page/edit/component/Content/Content.tsx index 571f0154..94671452 100644 --- a/src/page/edit/component/Content/Content.tsx +++ b/src/page/edit/component/Content/Content.tsx @@ -8,6 +8,7 @@ import { useMandalartHover } from '../../hook/useMandalartHover'; import Mandalart from '@/common/component/Mandalart/Mandalart'; import { useMandalAll } from '@/api/domain/mandalAll/hook'; import { useSubGoals, useUpdateSubGoal } from '@/api/domain/edit/hook'; +import { useMandalartId } from '@/common/hook/useMandalartId'; import type { CoreGoal, SubGoal } from '@/page/mandal/types/mandal'; interface ContentProps { @@ -15,10 +16,9 @@ interface ContentProps { setIsEditing: (value: boolean) => void; } -const MANDAL_ID = 1; - const Content = ({ isEditing, setIsEditing }: ContentProps) => { - const { data: mandalartData, isLoading: isMandalLoading } = useMandalAll(MANDAL_ID); + const mandalartId = useMandalartId(); + const { data: mandalartData, isLoading: isMandalLoading } = useMandalAll(mandalartId); const { isHovered, hoveredGoal, @@ -31,21 +31,22 @@ const Content = ({ isEditing, setIsEditing }: ContentProps) => { mandalartData, }); - const { isLoading: isSubGoalsLoading } = useSubGoals(MANDAL_ID, hoveredGoal?.id, undefined, { - enabled: !!hoveredGoal, + const activeCoreGoalId = hoveredGoal?.id && hoveredGoal.id > 0 ? hoveredGoal.id : undefined; + const { isLoading: isSubGoalsLoading } = useSubGoals(mandalartId, activeCoreGoalId, undefined, { + enabled: !!activeCoreGoalId, }); - const { mutate: updateGoal } = useUpdateSubGoal(MANDAL_ID); + const { mutate: updateGoal } = useUpdateSubGoal(mandalartId); - const isLoading = isMandalLoading || (hoveredGoal && isSubGoalsLoading); + const isLoading = !mandalartId || isMandalLoading || (hoveredGoal && isSubGoalsLoading); const handleSave = useCallback(() => { - if (!hoveredGoal) { + if (!hoveredGoal || !mandalartId) { return; } const validSubGoals = hoveredGoal.subGoals.filter((subGoal) => subGoal.title); - if (validSubGoals.length > 0) { + if (validSubGoals.length > 0 && hoveredGoal.id > 0) { updateGoal({ coreGoal: { position: hoveredGoal.position, @@ -61,7 +62,7 @@ const Content = ({ isEditing, setIsEditing }: ContentProps) => { setIsEditing(false); setIsHovered(true); - }, [hoveredGoal, updateGoal, setIsEditing, setIsHovered]); + }, [hoveredGoal, updateGoal, setIsEditing, setIsHovered, mandalartId]); useEffect(() => { if (!isEditing && hoveredGoal) { @@ -87,17 +88,19 @@ const Content = ({ isEditing, setIsEditing }: ContentProps) => { const validSubGoals = hoveredGoal.subGoals.filter((subGoal) => subGoal.title); if (validSubGoals.length > 0) { - updateGoal({ - coreGoal: { - position: hoveredGoal.position, - title: hoveredGoal.title, - }, - subGoals: validSubGoals.map((subGoal) => ({ - position: subGoal.position, - title: subGoal.title, - cycle: subGoal.cycle || 'DAILY', - })), - }); + if (mandalartId && hoveredGoal.id > 0) { + updateGoal({ + coreGoal: { + position: hoveredGoal.position, + title: hoveredGoal.title, + }, + subGoals: validSubGoals.map((subGoal) => ({ + position: subGoal.position, + title: subGoal.title, + cycle: subGoal.cycle || 'DAILY', + })), + }); + } } setHoveredGoal(goal); @@ -110,7 +113,16 @@ const Content = ({ isEditing, setIsEditing }: ContentProps) => { setIsHovered(true); setIsEditing(true); }, - [mandalartData, isEditing, hoveredGoal, updateGoal, setHoveredGoal, setIsHovered, setIsEditing], + [ + mandalartData, + isEditing, + hoveredGoal, + updateGoal, + setHoveredGoal, + setIsHovered, + setIsEditing, + mandalartId, + ], ); const handleTitleChange = useCallback( @@ -215,10 +227,10 @@ const Content = ({ isEditing, setIsEditing }: ContentProps) => { position={hoveredGoal.position} id={hoveredGoal.id} onSubGoalsChange={handleSubGoalsChange} - mandalartId={MANDAL_ID} + mandalartId={mandalartId} /> ); - }, [isLoading, hoveredGoal, handleTitleChange, handleSubGoalsChange]); + }, [isLoading, hoveredGoal, handleTitleChange, handleSubGoalsChange, mandalartId]); const renderSubGoals = useCallback(() => { if (isLoading) { @@ -274,7 +286,7 @@ const Content = ({ isEditing, setIsEditing }: ContentProps) => { onMouseLeave={handleMouseLeave} onClick={handleMandalartClick} > - {isMandalLoading ? ( + {!mandalartId || isMandalLoading ? (
로딩중...
) : (