Files
planner.sori.studio/src/components/StatsDashboard.vue

200 lines
7.8 KiB
Vue

<script setup>
import GuideTooltip from './GuideTooltip.vue'
defineProps({
overviewCards: {
type: Array,
required: true,
},
weeklyRecords: {
type: Array,
required: true,
},
recentRecords: {
type: Array,
required: true,
},
rangeStart: {
type: String,
required: true,
},
rangeEnd: {
type: String,
required: true,
},
bestDay: {
type: Object,
default: null,
},
selectedDateLabel: {
type: String,
required: true,
},
})
const emit = defineEmits(['update:range-start', 'update:range-end', 'quick-range'])
</script>
<template>
<section class="grid gap-6">
<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">
START DATE
<input
:value="rangeStart"
type="date"
class="rounded-2xl border border-stone-200 bg-white px-4 py-3 text-sm font-semibold text-stone-800 outline-none transition focus:border-stone-400"
@input="emit('update:range-start', $event.target.value)"
/>
</label>
<label class="flex flex-col gap-2 text-[11px] font-bold tracking-[0.14em] text-stone-500">
END DATE
<input
:value="rangeEnd"
type="date"
class="rounded-2xl border border-stone-200 bg-white px-4 py-3 text-sm font-semibold text-stone-800 outline-none transition focus:border-stone-400"
@input="emit('update:range-end', $event.target.value)"
/>
</label>
</div>
</div>
</article>
<div class="grid gap-4 md:grid-cols-2 xl:grid-cols-4">
<article
v-for="card in overviewCards"
:key="card.label"
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>
<p class="mt-2 text-[11px] font-semibold leading-5 tracking-[0.06em] text-stone-600">{{ card.caption }}</p>
</article>
</div>
<div class="grid gap-6 xl:grid-cols-[minmax(0,1.3fr)_minmax(320px,0.7fr)]">
<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">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 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-[64px] flex-col items-center justify-end gap-3"
>
<div class="flex h-44 items-end">
<div
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.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-stone-200 bg-white/86 p-6">
<div class="flex items-center gap-2">
<p class="text-[11px] font-bold tracking-[0.22em] text-stone-500">BEST DAY</p>
<GuideTooltip
title="Best Day"
description="선택 범위 안에서 FOCUSED TIME이 가장 큰 날짜를 보여줍니다."
:dismissible="false"
/>
</div>
<div v-if="bestDay" class="mt-4">
<h2 class="text-2xl font-semibold tracking-[-0.05em] text-stone-900">
{{ bestDay.dateLabel }}
</h2>
<p class="mt-3 text-[13px] font-semibold leading-6 text-stone-700">
{{ bestDay.summary }}
</p>
</div>
<p v-else class="mt-4 text-[13px] font-semibold leading-6 text-stone-600">
아직 통계를 보여줄 기록이 충분하지 않습니다.
</p>
</article>
<article class="rounded-[28px] border border-stone-200 bg-white/86 p-6">
<div class="flex items-center gap-2">
<p class="text-[11px] font-bold tracking-[0.22em] text-stone-500">RECENT RECORDS</p>
<GuideTooltip
title="Recent Records"
description="선택 범위 안의 기록을 날짜 내림차순으로 최대 5개까지 보여줍니다."
:dismissible="false"
/>
</div>
<div class="mt-4 space-y-4">
<div
v-for="record in recentRecords"
:key="record.key"
class="rounded-2xl border border-stone-200 bg-stone-50/80 p-4"
>
<div class="flex items-start justify-between gap-4">
<div>
<p class="text-[12px] font-bold tracking-[0.08em] text-stone-900">{{ record.dateLabel }}</p>
<p class="mt-2 text-[11px] font-semibold leading-5 text-stone-600">
{{ record.comment || '코멘트 없음' }}
</p>
</div>
<div class="text-right">
<p class="text-sm font-semibold tracking-[-0.03em] text-stone-900">{{ record.focusedTime }}</p>
<p class="mt-1 text-[11px] font-semibold text-stone-500">{{ record.completionRate }}%</p>
</div>
</div>
</div>
</div>
</article>
</div>
</div>
</section>
</template>