v0.1.52 - 이월 배지와 통계 설명 팝업 정리
This commit is contained in:
@@ -234,6 +234,8 @@
|
|||||||
- 오른쪽 플래너 사이드바의 중복 `STATS` 카드는 제거했다. 미완료 항목 이월 버튼은 `READ NEXT` 카드 아래로 이동했다.
|
- 오른쪽 플래너 사이드바의 중복 `STATS` 카드는 제거했다. 미완료 항목 이월 버튼은 `READ NEXT` 카드 아래로 이동했다.
|
||||||
- 통계 화면은 진입 시 END DATE를 오늘로 보정한다. `최근 1주`, `최근 1달` 빠른 선택을 추가했고, 기존 `WEEKLY FLOW`는 선택 범위 안에서 기록이 있는 날짜별 집중 흐름을 보여주는 `RANGE FLOW`로 이름과 라벨을 정리했다.
|
- 통계 화면은 진입 시 END DATE를 오늘로 보정한다. `최근 1주`, `최근 1달` 빠른 선택을 추가했고, 기존 `WEEKLY FLOW`는 선택 범위 안에서 기록이 있는 날짜별 집중 흐름을 보여주는 `RANGE FLOW`로 이름과 라벨을 정리했다.
|
||||||
- `BEST DAY`는 선택 기간 안에서 집중 시간이 가장 긴 날짜를 고르고, `RECENT RECORDS`는 선택 기간 안의 기록을 날짜 내림차순으로 최대 5개 보여준다.
|
- `BEST DAY`는 선택 기간 안에서 집중 시간이 가장 긴 날짜를 고르고, `RECENT RECORDS`는 선택 기간 안의 기록을 날짜 내림차순으로 최대 5개 보여준다.
|
||||||
|
- `CARRYOVER TASK` 선택 모달은 ESC로 닫힌다. 이월 배지의 시작일 안내는 오른쪽 패널 메시지 대신 배지 옆 팝업으로 표시한다.
|
||||||
|
- 통계의 `BEST DAY`, `RECENT RECORDS` 기준 설명은 본문 문장 대신 물음표 가이드 팝업으로 제공한다.
|
||||||
|
|
||||||
## 갱신 규칙
|
## 갱신 규칙
|
||||||
|
|
||||||
|
|||||||
39
src/App.vue
39
src/App.vue
@@ -85,7 +85,6 @@ const passwordBusy = ref(false)
|
|||||||
const profileMessage = ref('')
|
const profileMessage = ref('')
|
||||||
const passwordMessage = ref('')
|
const passwordMessage = ref('')
|
||||||
const carryoverMessage = ref('')
|
const carryoverMessage = ref('')
|
||||||
const carryoverInspectMessage = ref('')
|
|
||||||
const carryoverCheckPolicy = ref(readCarryoverCheckPolicy())
|
const carryoverCheckPolicy = ref(readCarryoverCheckPolicy())
|
||||||
const carryoverCheckPrompt = ref(null)
|
const carryoverCheckPrompt = ref(null)
|
||||||
const guideTooltipResetMessage = ref('')
|
const guideTooltipResetMessage = ref('')
|
||||||
@@ -665,7 +664,6 @@ function shiftDate(amount) {
|
|||||||
selectedDate.value = next
|
selectedDate.value = next
|
||||||
calendarViewDate.value = new Date(next)
|
calendarViewDate.value = new Date(next)
|
||||||
carryoverMessage.value = ''
|
carryoverMessage.value = ''
|
||||||
carryoverInspectMessage.value = ''
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function shiftCalendarMonth(amount) {
|
function shiftCalendarMonth(amount) {
|
||||||
@@ -684,7 +682,6 @@ function selectDate(date) {
|
|||||||
selectedDate.value = new Date(date)
|
selectedDate.value = new Date(date)
|
||||||
calendarViewDate.value = new Date(date)
|
calendarViewDate.value = new Date(date)
|
||||||
carryoverMessage.value = ''
|
carryoverMessage.value = ''
|
||||||
carryoverInspectMessage.value = ''
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateComment(record, value) {
|
function updateComment(record, value) {
|
||||||
@@ -803,6 +800,13 @@ function closeCarryoverCheckPrompt() {
|
|||||||
carryoverCheckPrompt.value = null
|
carryoverCheckPrompt.value = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleGlobalKeydown(event) {
|
||||||
|
if (event.key === 'Escape' && carryoverCheckPrompt.value) {
|
||||||
|
event.preventDefault()
|
||||||
|
closeCarryoverCheckPrompt()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function clearTasks(record, indexes) {
|
function clearTasks(record, indexes) {
|
||||||
indexes.forEach((index) => {
|
indexes.forEach((index) => {
|
||||||
if (!record.tasks[index]) {
|
if (!record.tasks[index]) {
|
||||||
@@ -858,15 +862,6 @@ function carryIncompleteTasksToNextDay() {
|
|||||||
: `${nextDateLabel} 빈칸 ${copyCount}개까지만 이월했습니다.`
|
: `${nextDateLabel} 빈칸 ${copyCount}개까지만 이월했습니다.`
|
||||||
}
|
}
|
||||||
|
|
||||||
function inspectCarryoverTask(task) {
|
|
||||||
if (!task.carryoverFrom) {
|
|
||||||
carryoverInspectMessage.value = ''
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
carryoverInspectMessage.value = `"${task.title}" 항목은 ${createDateLabel(task.carryoverFrom)}부터 이월된 할 일입니다.`
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateMemo(record, { index, value }) {
|
function updateMemo(record, { index, value }) {
|
||||||
record.memo[index].text = value
|
record.memo[index].text = value
|
||||||
schedulePlannerSyncForRecord(record)
|
schedulePlannerSyncForRecord(record)
|
||||||
@@ -1853,11 +1848,13 @@ onMounted(() => {
|
|||||||
})
|
})
|
||||||
updateWindowWidth()
|
updateWindowWidth()
|
||||||
window.addEventListener('resize', updateWindowWidth)
|
window.addEventListener('resize', updateWindowWidth)
|
||||||
|
window.addEventListener('keydown', handleGlobalKeydown)
|
||||||
restoreAuthSession()
|
restoreAuthSession()
|
||||||
})
|
})
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
window.removeEventListener('resize', updateWindowWidth)
|
window.removeEventListener('resize', updateWindowWidth)
|
||||||
|
window.removeEventListener('keydown', handleGlobalKeydown)
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -2414,7 +2411,6 @@ onBeforeUnmount(() => {
|
|||||||
@update:task-title="updateTaskTitle(planner, $event)"
|
@update:task-title="updateTaskTitle(planner, $event)"
|
||||||
@toggle:task="toggleTask(planner, $event)"
|
@toggle:task="toggleTask(planner, $event)"
|
||||||
@clear:tasks="clearTasks(planner, $event)"
|
@clear:tasks="clearTasks(planner, $event)"
|
||||||
@inspect:carryover="inspectCarryoverTask"
|
|
||||||
@update:memo-label="updateMemoLabel(planner, $event)"
|
@update:memo-label="updateMemoLabel(planner, $event)"
|
||||||
@update:memo="updateMemo(planner, $event)"
|
@update:memo="updateMemo(planner, $event)"
|
||||||
@update:timetable="updateTimetable(planner, $event)"
|
@update:timetable="updateTimetable(planner, $event)"
|
||||||
@@ -2545,12 +2541,6 @@ onBeforeUnmount(() => {
|
|||||||
>
|
>
|
||||||
{{ carryoverMessage }}
|
{{ carryoverMessage }}
|
||||||
</p>
|
</p>
|
||||||
<p
|
|
||||||
v-if="carryoverInspectMessage"
|
|
||||||
class="mt-3 rounded-2xl border border-stone-200 bg-white px-4 py-3 text-[11px] font-semibold leading-5 tracking-[0.06em] text-stone-600"
|
|
||||||
>
|
|
||||||
{{ carryoverInspectMessage }}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mt-5 border-t border-stone-200 pt-5">
|
<div class="mt-5 border-t border-stone-200 pt-5">
|
||||||
@@ -2695,12 +2685,6 @@ onBeforeUnmount(() => {
|
|||||||
>
|
>
|
||||||
{{ carryoverMessage }}
|
{{ carryoverMessage }}
|
||||||
</p>
|
</p>
|
||||||
<p
|
|
||||||
v-if="carryoverInspectMessage"
|
|
||||||
class="mt-3 rounded-2xl border border-stone-200 bg-white px-4 py-3 text-[11px] font-semibold leading-5 tracking-[0.06em] text-stone-600"
|
|
||||||
>
|
|
||||||
{{ carryoverInspectMessage }}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mt-5 border-t border-stone-200 pt-5">
|
<div class="mt-5 border-t border-stone-200 pt-5">
|
||||||
@@ -2747,7 +2731,6 @@ onBeforeUnmount(() => {
|
|||||||
@update:task-title="updateTaskTitle(planner, $event)"
|
@update:task-title="updateTaskTitle(planner, $event)"
|
||||||
@toggle:task="toggleTask(planner, $event)"
|
@toggle:task="toggleTask(planner, $event)"
|
||||||
@clear:tasks="clearTasks(planner, $event)"
|
@clear:tasks="clearTasks(planner, $event)"
|
||||||
@inspect:carryover="inspectCarryoverTask"
|
|
||||||
@update:memo-label="updateMemoLabel(planner, $event)"
|
@update:memo-label="updateMemoLabel(planner, $event)"
|
||||||
@update:memo="updateMemo(planner, $event)"
|
@update:memo="updateMemo(planner, $event)"
|
||||||
@update:timetable="updateTimetable(planner, $event)"
|
@update:timetable="updateTimetable(planner, $event)"
|
||||||
@@ -2772,7 +2755,6 @@ onBeforeUnmount(() => {
|
|||||||
@update:task-title="updateTaskTitle(secondaryPlanner, $event)"
|
@update:task-title="updateTaskTitle(secondaryPlanner, $event)"
|
||||||
@toggle:task="toggleTask(secondaryPlanner, $event)"
|
@toggle:task="toggleTask(secondaryPlanner, $event)"
|
||||||
@clear:tasks="clearTasks(secondaryPlanner, $event)"
|
@clear:tasks="clearTasks(secondaryPlanner, $event)"
|
||||||
@inspect:carryover="inspectCarryoverTask"
|
|
||||||
@update:memo-label="updateMemoLabel(secondaryPlanner, $event)"
|
@update:memo-label="updateMemoLabel(secondaryPlanner, $event)"
|
||||||
@update:memo="updateMemo(secondaryPlanner, $event)"
|
@update:memo="updateMemo(secondaryPlanner, $event)"
|
||||||
@update:timetable="updateTimetable(secondaryPlanner, $event)"
|
@update:timetable="updateTimetable(secondaryPlanner, $event)"
|
||||||
@@ -2865,7 +2847,6 @@ onBeforeUnmount(() => {
|
|||||||
@update:task-title="updateTaskTitle(planner, $event)"
|
@update:task-title="updateTaskTitle(planner, $event)"
|
||||||
@toggle:task="toggleTask(planner, $event)"
|
@toggle:task="toggleTask(planner, $event)"
|
||||||
@clear:tasks="clearTasks(planner, $event)"
|
@clear:tasks="clearTasks(planner, $event)"
|
||||||
@inspect:carryover="inspectCarryoverTask"
|
|
||||||
@update:memo-label="updateMemoLabel(planner, $event)"
|
@update:memo-label="updateMemoLabel(planner, $event)"
|
||||||
@update:memo="updateMemo(planner, $event)"
|
@update:memo="updateMemo(planner, $event)"
|
||||||
@update:timetable="updateTimetable(planner, $event)"
|
@update:timetable="updateTimetable(planner, $event)"
|
||||||
@@ -2892,7 +2873,6 @@ onBeforeUnmount(() => {
|
|||||||
@update:task-title="updateTaskTitle(planner, $event)"
|
@update:task-title="updateTaskTitle(planner, $event)"
|
||||||
@toggle:task="toggleTask(planner, $event)"
|
@toggle:task="toggleTask(planner, $event)"
|
||||||
@clear:tasks="clearTasks(planner, $event)"
|
@clear:tasks="clearTasks(planner, $event)"
|
||||||
@inspect:carryover="inspectCarryoverTask"
|
|
||||||
@update:memo-label="updateMemoLabel(planner, $event)"
|
@update:memo-label="updateMemoLabel(planner, $event)"
|
||||||
@update:memo="updateMemo(planner, $event)"
|
@update:memo="updateMemo(planner, $event)"
|
||||||
@update:timetable="updateTimetable(planner, $event)"
|
@update:timetable="updateTimetable(planner, $event)"
|
||||||
@@ -2916,7 +2896,6 @@ onBeforeUnmount(() => {
|
|||||||
@update:task-title="updateTaskTitle(secondaryPlanner, $event)"
|
@update:task-title="updateTaskTitle(secondaryPlanner, $event)"
|
||||||
@toggle:task="toggleTask(secondaryPlanner, $event)"
|
@toggle:task="toggleTask(secondaryPlanner, $event)"
|
||||||
@clear:tasks="clearTasks(secondaryPlanner, $event)"
|
@clear:tasks="clearTasks(secondaryPlanner, $event)"
|
||||||
@inspect:carryover="inspectCarryoverTask"
|
|
||||||
@update:memo-label="updateMemoLabel(secondaryPlanner, $event)"
|
@update:memo-label="updateMemoLabel(secondaryPlanner, $event)"
|
||||||
@update:memo="updateMemo(secondaryPlanner, $event)"
|
@update:memo="updateMemo(secondaryPlanner, $event)"
|
||||||
@update:timetable="updateTimetable(secondaryPlanner, $event)"
|
@update:timetable="updateTimetable(secondaryPlanner, $event)"
|
||||||
|
|||||||
@@ -14,6 +14,14 @@ const props = defineProps({
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true,
|
default: true,
|
||||||
},
|
},
|
||||||
|
dismissible: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
buttonLabel: {
|
||||||
|
type: String,
|
||||||
|
default: '?',
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const emit = defineEmits(['dismiss'])
|
const emit = defineEmits(['dismiss'])
|
||||||
@@ -68,7 +76,7 @@ onBeforeUnmount(() => {
|
|||||||
:aria-expanded="open"
|
:aria-expanded="open"
|
||||||
@click.stop="toggle"
|
@click.stop="toggle"
|
||||||
>
|
>
|
||||||
?
|
{{ buttonLabel }}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<span
|
<span
|
||||||
@@ -79,6 +87,7 @@ onBeforeUnmount(() => {
|
|||||||
<span class="block text-[10px] font-bold uppercase tracking-[0.2em] text-stone-500">{{ title }}</span>
|
<span class="block text-[10px] font-bold uppercase tracking-[0.2em] text-stone-500">{{ title }}</span>
|
||||||
<span class="mt-2 block text-[11px] font-semibold leading-5 tracking-[0.04em] text-stone-700">{{ description }}</span>
|
<span class="mt-2 block text-[11px] font-semibold leading-5 tracking-[0.04em] text-stone-700">{{ description }}</span>
|
||||||
<button
|
<button
|
||||||
|
v-if="dismissible"
|
||||||
type="button"
|
type="button"
|
||||||
class="mt-3 rounded-full border border-stone-200 px-3 py-2 text-[10px] font-bold tracking-[0.14em] text-stone-600 transition hover:border-stone-500 hover:text-stone-900"
|
class="mt-3 rounded-full border border-stone-200 px-3 py-2 text-[10px] font-bold tracking-[0.14em] text-stone-600 transition hover:border-stone-500 hover:text-stone-900"
|
||||||
@click="dismiss"
|
@click="dismiss"
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { onBeforeUnmount, ref } from 'vue'
|
import { onBeforeUnmount, ref } from 'vue'
|
||||||
|
import GuideTooltip from './GuideTooltip.vue'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
dateMain: {
|
dateMain: {
|
||||||
@@ -62,7 +63,6 @@ const emit = defineEmits([
|
|||||||
'update:task-title',
|
'update:task-title',
|
||||||
'toggle:task',
|
'toggle:task',
|
||||||
'clear:tasks',
|
'clear:tasks',
|
||||||
'inspect:carryover',
|
|
||||||
'update:memo-label',
|
'update:memo-label',
|
||||||
'update:memo',
|
'update:memo',
|
||||||
'update:timetable',
|
'update:timetable',
|
||||||
@@ -349,14 +349,13 @@ onBeforeUnmount(() => {
|
|||||||
@focus="clearTaskSelectionOnFocus"
|
@focus="clearTaskSelectionOnFocus"
|
||||||
@input="emit('update:task-title', { index, value: $event.target.value })"
|
@input="emit('update:task-title', { index, value: $event.target.value })"
|
||||||
/>
|
/>
|
||||||
<button
|
<GuideTooltip
|
||||||
v-if="task.carryoverFrom"
|
v-if="task.carryoverFrom"
|
||||||
type="button"
|
:title="'이월된 할 일'"
|
||||||
class="shrink-0 rounded-full border border-stone-300 px-2 py-1 text-[8px] font-bold tracking-[0.12em] text-stone-500 transition hover:border-stone-500 hover:text-stone-900"
|
:description="`이 항목은 ${task.carryoverFrom}부터 이월된 할 일입니다.`"
|
||||||
@click="emit('inspect:carryover', task)"
|
:dismissible="false"
|
||||||
>
|
button-label="이월"
|
||||||
이월
|
/>
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="flex h-full w-[36px] shrink-0 items-center justify-center p-[8px] sm:w-[42px] sm:p-[10px]">
|
<div class="flex h-full w-[36px] shrink-0 items-center justify-center p-[8px] sm:w-[42px] sm:p-[10px]">
|
||||||
<button
|
<button
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
|
import GuideTooltip from './GuideTooltip.vue'
|
||||||
|
|
||||||
defineProps({
|
defineProps({
|
||||||
overviewCards: {
|
overviewCards: {
|
||||||
type: Array,
|
type: Array,
|
||||||
@@ -140,10 +142,14 @@ const emit = defineEmits(['update:range-start', 'update:range-end', 'quick-range
|
|||||||
|
|
||||||
<div class="grid gap-6">
|
<div class="grid gap-6">
|
||||||
<article class="rounded-[28px] border border-stone-200 bg-white/86 p-6">
|
<article class="rounded-[28px] border border-stone-200 bg-white/86 p-6">
|
||||||
<p class="text-[11px] font-bold tracking-[0.22em] text-stone-500">BEST DAY</p>
|
<div class="flex items-center gap-2">
|
||||||
<p class="mt-2 text-[11px] font-semibold leading-5 text-stone-500">
|
<p class="text-[11px] font-bold tracking-[0.22em] text-stone-500">BEST DAY</p>
|
||||||
선택 기간 안에서 집중 시간이 가장 긴 날짜입니다.
|
<GuideTooltip
|
||||||
</p>
|
title="Best Day"
|
||||||
|
description="선택 범위 안에서 FOCUSED TIME이 가장 큰 날짜를 보여줍니다."
|
||||||
|
:dismissible="false"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div v-if="bestDay" class="mt-4">
|
<div v-if="bestDay" class="mt-4">
|
||||||
<h2 class="text-2xl font-semibold tracking-[-0.05em] text-stone-900">
|
<h2 class="text-2xl font-semibold tracking-[-0.05em] text-stone-900">
|
||||||
{{ bestDay.dateLabel }}
|
{{ bestDay.dateLabel }}
|
||||||
@@ -158,10 +164,14 @@ const emit = defineEmits(['update:range-start', 'update:range-end', 'quick-range
|
|||||||
</article>
|
</article>
|
||||||
|
|
||||||
<article class="rounded-[28px] border border-stone-200 bg-white/86 p-6">
|
<article class="rounded-[28px] border border-stone-200 bg-white/86 p-6">
|
||||||
<p class="text-[11px] font-bold tracking-[0.22em] text-stone-500">RECENT RECORDS</p>
|
<div class="flex items-center gap-2">
|
||||||
<p class="mt-2 text-[11px] font-semibold leading-5 text-stone-500">
|
<p class="text-[11px] font-bold tracking-[0.22em] text-stone-500">RECENT RECORDS</p>
|
||||||
선택 기간 안의 최근 기록을 날짜 내림차순으로 최대 5개 표시합니다.
|
<GuideTooltip
|
||||||
</p>
|
title="Recent Records"
|
||||||
|
description="선택 범위 안의 기록을 날짜 내림차순으로 최대 5개까지 보여줍니다."
|
||||||
|
:dismissible="false"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div class="mt-4 space-y-4">
|
<div class="mt-4 space-y-4">
|
||||||
<div
|
<div
|
||||||
v-for="record in recentRecords"
|
v-for="record in recentRecords"
|
||||||
|
|||||||
Reference in New Issue
Block a user