diff --git a/apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_hooks/use-home-todos-by-date.ts b/apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_hooks/use-home-todos-by-date.ts index fff1170b..7d2c5ad8 100644 --- a/apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_hooks/use-home-todos-by-date.ts +++ b/apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_hooks/use-home-todos-by-date.ts @@ -7,7 +7,6 @@ import type { ApiError } from "@/api/error/api-error"; import type { HomeViewDay } from "@/app/[locale]/(main)/(with-time-sidebar)/home/_types/home-view-type"; import type { Todo } from "@/app/[locale]/(main)/(with-time-sidebar)/home/_types/todo-type"; -import { getGetFocusTodoQueryKey } from "@/api/generated/endpoints/focus/focus"; import { useChangeStatus, useStartTimer, @@ -48,14 +47,28 @@ export const useHomeTodosByDate = ( const { mutate: changeSubtaskStatus } = useChangeSubtaskStatus(); const { mutate: reorderTodo } = useReorderTodo(); const { mutate: stopTimer } = useStopTimer(); - const { invalidateHomeView, invalidateTimerState, invalidateTimeBoxes } = - useTimerQueryInvalidation(); + const { + invalidateHomeView, + invalidateTimerState, + invalidateTimeBoxes, + invalidateFocusTodo, + } = useTimerQueryInvalidation(); const { mutate: startTimer } = useStartTimer({ - mutation: { onSuccess: invalidateTimerState }, + mutation: { + onSuccess: () => { + invalidateTimerState(); + invalidateFocusTodo(); + }, + }, }); const { mutate: changeStatus } = useChangeStatus({ - mutation: { onSuccess: invalidateTimerState }, + mutation: { + onSuccess: () => { + invalidateTimerState(); + invalidateFocusTodo(); + }, + }, }); useEffect(() => { @@ -77,9 +90,6 @@ export const useHomeTodosByDate = ( })); }; - const invalidateFocusTodo = () => { - queryClient.invalidateQueries({ queryKey: getGetFocusTodoQueryKey() }); - }; const invalidateHomeAndFocus = () => { invalidateHomeView(); invalidateFocusTodo(); diff --git a/apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_hooks/useTodayTodoList.ts b/apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_hooks/useTodayTodoList.ts index b4b49af2..5e087253 100644 --- a/apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_hooks/useTodayTodoList.ts +++ b/apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_hooks/useTodayTodoList.ts @@ -7,7 +7,6 @@ import type { ErrorType } from "@/api/client/custom-instance"; import type { ErrorDto } from "@/api/generated/models"; import type { TodayTodo } from "@/app/[locale]/(main)/(with-time-sidebar)/today/_types/today-type"; -import { getGetTodayQueryKey } from "@/api/generated/endpoints/home/home"; import { useChangeStatus, useStartTimer, @@ -45,14 +44,28 @@ export const useTodayTodoList = ( const { mutate: changeTodoStatus } = useChangeTodoStatus(); const { mutate: changeSubtaskStatus } = useChangeSubtaskStatus(); const { mutate: stopTimer } = useStopTimer(); - const { invalidateTimerState, invalidateTimeBoxes } = - useTimerQueryInvalidation(); + const { + invalidateTimerState, + invalidateTimeBoxes, + invalidateTodayView, + invalidateFocusTodo, + } = useTimerQueryInvalidation(); const { mutate: startTimer } = useStartTimer({ - mutation: { onSuccess: invalidateTimerState }, + mutation: { + onSuccess: () => { + invalidateTimerState(); + invalidateFocusTodo(); + }, + }, }); const { mutate: changeStatus } = useChangeStatus({ - mutation: { onSuccess: invalidateTimerState }, + mutation: { + onSuccess: () => { + invalidateTimerState(); + invalidateFocusTodo(); + }, + }, }); useEffect(() => { @@ -68,9 +81,6 @@ export const useTodayTodoList = ( ); }; - const invalidateTodayView = () => { - queryClient.invalidateQueries({ queryKey: getGetTodayQueryKey() }); - }; const invalidateTodoDetail = (todoId: number, dateKey: string) => { queryClient.invalidateQueries({ queryKey: getGetTodoDetailQueryKey(todoId, { date: dateKey }), @@ -96,6 +106,7 @@ export const useTodayTodoList = ( invalidateTodayView(); invalidateTimeBoxes(); invalidateTodoDetail(todoId, dateKey); + invalidateFocusTodo(); }, onError: () => { setTodos(previous); @@ -124,6 +135,7 @@ export const useTodayTodoList = ( onSuccess: () => { invalidateTodayView(); invalidateTodoDetail(todoId, dateKey); + invalidateFocusTodo(); }, }, ); @@ -207,6 +219,7 @@ export const useTodayTodoList = ( onSuccess: () => { invalidateTodayView(); invalidateTodoDetail(todoId, dateKey); + invalidateFocusTodo(); }, onError: () => { setTodos(previous); diff --git a/apps/timo-web/app/[locale]/(main)/focus/_components/FocusTaskItem.tsx b/apps/timo-web/app/[locale]/(main)/focus/_components/FocusTaskItem.tsx index a29c3ed1..1002ba8f 100644 --- a/apps/timo-web/app/[locale]/(main)/focus/_components/FocusTaskItem.tsx +++ b/apps/timo-web/app/[locale]/(main)/focus/_components/FocusTaskItem.tsx @@ -47,7 +47,7 @@ export const FocusTaskItem = ({
-

+

{title}

@@ -82,7 +82,7 @@ export const FocusTaskItem = ({ -
+
{subtasks.map((subtask) => (

@@ -103,7 +103,7 @@ export const FocusTaskItem = ({ ))} {memo && ( -

+

{memo}

)} diff --git a/apps/timo-web/app/[locale]/(main)/focus/_hooks/use-focus-session.ts b/apps/timo-web/app/[locale]/(main)/focus/_hooks/use-focus-session.ts index 60ccb1fe..e7e104df 100644 --- a/apps/timo-web/app/[locale]/(main)/focus/_hooks/use-focus-session.ts +++ b/apps/timo-web/app/[locale]/(main)/focus/_hooks/use-focus-session.ts @@ -5,7 +5,6 @@ import { useEffect, useRef } from "react"; import type { TimerSessionControlsHandle } from "@/components/timer/TimerSessionControls"; -import { getGetFocusTodoQueryKey } from "@/api/generated/endpoints/focus/focus"; import { useChangeStatus, useCompleteTimer, @@ -14,6 +13,7 @@ import { useStopTimer, } from "@/api/generated/endpoints/timer/timer"; import { + getGetTodoDetailQueryKey, useChangeSubtaskStatus, useChangeTodoStatus, } from "@/api/generated/endpoints/todo/todo"; @@ -41,10 +41,20 @@ export const useFocusSession = ({ const { data: focusView } = useFocusTodoQuery(); const { data: activeTimer } = useActiveTimer(); - const { invalidateActiveTimer, invalidateHomeView, invalidateTimeBoxes } = - useTimerQueryInvalidation(); - const invalidateFocusTodo = () => - queryClient.invalidateQueries({ queryKey: getGetFocusTodoQueryKey() }); + const { + invalidateActiveTimer, + invalidateHomeView, + invalidateTimeBoxes, + invalidateTodayView, + invalidateFocusTodo, + } = useTimerQueryInvalidation(); + const invalidateTodoDetail = () => { + const todo = focusView.todo; + if (!todo) return; + queryClient.invalidateQueries({ + queryKey: getGetTodoDetailQueryKey(todo.todoId, { date: focusView.date }), + }); + }; const { mutate: startTimer } = useStartTimer({ mutation: { @@ -84,6 +94,7 @@ export const useFocusSession = ({ invalidateFocusTodo(); invalidateHomeView(); invalidateTimeBoxes(); + invalidateTodayView(); }, onError: onMutationError, }, @@ -96,6 +107,7 @@ export const useFocusSession = ({ invalidateFocusTodo(); invalidateHomeView(); invalidateTimeBoxes(); + invalidateTodayView(); }, onError: onMutationError, }, @@ -105,6 +117,8 @@ export const useFocusSession = ({ onSuccess: () => { invalidateFocusTodo(); invalidateTimeBoxes(); + invalidateTodayView(); + invalidateTodoDetail(); }, onError: onMutationError, }, @@ -114,6 +128,8 @@ export const useFocusSession = ({ onSuccess: () => { invalidateFocusTodo(); invalidateTimeBoxes(); + invalidateTodayView(); + invalidateTodoDetail(); }, onError: onMutationError, }, diff --git a/apps/timo-web/components/layout/sidebar/time/TimerPanel.tsx b/apps/timo-web/components/layout/sidebar/time/TimerPanel.tsx index 562b0e4a..636562ac 100644 --- a/apps/timo-web/components/layout/sidebar/time/TimerPanel.tsx +++ b/apps/timo-web/components/layout/sidebar/time/TimerPanel.tsx @@ -29,8 +29,13 @@ export const TimerPanel = () => { const wasTimeUpRef = useRef(false); const { data: activeTimer } = useActiveTimer(); - const { invalidateActiveTimer, invalidateHomeView, invalidateTimeBoxes } = - useTimerQueryInvalidation(); + const { + invalidateActiveTimer, + invalidateHomeView, + invalidateTimeBoxes, + invalidateTodayView, + invalidateFocusTodo, + } = useTimerQueryInvalidation(); const { mutate: changeStatus } = useChangeStatus({ mutation: { @@ -56,6 +61,8 @@ export const TimerPanel = () => { invalidateActiveTimer(); invalidateHomeView(); invalidateTimeBoxes(); + invalidateTodayView(); + invalidateFocusTodo(); }, }, }); @@ -66,6 +73,8 @@ export const TimerPanel = () => { invalidateActiveTimer(); invalidateHomeView(); invalidateTimeBoxes(); + invalidateTodayView(); + invalidateFocusTodo(); }, }, }); @@ -74,6 +83,8 @@ export const TimerPanel = () => { onSuccess: () => { invalidateHomeView(); invalidateTimeBoxes(); + invalidateTodayView(); + invalidateFocusTodo(); }, }, }); diff --git a/apps/timo-web/components/modal/OverlayModal.tsx b/apps/timo-web/components/modal/OverlayModal.tsx index 68ac4d4b..f925faaa 100644 --- a/apps/timo-web/components/modal/OverlayModal.tsx +++ b/apps/timo-web/components/modal/OverlayModal.tsx @@ -1,6 +1,10 @@ "use client"; -import { cn, hasOpenFloatingLayer } from "@repo/timo-design-system/utils"; +import { + acquireModalStackIndex, + cn, + hasOpenFloatingLayer, +} from "@repo/timo-design-system/utils"; import FocusTrap from "focus-trap-react"; import { useEffect, useRef, useState } from "react"; import { createPortal } from "react-dom"; @@ -8,6 +12,11 @@ import { createPortal } from "react-dom"; import type { ReactNode } from "react"; const EXIT_ANIMATION_DURATION = 200; +// tailwind-config theme.css의 --z-index-modal-overlay/--z-index-modal-panel과 동일한 기준값이다. +const BASE_OVERLAY_Z_INDEX = 40; +const BASE_PANEL_Z_INDEX = 50; +// 중첩된 모달의 오버레이가 그 아래 모달의 패널(z=50)보다 항상 높도록, 한 단계당 overlay/panel 차이(10)보다 큰 폭으로 올린다. +const MODAL_STACK_Z_INDEX_STEP = 20; interface OverlayModalProps { isOpen: boolean; @@ -28,6 +37,7 @@ export const OverlayModal = ({ }: OverlayModalProps) => { const [shouldRender, setShouldRender] = useState(isOpen); const [isVisible, setIsVisible] = useState(false); + const [stackIndex, setStackIndex] = useState(0); const dialogRef = useRef(null); const wasFloatingLayerOpenRef = useRef(false); @@ -43,6 +53,7 @@ export const OverlayModal = ({ return () => clearTimeout(hideTimer); } + setStackIndex(acquireModalStackIndex()); setShouldRender(true); const showFrame = requestAnimationFrame(() => setIsVisible(true)); @@ -71,13 +82,19 @@ export const OverlayModal = ({ if (!shouldRender) return null; + const overlayZIndex = + BASE_OVERLAY_Z_INDEX + stackIndex * MODAL_STACK_Z_INDEX_STEP; + const panelZIndex = + BASE_PANEL_Z_INDEX + stackIndex * MODAL_STACK_Z_INDEX_STEP; + return createPortal( <>
{ wasFloatingLayerOpenRef.current = hasOpenFloatingLayer(); }} @@ -102,8 +119,9 @@ export const OverlayModal = ({ aria-modal="true" aria-label={ariaLabel} tabIndex={-1} + style={{ zIndex: panelZIndex }} className={cn( - "fixed top-1/2 left-1/2 z-50 flex -translate-x-1/2 -translate-y-1/2 flex-col rounded-[4px] bg-white transition-all duration-200 ease-out", + "fixed top-1/2 left-1/2 flex -translate-x-1/2 -translate-y-1/2 flex-col rounded-[4px] bg-white transition-all duration-200 ease-out", isVisible ? "scale-100 opacity-100" : "scale-95 opacity-0", className, )} diff --git a/apps/timo-web/hooks/timer/use-timer-query-invalidation.ts b/apps/timo-web/hooks/timer/use-timer-query-invalidation.ts index c9d009ea..38b3662a 100644 --- a/apps/timo-web/hooks/timer/use-timer-query-invalidation.ts +++ b/apps/timo-web/hooks/timer/use-timer-query-invalidation.ts @@ -2,7 +2,11 @@ import { useQueryClient } from "@tanstack/react-query"; -import { getGetHomeQueryKey } from "@/api/generated/endpoints/home/home"; +import { getGetFocusTodoQueryKey } from "@/api/generated/endpoints/focus/focus"; +import { + getGetHomeQueryKey, + getGetTodayQueryKey, +} from "@/api/generated/endpoints/home/home"; import { getGetTimeBoxesQueryKey } from "@/api/generated/endpoints/time-box/time-box"; import { getGetActiveTimerQueryKey } from "@/api/generated/endpoints/timer/timer"; @@ -15,6 +19,10 @@ export const useTimerQueryInvalidation = () => { queryClient.invalidateQueries({ queryKey: getGetHomeQueryKey() }); const invalidateTimeBoxes = () => queryClient.invalidateQueries({ queryKey: getGetTimeBoxesQueryKey() }); + const invalidateTodayView = () => + queryClient.invalidateQueries({ queryKey: getGetTodayQueryKey() }); + const invalidateFocusTodo = () => + queryClient.invalidateQueries({ queryKey: getGetFocusTodoQueryKey() }); const invalidateTimerState = () => { invalidateActiveTimer(); invalidateHomeView(); @@ -25,6 +33,8 @@ export const useTimerQueryInvalidation = () => { invalidateActiveTimer, invalidateHomeView, invalidateTimeBoxes, + invalidateTodayView, + invalidateFocusTodo, invalidateTimerState, }; }; diff --git a/apps/timo-web/hooks/todo-modal/detail/use-delete-todo-submit.ts b/apps/timo-web/hooks/todo-modal/detail/use-delete-todo-submit.ts index 0ad7d7da..74fffc5b 100644 --- a/apps/timo-web/hooks/todo-modal/detail/use-delete-todo-submit.ts +++ b/apps/timo-web/hooks/todo-modal/detail/use-delete-todo-submit.ts @@ -5,6 +5,7 @@ import { useQueryClient } from "@tanstack/react-query"; import type { ErrorType } from "@/api/client/custom-instance"; import type { ErrorDto } from "@/api/generated/models"; +import { getGetFocusTodoQueryKey } from "@/api/generated/endpoints/focus/focus"; import { getGetHomeQueryKey, getGetTodayQueryKey, @@ -30,6 +31,9 @@ export const useDeleteTodoSubmit = () => { onSuccess: () => { queryClient.invalidateQueries({ queryKey: getGetHomeQueryKey() }); queryClient.invalidateQueries({ queryKey: getGetTodayQueryKey() }); + queryClient.invalidateQueries({ + queryKey: getGetFocusTodoQueryKey(), + }); onSuccess(); }, onError, diff --git a/apps/timo-web/hooks/todo-modal/detail/use-update-todo-submit.ts b/apps/timo-web/hooks/todo-modal/detail/use-update-todo-submit.ts index b145e719..6a9379f5 100644 --- a/apps/timo-web/hooks/todo-modal/detail/use-update-todo-submit.ts +++ b/apps/timo-web/hooks/todo-modal/detail/use-update-todo-submit.ts @@ -5,6 +5,7 @@ import { useQueryClient } from "@tanstack/react-query"; import type { ErrorType } from "@/api/client/custom-instance"; import type { ErrorDto, TodoUpdateRequest } from "@/api/generated/models"; +import { getGetFocusTodoQueryKey } from "@/api/generated/endpoints/focus/focus"; import { getGetHomeQueryKey, getGetTodayQueryKey, @@ -42,6 +43,9 @@ export const useUpdateTodoSubmit = () => { queryClient.invalidateQueries({ queryKey: getGetTodoDetailQueryKey(todoId, { date }), }); + queryClient.invalidateQueries({ + queryKey: getGetFocusTodoQueryKey(), + }); onSuccess?.(); }, onError,