Skip to content

[#243][fe][schedule]feat: 일정 삭제 화면#245

Merged
cchoijjinyoung merged 7 commits intodevfrom
feat/#243
Oct 20, 2025
Merged

[#243][fe][schedule]feat: 일정 삭제 화면#245
cchoijjinyoung merged 7 commits intodevfrom
feat/#243

Conversation

@cchoijjinyoung
Copy link
Copy Markdown
Contributor

@cchoijjinyoung cchoijjinyoung commented Oct 4, 2025

#️⃣연관된 이슈

변경 타입

  • 신규 기능 추가/수정
  • 버그 수정
  • 리팩토링
  • 설정
  • 비기능 (주석 등 기능에 영향을 주지 않음)

변경 내용

  • as-is

  • to-be(변경 후 설명을 여기에 작성)

    • 일정 삭제 화면
    • 삭제 버튼 추가
    • 삭제 버튼 클릭 시 '해당 일정 삭제/이후 일정 삭제' two 버튼 모달 추가

체크리스트

  • 코드가 제대로 동작하는지 확인했습니다.
  • 관련 테스트를 추가했습니다.
  • 문서(코드, 주석, README 등)를 업데이트했습니다.

코멘트

Summary by CodeRabbit

  • 신기능

    • 일정 삭제 확인 모달 추가: “해당 일정만 삭제” 또는 “이후 일정 모두 삭제” 선택 가능
    • 일정 폼에 모드 지원(생성/편집): 편집 모드에서 삭제 옵션과 관련 동작 제공
    • 수정 화면에서 삭제 기능 연동: 단일 삭제/연속 삭제 수행 및 성공 후 이동/알림 처리
  • 버그 수정

    • 반복 일정 계산 시 종료일(until) 준수 및 취소된 예외(overrides) 무시로 목록 표시 정확도 향상
    • 예외 처리 필터링 개선으로 반복 일정 확장 결과 안정성 강화

@cchoijjinyoung cchoijjinyoung self-assigned this Oct 4, 2025
@cchoijjinyoung cchoijjinyoung added the ✨ feature 기능 구현 label Oct 4, 2025
@cchoijjinyoung cchoijjinyoung added the 🎨 frontend 프론트 마크업 및 스타일링 label Oct 4, 2025
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Oct 4, 2025

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

  • 일정 삭제 기능 추가: 단건 삭제와 기준 시점 이후 전체 삭제를 위한 API 헬퍼(deleteSchedule, deleteSchedulesStartingFrom)와 서비스 래퍼 추가
  • 수정 화면에 삭제 플로우 도입: ScheduleForm에 mode(prop)와 삭제 이벤트(deleteOne, deleteAll) 추가, DeleteScheduleModal 신규 컴포넌트로 확인 모달 제공
  • Create/Modify 뷰에서 ScheduleForm 사용 방식 업데이트(mode 전달, 삭제 이벤트 바인딩)
  • useScheduleForm에 삭제 핸들러(onDeleteOne, onDeleteAll) 추가 및 갱신/재조회 로직 연동
  • rruleUtils에서 반복 일정 확장 로직 보강: CANCELLED 오버라이드 필터링, until 경계 고려, 디버그 로그 추가

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User as 사용자
  participant MV as ModifyScheduleView
  participant SF as ScheduleForm (mode="edit")
  participant DM as DeleteScheduleModal
  participant USF as useScheduleForm
  participant SS as scheduleService
  participant API as schedule/api.js
  participant BE as Backend

  User->>SF: "삭제" 버튼 클릭
  SF->>DM: v-model=true (모달 열기)

  alt 해당 일정만 삭제
    User->>DM: "해당 일정만 삭제"
    DM-->>SF: emit deleteOne
    SF-->>MV: emit deleteOne(formData)
    MV->>USF: onDeleteOne(payload)
    USF->>SS: deleteSchedule({scheduleId, instanceDate, startAt, endAt})
    SS->>API: deleteSchedule(payload)
    API->>BE: DELETE /v1/schedules (body=payload)
    alt 성공
      BE-->>API: 200
      API-->>SS: resolve
      SS-->>USF: resolve
      USF->>USF: 날짜 갱신/재조회
      USF-->>MV: onSuccess 콜백
      MV-->>User: 화면 이동/알림
    else 실패
      BE-->>API: 4xx/5xx
      API-->>SS: error
      SS-->>USF: error
      USF-->>MV: onError 콜백
      MV-->>User: 에러 표시
    end
    DM-->>SF: emit update:modelValue(false)
  else 이후 일정 모두 삭제
    User->>DM: "이후 일정 모두 삭제"
    DM-->>SF: emit deleteAll
    SF-->>MV: emit deleteAll(formData)
    MV->>USF: onDeleteAll({scheduleId, fromAt})
    USF->>SS: deleteSchedulesStartingFrom({scheduleId, fromAt})
    SS->>API: deleteSchedulesStartingFrom(payload)
    API->>BE: DELETE /v1/schedules/from (body=payload)
    alt 성공
      BE-->>API: 200
      API-->>SS: resolve
      SS-->>USF: resolve
      USF->>USF: 날짜 갱신/재조회
      USF-->>MV: onSuccess 콜백
      MV-->>User: 화면 이동/알림
    else 실패
      BE-->>API: 4xx/5xx
      API-->>SS: error
      SS-->>USF: error
      USF-->>MV: onError 콜백
      MV-->>User: 에러 표시
    end
    DM-->>SF: emit update:modelValue(false)
  end
Loading
sequenceDiagram
  autonumber
  participant RU as rruleUtils.expandRecurringSchedule
  participant OV as scheduleOverrides

  RU->>RU: 초기 인스턴스 계산
  RU->>OV: override 목록 필터링(overrideDate 존재 ∧ type≠CANCELLED)
  loop 반복 확장
    RU->>RU: 다음 발생일 계산
    alt until 경계 존재
      RU->>RU: nextDate > until 이면 중단
    end
    RU->>RU: CANCELLED 오버라이드 스킵
    RU->>RU: 유효 오버라이드 적용
  end
  RU-->>RU: 결과 반환
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Out of Scope Changes Check ⚠️ Warning rruleUtils.js 파일에서 재귀 스케줄 확장 로직 수정과 디버깅 로그 추가는 ‘일정 삭제 화면’ 구현과 직접적인 관련이 없어 이 PR의 범위를 벗어난 변경 사항으로 보입니다. rruleUtils.js에 대한 수정은 별도의 PR로 분리하거나 삭제 기능 구현과 연관된 부분만 남기고 분리하여 관리하는 것을 권장합니다.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed 제목은 ‘일정 삭제 화면’ 구현이라는 PR의 핵심 변경 사항을 정확히 요약하고 있어 팀원이 변경 내역을 한눈에 이해하기에 충분히 구체적이고 명확합니다.
Linked Issues Check ✅ Passed PR은 삭제 버튼 클릭 시 모달을 띄워 ‘해당 일정만 삭제’와 ‘이후 일정 모두 삭제’라는 두 가지 옵션을 제공하는 UI 컴포넌트와 서비스, 훅, 뷰 단의 연동을 모두 포함하여 이슈 #243의 요구 사항을 충족합니다.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 91a5095 and 17940b1.

📒 Files selected for processing (8)
  • front/src/features/schedule/api.js (1 hunks)
  • front/src/features/schedule/components/DeleteScheduleModal.vue (1 hunks)
  • front/src/features/schedule/components/ScheduleForm.vue (2 hunks)
  • front/src/features/schedule/scheduleService.js (2 hunks)
  • front/src/features/schedule/useScheduleForm.js (7 hunks)
  • front/src/features/schedule/views/CreateScheduleView.vue (1 hunks)
  • front/src/features/schedule/views/ModifyScheduleView.vue (3 hunks)
  • front/src/shared/utils/rruleUtils.js (5 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-07-22T08:47:21.989Z
Learnt from: HanaHww2
PR: HI-dle/rouby#183
File: front/src/shared/utils/rruleUtils.js:144-144
Timestamp: 2025-07-22T08:47:21.989Z
Learning: front/src/shared/utils/rruleUtils.js에서 schedule 인스턴스는 의도적으로 `id`와 `originId` 두 속성을 모두 가집니다. override인 경우 `id`가 달라지므로 `originId`로 원본 스케줄을 추적하고, 인스턴스 키는 `instance.id`(override의 경우 override ID)를 사용합니다.

Applied to files:

  • front/src/shared/utils/rruleUtils.js
📚 Learning: 2025-07-09T16:16:59.462Z
Learnt from: HanaHww2
PR: HI-dle/rouby#113
File: front/src/features/schedule/validations.js:3-28
Timestamp: 2025-07-09T16:16:59.462Z
Learning: In front/src/features/schedule/useScheduleForm.js, errors are cleared reactively when form input values change rather than clearing all errors at the start of validation. This provides better UX as users see errors disappear immediately when they start fixing the specific fields that caused them.

Applied to files:

  • front/src/features/schedule/useScheduleForm.js
🧬 Code graph analysis (3)
front/src/features/schedule/api.js (1)
front/src/features/schedule/scheduleService.js (4)
  • deleteSchedule (30-32)
  • deleteSchedule (30-32)
  • deleteSchedulesStartingFrom (34-36)
  • deleteSchedulesStartingFrom (34-36)
front/src/features/schedule/scheduleService.js (2)
front/src/features/schedule/api.js (4)
  • deleteSchedule (26-32)
  • deleteSchedule (26-32)
  • deleteSchedulesStartingFrom (34-40)
  • deleteSchedulesStartingFrom (34-40)
front/src/shared/utils/errorUtils.js (1)
  • wrapApi (33-41)
front/src/features/schedule/useScheduleForm.js (3)
front/src/shared/utils/dateTimeUtils.js (2)
  • formatDateTime (5-23)
  • formatDateTime (5-23)
front/src/features/schedule/validations.js (2)
  • validateForm (3-28)
  • validateForm (3-28)
front/src/features/schedule/scheduleService.js (8)
  • createSchedule (7-12)
  • createSchedule (7-12)
  • updateSchedule (22-28)
  • updateSchedule (22-28)
  • deleteSchedule (30-32)
  • deleteSchedule (30-32)
  • deleteSchedulesStartingFrom (34-36)
  • deleteSchedulesStartingFrom (34-36)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: test

default: 'create', // 'create' | 'edit'
},
})
const emit = defineEmits(['submit', 'cancel', 'inputDatetime', 'autoResize'])
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

