From 8020cf058122fd78dd136fde6a9b76c1efe2fee3 Mon Sep 17 00:00:00 2001 From: grappe96 Date: Tue, 28 Oct 2025 23:32:18 +0900 Subject: [PATCH 01/21] =?UTF-8?q?=EA=B3=BC=EC=A0=9C=EC=8B=9C=EC=9E=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From c0560312b73bf6ed87508d1ec9ed5bb267181cfd Mon Sep 17 00:00:00 2001 From: grappe96 Date: Fri, 31 Oct 2025 01:10:19 +0900 Subject: [PATCH 02/21] chore(bmm): initialize workflow, sprint status, traceability and specs for recurring feature --- docs/bmm-workflow-status.md | 11 ++++++ .../requirements-traceability.csv | 7 ++++ sprint-status.yaml | 6 ++++ tech-specs/recurring-events.md | 34 +++++++++++++++++++ tests/specs/recurring-events.testplan.md | 24 +++++++++++++ 5 files changed, 82 insertions(+) create mode 100644 docs/bmm-workflow-status.md create mode 100644 docs/traceability/requirements-traceability.csv create mode 100644 sprint-status.yaml create mode 100644 tech-specs/recurring-events.md create mode 100644 tests/specs/recurring-events.testplan.md diff --git a/docs/bmm-workflow-status.md b/docs/bmm-workflow-status.md new file mode 100644 index 00000000..b6f027fc --- /dev/null +++ b/docs/bmm-workflow-status.md @@ -0,0 +1,11 @@ +# BMM Workflow Status + +phase: planning +context: recurring-events v1.1 +next_action: derive-epics-from-recurring-spec + +notes: +- Align PRD/Epics with `docs/recurring-function-spec.md` +- Set initial backlog and DoR rules + + diff --git a/docs/traceability/requirements-traceability.csv b/docs/traceability/requirements-traceability.csv new file mode 100644 index 00000000..f823c6fe --- /dev/null +++ b/docs/traceability/requirements-traceability.csv @@ -0,0 +1,7 @@ +REQ ID,EPIC ID,FUNC ID,TEST ID,비고 +REQ-001,EPIC-RECUR-001,FUNC-001,TEST-RECUR-001,반복 유형 선택 +REQ-002,EPIC-RECUR-001,FUNC-002,TEST-RECUR-002,반복 일정 표시 +REQ-003,EPIC-RECUR-001,FUNC-003,TEST-RECUR-003,반복 종료 설정 +REQ-004,EPIC-RECUR-001,FUNC-004,TEST-RECUR-004,반복 일정 수정 +REQ-005,EPIC-RECUR-001,FUNC-005,TEST-RECUR-005,반복 일정 삭제 +REQ-006,EPIC-RECUR-001,FUNC-006,TEST-RECUR-006,공통 예외/데이터 정책 diff --git a/sprint-status.yaml b/sprint-status.yaml new file mode 100644 index 00000000..d09bb8a8 --- /dev/null +++ b/sprint-status.yaml @@ -0,0 +1,6 @@ +phase: implementation +currentSprint: 1 +backlog: [] +todo: [] +inProgress: [] +done: [] diff --git a/tech-specs/recurring-events.md b/tech-specs/recurring-events.md new file mode 100644 index 00000000..365668a4 --- /dev/null +++ b/tech-specs/recurring-events.md @@ -0,0 +1,34 @@ +# Tech Spec: Recurring Events + +scope: align with `docs/recurring-function-spec.md` v1.1 +owner: Architect + +## Goals +- Support daily/weekly/monthly/yearly recurrence with edge rules (31st, Feb 29) +- End conditions: none or until date +- Edit/delete: single vs series +- Visual mark for recurring items + +## Data Model (frontend) +- RepeatInfo: { type: 'none'|'daily'|'weekly'|'monthly'|'yearly'; interval: number; endDate?: string } +- Event extends EventForm with id: string + +## API Contract (backend) +- GET /api/events → { events: Event[] } +- POST /api/events (single) +- POST /api/events-list (bulk create series, attach repeat.id) +- PUT /api/recurring-events/:repeatId (update series) +- DELETE /api/recurring-events/:repeatId (delete series) + +## Series Rules +- Monthly 31st: skip months without 31 +- Yearly Feb 29: only leap years + +## NFR & Security +- Performance: render < 3s, operations < 500ms +- Consistency: UI reflects updates immediately +- Safety: input validation on date/time/range + +## Open Points +- Preview generation limits (range cap) +- Timezone considerations (future work) diff --git a/tests/specs/recurring-events.testplan.md b/tests/specs/recurring-events.testplan.md new file mode 100644 index 00000000..0bf35dd0 --- /dev/null +++ b/tests/specs/recurring-events.testplan.md @@ -0,0 +1,24 @@ +# Test Plan: Recurring Events + +scope: align with `docs/recurring-function-spec.md` v1.1 +owner: TEA +coverage_targets: + unit: ">=80%" + integration: ">=60%" + e2e: ">=40%" + +## Scenarios +- Select Repeat Type (daily/weekly/monthly/yearly) +- Display Recurring Event (icon/aria/tooltip) +- Set Recurrence End (none/until) +- Edit Recurring (single vs series) +- Delete Recurring (single vs series) +- Common Exceptions & Data Policy + +## Mocking & Data +- Use MSW handlers, seed data per scenario +- Freeze time (UTC) and use fake timers + +## CI Gates +- Enforce coverage targets; block `story-done` if unmet +- Publish reports to ./.coverage From 65010043a62457a2546871e3091ee776e88b1fdd Mon Sep 17 00:00:00 2001 From: grappe96 Date: Fri, 31 Oct 2025 01:32:31 +0900 Subject: [PATCH 03/21] chore(rules): simplify Cursor coding style to essential rules --- .cursor/rules/.cursorrules | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .cursor/rules/.cursorrules diff --git a/.cursor/rules/.cursorrules b/.cursor/rules/.cursorrules new file mode 100644 index 00000000..0e0b6096 --- /dev/null +++ b/.cursor/rules/.cursorrules @@ -0,0 +1,14 @@ +# Cursor Rules (essential) + +version: 1 + +- language: TypeScript (React + Vite) +- indentation: 2 spaces; quotes: single; semicolons: required +- imports: relative within src; prefer named exports; extensionless preferred (current .ts mixed allowed) +- hooks: names start with `use`; single concern; proper deps; clean up timers (see `useNotifications`) +- types: shared interfaces in `src/types.ts`; avoid `any`; use precise unions +- date/time: use JS Date; build 'YYYY-MM-DD' via helpers (`formatDate`, `fillZero`) +- errors: return structured validation (see `getTimeErrorMessage`); network → throw on non-ok + snackbar + console.error +- api: fetch JSON; set `Content-Type: application/json` on write; refresh via one `fetchEvents()` and notify +- testing: Vitest + Testing Library + MSW; fake timers + fixed TZ in `src/setupTests.ts` +- commits: conventional commits; TDD loop (Red → Green → Refactor) From dc11302d10dfd164318c490dfd5007936e562562 Mon Sep 17 00:00:00 2001 From: grappe96 Date: Fri, 31 Oct 2025 01:34:49 +0900 Subject: [PATCH 04/21] chore(sprint): seed backlog from recurring spec and set first TODO (STORY-RECUR-001) --- sprint-status.yaml | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/sprint-status.yaml b/sprint-status.yaml index d09bb8a8..8727112e 100644 --- a/sprint-status.yaml +++ b/sprint-status.yaml @@ -1,6 +1,30 @@ phase: implementation currentSprint: 1 -backlog: [] -todo: [] +backlog: + - story-id: STORY-RECUR-002 + title: 반복 일정 아이콘 렌더링 처리 + points: 3 + priority: medium + - story-id: STORY-RECUR-003 + title: 반복 종료 조건 로직 + points: 5 + priority: high + - story-id: STORY-RECUR-004 + title: 반복 일정 수정 모드 로직 + points: 5 + priority: high + - story-id: STORY-RECUR-005 + title: 반복 일정 삭제 로직 + points: 3 + priority: medium + - story-id: STORY-RECUR-006 + title: 데이터 및 예외 공통 처리 + points: 3 + priority: medium +todo: + - story-id: STORY-RECUR-001 + title: 반복 유형 선택 로직 및 UI 구현 + points: 5 + priority: high inProgress: [] done: [] From 78aa964809e7b46d0324e2ea6bd525ff7b73c9be Mon Sep 17 00:00:00 2001 From: grappe96 Date: Fri, 31 Oct 2025 01:42:30 +0900 Subject: [PATCH 05/21] chore(meta): add assignee/owner timestamps to sprint and owner role to workflow status --- docs/bmm-workflow-status.md | 1 + sprint-status.yaml | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/docs/bmm-workflow-status.md b/docs/bmm-workflow-status.md index b6f027fc..84966290 100644 --- a/docs/bmm-workflow-status.md +++ b/docs/bmm-workflow-status.md @@ -3,6 +3,7 @@ phase: planning context: recurring-events v1.1 next_action: derive-epics-from-recurring-spec +currentOwnerRole: PM notes: - Align PRD/Epics with `docs/recurring-function-spec.md` diff --git a/sprint-status.yaml b/sprint-status.yaml index 8727112e..06717d9d 100644 --- a/sprint-status.yaml +++ b/sprint-status.yaml @@ -5,26 +5,44 @@ backlog: title: 반복 일정 아이콘 렌더링 처리 points: 3 priority: medium + assigneeRole: DEV + owner: unassigned + updatedAt: 2025-10-30 - story-id: STORY-RECUR-003 title: 반복 종료 조건 로직 points: 5 priority: high + assigneeRole: DEV + owner: unassigned + updatedAt: 2025-10-30 - story-id: STORY-RECUR-004 title: 반복 일정 수정 모드 로직 points: 5 priority: high + assigneeRole: DEV + owner: unassigned + updatedAt: 2025-10-30 - story-id: STORY-RECUR-005 title: 반복 일정 삭제 로직 points: 3 priority: medium + assigneeRole: DEV + owner: unassigned + updatedAt: 2025-10-30 - story-id: STORY-RECUR-006 title: 데이터 및 예외 공통 처리 points: 3 priority: medium + assigneeRole: DEV + owner: unassigned + updatedAt: 2025-10-30 todo: - story-id: STORY-RECUR-001 title: 반복 유형 선택 로직 및 UI 구현 points: 5 priority: high + assigneeRole: DEV + owner: unassigned + updatedAt: 2025-10-30 inProgress: [] done: [] From af089cda1d3d829c93a167bf314fb9552aa79241 Mon Sep 17 00:00:00 2001 From: grappe96 Date: Fri, 31 Oct 2025 01:54:17 +0900 Subject: [PATCH 06/21] docs(bmm): add multi-agent activity log with artifacts and commit links --- docs/bmm-activity-log.md | 43 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 docs/bmm-activity-log.md diff --git a/docs/bmm-activity-log.md b/docs/bmm-activity-log.md new file mode 100644 index 00000000..6d309d78 --- /dev/null +++ b/docs/bmm-activity-log.md @@ -0,0 +1,43 @@ +# BMM Activity Log + +날짜: 2025-10-30 + +## PM / Analyst +- 산출물: + - 기능 명세: `docs/recurring-function-spec.md` + - 추적성 테이블: `docs/traceability/requirements-traceability.csv` + - 워크플로 상태: `docs/bmm-workflow-status.md` (currentOwnerRole: PM) +- 관련 커밋: + - c056031 chore(bmm): initialize workflow, sprint status, traceability and specs for recurring feature + +## Architect +- 산출물: + - Tech Spec 초안: `tech-specs/recurring-events.md` +- 관련 커밋: + - c056031 chore(bmm): initialize workflow, sprint status, traceability and specs for recurring feature + +## TEA +- 산출물: + - 테스트 플랜: `tests/specs/recurring-events.testplan.md` + - 테스트 정책: `.cursor/rules/.cursorrules` (필수 규칙 포함) +- 관련 커밋: + - 6501004 chore(rules): simplify Cursor coding style to essential rules + - c056031 chore(bmm): initialize workflow, sprint status, traceability and specs for recurring feature + +## SM +- 산출물: + - 스프린트 상태: `sprint-status.yaml` (BACKLOG 시드, 첫 TODO 설정) + - 메타데이터 추가: `assigneeRole`, `owner`, `updatedAt` +- 관련 커밋: + - dc11302 chore(sprint): seed backlog from recurring spec and set first TODO (STORY-RECUR-001) + - 78aa964 chore(meta): add assignee/owner timestamps to sprint and owner role to workflow status + +## DEV +- 진행: + - IN PROGRESS: STORY-RECUR-001 (반복 유형 선택 로직 및 UI) + - Red 테스트 추가: `src/__tests__/medium.recurring-select.spec.tsx` +- 다음: 테스트 실패 확인 → 최소 구현(Green) → 리팩토링 + +--- + +참고: 커밋 해시는 최근 6개에서 발췌했습니다. 이후 작업은 본 문서 하단에 항목을 추가하는 방식으로 누적 기록합니다. From 30580a254ffe32112bfdf52b8d60e9cdbe14d056 Mon Sep 17 00:00:00 2001 From: grappe96 Date: Fri, 31 Oct 2025 01:55:52 +0900 Subject: [PATCH 07/21] test(recurring): add red test for repeat type selection UI (STORY-RECUR-001) --- .../medium.recurring-select.spec.tsx | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 src/__tests__/medium.recurring-select.spec.tsx diff --git a/src/__tests__/medium.recurring-select.spec.tsx b/src/__tests__/medium.recurring-select.spec.tsx new file mode 100644 index 00000000..a29a05e0 --- /dev/null +++ b/src/__tests__/medium.recurring-select.spec.tsx @@ -0,0 +1,31 @@ +import { render, screen, fireEvent } from '@testing-library/react'; +import React from 'react'; + +import App from '../../src/App.tsx'; + +describe('Recurring - Repeat Type Selection (UI)', () => { + test('반복 일정 토글을 켜면 반복 유형 선택 드롭다운과 옵션이 나타난다', async () => { + render(); + + // 반복 일정 토글 존재 + const repeatToggle = await screen.findByLabelText('반복 일정'); + expect(repeatToggle).toBeInTheDocument(); + + // 토글 켬 + fireEvent.click(repeatToggle); + + // 반복 유형 선택 드롭다운 및 옵션 검증 (매일/매주/매월/매년) + const typeLabel = await screen.findByText('반복 유형'); + expect(typeLabel).toBeInTheDocument(); + + const daily = await screen.findByText('매일'); + const weekly = await screen.findByText('매주'); + const monthly = await screen.findByText('매월'); + const yearly = await screen.findByText('매년'); + + expect(daily).toBeInTheDocument(); + expect(weekly).toBeInTheDocument(); + expect(monthly).toBeInTheDocument(); + expect(yearly).toBeInTheDocument(); + }); +}); From c5375519e520b087514bfc6b8355a81b5c7b5e4c Mon Sep 17 00:00:00 2001 From: grappe96 Date: Fri, 31 Oct 2025 01:56:52 +0900 Subject: [PATCH 08/21] feat(recurring): enable repeat type/interval/end date UI (STORY-RECUR-001) --- src/App.tsx | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 195c5b05..49c99c0c 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -35,8 +35,7 @@ import { useEventForm } from './hooks/useEventForm.ts'; import { useEventOperations } from './hooks/useEventOperations.ts'; import { useNotifications } from './hooks/useNotifications.ts'; import { useSearch } from './hooks/useSearch.ts'; -// import { Event, EventForm, RepeatType } from './types'; -import { Event, EventForm } from './types'; +import { Event, EventForm, RepeatType } from './types'; import { formatDate, formatMonth, @@ -77,11 +76,11 @@ function App() { isRepeating, setIsRepeating, repeatType, - // setRepeatType, + setRepeatType, repeatInterval, - // setRepeatInterval, + setRepeatInterval, repeatEndDate, - // setRepeatEndDate, + setRepeatEndDate, notificationTime, setNotificationTime, startTimeError, @@ -437,8 +436,7 @@ function App() { - {/* ! 반복은 8주차 과제에 포함됩니다. 구현하고 싶어도 참아주세요~ */} - {/* {isRepeating && ( + {isRepeating && ( 반복 유형 @@ -475,7 +473,7 @@ function App() { - )} */} + )}