mirror of
https://github.com/Litlyx/litlyx
synced 2025-12-10 15:58:38 +01:00
fix topcards reactivity
This commit is contained in:
@@ -5,50 +5,33 @@ import type { Slice } from '@services/DateService';
|
|||||||
|
|
||||||
const { data: metricsInfo } = useMetricsData();
|
const { data: metricsInfo } = useMetricsData();
|
||||||
|
|
||||||
|
const { snapshot, safeSnapshotDates } = useSnapshot()
|
||||||
|
|
||||||
|
const snapshotFrom = computed(() => new Date(snapshot.value?.from || '0').getTime());
|
||||||
|
const snapshotTo = computed(() => new Date(snapshot.value?.to || Date.now()).getTime());
|
||||||
|
|
||||||
|
const snapshotDays = computed(() => {
|
||||||
type Data = {
|
return (snapshotTo.value - snapshotFrom.value) / 1000 / 60 / 60 / 24;
|
||||||
data: number[],
|
|
||||||
labels: string[],
|
|
||||||
trend: number,
|
|
||||||
ready: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const { snapshot } = useSnapshot()
|
|
||||||
|
|
||||||
const visitsData = reactive<Data>({ data: [], labels: [], trend: 0, ready: false });
|
|
||||||
const eventsData = reactive<Data>({ data: [], labels: [], trend: 0, ready: false });
|
|
||||||
const sessionsData = reactive<Data>({ data: [], labels: [], trend: 0, ready: false });
|
|
||||||
const sessionsDurationData = reactive<Data>({ data: [], labels: [], trend: 0, ready: false });
|
|
||||||
|
|
||||||
const snapshotFrom = computed(() => {
|
|
||||||
return new Date(snapshot.value?.from || '0').getTime();
|
|
||||||
});
|
|
||||||
|
|
||||||
const snapshotTo = computed(() => {
|
|
||||||
return new Date(snapshot.value?.to || Date.now()).getTime();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const avgVisitDay = computed(() => {
|
const avgVisitDay = computed(() => {
|
||||||
const days = (snapshotTo.value - snapshotFrom.value) / 1000 / 60 / 60 / 24;
|
if (!visitsData.data.value) return '0.00';
|
||||||
const counts = visitsData.data.reduce((a, e) => e + a, 0);
|
const counts = visitsData.data.value.data.reduce((a, e) => e + a, 0);
|
||||||
const avg = counts / Math.max(days, 1);
|
const avg = counts / Math.max(snapshotDays.value, 1);
|
||||||
return avg.toFixed(2);
|
return avg.toFixed(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
const avgEventsDay = computed(() => {
|
const avgEventsDay = computed(() => {
|
||||||
const days = (snapshotTo.value - snapshotFrom.value) / 1000 / 60 / 60 / 24;
|
if (!eventsData.data.value) return '0.00';
|
||||||
const counts = eventsData.data.reduce((a, e) => e + a, 0);
|
const counts = eventsData.data.value.data.reduce((a, e) => e + a, 0);
|
||||||
const avg = counts / Math.max(days, 1);
|
const avg = counts / Math.max(snapshotDays.value, 1);
|
||||||
return avg.toFixed(2);
|
return avg.toFixed(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
const avgSessionsDay = computed(() => {
|
const avgSessionsDay = computed(() => {
|
||||||
const days = (snapshotTo.value - snapshotFrom.value) / 1000 / 60 / 60 / 24;
|
if (!sessionsData.data.value) return '0.00';
|
||||||
const counts = sessionsData.data.reduce((a, e) => e + a, 0);
|
const counts = sessionsData.data.value.data.reduce((a, e) => e + a, 0);
|
||||||
const avg = counts / Math.max(days, 1);
|
const avg = counts / Math.max(snapshotDays.value, 1);
|
||||||
return avg.toFixed(2);
|
return avg.toFixed(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -56,23 +39,12 @@ const avgSessionsDay = computed(() => {
|
|||||||
const avgSessionDuration = computed(() => {
|
const avgSessionDuration = computed(() => {
|
||||||
if (!metricsInfo.value) return '0.00';
|
if (!metricsInfo.value) return '0.00';
|
||||||
const avg = metricsInfo.value.avgSessionDuration;
|
const avg = metricsInfo.value.avgSessionDuration;
|
||||||
|
|
||||||
let hours = 0;
|
let hours = 0;
|
||||||
let minutes = 0;
|
let minutes = 0;
|
||||||
let seconds = 0;
|
let seconds = 0;
|
||||||
seconds += avg * 60;
|
seconds += avg * 60;
|
||||||
|
while (seconds > 60) { seconds -= 60; minutes += 1; }
|
||||||
while (seconds > 60) {
|
while (minutes > 60) { minutes -= 60; hours += 1; }
|
||||||
seconds -= 60;
|
|
||||||
minutes += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (minutes > 60) {
|
|
||||||
minutes -= 60;
|
|
||||||
hours += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return `${hours > 0 ? hours + 'h ' : ''}${minutes}m ${seconds.toFixed()}s`
|
return `${hours > 0 ? hours + 'h ' : ''}${minutes}m ${seconds.toFixed()}s`
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -85,90 +57,88 @@ const chartSlice = computed(() => {
|
|||||||
return 'month' as Slice;
|
return 'month' as Slice;
|
||||||
});
|
});
|
||||||
|
|
||||||
async function loadData(timelineEndpointName: string, target: Data) {
|
|
||||||
|
|
||||||
target.ready = false;
|
function transformResponse(input: { _id: string, count: number }[]) {
|
||||||
|
const data = input.map(e => e.count);
|
||||||
|
const labels = input.map(e => DateService.getChartLabelFromISO(e._id, navigator.language, chartSlice.value));
|
||||||
|
const pool = [...input.map(e => e.count)];
|
||||||
|
pool.pop();
|
||||||
|
const avg = pool.reduce((a, e) => a + e, 0) / pool.length;
|
||||||
|
const diffPercent: number = (100 / avg * (input.at(-1)?.count || 0)) - 100;
|
||||||
|
const trend = Math.max(Math.min(diffPercent, 99), -99);
|
||||||
|
return { data, labels, trend }
|
||||||
|
}
|
||||||
|
|
||||||
const response = useTimeline(timelineEndpointName as any, chartSlice);
|
const activeProject = useActiveProject();
|
||||||
|
|
||||||
response.onRequest(() => {
|
function getBody() {
|
||||||
target.ready = false;
|
return JSON.stringify({
|
||||||
target.data = [];
|
from: safeSnapshotDates.value.from,
|
||||||
target.labels = [];
|
to: safeSnapshotDates.value.to,
|
||||||
})
|
slice: chartSlice.value
|
||||||
|
|
||||||
response.onResponse(data => {
|
|
||||||
|
|
||||||
if (!data.value) return;
|
|
||||||
|
|
||||||
target.data = data.value.map(e => e.count);
|
|
||||||
target.labels = data.value.map(e => DateService.getChartLabelFromISO(e._id, navigator.language, chartSlice.value));
|
|
||||||
|
|
||||||
const pool = [...data.value.map(e => e.count)];
|
|
||||||
pool.pop();
|
|
||||||
const avg = pool.reduce((a, e) => a + e, 0) / pool.length;
|
|
||||||
|
|
||||||
const diffPercent: number = (100 / avg * (data.value.at(-1)?.count || 0)) - 100;
|
|
||||||
|
|
||||||
target.trend = Math.max(Math.min(diffPercent, 99), -99);
|
|
||||||
|
|
||||||
target.ready = true;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
response.execute();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const visitsData = useFetch(`/api/metrics/${activeProject.value?._id}/timeline/visits`, {
|
||||||
|
method: 'POST', ...signHeaders({ v2: 'true' }), body: getBody(), transform: transformResponse,
|
||||||
|
lazy: true, immediate: false
|
||||||
|
});
|
||||||
|
|
||||||
async function loadAllData() {
|
const eventsData = useFetch(`/api/metrics/${activeProject.value?._id}/timeline/events`, {
|
||||||
console.log('LOAD ALL DATA')
|
method: 'POST', ...signHeaders({ v2: 'true' }), body: getBody(), transform: transformResponse,
|
||||||
await Promise.all([
|
lazy: true, immediate: false
|
||||||
loadData('visits', visitsData),
|
});
|
||||||
loadData('events', eventsData),
|
|
||||||
loadData('sessions', sessionsData),
|
const sessionsData = useFetch(`/api/metrics/${activeProject.value?._id}/timeline/sessions`, {
|
||||||
loadData('sessions_duration', sessionsDurationData),
|
method: 'POST', ...signHeaders({ v2: 'true' }), body: getBody(), transform: transformResponse,
|
||||||
])
|
lazy: true, immediate: false
|
||||||
}
|
});
|
||||||
|
|
||||||
|
const sessionsDurationData = useFetch(`/api/metrics/${activeProject.value?._id}/timeline/sessions_duration`, {
|
||||||
|
method: 'POST', ...signHeaders({ v2: 'true' }), body: getBody(), transform: transformResponse,
|
||||||
|
lazy: true, immediate: false
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
|
console.log('MOUNTED')
|
||||||
await loadAllData();
|
visitsData.execute();
|
||||||
watch(snapshot, async () => {
|
eventsData.execute();
|
||||||
await loadAllData();
|
sessionsData.execute();
|
||||||
})
|
sessionsDurationData.execute();
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="gap-6 px-6 grid grid-cols-1 md:grid-cols-2 xl:grid-cols-2 m-cards-wrap:grid-cols-4" v-if="metricsInfo">
|
<div class="gap-6 px-6 grid grid-cols-1 md:grid-cols-2 xl:grid-cols-2 m-cards-wrap:grid-cols-4" v-if="metricsInfo">
|
||||||
|
|
||||||
<DashboardCountCard :ready="visitsData.ready" icon="far fa-earth" text="Total page visits"
|
<DashboardCountCard :ready="!visitsData.pending.value" icon="far fa-earth" text="Total page visits"
|
||||||
:value="formatNumberK(visitsData.data.reduce((a, e) => a + e, 0))"
|
:value="formatNumberK(visitsData.data.value?.data.reduce((a, e) => a + e, 0) || '...')"
|
||||||
:avg="formatNumberK(avgVisitDay) + '/day'" :trend="visitsData.trend" :data="visitsData.data"
|
:avg="formatNumberK(avgVisitDay) + '/day'" :trend="visitsData.data.value?.trend"
|
||||||
:labels="visitsData.labels" color="#5655d7">
|
:data="visitsData.data.value?.data" :labels="visitsData.data.value?.labels" color="#5655d7">
|
||||||
</DashboardCountCard>
|
</DashboardCountCard>
|
||||||
|
|
||||||
<DashboardCountCard :ready="eventsData.ready" icon="far fa-flag" text="Total custom events"
|
<DashboardCountCard :ready="!eventsData.pending.value" icon="far fa-flag" text="Total custom events"
|
||||||
:value="formatNumberK(eventsData.data.reduce((a, e) => a + e, 0))"
|
:value="formatNumberK(eventsData.data.value?.data.reduce((a, e) => a + e, 0) || '...')"
|
||||||
:avg="formatNumberK(avgEventsDay) + '/day'" :trend="eventsData.trend" :data="eventsData.data"
|
:avg="formatNumberK(avgEventsDay) + '/day'" :trend="eventsData.data.value?.trend"
|
||||||
:labels="eventsData.labels" color="#1e9b86">
|
:data="eventsData.data.value?.data" :labels="eventsData.data.value?.labels" color="#1e9b86">
|
||||||
</DashboardCountCard>
|
</DashboardCountCard>
|
||||||
|
|
||||||
<DashboardCountCard :ready="sessionsData.ready" icon="far fa-user" text="Unique visits sessions"
|
|
||||||
:value="formatNumberK(sessionsData.data.reduce((a, e) => a + e, 0))"
|
<DashboardCountCard :ready="!sessionsData.pending.value" icon="far fa-user" text="Unique visits sessions"
|
||||||
:avg="formatNumberK(avgSessionsDay) + '/day'" :trend="sessionsData.trend" :data="sessionsData.data"
|
:value="formatNumberK(sessionsData.data.value?.data.reduce((a, e) => a + e, 0) || '...')"
|
||||||
:labels="sessionsData.labels" color="#4abde8">
|
:avg="formatNumberK(avgSessionsDay) + '/day'" :trend="sessionsData.data.value?.trend"
|
||||||
|
:data="sessionsData.data.value?.data" :labels="sessionsData.data.value?.labels" color="#4abde8">
|
||||||
</DashboardCountCard>
|
</DashboardCountCard>
|
||||||
|
|
||||||
<DashboardCountCard :ready="sessionsDurationData.ready" icon="far fa-timer" text="Avg session time"
|
|
||||||
:value="avgSessionDuration" :trend="sessionsDurationData.trend" :data="sessionsDurationData.data"
|
<DashboardCountCard :ready="!sessionsDurationData.pending.value" icon="far fa-timer" text="Avg session time"
|
||||||
:labels="sessionsDurationData.labels" color="#f56523">
|
:value="avgSessionDuration" :trend="sessionsDurationData.data.value?.trend"
|
||||||
|
:data="sessionsDurationData.data.value?.data" :labels="sessionsDurationData.data.value?.labels"
|
||||||
|
color="#f56523">
|
||||||
</DashboardCountCard>
|
</DashboardCountCard>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -77,14 +77,17 @@ const limitAlertActions: any[] = [
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
</script>
|
const { snapshot } = useSnapshot();
|
||||||
|
const topCardsKey = computed(() => `${snapshot.value._id.toString()}`);
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
||||||
<div class="dashboard w-full h-full overflow-y-auto pb-20 md:pt-4 lg:pt-0">
|
<div class="dashboard w-full h-full overflow-y-auto pb-20 md:pt-4 lg:pt-0">
|
||||||
|
|
||||||
<div :key="'home-' + isLiveDemo()" v-if="projects && activeProject && firstInteraction && !changingProject">
|
<div :key="'home-' + isLiveDemo()" v-if="projects && activeProject && firstInteraction">
|
||||||
|
|
||||||
<div class="w-full px-4 py-2">
|
<div class="w-full px-4 py-2">
|
||||||
<!-- <div v-if="limitsInfo && !limitsInfo.limited"
|
<!-- <div v-if="limitsInfo && !limitsInfo.limited"
|
||||||
@@ -113,10 +116,10 @@ const limitAlertActions: any[] = [
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<DashboardTopSection></DashboardTopSection>
|
<!-- <DashboardTopSection></DashboardTopSection> -->
|
||||||
<DashboardTopCards></DashboardTopCards>
|
<DashboardTopCards :key="topCardsKey"></DashboardTopCards>
|
||||||
|
|
||||||
|
|
||||||
|
<!--
|
||||||
<div class="mt-6 px-6 flex gap-6 flex-col 2xl:flex-row">
|
<div class="mt-6 px-6 flex gap-6 flex-col 2xl:flex-row">
|
||||||
|
|
||||||
<CardTitled class="p-4 flex-1 w-full" title="Visits trends" sub="Shows trends in page visits.">
|
<CardTitled class="p-4 flex-1 w-full" title="Visits trends" sub="Shows trends in page visits.">
|
||||||
@@ -186,7 +189,7 @@ const limitAlertActions: any[] = [
|
|||||||
<div class="flex-1">
|
<div class="flex-1">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div> -->
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user