v0.1.51 - 통계 차트와 플래너 하단 정렬 보정

This commit is contained in:
2026-04-23 15:20:43 +09:00
parent bebd8ed8a6
commit 6c69658d33
4 changed files with 63 additions and 15 deletions

View File

@@ -232,6 +232,8 @@
- 이월된 할 일은 `carryoverFrom` 날짜를 가진다. TASK 본문에는 `이월` 배지를 표시하고, 클릭하면 오른쪽 `READ NEXT` 영역에 원래 시작 날짜를 안내한다.
- 이월된 할 일을 완료할 때는 이전 날짜의 같은 이월 항목까지 모두 체크할지, 현재 날짜만 체크할지 선택한다. 기본값은 `항상 물어보기`이며, SETTINGS의 `CARRYOVER CHECK`에서 `항상 이전까지 체크` / `항상 오늘만 체크`로 바꿀 수 있다.
- 오른쪽 플래너 사이드바의 중복 `STATS` 카드는 제거했다. 미완료 항목 이월 버튼은 `READ NEXT` 카드 아래로 이동했다.
- 통계 화면은 진입 시 END DATE를 오늘로 보정한다. `최근 1주`, `최근 1달` 빠른 선택을 추가했고, 기존 `WEEKLY FLOW`는 선택 범위 안에서 기록이 있는 날짜별 집중 흐름을 보여주는 `RANGE FLOW`로 이름과 라벨을 정리했다.
- `BEST DAY`는 선택 기간 안에서 집중 시간이 가장 긴 날짜를 고르고, `RECENT RECORDS`는 선택 기간 안의 기록을 날짜 내림차순으로 최대 5개 보여준다.
## 갱신 규칙

View File

