Smarter initial analytics page when there is no visitor data yet.
This commit is contained in:
@@ -233,7 +233,6 @@ const abuseRows = computed(() => formatAbuseRows(rangeData.value?.abuse || [], c
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<p v-else class="empty">no crawler hits recorded yet</p>
|
|
||||||
|
|
||||||
<div v-if="abuseRows.length" class="visit-table-wrap">
|
<div v-if="abuseRows.length" class="visit-table-wrap">
|
||||||
<table class="visit-table">
|
<table class="visit-table">
|
||||||
|
|||||||
@@ -68,8 +68,8 @@ const viewChart = computed(() => buildChart(viewSeries.value, now.value))
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<section v-for="c in [
|
<section v-for="c in [
|
||||||
{ ylabel: 'visits', chart: visitChart, legend: true, empty: 'no visits recorded yet' },
|
{ ylabel: 'visits', chart: visitChart, legend: true },
|
||||||
{ ylabel: 'views', chart: viewChart, legend: false, empty: 'no views recorded yet' },
|
{ ylabel: 'views', chart: viewChart, legend: false },
|
||||||
]" :key="c.ylabel">
|
]" :key="c.ylabel">
|
||||||
<template v-if="c.chart">
|
<template v-if="c.chart">
|
||||||
<svg class="chart" :viewBox="`${-MARGIN_L} 0 ${VIEW_W} ${VIEW_H}`"
|
<svg class="chart" :viewBox="`${-MARGIN_L} 0 ${VIEW_W} ${VIEW_H}`"
|
||||||
@@ -116,7 +116,6 @@ const viewChart = computed(() => buildChart(viewSeries.value, now.value))
|
|||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
</template>
|
</template>
|
||||||
<p v-else class="empty">{{ c.empty }}</p>
|
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -63,9 +63,24 @@ export function sumRange(raw, t0, t1) {
|
|||||||
export function weeklySeries(buckets) {
|
export function weeklySeries(buckets) {
|
||||||
const raw = rawTimes(buckets)
|
const raw = rawTimes(buckets)
|
||||||
const times = Object.keys(raw).map(Number)
|
const times = Object.keys(raw).map(Number)
|
||||||
if (!times.length) return null
|
|
||||||
const now = Date.now()
|
const now = Date.now()
|
||||||
const thisMonday = mondayUTC(now)
|
const thisMonday = mondayUTC(now)
|
||||||
|
if (!times.length) {
|
||||||
|
const points = []
|
||||||
|
const end = Math.min(thisMonday + WEEK, Math.floor(now / MIN5) * MIN5 + MIN5)
|
||||||
|
for (let t = thisMonday; t < end; t += MIN5) {
|
||||||
|
points.push({ t, count: 0 })
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
series: [{ points, label: `Week ${isoWeek(thisMonday)}`, opacity: 1, area: true }],
|
||||||
|
t0: thisMonday,
|
||||||
|
t1: thisMonday + WEEK,
|
||||||
|
rate: HOUR / MIN5,
|
||||||
|
binMinutes: 5,
|
||||||
|
unitMinutes: 60,
|
||||||
|
unit: 'hour',
|
||||||
|
}
|
||||||
|
}
|
||||||
const oldest = Math.min(...times)
|
const oldest = Math.min(...times)
|
||||||
// Weeks back as far as the data reaches: difference in Monday indices.
|
// Weeks back as far as the data reaches: difference in Monday indices.
|
||||||
const available = (thisMonday - mondayUTC(oldest)) / WEEK + 1
|
const available = (thisMonday - mondayUTC(oldest)) / WEEK + 1
|
||||||
@@ -113,13 +128,27 @@ export function weeklySeries(buckets) {
|
|||||||
export function rollingSeries(buckets, rangeKey) {
|
export function rollingSeries(buckets, rangeKey) {
|
||||||
const raw = rawTimes(buckets)
|
const raw = rawTimes(buckets)
|
||||||
const times = Object.keys(raw).map(Number)
|
const times = Object.keys(raw).map(Number)
|
||||||
if (!times.length) return null
|
|
||||||
const { span, bucket, minSpan = 0 } = RANGES[rangeKey]
|
const { span, bucket, minSpan = 0 } = RANGES[rangeKey]
|
||||||
const t1 = Date.now()
|
const t1 = Date.now()
|
||||||
const earliest = Math.min(...times)
|
|
||||||
const t0 = Math.floor((span != null
|
const t0 = Math.floor((span != null
|
||||||
? t1 - span
|
? t1 - span
|
||||||
: Math.min(earliest, t1 - minSpan)) / DAY) * DAY
|
: Math.min(times.length ? Math.min(...times) : Infinity, t1 - minSpan)) / DAY) * DAY
|
||||||
|
if (!times.length) {
|
||||||
|
const bucketMs = t1 - t0 <= 31 * DAY ? Math.min(bucket, 6 * HOUR) : bucket
|
||||||
|
const points = []
|
||||||
|
for (let t = t0; t < t1; t += bucketMs) {
|
||||||
|
points.push({ t, count: 0 })
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
series: [{ points, label: '', opacity: 1, area: true }],
|
||||||
|
t0,
|
||||||
|
t1,
|
||||||
|
rate: DAY / bucketMs,
|
||||||
|
binMinutes: bucketMs / 60e3,
|
||||||
|
unitMinutes: 24 * 60,
|
||||||
|
unit: 'day',
|
||||||
|
}
|
||||||
|
}
|
||||||
// The bucket follows the actual window length, not the range key: when
|
// The bucket follows the actual window length, not the range key: when
|
||||||
// "all" is capped to its 30-day minimum it covers the very window "month"
|
// "all" is capped to its 30-day minimum it covers the very window "month"
|
||||||
// does, and daily bins would draw a different curve over the same data
|
// does, and daily bins would draw a different curve over the same data
|
||||||
|
|||||||
Reference in New Issue
Block a user