Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bc2e981577 |
@@ -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개 서비스 초안이 추가되었다.
|
||||
|
||||
2
TODO.md
2
TODO.md
@@ -38,7 +38,7 @@
|
||||
|
||||
- [x] 목표 관리 패널 기본 구조를 설계한다.
|
||||
- [x] 선택한 목표 기준으로 `D-DAY`가 자동 계산되게 한다.
|
||||
- [ ] 우측 요약 패널의 `PREV SNAPSHOT`, `READ NEXT`를 실제 데이터 기반으로 연결한다.
|
||||
- [x] 우측 요약 패널의 `PREV SNAPSHOT`, `READ NEXT`를 실제 데이터 기반으로 연결한다.
|
||||
- [ ] 다음날 할 일 자동 제안 규칙을 정리한다.
|
||||
- [x] 오른쪽 패널에 `D-DAY 사용` 토글과 목표 검색/선택 UI를 추가한다.
|
||||
- [x] 목표를 여러 개 생성하고 날짜별 대표 목표를 선택할 수 있게 한다.
|
||||
|
||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -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"
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "ten-minute-planner",
|
||||
"private": true,
|
||||
"version": "0.1.20",
|
||||
"version": "0.1.21",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
54
src/App.vue
54
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(() => {
|
||||
<p class="mb-4 text-[11px] font-bold tracking-[0.22em] text-ink">PREV SNAPSHOT</p>
|
||||
<div class="space-y-3">
|
||||
<p
|
||||
v-for="item in planner.prevSummary"
|
||||
v-for="item in prevSnapshotItems"
|
||||
:key="item"
|
||||
class="border-b border-stone-200 pb-3 text-[11px] font-semibold tracking-[0.08em] text-stone-700 last:border-b-0 last:pb-0"
|
||||
>
|
||||
@@ -1528,7 +1578,7 @@ onMounted(() => {
|
||||
<p class="mb-4 text-[11px] font-bold tracking-[0.22em] text-ink">READ NEXT</p>
|
||||
<div class="space-y-3">
|
||||
<p
|
||||
v-for="item in planner.nextFocus"
|
||||
v-for="item in readNextItems"
|
||||
:key="item"
|
||||
class="border-b border-stone-200 pb-3 text-[11px] font-semibold tracking-[0.08em] text-stone-700 last:border-b-0 last:pb-0"
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user