새로 추가한 삭제 이벤트를 defineEmits에 선언해야 합니다.

emit('deleteOne', ...), emit('deleteAll', ...)를 호출하지만 defineEmits 배열에 포함되어 있지 않아 런타임 경고와 타입 추론 오류가 발생합니다. 아래처럼 이벤트 목록을 업데이트해주세요.

-const emit = defineEmits(['submit', 'cancel', 'inputDatetime', 'autoResize'])
+const emit = defineEmits([
+  'submit',
+  'cancel',
+  'inputDatetime',
+  'autoResize',
+  'deleteOne',
+  'deleteAll',
+])
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const emit = defineEmits(['submit', 'cancel', 'inputDatetime', 'autoResize'])
const emit = defineEmits([
'submit',
'cancel',
'inputDatetime',
'autoResize',
'deleteOne',
'deleteAll',
])
🤖 Prompt for AI Agents
In front/src/features/schedule/components/ScheduleForm.vue around line 24, the
component calls emit('deleteOne', ...) and emit('deleteAll', ...) but those
events are not declared in the defineEmits array, causing runtime warnings and
broken type inference; update the defineEmits call to include 'deleteOne' and
'deleteAll' (and any relevant payload typings if using TypeScript) so the
emitted events are declared and properly typed.

Comment on lines +40 to +41
initValues.allDay ? subDays(initValues.end, 1) : initValues.end,
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

