diff --git a/HANDOFF.md b/HANDOFF.md index ac319a1..be10680 100644 --- a/HANDOFF.md +++ b/HANDOFF.md @@ -4,7 +4,7 @@ - 프로젝트명: 10 Minute Planner 웹 UI - 기술 스택: Vue 3 + Vite + TailwindCSS + JavaScript -- 현재 기준 버전: `v0.1.20` +- 현재 기준 버전: `v0.1.21` 준비 중 - Git 원격 저장소: `https://git.sori.studio/zenn/planner.sori.studio.git` ## 기준 디자인 @@ -149,6 +149,8 @@ - 목표 상태 개념은 제거했고, 목표는 기간이 있으면 곧바로 D-DAY 후보가 되는 단순 구조로 정리했다. - GOALS 화면에서는 수정 중인 카드가 시각적으로 강조되고, 목표 삭제 버튼이 추가되었다. - 목표 삭제 시 과거 날짜를 포함해 어떤 날짜에서도 해당 목표는 더 이상 표시되지 않는다. +- 우측 패널의 `PREV SNAPSHOT`은 선택 날짜 이전의 가장 최근 기록을 읽어서 날짜, 집중 시간, 완료 개수, 코멘트 또는 대표 작업을 보여준다. +- 우측 패널의 `READ NEXT`는 다음 날 첫 작업, 오늘의 미완료 작업, 오늘 코멘트를 기반으로 이어보기 문구를 자동 생성한다. - 백엔드는 SQLite 파일 기반 구조에서 PostgreSQL 연결 구조로 교체되었다. - `planner_entries.payload`는 문자열이 아니라 PostgreSQL `JSONB`로 저장되도록 바뀌었다. - `docker-compose.yml` 기준으로 `postgres`, `backend`, `frontend(nginx)` 3개 서비스 초안이 추가되었다. diff --git a/TODO.md b/TODO.md index c52d3ae..39e7fb3 100644 --- a/TODO.md +++ b/TODO.md @@ -38,7 +38,7 @@ - [x] 목표 관리 패널 기본 구조를 설계한다. - [x] 선택한 목표 기준으로 `D-DAY`가 자동 계산되게 한다. -- [ ] 우측 요약 패널의 `PREV SNAPSHOT`, `READ NEXT`를 실제 데이터 기반으로 연결한다. +- [x] 우측 요약 패널의 `PREV SNAPSHOT`, `READ NEXT`를 실제 데이터 기반으로 연결한다. - [ ] 다음날 할 일 자동 제안 규칙을 정리한다. - [x] 오른쪽 패널에 `D-DAY 사용` 토글과 목표 검색/선택 UI를 추가한다. - [x] 목표를 여러 개 생성하고 날짜별 대표 목표를 선택할 수 있게 한다. diff --git a/package-lock.json b/package-lock.json index 4f2fc22..3f47455 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ten-minute-planner", - "version": "0.1.20", + "version": "0.1.21", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ten-minute-planner", - "version": "0.1.20", + "version": "0.1.21", "dependencies": { "vue": "^3.5.13" }, diff --git a/package.json b/package.json index d84e004..5f469cc 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "ten-minute-planner", "private": true, - "version": "0.1.20", + "version": "0.1.21", "type": "module", "scripts": { "dev": "vite", diff --git a/src/App.vue b/src/App.vue index 5d557b3..ad2062a 100644 --- a/src/App.vue +++ b/src/App.vue @@ -636,6 +636,56 @@ const bestDay = computed(() => { } }) +const previousRecordedEntry = computed(() => { + const selectedKey = toKey(selectedDate.value) + + return [...plannerEntries.value] + .filter(([key]) => key < selectedKey) + .sort(([leftKey], [rightKey]) => rightKey.localeCompare(leftKey))[0] ?? null +}) + +const prevSnapshotItems = computed(() => { + if (!previousRecordedEntry.value) { + return [ + '이전 기록 없음', + '첫 기록을 쌓아보세요.', + '오늘부터 흐름을 만들 수 있습니다.', + ] + } + + const [entryKey, entryRecord] = previousRecordedEntry.value + const completedCount = entryRecord.tasks.filter((task) => task.title.trim() && task.checked).length + const previousComment = entryRecord.comment.trim() + const previousTopTask = entryRecord.tasks.find((task) => task.title.trim()) + + return [ + `${createDateLabel(entryKey)} 기록`, + `${formatTotalTime(entryRecord)} 집중 / 완료 ${completedCount}개`, + previousComment || (previousTopTask ? `주요 작업: ${previousTopTask.title}` : '남겨진 코멘트 없음'), + ] +}) + +const readNextItems = computed(() => { + const nextDayFirstTask = secondaryPlanner.value.tasks.find((task) => task.title.trim()) + const incompleteTasks = planner.value.tasks.filter((task) => task.title.trim() && !task.checked) + const carryTask = incompleteTasks[0]?.title + const todayComment = planner.value.comment.trim() + + return [ + nextDayFirstTask + ? `내일 첫 작업: ${nextDayFirstTask.title}` + : carryTask + ? `내일 이어갈 첫 작업: ${carryTask}` + : '내일 첫 작업은 아직 비어 있습니다.', + incompleteTasks.length > 0 + ? `미완료 ${incompleteTasks.length}개를 이어서 볼 수 있습니다.` + : '오늘 미완료 작업은 없습니다.', + todayComment + ? `오늘 코멘트 이어보기: ${todayComment}` + : '오늘 코멘트는 아직 비어 있습니다.', + ] +}) + watch( [plannerRecords, selectedDate, calendarViewDate, statsRangeStart, statsRangeEnd], () => { @@ -1493,7 +1543,7 @@ onMounted(() => {

PREV SNAPSHOT

@@ -1528,7 +1578,7 @@ onMounted(() => {

READ NEXT