Refactors time-based filtering by combining startAt/endAt into a single time object Adds support for additional filter types in stats API queries Implements deep watching for both time and filter changes Improves code organization and maintainability while enabling more flexible data filtering
78 lines
2.2 KiB
Vue
78 lines
2.2 KiB
Vue
<script setup>
|
|
import NumberFlow from '@number-flow/vue'
|
|
import { Flame, MousePointerClick, Users } from 'lucide-vue-next'
|
|
|
|
const defaultData = Object.freeze({
|
|
visits: 0,
|
|
visitors: 0,
|
|
referers: 0,
|
|
})
|
|
|
|
const counters = ref(defaultData)
|
|
|
|
const id = inject('id')
|
|
const time = inject('time')
|
|
const filters = inject('filters')
|
|
async function getLinkCounters() {
|
|
counters.value = defaultData
|
|
const { data } = await useAPI('/api/stats/counters', {
|
|
query: {
|
|
id: id.value,
|
|
startAt: time.value.startAt,
|
|
endAt: time.value.endAt,
|
|
...filters.value,
|
|
},
|
|
})
|
|
counters.value = data?.[0]
|
|
}
|
|
|
|
const stopWatchQueryChange = watch([time, filters], getLinkCounters, {
|
|
deep: true,
|
|
})
|
|
|
|
onMounted(async () => {
|
|
getLinkCounters()
|
|
})
|
|
|
|
onBeforeUnmount(() => {
|
|
stopWatchQueryChange()
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<div class="grid gap-4 sm:gap-3 lg:gap-4 sm:grid-cols-3">
|
|
<Card>
|
|
<CardHeader class="flex flex-row justify-between items-center pb-2 space-y-0">
|
|
<CardTitle class="text-sm font-medium">
|
|
Visits
|
|
</CardTitle>
|
|
<MousePointerClick class="w-4 h-4 text-muted-foreground" />
|
|
</CardHeader>
|
|
<CardContent>
|
|
<NumberFlow class="text-2xl font-bold" :class="{ 'blur-md opacity-60': !counters.visits }" :value="counters.visits" />
|
|
</CardContent>
|
|
</Card>
|
|
<Card>
|
|
<CardHeader class="flex flex-row justify-between items-center pb-2 space-y-0">
|
|
<CardTitle class="text-sm font-medium">
|
|
Visitors
|
|
</CardTitle>
|
|
<Users class="w-4 h-4 text-muted-foreground" />
|
|
</CardHeader>
|
|
<CardContent>
|
|
<NumberFlow class="text-2xl font-bold" :class="{ 'blur-md opacity-60': !counters.visitors }" :value="counters.visitors" />
|
|
</CardContent>
|
|
</Card>
|
|
<Card>
|
|
<CardHeader class="flex flex-row justify-between items-center pb-2 space-y-0">
|
|
<CardTitle class="text-sm font-medium">
|
|
Referers
|
|
</CardTitle>
|
|
<Flame class="w-4 h-4 text-muted-foreground" />
|
|
</CardHeader>
|
|
<CardContent>
|
|
<NumberFlow class="text-2xl font-bold" :class="{ 'blur-md opacity-60': !counters.referers }" :value="counters.referers" />
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
</template>
|