하루종일 일정 초기화 시 subDays에 문자열을 직접 전달하면 TypeError가 발생합니다.

API에서 내려오는 initValues.end는 문자열인데, 현재 코드에서는 그대로 subDays에 넘겨 즉시 예외가 발생합니다. 날짜 객체로 변환한 뒤 하루를 빼도록 수정해주세요.

-          initValues.allDay ? subDays(initValues.end, 1) : initValues.end,
+          initValues.allDay
+            ? subDays(new Date(initValues.end), 1)
+            : initValues.end,
🤖 Prompt for AI Agents
In front/src/features/schedule/useScheduleForm.js around lines 40-41,
initValues.end is a string from the API and is passed directly to subDays
causing a TypeError; convert the string to a Date object before calling subDays
(e.g., use new Date(initValues.end) or parseISO/init parsing used in the
codebase) and guard against null/undefined so you call subDays only with a valid
Date.

Comment on lines +67 to +93
const handleDeleteOne = async (schedule) => {
const instanceDate = route.params.date
console.log(schedule)
await onDeleteOne(
{ scheduleId: schedule.id, instanceDate: instanceDate, startAt: schedule.start, endAt: schedule.end },
() => {
router.push('/schedule/daily/list')
},
(msg) => {
errorModal.msg = msg
errorModal.show = true
},
)
}

