AdSense 대시보드 요약 정리

This commit is contained in:
2026-07-20 17:11:30 +09:00
parent e7c4f952a6
commit c0684737ce
9 changed files with 215 additions and 95 deletions

View File

@@ -64,11 +64,19 @@ const { data: realtime, refresh: refreshRealtime } = await useFetch('/admin/api/
const createEmptyAdsensePeriod = (label) => ({
label,
key: '',
estimatedEarnings: 0,
pageViews: 0,
impressions: 0,
clicks: 0,
pageViewsRpm: 0
pageViewsRpm: 0,
currencyCode: ''
})
const createEmptyAdsenseBalance = () => ({
balanceAmount: '',
lastPaymentAmount: '',
lastPaymentDate: ''
})
const {
@@ -81,11 +89,10 @@ const {
configured: false,
ok: false,
message: '',
currencyCode: 'KRW',
currencyCode: 'USD',
generatedAt: '',
today: createEmptyAdsensePeriod('오늘'),
yesterday: createEmptyAdsensePeriod('어제'),
month: createEmptyAdsensePeriod('이번 달')
periods: [],
balance: createEmptyAdsenseBalance()
})
})
@@ -179,11 +186,8 @@ const chartPagination = computed(() => {
return paginateAnalyticsTrendRows(trendRows.value, selectedAnalyticsPeriod.value, analyticsChartPageOffset.value)
})
const adsenseCards = computed(() => [
adsenseSummary.value.today || createEmptyAdsensePeriod('오늘'),
adsenseSummary.value.yesterday || createEmptyAdsensePeriod('어제'),
adsenseSummary.value.month || createEmptyAdsensePeriod('이번 달')
])
const adsenseCards = computed(() => adsenseSummary.value.periods || [])
const adsenseBalance = computed(() => adsenseSummary.value.balance || createEmptyAdsenseBalance())
const adsenseGeneratedLabel = computed(() => {
const value = adsenseSummary.value?.generatedAt
@@ -199,17 +203,17 @@ const adsenseGeneratedLabel = computed(() => {
* AdSense 통화 코드를 표시 가능한 값으로 보정한다.
* @returns {string} 통화 코드
*/
const getAdsenseCurrencyCode = () => {
const currencyCode = adsenseSummary.value?.currencyCode || 'KRW'
const getAdsenseCurrencyCode = (currencyCode) => {
const value = currencyCode || adsenseSummary.value?.currencyCode || 'USD'
try {
new Intl.NumberFormat('ko-KR', {
style: 'currency',
currency: currencyCode
currency: value
}).format(0)
return currencyCode
return value
} catch {
return 'KRW'
return 'USD'
}
}
@@ -227,13 +231,14 @@ const formatNumber = (value) => {
* @param {number} value - 금액
* @returns {string} 표시 문자열
*/
const formatAdsenseCurrency = (value) => {
const currencyCode = getAdsenseCurrencyCode()
const formatAdsenseCurrency = (value, currencyCode = '') => {
const resolvedCurrencyCode = getAdsenseCurrencyCode(currencyCode)
return new Intl.NumberFormat('ko-KR', {
style: 'currency',
currency: currencyCode,
maximumFractionDigits: currencyCode === 'KRW' ? 0 : 2
currency: resolvedCurrencyCode,
minimumFractionDigits: resolvedCurrencyCode === 'KRW' ? 0 : 2,
maximumFractionDigits: resolvedCurrencyCode === 'KRW' ? 0 : 2
}).format(Number(value || 0))
}
@@ -242,13 +247,14 @@ const formatAdsenseCurrency = (value) => {
* @param {number} value - RPM
* @returns {string} 표시 문자열
*/
const formatAdsenseRpm = (value) => {
const currencyCode = getAdsenseCurrencyCode()
const formatAdsenseRpm = (value, currencyCode = '') => {
const resolvedCurrencyCode = getAdsenseCurrencyCode(currencyCode)
return new Intl.NumberFormat('ko-KR', {
style: 'currency',
currency: currencyCode,
maximumFractionDigits: currencyCode === 'KRW' ? 0 : 2
currency: resolvedCurrencyCode,
minimumFractionDigits: resolvedCurrencyCode === 'KRW' ? 0 : 2,
maximumFractionDigits: resolvedCurrencyCode === 'KRW' ? 0 : 2
}).format(Number(value || 0))
}
@@ -692,7 +698,7 @@ watch(trendRows, () => {
AdSense
</h2>
<p class="admin-dashboard__section-description mt-1 text-xs text-muted">
예상 수익·페이지뷰·클릭 기준 요약
예상 수입
<span v-if="adsenseGeneratedLabel">· {{ adsenseGeneratedLabel }} 갱신</span>
</p>
</div>
@@ -707,39 +713,54 @@ watch(trendRows, () => {
</div>
<p
v-if="!adsenseSummary.configured"
class="admin-dashboard__adsense-message mt-3 border border-dashed border-line bg-paper px-3 py-2 text-xs text-muted"
>
AdSense 환경 변수를 설정하면 이곳에 수익 요약이 표시됩니다.
</p>
<p
v-else-if="!adsenseSummary.ok"
v-if="adsenseSummary.configured && !adsenseSummary.ok"
class="admin-dashboard__adsense-message mt-3 border border-red-100 bg-red-50 px-3 py-2 text-xs text-red-700"
>
{{ adsenseSummary.message || 'AdSense 통계를 불러오지 못했습니다.' }}
</p>
<div class="admin-dashboard__adsense-grid mt-3 grid gap-3 lg:grid-cols-3">
<article
v-for="card in adsenseCards"
:key="card.label"
class="admin-dashboard__adsense-card border border-line bg-paper p-3"
>
<div class="flex items-start justify-between gap-3">
<p class="text-xs font-semibold uppercase text-muted">
{{ card.label }}
</p>
<p class="text-[11px] text-muted">
RPM {{ formatAdsenseRpm(card.pageViewsRpm) }}
</p>
<div class="admin-dashboard__adsense-layout mt-3 grid gap-3 xl:grid-cols-[minmax(0,5fr)_minmax(220px,1fr)]">
<div class="admin-dashboard__adsense-earnings bg-[#2f6cc4] p-4 text-white shadow-sm">
<h3 class="text-sm font-semibold">
예상 수입
</h3>
<div class="mt-5 grid gap-5 md:grid-cols-3 xl:grid-cols-5">
<article
v-for="card in adsenseCards"
:key="card.key || card.label"
class="admin-dashboard__adsense-card min-w-0"
>
<p class="text-xs font-semibold text-white/90">
{{ card.label }}
</p>
<strong class="mt-2 block text-2xl font-medium leading-tight text-white">
{{ formatAdsenseCurrency(card.estimatedEarnings, card.currencyCode) }}
</strong>
<p class="mt-2 text-[11px] leading-snug text-white/80">
페이지뷰 {{ formatNumber(card.pageViews) }} · 노출 {{ formatNumber(card.impressions) }} · 클릭 {{ formatNumber(card.clicks) }}
</p>
<p class="mt-1 text-[11px] leading-snug text-white/80">
RPM {{ formatAdsenseRpm(card.pageViewsRpm, card.currencyCode) }}
</p>
</article>
</div>
<strong class="mt-2 block text-xl text-ink">
{{ formatAdsenseCurrency(card.estimatedEarnings) }}
</div>
<aside class="admin-dashboard__adsense-balance bg-[#2f6cc4] p-4 text-white shadow-sm">
<h3 class="text-sm font-semibold">
잔고
</h3>
<strong class="mt-8 block text-2xl font-medium leading-tight text-white">
{{ adsenseBalance.balanceAmount || '-' }}
</strong>
<p class="mt-2 text-xs text-muted">
페이지뷰 {{ formatNumber(card.pageViews) }} · 노출 {{ formatNumber(card.impressions) }} · 클릭 {{ formatNumber(card.clicks) }}
<p class="mt-2 text-[11px] leading-snug text-white/85">
최종 지급일
</p>
</article>
<p class="mt-0.5 text-[11px] leading-snug text-white/85">
{{ adsenseBalance.lastPaymentDate || '-' }}
<span v-if="adsenseBalance.lastPaymentAmount"> · {{ adsenseBalance.lastPaymentAmount }}</span>
</p>
</aside>
</div>
</section>