v1.3.3: 자체 최소 통계 및 스플래시 localStorage 정리
- 일별 익명 방문자 해시·사이트/게시물 통계(030 마이그레이션) - POST /api/analytics/pageview, 관리자 analytics API, 클라이언트 트래커 - 관리자 대시보드 통계 카드·인기 게시물 Top 5 - 스플래시: SITE_BRAND_LOGO_TEXT localStorage 제거
This commit is contained in:
@@ -7,6 +7,19 @@ const { data: posts } = await useFetch('/admin/api/posts', {
|
||||
default: () => []
|
||||
})
|
||||
|
||||
const { data: analyticsSummary } = await useFetch('/admin/api/analytics/summary', {
|
||||
default: () => ({
|
||||
todayVisitors: 0,
|
||||
visitorsLast7Days: 0,
|
||||
pageViewsLast30Days: 0
|
||||
})
|
||||
})
|
||||
|
||||
const { data: topPosts } = await useFetch('/admin/api/analytics/posts', {
|
||||
query: { days: 30, limit: 5 },
|
||||
default: () => []
|
||||
})
|
||||
|
||||
const publishedCount = computed(() => posts.value.filter((post) => post.status === 'published').length)
|
||||
const draftCount = computed(() => posts.value.filter((post) => post.status === 'draft').length)
|
||||
</script>
|
||||
@@ -21,30 +34,96 @@ const draftCount = computed(() => posts.value.filter((post) => post.status === '
|
||||
대시보드
|
||||
</h1>
|
||||
</div>
|
||||
<div class="admin-dashboard__body grid gap-4 bg-paper p-6 text-sm text-muted md:grid-cols-3">
|
||||
<section class="admin-dashboard__metric border border-line bg-white p-4">
|
||||
<p class="admin-dashboard__metric-label text-xs font-semibold uppercase">
|
||||
Posts
|
||||
</p>
|
||||
<strong class="admin-dashboard__metric-value mt-2 block text-3xl text-ink">
|
||||
{{ posts.length }}
|
||||
</strong>
|
||||
<div class="admin-dashboard__body space-y-6 bg-paper p-6">
|
||||
<section class="admin-dashboard__analytics grid gap-4 md:grid-cols-2 lg:grid-cols-4">
|
||||
<article class="admin-dashboard__metric border border-line bg-white p-4">
|
||||
<p class="admin-dashboard__metric-label text-xs font-semibold uppercase text-muted">
|
||||
오늘 방문
|
||||
</p>
|
||||
<strong class="admin-dashboard__metric-value mt-2 block text-3xl text-ink">
|
||||
{{ analyticsSummary.todayVisitors }}
|
||||
</strong>
|
||||
</article>
|
||||
<article class="admin-dashboard__metric border border-line bg-white p-4">
|
||||
<p class="admin-dashboard__metric-label text-xs font-semibold uppercase text-muted">
|
||||
7일 방문
|
||||
</p>
|
||||
<strong class="admin-dashboard__metric-value mt-2 block text-3xl text-ink">
|
||||
{{ analyticsSummary.visitorsLast7Days }}
|
||||
</strong>
|
||||
</article>
|
||||
<article class="admin-dashboard__metric border border-line bg-white p-4">
|
||||
<p class="admin-dashboard__metric-label text-xs font-semibold uppercase text-muted">
|
||||
30일 조회
|
||||
</p>
|
||||
<strong class="admin-dashboard__metric-value mt-2 block text-3xl text-ink">
|
||||
{{ analyticsSummary.pageViewsLast30Days }}
|
||||
</strong>
|
||||
</article>
|
||||
<article class="admin-dashboard__metric border border-line bg-white p-4">
|
||||
<p class="admin-dashboard__metric-label text-xs font-semibold uppercase text-muted">
|
||||
게시물
|
||||
</p>
|
||||
<strong class="admin-dashboard__metric-value mt-2 block text-3xl text-ink">
|
||||
{{ posts.length }}
|
||||
</strong>
|
||||
<p class="admin-dashboard__metric-note mt-2 text-xs text-muted">
|
||||
발행 {{ publishedCount }} · 초안 {{ draftCount }}
|
||||
</p>
|
||||
</article>
|
||||
</section>
|
||||
<section class="admin-dashboard__metric border border-line bg-white p-4">
|
||||
<p class="admin-dashboard__metric-label text-xs font-semibold uppercase">
|
||||
Published
|
||||
|
||||
<section class="admin-dashboard__top-posts border border-line bg-white p-4">
|
||||
<h2 class="admin-dashboard__section-title text-sm font-semibold text-ink">
|
||||
인기 게시물 (30일)
|
||||
</h2>
|
||||
<ul
|
||||
v-if="topPosts.length"
|
||||
class="admin-dashboard__top-posts-list mt-4 space-y-3 text-sm"
|
||||
>
|
||||
<li
|
||||
v-for="(item, index) in topPosts"
|
||||
:key="item.id"
|
||||
class="admin-dashboard__top-posts-item flex items-start justify-between gap-4 border-b border-line pb-3 last:border-b-0 last:pb-0"
|
||||
>
|
||||
<div class="min-w-0 flex-1">
|
||||
<p class="admin-dashboard__top-posts-rank text-xs font-semibold uppercase text-muted">
|
||||
#{{ index + 1 }}
|
||||
</p>
|
||||
<NuxtLink
|
||||
:to="`/post/${item.slug}`"
|
||||
class="admin-dashboard__top-posts-title mt-1 block truncate font-medium text-ink hover:underline"
|
||||
target="_blank"
|
||||
>
|
||||
{{ item.title }}
|
||||
</NuxtLink>
|
||||
</div>
|
||||
<dl class="admin-dashboard__top-posts-stats shrink-0 text-right text-xs text-muted">
|
||||
<div>
|
||||
<dt class="inline">
|
||||
조회
|
||||
</dt>
|
||||
<dd class="inline font-semibold text-ink">
|
||||
{{ item.views }}
|
||||
</dd>
|
||||
</div>
|
||||
<div class="mt-1">
|
||||
<dt class="inline">
|
||||
읽음
|
||||
</dt>
|
||||
<dd class="inline font-semibold text-ink">
|
||||
{{ item.reads }}
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</li>
|
||||
</ul>
|
||||
<p
|
||||
v-else
|
||||
class="admin-dashboard__top-posts-empty mt-4 text-sm text-muted"
|
||||
>
|
||||
아직 집계된 게시물 조회 데이터가 없습니다.
|
||||
</p>
|
||||
<strong class="admin-dashboard__metric-value mt-2 block text-3xl text-ink">
|
||||
{{ publishedCount }}
|
||||
</strong>
|
||||
</section>
|
||||
<section class="admin-dashboard__metric border border-line bg-white p-4">
|
||||
<p class="admin-dashboard__metric-label text-xs font-semibold uppercase">
|
||||
Draft
|
||||
</p>
|
||||
<strong class="admin-dashboard__metric-value mt-2 block text-3xl text-ink">
|
||||
{{ draftCount }}
|
||||
</strong>
|
||||
</section>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
Reference in New Issue
Block a user