관리자 대시보드 통계 상세 UX 개선
This commit is contained in:
@@ -22,6 +22,7 @@ import {
|
||||
import { getPostgresClient } from './postgres-client.js'
|
||||
import { getRuntimeEnvValue } from '../utils/runtime-env.js'
|
||||
import { getMemberSession } from '../utils/member-auth.js'
|
||||
import { getClientIp } from '../utils/request-ip.js'
|
||||
|
||||
/** @type {number} 마지막 통계 보관 정리 실행 시각 */
|
||||
let lastAnalyticsRetentionPurgeAt = 0
|
||||
@@ -788,6 +789,7 @@ export const recordAnalyticsHeartbeat = async (input) => {
|
||||
const maxScrollRatio = clampAnalyticsScrollRatio(input.maxScrollRatio)
|
||||
const memberSession = getMemberSession(input.event)
|
||||
const userId = memberSession?.userId || null
|
||||
const ipAddress = getClientIp(input.event)
|
||||
|
||||
const previousRows = await sql`
|
||||
SELECT duration_seconds, max_scroll_ratio
|
||||
@@ -810,6 +812,7 @@ export const recordAnalyticsHeartbeat = async (input) => {
|
||||
post_slug,
|
||||
page_id,
|
||||
page_slug,
|
||||
ip_address,
|
||||
duration_seconds,
|
||||
max_scroll_ratio
|
||||
)
|
||||
@@ -821,6 +824,7 @@ export const recordAnalyticsHeartbeat = async (input) => {
|
||||
${postSlug},
|
||||
${pageId},
|
||||
${pageSlug},
|
||||
${ipAddress},
|
||||
${durationSeconds},
|
||||
${maxScrollRatio}
|
||||
)
|
||||
@@ -832,6 +836,7 @@ export const recordAnalyticsHeartbeat = async (input) => {
|
||||
post_slug = EXCLUDED.post_slug,
|
||||
page_id = EXCLUDED.page_id,
|
||||
page_slug = EXCLUDED.page_slug,
|
||||
ip_address = EXCLUDED.ip_address,
|
||||
duration_seconds = GREATEST(analytics_active_sessions.duration_seconds, EXCLUDED.duration_seconds),
|
||||
max_scroll_ratio = GREATEST(analytics_active_sessions.max_scroll_ratio, EXCLUDED.max_scroll_ratio),
|
||||
last_seen_at = now()
|
||||
@@ -1355,6 +1360,7 @@ export const getAnalyticsActiveSessions = async (options = {}) => {
|
||||
analytics_active_sessions.path,
|
||||
analytics_active_sessions.post_slug,
|
||||
analytics_active_sessions.page_slug,
|
||||
analytics_active_sessions.ip_address,
|
||||
analytics_active_sessions.duration_seconds,
|
||||
analytics_active_sessions.max_scroll_ratio,
|
||||
analytics_active_sessions.last_seen_at,
|
||||
@@ -1377,6 +1383,7 @@ export const getAnalyticsActiveSessions = async (options = {}) => {
|
||||
path: row.path,
|
||||
postSlug: row.post_slug || '',
|
||||
pageSlug: row.page_slug || '',
|
||||
ipAddress: row.ip_address || '',
|
||||
postTitle: row.post_title || '',
|
||||
pageTitle: row.page_title || '',
|
||||
durationSeconds: Number(row.duration_seconds || 0),
|
||||
@@ -1511,6 +1518,7 @@ export const getAnalyticsRangeDetail = async (options = {}) => {
|
||||
}
|
||||
|
||||
await purgeAnalyticsRetention(sql)
|
||||
const monthlyStartDay = getAnalyticsDayBefore(endDay, 29)
|
||||
|
||||
const summaryRows = await sql`
|
||||
SELECT
|
||||
@@ -1573,13 +1581,20 @@ export const getAnalyticsRangeDetail = async (options = {}) => {
|
||||
posts.id,
|
||||
posts.title,
|
||||
posts.slug,
|
||||
posts.created_at,
|
||||
COALESCE(SUM(post_analytics_daily.views), 0)::int AS views,
|
||||
COALESCE(SUM(post_analytics_daily.views) FILTER (
|
||||
WHERE post_analytics_daily.day >= ${monthlyStartDay}::date
|
||||
), 0)::int AS monthly_views,
|
||||
COALESCE(SUM(post_analytics_daily.reads), 0)::int AS reads,
|
||||
COALESCE(SUM(post_analytics_daily.visitors), 0)::int AS visitors
|
||||
COALESCE(SUM(post_analytics_daily.visitors), 0)::int AS visitors,
|
||||
COALESCE(SUM(post_analytics_daily.scroll_50), 0)::int AS scroll_50,
|
||||
COALESCE(SUM(post_analytics_daily.scroll_75), 0)::int AS scroll_75,
|
||||
COALESCE(SUM(post_analytics_daily.scroll_100), 0)::int AS scroll_100
|
||||
FROM post_analytics_daily
|
||||
INNER JOIN posts ON posts.id = post_analytics_daily.post_id
|
||||
WHERE post_analytics_daily.day BETWEEN ${startDay}::date AND ${endDay}::date
|
||||
GROUP BY posts.id, posts.title, posts.slug
|
||||
GROUP BY posts.id, posts.title, posts.slug, posts.created_at
|
||||
ORDER BY views DESC, reads DESC, posts.published_at DESC NULLS LAST
|
||||
LIMIT ${limit}
|
||||
`
|
||||
@@ -1589,12 +1604,19 @@ export const getAnalyticsRangeDetail = async (options = {}) => {
|
||||
pages.id,
|
||||
pages.title,
|
||||
pages.slug,
|
||||
pages.created_at,
|
||||
COALESCE(SUM(page_analytics_daily.views), 0)::int AS views,
|
||||
COALESCE(SUM(page_analytics_daily.visitors), 0)::int AS visitors
|
||||
COALESCE(SUM(page_analytics_daily.views) FILTER (
|
||||
WHERE page_analytics_daily.day >= ${monthlyStartDay}::date
|
||||
), 0)::int AS monthly_views,
|
||||
COALESCE(SUM(page_analytics_daily.visitors), 0)::int AS visitors,
|
||||
COALESCE(SUM(page_analytics_daily.scroll_50), 0)::int AS scroll_50,
|
||||
COALESCE(SUM(page_analytics_daily.scroll_75), 0)::int AS scroll_75,
|
||||
COALESCE(SUM(page_analytics_daily.scroll_100), 0)::int AS scroll_100
|
||||
FROM page_analytics_daily
|
||||
INNER JOIN pages ON pages.id = page_analytics_daily.page_id
|
||||
WHERE page_analytics_daily.day BETWEEN ${startDay}::date AND ${endDay}::date
|
||||
GROUP BY pages.id, pages.title, pages.slug
|
||||
GROUP BY pages.id, pages.title, pages.slug, pages.created_at
|
||||
ORDER BY views DESC, visitors DESC, pages.updated_at DESC NULLS LAST
|
||||
LIMIT ${limit}
|
||||
`
|
||||
@@ -1636,16 +1658,26 @@ export const getAnalyticsRangeDetail = async (options = {}) => {
|
||||
id: row.id,
|
||||
title: row.title,
|
||||
slug: row.slug,
|
||||
createdAt: row.created_at ? new Date(row.created_at).toISOString() : null,
|
||||
views: Number(row.views || 0),
|
||||
monthlyViews: Number(row.monthly_views || 0),
|
||||
reads: Number(row.reads || 0),
|
||||
visitors: Number(row.visitors || 0)
|
||||
visitors: Number(row.visitors || 0),
|
||||
scroll50: Number(row.scroll_50 || 0),
|
||||
scroll75: Number(row.scroll_75 || 0),
|
||||
scroll100: Number(row.scroll_100 || 0)
|
||||
})),
|
||||
topPages: pageRows.map((row) => ({
|
||||
id: row.id,
|
||||
title: row.title,
|
||||
slug: row.slug,
|
||||
createdAt: row.created_at ? new Date(row.created_at).toISOString() : null,
|
||||
views: Number(row.views || 0),
|
||||
visitors: Number(row.visitors || 0)
|
||||
monthlyViews: Number(row.monthly_views || 0),
|
||||
visitors: Number(row.visitors || 0),
|
||||
scroll50: Number(row.scroll_50 || 0),
|
||||
scroll75: Number(row.scroll_75 || 0),
|
||||
scroll100: Number(row.scroll_100 || 0)
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user