@@ -959,7 +959,7 @@ const overviewCards = computed(() => [
},
{
label: 'COMPLETED TASKS',
value: `${completedRecordedTasks.value}/${totalRecordedTasks.value || 0}`,
value: `${completedRecordedTasks.value} / ${totalRecordedTasks.value || 0}`,
caption: '완료된 할 일과 전체 입력된 할 일 수',
},
])
@@ -985,6 +985,7 @@ const weeklyRecords = computed(() => {
return {
key,
weekday: weekdayShort,
dateLabel: `${`${date.getMonth() + 1}`.padStart(2, '0')}.${`${date.getDate()}`.padStart(2, '0')}`,
focusedMinutes,
focusedTime: formatMinutesKorean(focusedMinutes),
}
@@ -1211,6 +1212,14 @@ function closeRightPanel() {
rightPanelOpen.value = false
}
function applyStatsQuickRange(days) {
const endDate = new Date()
const startDate = new Date(endDate)
startDate.setDate(endDate.getDate() - (days - 1))
statsRangeStart.value = toKey(startDate)
statsRangeEnd.value = toKey(endDate)
}
function setScreenMode(mode) {
if (mode === 'admin' && !isAdmin.value) {
return
@@ -1218,6 +1227,10 @@ function setScreenMode(mode) {
screenMode.value = mode
if (mode === 'stats') {
statsRangeEnd.value = toKey(new Date())
}
if (mode === 'admin') {
void loadAdminDashboard()
}
@@ -2829,6 +2842,7 @@ onBeforeUnmount(() => {
:range-end="normalizedStatsRange.endKey"
@update:range-start="statsRangeStart = $event"
@update:range-end="statsRangeEnd = $event"
@quick-range="applyStatsQuickRange"
/>
<section v-if="isAuthenticated" class="print-only">

View File

@@ -304,7 +304,7 @@ onBeforeUnmount(() => {
</div>
<div class="planner-sheet__body flex flex-col gap-5 py-[10px] lg:flex-row lg:gap-4">
<div class="planner-sheet__lists flex w-full flex-1 flex-col gap-6 lg:w-[394px]">
<div class="planner-sheet__lists flex w-full flex-1 flex-col gap-[25px] lg:w-[394px]">
<section>
<div class="flex items-center gap-2 text-muted">
<span class="shrink-0">TASKS</span>

View File

@@ -30,18 +30,34 @@ defineProps({
},
})
const emit = defineEmits(['update:range-start', 'update:range-end'])
const emit = defineEmits(['update:range-start', 'update:range-end', 'quick-range'])
</script>
<template>
<section class="grid gap-6">
<article class="rounded-[28px] border border-white/60 bg-white/80 p-5 shadow-paper backdrop-blur">
<article class="rounded-[28px] border border-stone-200 bg-white/86 p-5">
<div class="flex flex-col gap-4 lg:flex-row lg:items-end lg:justify-between">
<div>
<p class="text-[11px] font-bold tracking-[0.22em] text-stone-500">RANGE</p>
<h2 class="mt-2 text-2xl font-semibold tracking-[-0.05em] text-stone-900">
원하는 기간 기준으로 통계 보기
</h2>
<div class="mt-4 flex flex-wrap gap-2">
<button
type="button"
class="rounded-full border border-stone-200 bg-white px-4 py-2 text-[11px] font-bold tracking-[0.12em] text-stone-600 transition hover:border-stone-500 hover:text-stone-900"
@click="emit('quick-range', 7)"
>
최근 1
</button>
<button
type="button"
class="rounded-full border border-stone-200 bg-white px-4 py-2 text-[11px] font-bold tracking-[0.12em] text-stone-600 transition hover:border-stone-500 hover:text-stone-900"
@click="emit('quick-range', 30)"
>
최근 1
</button>
</div>
</div>
<div class="grid gap-3 sm:grid-cols-2">
<label class="flex flex-col gap-2 text-[11px] font-bold tracking-[0.14em] text-stone-500">
@@ -70,7 +86,7 @@ const emit = defineEmits(['update:range-start', 'update:range-end'])
<article
v-for="card in overviewCards"
:key="card.label"
class="rounded-[28px] border border-white/60 bg-white/80 p-5 shadow-paper backdrop-blur"
class="rounded-[28px] border border-stone-200 bg-white/86 p-5"
>
<p class="text-[11px] font-bold tracking-[0.22em] text-stone-500">{{ card.label }}</p>
<p class="mt-4 text-[34px] font-semibold tracking-[-0.06em] text-stone-900">{{ card.value }}</p>
@@ -79,42 +95,55 @@ const emit = defineEmits(['update:range-start', 'update:range-end'])
</div>
<div class="grid gap-6 xl:grid-cols-[minmax(0,1.3fr)_minmax(320px,0.7fr)]">
<article class="rounded-[28px] border border-white/60 bg-white/80 p-6 shadow-paper backdrop-blur">
<article class="rounded-[28px] border border-stone-200 bg-white/86 p-6">
<div class="flex items-end justify-between gap-4">
<div>
<p class="text-[11px] font-bold tracking-[0.22em] text-stone-500">WEEKLY FLOW</p>
<p class="text-[11px] font-bold tracking-[0.22em] text-stone-500">RANGE FLOW</p>
<h2 class="mt-2 text-2xl font-semibold tracking-[-0.05em] text-stone-900">
선택 기간 기록 흐름
선택 기간 날짜별 집중 흐름
</h2>
<p class="mt-2 text-[12px] font-semibold leading-5 text-stone-500">
기록이 있는 날짜만 날짜순으로 표시합니다.
</p>
</div>
<p class="text-[11px] font-semibold tracking-[0.06em] text-stone-500">
기준 날짜: {{ selectedDateLabel }}
</p>
</div>
<div class="mt-8 flex gap-3 overflow-x-auto pb-2">
<div class="mt-8 flex min-h-[230px] items-stretch gap-3 overflow-x-auto border-b border-stone-200 pb-4">
<div
v-for="record in weeklyRecords"
:key="record.key"
class="flex min-w-[56px] flex-col items-center gap-3"
class="flex min-w-[64px] flex-col items-center justify-end gap-3"
>
<div class="flex h-40 items-end">
<div class="flex h-44 items-end">
<div
class="w-10 rounded-full bg-stone-900/90 transition-all"
class="w-11 rounded-t-full bg-stone-900/90 transition-all"
:style="{ height: `${record.barHeight}%` }"
/>
</div>
<div class="text-center">
<p class="text-[11px] font-bold tracking-[0.12em] text-stone-500">{{ record.weekday }}</p>
<p class="text-[11px] font-bold tracking-[0.12em] text-stone-500">{{ record.dateLabel }}</p>
<p class="mt-1 text-[10px] font-bold tracking-[0.12em] text-stone-400">{{ record.weekday }}</p>
<p class="mt-1 text-[11px] font-semibold text-stone-800">{{ record.focusedTime }}</p>
</div>
</div>
<p
v-if="weeklyRecords.length === 0"
class="flex min-h-[180px] w-full items-center justify-center rounded-2xl border border-dashed border-stone-300 text-sm font-semibold text-stone-500"
>
선택 기간에 기록이 없습니다.
</p>
</div>
</article>
<div class="grid gap-6">
<article class="rounded-[28px] border border-white/60 bg-white/80 p-6 shadow-paper backdrop-blur">
<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>
<p class="mt-2 text-[11px] font-semibold leading-5 text-stone-500">
선택 기간 안에서 집중 시간이 가장 날짜입니다.
</p>
<div v-if="bestDay" class="mt-4">
<h2 class="text-2xl font-semibold tracking-[-0.05em] text-stone-900">
{{ bestDay.dateLabel }}
@@ -128,8 +157,11 @@ const emit = defineEmits(['update:range-start', 'update:range-end'])
</p>
</article>
<article class="rounded-[28px] border border-white/60 bg-white/80 p-6 shadow-paper backdrop-blur">
<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>
<p class="mt-2 text-[11px] font-semibold leading-5 text-stone-500">
선택 기간 안의 최근 기록을 날짜 내림차순으로 최대 5 표시합니다.
</p>
<div class="mt-4 space-y-4">
<div
v-for="record in recentRecords"