const handleDeleteAll = async (schedule) => {
await onDeleteAll(
{ scheduleId: schedule.id, fromAt: schedule.start },
() => {
router.push('/schedule/daily/list')
},
(msg) => {
errorModal.msg = msg
errorModal.show = true
},
)
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

삭제 요청에 필요한 필드가 잘못 전달되어 런타임 예외와 잘못된 API 호출이 발생합니다.

현재 핸들러는 이벤트 인자로 넘어오는 schedule을 기대하지만, DeleteScheduleModalScheduleForm 연쇄에서 payload가 없는 경우 scheduleundefined가 되어 즉시 TypeError가 발생합니다. 또한 스토어에서 가져온 인스턴스는 startAt/endAt 속성을 사용하기 때문에 schedule.start/schedule.end로 보내면 값이 undefined가 되어 서버가 일정을 식별하지 못합니다. 상위 스코프의 schedule을 직접 사용하고 올바른 필드명을 전달하도록 수정해주세요.

-const handleDeleteOne = async (schedule) => {
-  const instanceDate = route.params.date
-  console.log(schedule)
-  await onDeleteOne(
-    { scheduleId: schedule.id, instanceDate: instanceDate, startAt: schedule.start, endAt: schedule.end },
+const handleDeleteOne = async () => {
+  if (!schedule) return
+  const instanceDate = route.params.date
+  await onDeleteOne(
+    {
+      scheduleId: schedule.id,
+      instanceDate,
+      startAt: schedule.startAt,
+      endAt: schedule.endAt,
+    },
@@
-const handleDeleteAll = async (schedule) => {
-  await onDeleteAll(
-    { scheduleId: schedule.id, fromAt: schedule.start },
+const handleDeleteAll = async () => {
+  if (!schedule) return
+  await onDeleteAll(
+    {
+      scheduleId: schedule.id,
+      fromAt: schedule.startAt,
+    },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const handleDeleteOne = async (schedule) => {
const instanceDate = route.params.date
console.log(schedule)
await onDeleteOne(
{ scheduleId: schedule.id, instanceDate: instanceDate, startAt: schedule.start, endAt: schedule.end },
() => {
router.push('/schedule/daily/list')
},
(msg) => {
errorModal.msg = msg
errorModal.show = true
},
)
}
const handleDeleteAll = async (schedule) => {
await onDeleteAll(
{ scheduleId: schedule.id, fromAt: schedule.start },
() => {
router.push('/schedule/daily/list')
},
(msg) => {
errorModal.msg = msg
errorModal.show = true
},
)
}
const handleDeleteOne = async () => {
if (!schedule) return
const instanceDate = route.params.date
await onDeleteOne(
{
scheduleId: schedule.id,
instanceDate,
startAt: schedule.startAt,
endAt: schedule.endAt,
},
() => {
router.push('/schedule/daily/list')
},
(msg) => {
errorModal.msg = msg
errorModal.show = true
},
)
}
const handleDeleteAll = async () => {
if (!schedule) return
await onDeleteAll(
{
scheduleId: schedule.id,
fromAt: schedule.startAt,
},
() => {
router.push('/schedule/daily/list')
},
(msg) => {
errorModal.msg = msg
errorModal.show = true
},
)
}
🤖 Prompt for AI Agents
In front/src/features/schedule/views/ModifyScheduleView.vue around lines 67 to
93, the handlers read an event arg `schedule` which can be undefined and they
send wrong property names (using schedule.start/schedule.end) to the API;
instead reference the component's outer-scope `schedule` variable (not the event
arg) and send the correct fields: use schedule.id and schedule.startAt /
schedule.endAt (keep instanceDate from route.params.date), and add a simple
guard to return early if the outer-scope schedule is falsy before calling
onDeleteOne/onDeleteAll so no TypeError occurs.

Comment thread front/src/shared/utils/rruleUtils.js Outdated
Comment on lines 23 to 24
console.log(rruleStr)
const vevent = new ICAL.Component('vevent')
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

디버그용 console.log를 제거해주세요.

공용 util에서 남아 있는 console.log는 운영 환경에서 불필요한 노이즈이자 민감한 데이터가 노출될 가능성이 있습니다. 로그 대신 디버깅 도구나 명시적인 로거를 사용해주세요.

-  console.log(rruleStr)
@@
-  console.log(recurrence)
@@
-    console.log("언틸" + recurrence.until)

Also applies to: 64-65, 98-101

🤖 Prompt for AI Agents
In front/src/shared/utils/rruleUtils.js around lines 23-24 (and also at 64-65
and 98-101), there are leftover console.log debug statements that should be
removed; delete those console.log calls and, if runtime visibility is required,
replace them with calls to the project's logger (e.g., import and use the
existing logger or a debug utility) or wrap them behind a debug flag so no
sensitive data is printed in production. Ensure no other console.* calls remain
in this util and run a quick search to confirm removal.


const handleDeleteOne = async (schedule) => {
const instanceDate = route.params.date
console.log(schedule)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

콘솔 로그 지워주세요

Comment thread front/src/shared/utils/rruleUtils.js Outdated
format(parseISO(o.overrideDate), 'yyyy-MM-dd'),
),
(schedule.scheduleOverrides || [])
.filter(o => o.overrideDate && o.overrideType?.toUpperCase() !== 'CANCELLED')
Copy link
Copy Markdown
Contributor

@HanaHww2 HanaHww2 Oct 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

여기서 필터링하면 이후 로직에서 오버라이드 되었던 일자에도 일정을 생성하게 됩니다.
아래 오버라이드 일정 만드는 부분에서 조건절로 취소인 경우는 제외하기만 하면 될 거 같아요.

Comment thread front/src/shared/utils/rruleUtils.js Outdated
)
}

// ---------- overridesType이 cancelled면 필터링하자
Copy link
Copy Markdown
Contributor

@HanaHww2 HanaHww2 Oct 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

불필요한 주석이나 콘솔로그는 제거 부탁드릴게요~

Comment thread front/src/shared/utils/rruleUtils.js Outdated

console.log("언틸" + recurrence.until)
// 반복 종료 체크: recurrence.until 또는 untilAt 반영
if (recurrence.until && nextDate > parseISO(recurrence.until)) break
Copy link
Copy Markdown
Contributor

@HanaHww2 HanaHww2 Oct 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

until 은 createIcalComponent 메소드에서 리커런스룰을 통해서 반영되어 일정 목록을 생성해주는지 확인 부탁드릴게요.

Comment thread front/src/features/schedule/api.js Outdated
}
// 단일 일정 삭제
return axios.patch('/v1/schedules', payload, {
headers: { 'Content-Type': 'application/json' },
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이거 제거하고 다시 확인 부탁드립니다.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이걸 제거하면 안되더라구요

Comment thread front/src/shared/utils/rruleUtils.js Outdated
const { rangeStart, rangeEnd } = getMonthRange(monthKey)

// 반복이 없는 경우
console.log(recurrence)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

콘솔로그 제거 부탁드립니다.

Copy link
Copy Markdown
Contributor

@HanaHww2 HanaHww2 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

수고 많으셨습니다~!!

@cchoijjinyoung cchoijjinyoung merged commit c355bd2 into dev Oct 20, 2025
1 check passed
@HanaHww2 HanaHww2 removed this from Rouby_jjang Oct 20, 2025
@HanaHww2 HanaHww2 deleted the feat/#243 branch October 20, 2025 05:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨ feature 기능 구현 🎨 frontend 프론트 마크업 및 스타일링

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[schedule] 일정 삭제 화면

2 participants