From d13613aa53094a099fb518d784b085470be8215a Mon Sep 17 00:00:00 2001 From: Yeonjin Kim Date: Fri, 10 Apr 2026 00:02:05 +0900 Subject: [PATCH 1/3] =?UTF-8?q?fix:=20=EC=9D=BC=EC=A0=95=C2=B7=ED=95=A0?= =?UTF-8?q?=EC=9D=BC=20=EC=A0=84=ED=99=98=20=EC=9E=85=EB=A0=A5=EA=B3=BC=20?= =?UTF-8?q?=EB=AF=B8=EB=A6=AC=EB=B3=B4=EA=B8=B0=20=EB=8F=99=EA=B8=B0?= =?UTF-8?q?=ED=99=94=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CustomCalendar/CalendarModals.tsx | 149 ++++++++++++++---- .../hooks/form/useScheduleFormFields.ts | 8 +- src/shared/hooks/form/useTodoFormFields.ts | 8 +- .../ui/Modals/ItemEditorModal/index.tsx | 89 ++++++++++- 4 files changed, 216 insertions(+), 38 deletions(-) diff --git a/src/features/Calendar/components/CustomCalendar/CalendarModals.tsx b/src/features/Calendar/components/CustomCalendar/CalendarModals.tsx index cba8203..9c5d28f 100644 --- a/src/features/Calendar/components/CustomCalendar/CalendarModals.tsx +++ b/src/features/Calendar/components/CustomCalendar/CalendarModals.tsx @@ -1,8 +1,11 @@ import moment from 'moment' -import { useMemo } from 'react' +import { useMemo, useState } from 'react' import { useDetailEventQuery } from '@/shared/hooks/query/useCalendarQueries' import type { CalendarEvent } from '@/shared/types/calendar/types' +import type { RepeatConfigSchema } from '@/shared/types/event/event' +import type { ItemEditorDraft } from '@/shared/types/modal/itemEditor' +import { defaultRepeatConfig } from '@/shared/types/recurrence/repeat' import ScheduleEditorModal from '@/shared/ui/Modals/ScheduleEditor' import TodoEditorModal from '@/shared/ui/Modals/TodoEditor' @@ -27,6 +30,107 @@ type CalendarModalsProps = { } } +const pad2 = (value: number) => String(value).padStart(2, '0') + +const formatTimeFromDate = (value: Date) => `${pad2(value.getHours())}:${pad2(value.getMinutes())}` + +const getDefaultDraft = ( + date: string, + initialType: 'todo' | 'schedule', + initialEvent?: CalendarEvent | null, +): ItemEditorDraft => { + const baseStart = initialEvent?.start ? new Date(initialEvent.start) : new Date(date) + const baseEnd = + initialEvent?.end && new Date(initialEvent.end).getTime() !== baseStart.getTime() + ? new Date(initialEvent.end) + : new Date(baseStart.getTime() + 60 * 60 * 1000) + + return { + title: + initialType === 'schedule' && initialEvent?.title === '새 일정' + ? '' + : (initialEvent?.title ?? ''), + description: initialEvent?.content ?? '', + startDate: baseStart, + endDate: baseEnd, + startTime: formatTimeFromDate(baseStart), + endTime: initialType === 'todo' ? formatTimeFromDate(baseStart) : formatTimeFromDate(baseEnd), + isAllday: initialEvent?.isAllDay ?? false, + eventColor: initialEvent?.color ?? (initialType === 'todo' ? 'GRAY' : 'BLUE'), + repeatConfig: defaultRepeatConfig as RepeatConfigSchema, + location: initialEvent?.location ?? '', + address: initialEvent?.address ?? null, + } +} + +type DraftBackedModalProps = { + modalDate: string + modalEventId: CalendarEvent['id'] + modalEvent: CalendarEvent | null + detailEvent: CalendarEvent | null + isModalEditing: boolean + modalMode: 'modal' | 'inline' + onCloseModal: () => void + eventActions: CalendarModalsProps['eventActions'] +} + +const DraftBackedModal = ({ + modalDate, + modalEventId, + modalEvent, + detailEvent, + isModalEditing, + modalMode, + onCloseModal, + eventActions, +}: DraftBackedModalProps) => { + const isTodoModal = modalEvent?.type === 'todo' + const activeEvent = detailEvent ?? modalEvent + const [draftValues, setDraftValues] = useState(() => + isModalEditing + ? null + : getDefaultDraft(modalDate, isTodoModal ? 'todo' : 'schedule', activeEvent), + ) + + if (isTodoModal) { + return ( + + ) + } + + return ( + + ) +} + const CalendarModals = ({ modalDate, modalEventId, @@ -60,39 +164,22 @@ const CalendarModals = ({ id: result.id ?? safeDetailEventId ?? 0, } }, [data, safeDetailEventId]) + const resetKey = `${String(modalEventId ?? 'closed')}::${occurrenceDate}::${isModalEditing ? 'edit' : 'create'}` + return ( <> {/* ItemEditorModal 내부 포털을 그대로 사용해, 리사이즈 시 같은 폼 상태로 루트만 이동합니다. */} - {shouldRenderModal && isTodoModal && ( - - )} - {shouldRenderModal && !isTodoModal && ( - )} diff --git a/src/shared/hooks/form/useScheduleFormFields.ts b/src/shared/hooks/form/useScheduleFormFields.ts index 663c3b6..be0903f 100644 --- a/src/shared/hooks/form/useScheduleFormFields.ts +++ b/src/shared/hooks/form/useScheduleFormFields.ts @@ -1,5 +1,5 @@ import { yupResolver } from '@hookform/resolvers/yup' -import { useEffect, useMemo } from 'react' +import { useEffect, useMemo, useRef } from 'react' import { type Control, type Resolver, useForm, type UseFormReturn, useWatch } from 'react-hook-form' import { addScheduleSchema } from '@/shared/schemas/schedule' @@ -113,6 +113,7 @@ export const useScheduleFormFields = ({ () => buildScheduleDefaultValues({ date, initialEvent, draftValues }), [date, draftValues, initialEvent], ) + const previousResetKeyRef = useRef(`${date}::${String(initialEvent?.id ?? 'new')}`) const formMethods = useForm({ resolver, defaultValues: initialValues, @@ -148,8 +149,11 @@ export const useScheduleFormFields = ({ useEffect(() => { if (isEditing) return + const nextResetKey = `${date}::${String(initialEvent?.id ?? 'new')}` + if (previousResetKeyRef.current === nextResetKey) return + previousResetKeyRef.current = nextResetKey reset(initialValues) - }, [date, initialValues, isEditing, reset]) + }, [date, initialEvent?.id, initialValues, isEditing, reset]) useEffect(() => { if (isEditing || !onDraftChange) return diff --git a/src/shared/hooks/form/useTodoFormFields.ts b/src/shared/hooks/form/useTodoFormFields.ts index b26794f..0197c5c 100644 --- a/src/shared/hooks/form/useTodoFormFields.ts +++ b/src/shared/hooks/form/useTodoFormFields.ts @@ -1,5 +1,5 @@ import { yupResolver } from '@hookform/resolvers/yup' -import { useEffect, useMemo } from 'react' +import { useEffect, useMemo, useRef } from 'react' import { type Control, type Resolver, useForm, type UseFormReturn, useWatch } from 'react-hook-form' import { addTodoSchema } from '@/shared/schemas/todo' @@ -73,6 +73,7 @@ export const useTodoFormFields = ({ () => buildTodoDefaultValues({ date, initialEvent, draftValues }), [date, draftValues, initialEvent], ) + const previousResetKeyRef = useRef(`${date}::${String(initialEvent?.id ?? 'new')}`) const formMethods = useForm({ resolver, defaultValues: initialValues, @@ -98,8 +99,11 @@ export const useTodoFormFields = ({ useEffect(() => { if (isEditing) return + const nextResetKey = `${date}::${String(initialEvent?.id ?? 'new')}` + if (previousResetKeyRef.current === nextResetKey) return + previousResetKeyRef.current = nextResetKey reset(initialValues) - }, [date, initialValues, isEditing, reset]) + }, [date, initialEvent?.id, initialValues, isEditing, reset]) useEffect(() => { if (isEditing || !onDraftChange) return diff --git a/src/shared/ui/Modals/ItemEditorModal/index.tsx b/src/shared/ui/Modals/ItemEditorModal/index.tsx index e3c4c46..e1a07c1 100644 --- a/src/shared/ui/Modals/ItemEditorModal/index.tsx +++ b/src/shared/ui/Modals/ItemEditorModal/index.tsx @@ -17,6 +17,18 @@ const pad2 = (value: number) => String(value).padStart(2, '0') const formatTimeFromDate = (value: Date) => `${pad2(value.getHours())}:${pad2(value.getMinutes())}` +const buildDateTime = (fallbackDate: string, dateValue?: Date | null, timeValue?: string) => { + const nextDate = dateValue ? new Date(dateValue) : new Date(fallbackDate) + if (!timeValue) { + nextDate.setHours(0, 0, 0, 0) + return nextDate + } + + const [hour, minute] = timeValue.split(':').map((value) => Number.parseInt(value, 10)) + nextDate.setHours(Number.isNaN(hour) ? 0 : hour, Number.isNaN(minute) ? 0 : minute, 0, 0) + return nextDate +} + const getDefaultDraft = ( date: string, initialType: ItemType, @@ -65,6 +77,8 @@ type ItemEditorModalProps = { allDay: boolean, occurrenceDate?: CalendarEvent['occurrenceDate'], ) => void + draftValues?: ItemEditorDraft | null + onDraftChange?: (draft: ItemEditorDraft | null) => void } const ItemEditorModal = ({ @@ -80,16 +94,30 @@ const ItemEditorModal = ({ onEventTitleConfirm, onEventTypeChange, onEventTimingChange, + draftValues: externalDraftValues, + onDraftChange: onExternalDraftChange, }: ItemEditorModalProps) => { const [activeType, setActiveType] = useState(initialType) - const [draftValues, setDraftValues] = useState(() => + const [internalDraftValues, setInternalDraftValues] = useState(() => isEditing ? null : getDefaultDraft(date, initialType, initialEvent), ) + const draftValues = externalDraftValues ?? internalDraftValues + const setDraftValues = useCallback( + (draft: ItemEditorDraft | null) => { + if (onExternalDraftChange) { + onExternalDraftChange(draft) + return + } + setInternalDraftValues(draft) + }, + [onExternalDraftChange], + ) const [footerChildren, setFooterChildren] = useState(null) const [deleteHandler, setDeleteHandler] = useState<() => void>(() => () => undefined) const [closeGuard, setCloseGuard] = useState boolean)>(null) const [modalWrapperElement, setModalWrapperElement] = useState(null) const modalWrapperRef = useRef(null) + const previousActiveTypeRef = useRef(activeType) const noopDeleteHandler = useCallback(() => undefined, []) const registerDeleteHandler = useCallback( @@ -120,14 +148,69 @@ const ItemEditorModal = ({ }, [initialType]) useEffect(() => { - setDraftValues(isEditing ? null : getDefaultDraft(date, initialType, initialEvent)) - }, [date, initialEvent, initialType, isEditing]) + if (externalDraftValues !== undefined) return + setInternalDraftValues(isEditing ? null : getDefaultDraft(date, initialType, initialEvent)) + }, [date, externalDraftValues, initialEvent, initialType, isEditing]) useEffect(() => { if (eventId == null || eventId === 0) return onEventTypeChange?.(eventId, activeType) }, [activeType, eventId, onEventTypeChange]) + useEffect(() => { + if (!showTypeTabs) return + if (previousActiveTypeRef.current === activeType) return + previousActiveTypeRef.current = activeType + if (eventId == null || eventId === 0) return + if (!onEventTimingChange) return + + const startDate = + draftValues?.startDate ?? (initialEvent?.start ? new Date(initialEvent.start) : null) + const endDate = + draftValues?.endDate ?? (initialEvent?.end ? new Date(initialEvent.end) : startDate) + const isAllDay = draftValues?.isAllday ?? initialEvent?.isAllDay ?? false + const occurrenceDate = initialEvent?.occurrenceDate + + if (activeType === 'todo') { + if (isAllDay) { + const start = new Date(startDate ?? new Date(date)) + start.setHours(0, 0, 0, 0) + const end = new Date(start) + end.setHours(23, 59, 59, 999) + onEventTimingChange(eventId, start, end, true, occurrenceDate) + return + } + + const point = buildDateTime(date, startDate, draftValues?.endTime ?? draftValues?.startTime) + onEventTimingChange(eventId, point, point, false, occurrenceDate) + return + } + + if (isAllDay) { + const start = new Date(startDate ?? new Date(date)) + start.setHours(0, 0, 0, 0) + const end = new Date(endDate ?? start) + end.setHours(23, 59, 59, 999) + onEventTimingChange(eventId, start, end, true, occurrenceDate) + return + } + + const start = buildDateTime(date, startDate, draftValues?.startTime) + const end = buildDateTime(date, endDate ?? startDate, draftValues?.endTime) + onEventTimingChange(eventId, start, end, false, occurrenceDate) + }, [ + activeType, + date, + draftValues, + eventId, + initialEvent?.end, + initialEvent?.isAllDay, + initialEvent?.occurrenceDate, + initialEvent?.start, + onEventTimingChange, + showTypeTabs, + ]) + const handleSubmit = useCallback(() => { const submitFormId = activeType === 'todo' ? 'add-todo-form' : 'add-schedule-form' const scopedTarget = modalWrapperRef.current?.querySelector( From 6415d0484f393541a67d4825b71a3f9f379a4f2c Mon Sep 17 00:00:00 2001 From: Yeonjin Kim Date: Fri, 10 Apr 2026 00:02:29 +0900 Subject: [PATCH 2/3] =?UTF-8?q?style:=20=EC=8B=9C=EA=B0=84=20=EC=9E=98?= =?UTF-8?q?=EB=A6=BC=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Calendar/components/CustomEvent/CustomEvent.style.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/features/Calendar/components/CustomEvent/CustomEvent.style.ts b/src/features/Calendar/components/CustomEvent/CustomEvent.style.ts index 98ade74..7cf1ca3 100644 --- a/src/features/Calendar/components/CustomEvent/CustomEvent.style.ts +++ b/src/features/Calendar/components/CustomEvent/CustomEvent.style.ts @@ -98,7 +98,7 @@ export const WeekEventContainer = styled.div<{ export const EventTitle = styled.div` font-weight: 400; font-size: 12px; - width: 45px; + width: 38px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; From d26d5f7bd29991e84363886801316759474a90b0 Mon Sep 17 00:00:00 2001 From: Yeonjin Kim Date: Fri, 10 Apr 2026 00:19:08 +0900 Subject: [PATCH 3/3] =?UTF-8?q?fix:=20=EB=A6=AC=EB=B7=B0=20=EB=B0=98?= =?UTF-8?q?=EC=98=81=EC=9C=BC=EB=A1=9C=20=EC=9D=BC=EC=A0=95=C2=B7=ED=95=A0?= =?UTF-8?q?=EC=9D=BC=20=EC=A0=84=ED=99=98=20draft=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CustomCalendar/CalendarModals.tsx | 44 ++----------- .../ui/Modals/ItemEditorModal/index.tsx | 65 ++++++------------- src/shared/utils/index.ts | 1 + src/shared/utils/itemEditorDraft.ts | 37 +++++++++++ 4 files changed, 64 insertions(+), 83 deletions(-) create mode 100644 src/shared/utils/itemEditorDraft.ts diff --git a/src/features/Calendar/components/CustomCalendar/CalendarModals.tsx b/src/features/Calendar/components/CustomCalendar/CalendarModals.tsx index 9c5d28f..6d9bedd 100644 --- a/src/features/Calendar/components/CustomCalendar/CalendarModals.tsx +++ b/src/features/Calendar/components/CustomCalendar/CalendarModals.tsx @@ -3,11 +3,10 @@ import { useMemo, useState } from 'react' import { useDetailEventQuery } from '@/shared/hooks/query/useCalendarQueries' import type { CalendarEvent } from '@/shared/types/calendar/types' -import type { RepeatConfigSchema } from '@/shared/types/event/event' import type { ItemEditorDraft } from '@/shared/types/modal/itemEditor' -import { defaultRepeatConfig } from '@/shared/types/recurrence/repeat' import ScheduleEditorModal from '@/shared/ui/Modals/ScheduleEditor' import TodoEditorModal from '@/shared/ui/Modals/TodoEditor' +import { buildDefaultItemEditorDraft } from '@/shared/utils' type CalendarModalsProps = { modalDate: string @@ -30,39 +29,6 @@ type CalendarModalsProps = { } } -const pad2 = (value: number) => String(value).padStart(2, '0') - -const formatTimeFromDate = (value: Date) => `${pad2(value.getHours())}:${pad2(value.getMinutes())}` - -const getDefaultDraft = ( - date: string, - initialType: 'todo' | 'schedule', - initialEvent?: CalendarEvent | null, -): ItemEditorDraft => { - const baseStart = initialEvent?.start ? new Date(initialEvent.start) : new Date(date) - const baseEnd = - initialEvent?.end && new Date(initialEvent.end).getTime() !== baseStart.getTime() - ? new Date(initialEvent.end) - : new Date(baseStart.getTime() + 60 * 60 * 1000) - - return { - title: - initialType === 'schedule' && initialEvent?.title === '새 일정' - ? '' - : (initialEvent?.title ?? ''), - description: initialEvent?.content ?? '', - startDate: baseStart, - endDate: baseEnd, - startTime: formatTimeFromDate(baseStart), - endTime: initialType === 'todo' ? formatTimeFromDate(baseStart) : formatTimeFromDate(baseEnd), - isAllday: initialEvent?.isAllDay ?? false, - eventColor: initialEvent?.color ?? (initialType === 'todo' ? 'GRAY' : 'BLUE'), - repeatConfig: defaultRepeatConfig as RepeatConfigSchema, - location: initialEvent?.location ?? '', - address: initialEvent?.address ?? null, - } -} - type DraftBackedModalProps = { modalDate: string modalEventId: CalendarEvent['id'] @@ -84,12 +50,12 @@ const DraftBackedModal = ({ onCloseModal, eventActions, }: DraftBackedModalProps) => { - const isTodoModal = modalEvent?.type === 'todo' const activeEvent = detailEvent ?? modalEvent + const isTodoModal = activeEvent?.type === 'todo' const [draftValues, setDraftValues] = useState(() => isModalEditing ? null - : getDefaultDraft(modalDate, isTodoModal ? 'todo' : 'schedule', activeEvent), + : buildDefaultItemEditorDraft(modalDate, isTodoModal ? 'todo' : 'schedule', activeEvent), ) if (isTodoModal) { @@ -99,7 +65,7 @@ const DraftBackedModal = ({ onClose={onCloseModal} mode={modalMode} eventId={modalEventId} - event={modalEvent} + event={activeEvent} showTypeTabs={!isModalEditing} draftValues={draftValues} onDraftChange={setDraftValues} @@ -118,7 +84,7 @@ const DraftBackedModal = ({ onClose={onCloseModal} mode={modalMode} eventId={modalEventId} - event={detailEvent ?? modalEvent} + event={activeEvent} isEditing={isModalEditing} showTypeTabs={!isModalEditing} draftValues={draftValues} diff --git a/src/shared/ui/Modals/ItemEditorModal/index.tsx b/src/shared/ui/Modals/ItemEditorModal/index.tsx index e1a07c1..4b96831 100644 --- a/src/shared/ui/Modals/ItemEditorModal/index.tsx +++ b/src/shared/ui/Modals/ItemEditorModal/index.tsx @@ -2,21 +2,16 @@ import { type ReactNode, useCallback, useEffect, useMemo, useRef, useState } fro import { createPortal } from 'react-dom' import type { CalendarEvent } from '@/shared/types/calendar/types' -import type { RepeatConfigSchema } from '@/shared/types/event/event' import type { ItemEditorDraft } from '@/shared/types/modal/itemEditor' -import { defaultRepeatConfig } from '@/shared/types/recurrence/repeat' import ScheduleEditorForm from '@/shared/ui/Modals/ScheduleEditor/ScheduleEditorForm' import TodoEditorForm from '@/shared/ui/Modals/TodoEditor/TodoEditorForm' +import { buildDefaultItemEditorDraft } from '@/shared/utils' import EditorModalLayout from './EditorModalLayout' import * as S from './ItemEditorModal.style' type ItemType = 'todo' | 'schedule' -const pad2 = (value: number) => String(value).padStart(2, '0') - -const formatTimeFromDate = (value: Date) => `${pad2(value.getHours())}:${pad2(value.getMinutes())}` - const buildDateTime = (fallbackDate: string, dateValue?: Date | null, timeValue?: string) => { const nextDate = dateValue ? new Date(dateValue) : new Date(fallbackDate) if (!timeValue) { @@ -29,35 +24,6 @@ const buildDateTime = (fallbackDate: string, dateValue?: Date | null, timeValue? return nextDate } -const getDefaultDraft = ( - date: string, - initialType: ItemType, - initialEvent?: CalendarEvent | null, -): ItemEditorDraft => { - const baseStart = initialEvent?.start ? new Date(initialEvent.start) : new Date(date) - const baseEnd = - initialEvent?.end && new Date(initialEvent.end).getTime() !== baseStart.getTime() - ? new Date(initialEvent.end) - : new Date(baseStart.getTime() + 60 * 60 * 1000) - - return { - title: - initialType === 'schedule' && initialEvent?.title === '새 일정' - ? '' - : (initialEvent?.title ?? ''), - description: initialEvent?.content ?? '', - startDate: baseStart, - endDate: baseEnd, - startTime: formatTimeFromDate(baseStart), - endTime: initialType === 'todo' ? formatTimeFromDate(baseStart) : formatTimeFromDate(baseEnd), - isAllday: initialEvent?.isAllDay ?? false, - eventColor: initialEvent?.color ?? (initialType === 'todo' ? 'GRAY' : 'BLUE'), - repeatConfig: defaultRepeatConfig as RepeatConfigSchema, - location: initialEvent?.location ?? '', - address: initialEvent?.address ?? null, - } -} - type ItemEditorModalProps = { onClose: () => void date: string @@ -99,9 +65,10 @@ const ItemEditorModal = ({ }: ItemEditorModalProps) => { const [activeType, setActiveType] = useState(initialType) const [internalDraftValues, setInternalDraftValues] = useState(() => - isEditing ? null : getDefaultDraft(date, initialType, initialEvent), + isEditing ? null : buildDefaultItemEditorDraft(date, initialType, initialEvent), ) const draftValues = externalDraftValues ?? internalDraftValues + const draftValuesRef = useRef(draftValues) const setDraftValues = useCallback( (draft: ItemEditorDraft | null) => { if (onExternalDraftChange) { @@ -120,6 +87,10 @@ const ItemEditorModal = ({ const previousActiveTypeRef = useRef(activeType) const noopDeleteHandler = useCallback(() => undefined, []) + useEffect(() => { + draftValuesRef.current = draftValues + }, [draftValues]) + const registerDeleteHandler = useCallback( (handler?: (() => void) | null) => { setDeleteHandler(() => handler ?? noopDeleteHandler) @@ -149,7 +120,9 @@ const ItemEditorModal = ({ useEffect(() => { if (externalDraftValues !== undefined) return - setInternalDraftValues(isEditing ? null : getDefaultDraft(date, initialType, initialEvent)) + setInternalDraftValues( + isEditing ? null : buildDefaultItemEditorDraft(date, initialType, initialEvent), + ) }, [date, externalDraftValues, initialEvent, initialType, isEditing]) useEffect(() => { @@ -164,11 +137,12 @@ const ItemEditorModal = ({ if (eventId == null || eventId === 0) return if (!onEventTimingChange) return + const latestDraftValues = draftValuesRef.current const startDate = - draftValues?.startDate ?? (initialEvent?.start ? new Date(initialEvent.start) : null) + latestDraftValues?.startDate ?? (initialEvent?.start ? new Date(initialEvent.start) : null) const endDate = - draftValues?.endDate ?? (initialEvent?.end ? new Date(initialEvent.end) : startDate) - const isAllDay = draftValues?.isAllday ?? initialEvent?.isAllDay ?? false + latestDraftValues?.endDate ?? (initialEvent?.end ? new Date(initialEvent.end) : startDate) + const isAllDay = latestDraftValues?.isAllday ?? initialEvent?.isAllDay ?? false const occurrenceDate = initialEvent?.occurrenceDate if (activeType === 'todo') { @@ -181,7 +155,11 @@ const ItemEditorModal = ({ return } - const point = buildDateTime(date, startDate, draftValues?.endTime ?? draftValues?.startTime) + const point = buildDateTime( + date, + startDate, + latestDraftValues?.endTime ?? latestDraftValues?.startTime, + ) onEventTimingChange(eventId, point, point, false, occurrenceDate) return } @@ -195,13 +173,12 @@ const ItemEditorModal = ({ return } - const start = buildDateTime(date, startDate, draftValues?.startTime) - const end = buildDateTime(date, endDate ?? startDate, draftValues?.endTime) + const start = buildDateTime(date, startDate, latestDraftValues?.startTime) + const end = buildDateTime(date, endDate ?? startDate, latestDraftValues?.endTime) onEventTimingChange(eventId, start, end, false, occurrenceDate) }, [ activeType, date, - draftValues, eventId, initialEvent?.end, initialEvent?.isAllDay, diff --git a/src/shared/utils/index.ts b/src/shared/utils/index.ts index 8c9fa56..1554332 100644 --- a/src/shared/utils/index.ts +++ b/src/shared/utils/index.ts @@ -1,6 +1,7 @@ export * from './date' export * from './error' export * from './formError' +export * from './itemEditorDraft' export * from './priority' export * from './recurrenceGroup' export * from './repeatConfig' diff --git a/src/shared/utils/itemEditorDraft.ts b/src/shared/utils/itemEditorDraft.ts new file mode 100644 index 0000000..5cfcd18 --- /dev/null +++ b/src/shared/utils/itemEditorDraft.ts @@ -0,0 +1,37 @@ +import type { CalendarEvent } from '@/shared/types/calendar/types' +import type { RepeatConfigSchema } from '@/shared/types/event/event' +import type { ItemEditorDraft } from '@/shared/types/modal/itemEditor' +import { defaultRepeatConfig } from '@/shared/types/recurrence/repeat' + +const pad2 = (value: number) => String(value).padStart(2, '0') + +const formatTimeFromDate = (value: Date) => `${pad2(value.getHours())}:${pad2(value.getMinutes())}` + +export const buildDefaultItemEditorDraft = ( + date: string, + initialType: 'todo' | 'schedule', + initialEvent?: CalendarEvent | null, +): ItemEditorDraft => { + const baseStart = initialEvent?.start ? new Date(initialEvent.start) : new Date(date) + const baseEnd = + initialEvent?.end && new Date(initialEvent.end).getTime() !== baseStart.getTime() + ? new Date(initialEvent.end) + : new Date(baseStart.getTime() + 60 * 60 * 1000) + + return { + title: + initialType === 'schedule' && initialEvent?.title === '새 일정' + ? '' + : (initialEvent?.title ?? ''), + description: initialEvent?.content ?? '', + startDate: baseStart, + endDate: baseEnd, + startTime: formatTimeFromDate(baseStart), + endTime: initialType === 'todo' ? formatTimeFromDate(baseStart) : formatTimeFromDate(baseEnd), + isAllday: initialEvent?.isAllDay ?? false, + eventColor: initialEvent?.color ?? (initialType === 'todo' ? 'GRAY' : 'BLUE'), + repeatConfig: defaultRepeatConfig as RepeatConfigSchema, + location: initialEvent?.location ?? '', + address: initialEvent?.address ?? null, + } +}