인기 페이지 통계와 추천 사이트 메타데이터 추가 v1.5.9
This commit is contained in:
@@ -43,6 +43,11 @@ const { data: topPosts, refresh: refreshTopPosts } = await useFetch('/admin/api/
|
||||
default: () => []
|
||||
})
|
||||
|
||||
const { data: topPages, refresh: refreshTopPages } = await useFetch('/admin/api/analytics/pages', {
|
||||
query: topPostsQuery,
|
||||
default: () => []
|
||||
})
|
||||
|
||||
const { data: realtime, refresh: refreshRealtime } = await useFetch('/admin/api/analytics/realtime', {
|
||||
query: { limit: 20 },
|
||||
default: () => ({
|
||||
@@ -250,6 +255,10 @@ const getSessionViewingTitle = (session) => {
|
||||
return session.postTitle
|
||||
}
|
||||
|
||||
if (session.pageTitle) {
|
||||
return session.pageTitle
|
||||
}
|
||||
|
||||
if (session.path === '/') {
|
||||
return '홈'
|
||||
}
|
||||
@@ -263,6 +272,7 @@ onMounted(() => {
|
||||
refreshTimer = window.setInterval(() => {
|
||||
refreshSummary()
|
||||
refreshTopPosts()
|
||||
refreshTopPages()
|
||||
refreshRealtime()
|
||||
}, 30000)
|
||||
})
|
||||
@@ -276,6 +286,7 @@ onUnmounted(() => {
|
||||
watch(selectedAnalyticsDays, () => {
|
||||
refreshSummary()
|
||||
refreshTopPosts()
|
||||
refreshTopPages()
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -435,74 +446,145 @@ watch(selectedAnalyticsDays, () => {
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<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">
|
||||
인기 게시물 ({{ analyticsRangeLabel }})
|
||||
</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="admin-dashboard__popular-grid grid gap-6 xl:grid-cols-2">
|
||||
<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">
|
||||
인기 게시물 ({{ analyticsRangeLabel }})
|
||||
</h2>
|
||||
<ul
|
||||
v-if="topPosts.length"
|
||||
class="admin-dashboard__top-posts-list mt-4 space-y-3 text-sm"
|
||||
>
|
||||
<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>
|
||||
<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>
|
||||
<div class="mt-1">
|
||||
<dt class="inline">
|
||||
읽음
|
||||
</dt>
|
||||
<dd class="inline font-semibold text-ink">
|
||||
{{ item.reads }}
|
||||
</dd>
|
||||
<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>
|
||||
<div class="mt-1">
|
||||
<dt class="inline">
|
||||
체류
|
||||
</dt>
|
||||
<dd class="inline font-semibold text-ink">
|
||||
{{ formatEngagedDuration(item.avgEngagedSeconds) }}
|
||||
</dd>
|
||||
</div>
|
||||
<div class="mt-1">
|
||||
<dt class="inline">
|
||||
50/75/100%
|
||||
</dt>
|
||||
<dd class="inline font-semibold text-ink">
|
||||
{{ item.scroll50 }}/{{ item.scroll75 }}/{{ item.scroll100 }}
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</li>
|
||||
</ul>
|
||||
<p
|
||||
v-else
|
||||
class="admin-dashboard__top-posts-empty mt-4 text-sm text-muted"
|
||||
>
|
||||
아직 집계된 게시물 조회 데이터가 없습니다.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section class="admin-dashboard__top-pages border border-line bg-white p-4">
|
||||
<h2 class="admin-dashboard__section-title text-sm font-semibold text-ink">
|
||||
인기 페이지 ({{ analyticsRangeLabel }})
|
||||
</h2>
|
||||
<ul
|
||||
v-if="topPages.length"
|
||||
class="admin-dashboard__top-pages-list mt-4 space-y-3 text-sm"
|
||||
>
|
||||
<li
|
||||
v-for="(item, index) in topPages"
|
||||
:key="item.id"
|
||||
class="admin-dashboard__top-pages-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-pages-rank text-xs font-semibold uppercase text-muted">
|
||||
#{{ index + 1 }}
|
||||
</p>
|
||||
<NuxtLink
|
||||
:to="`/pages/${item.slug}`"
|
||||
class="admin-dashboard__top-pages-title mt-1 block truncate font-medium text-ink hover:underline"
|
||||
target="_blank"
|
||||
>
|
||||
{{ item.title }}
|
||||
</NuxtLink>
|
||||
</div>
|
||||
<div class="mt-1">
|
||||
<dt class="inline">
|
||||
체류
|
||||
</dt>
|
||||
<dd class="inline font-semibold text-ink">
|
||||
{{ formatEngagedDuration(item.avgEngagedSeconds) }}
|
||||
</dd>
|
||||
</div>
|
||||
<div class="mt-1">
|
||||
<dt class="inline">
|
||||
50/75/100%
|
||||
</dt>
|
||||
<dd class="inline font-semibold text-ink">
|
||||
{{ item.scroll50 }}/{{ item.scroll75 }}/{{ item.scroll100 }}
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</li>
|
||||
</ul>
|
||||
<p
|
||||
v-else
|
||||
class="admin-dashboard__top-posts-empty mt-4 text-sm text-muted"
|
||||
>
|
||||
아직 집계된 게시물 조회 데이터가 없습니다.
|
||||
</p>
|
||||
</section>
|
||||
<dl class="admin-dashboard__top-pages-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.visitors }}
|
||||
</dd>
|
||||
</div>
|
||||
<div class="mt-1">
|
||||
<dt class="inline">
|
||||
체류
|
||||
</dt>
|
||||
<dd class="inline font-semibold text-ink">
|
||||
{{ formatEngagedDuration(item.avgEngagedSeconds) }}
|
||||
</dd>
|
||||
</div>
|
||||
<div class="mt-1">
|
||||
<dt class="inline">
|
||||
50/75/100%
|
||||
</dt>
|
||||
<dd class="inline font-semibold text-ink">
|
||||
{{ item.scroll50 }}/{{ item.scroll75 }}/{{ item.scroll100 }}
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</li>
|
||||
</ul>
|
||||
<p
|
||||
v-else
|
||||
class="admin-dashboard__top-pages-empty mt-4 text-sm text-muted"
|
||||
>
|
||||
아직 집계된 페이지 조회 데이터가 없습니다.
|
||||
</p>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user