From 3ba6cd171b1cf247dea85da3af5e0c92d0d9a029 Mon Sep 17 00:00:00 2001 From: Emily Date: Mon, 30 Sep 2024 17:01:16 +0200 Subject: [PATCH] add bouncing rate + adjustments --- dashboard/components/dashboard/CountCard.vue | 6 +- dashboard/components/dashboard/TopCards.vue | 68 ++++++++++++---- dashboard/package.json | 3 +- dashboard/pages/dashboard/visits.vue | 40 ++++++---- dashboard/pages/events.vue | 20 ++++- dashboard/pages/index.vue | 2 +- dashboard/pnpm-lock.yaml | 77 +++++++++++++----- .../server/api/auth/google_login.post.ts | 10 ++- dashboard/server/api/data/bouncing_rate.ts | 79 +++++++++++++++++++ dashboard/server/api/data/count.ts | 61 ++++++++++++++ dashboard/server/api/project/generate_csv.ts | 62 +++++++++++++++ shared/schema/UserSchema.ts | 20 ++++- 12 files changed, 391 insertions(+), 57 deletions(-) create mode 100644 dashboard/server/api/data/bouncing_rate.ts create mode 100644 dashboard/server/api/data/count.ts diff --git a/dashboard/components/dashboard/CountCard.vue b/dashboard/components/dashboard/CountCard.vue index bc43c4e..82eafa2 100644 --- a/dashboard/components/dashboard/CountCard.vue +++ b/dashboard/components/dashboard/CountCard.vue @@ -9,7 +9,8 @@ const props = defineProps<{ color: string, data?: number[], labels?: string[], - ready?: boolean + ready?: boolean, + slow?: boolean }>(); const { snapshotDuration } = useSnapshot() @@ -59,8 +60,9 @@ const uTooltipText = computed(() => { :color="props.color"> -
+
+
Can be very slow on large snapshots
diff --git a/dashboard/components/dashboard/TopCards.vue b/dashboard/components/dashboard/TopCards.vue index dc696a4..bfbf14e 100644 --- a/dashboard/components/dashboard/TopCards.vue +++ b/dashboard/components/dashboard/TopCards.vue @@ -21,12 +21,12 @@ const avgVisitDay = computed(() => { return avg.toFixed(2); }); -const avgEventsDay = computed(() => { - if (!eventsData.data.value) return '0.00'; - const counts = eventsData.data.value.data.reduce((a, e) => e + a, 0); - const avg = counts / Math.max(snapshotDays.value, 1); - return avg.toFixed(2); -}); +// const avgEventsDay = computed(() => { +// if (!eventsData.data.value) return '0.00'; +// const counts = eventsData.data.value.data.reduce((a, e) => e + a, 0); +// const avg = counts / Math.max(snapshotDays.value, 1); +// return avg.toFixed(2); +// }); const avgSessionsDay = computed(() => { if (!sessionsData.data.value) return '0.00'; @@ -35,6 +35,16 @@ const avgSessionsDay = computed(() => { return avg.toFixed(2); }); +const avgBouncingRate = computed(() => { + if (!bouncingRateData.data.value) return '0.00 %' + + const counts = bouncingRateData.data.value.data + .filter(e => e > 0) + .reduce((a, e) => e + a, 0); + + const avg = counts / Math.max(bouncingRateData.data.value.data.filter(e => e > 0).length, 1); + return avg.toFixed(2) + ' %'; +}) const avgSessionDuration = computed(() => { if (!metricsInfo.value) return '0.00'; @@ -49,6 +59,8 @@ const avgSessionDuration = computed(() => { }); + + const chartSlice = computed(() => { const snapshotSizeMs = new Date(snapshot.value.to).getTime() - new Date(snapshot.value.from).getTime(); if (snapshotSizeMs < 1000 * 60 * 60 * 24 * 6) return 'hour' as Slice; @@ -79,15 +91,25 @@ function getBody() { }); } +const computedHeaders = computed(() => { + return { + ...signHeaders().headers, + 'x-pid': activeProject.value?._id.toString() || '', + 'x-from': safeSnapshotDates.value.from, + 'x-to': safeSnapshotDates.value.to, + 'x-slice': chartSlice.value + } +}) + const visitsData = useFetch(`/api/metrics/${activeProject.value?._id}/timeline/visits`, { method: 'POST', ...signHeaders({ v2: 'true' }), body: getBody(), transform: transformResponse, lazy: true, immediate: false }); -const eventsData = useFetch(`/api/metrics/${activeProject.value?._id}/timeline/events`, { - method: 'POST', ...signHeaders({ v2: 'true' }), body: getBody(), transform: transformResponse, - lazy: true, immediate: false -}); +// const eventsData = useFetch(`/api/metrics/${activeProject.value?._id}/timeline/events`, { +// method: 'POST', ...signHeaders({ v2: 'true' }), body: getBody(), transform: transformResponse, +// lazy: true, immediate: false +// }); const sessionsData = useFetch(`/api/metrics/${activeProject.value?._id}/timeline/sessions`, { method: 'POST', ...signHeaders({ v2: 'true' }), body: getBody(), transform: transformResponse, @@ -99,12 +121,26 @@ const sessionsDurationData = useFetch(`/api/metrics/${activeProject.value?._id}/ lazy: true, immediate: false }); +const bouncingRateData = useFetch(`/api/data/bouncing_rate`, { + headers: computedHeaders, lazy: true, immediate: false, + transform: (input: { data: string, value: number | null }[]) => { + const data = input.map(e => e.value || 0); + const labels = input.map(e => DateService.getChartLabelFromISO(e.data, navigator.language, chartSlice.value)); + const pool = [...input.map(e => e.value || 0)]; + pool.pop(); + const avg = pool.reduce((a, e) => a + e, 0) / pool.length; + const diffPercent: number = (100 / avg * (input.at(-1)?.value || 0)) - 100; + const trend = Math.max(Math.min(diffPercent, 99), -99); + return { data, labels, trend } + } +}) + onMounted(async () => { visitsData.execute(); - eventsData.execute(); + bouncingRateData.execute(); sessionsData.execute(); - sessionsDurationData.execute(); + sessionsDurationData.execute() }); @@ -120,10 +156,10 @@ onMounted(async () => { :data="visitsData.data.value?.data" :labels="visitsData.data.value?.labels" color="#5655d7"> - + diff --git a/dashboard/package.json b/dashboard/package.json index f66132a..808cb7c 100644 --- a/dashboard/package.json +++ b/dashboard/package.json @@ -22,7 +22,8 @@ "chartjs-plugin-annotation": "^2.2.1", "date-fns": "^3.6.0", "dayjs": "^1.11.11", - "google-auth-library": "^9.9.0", + "google-auth-library": "^9.10.0", + "googleapis": "^144.0.0", "highlight.js": "^11.10.0", "jsonwebtoken": "^9.0.2", "litlyx-js": "^1.0.2", diff --git a/dashboard/pages/dashboard/visits.vue b/dashboard/pages/dashboard/visits.vue index 36a907d..f766023 100644 --- a/dashboard/pages/dashboard/visits.vue +++ b/dashboard/pages/dashboard/visits.vue @@ -47,18 +47,24 @@ onMounted(async () => { const creatingCsv = ref(false); -async function downloadCSV() { +async function downloadCSV(isGoogle: boolean) { creatingCsv.value = true; - const result = await $fetch(`/api/project/generate_csv?mode=visits&slice=${options.indexOf(selectedTimeFrom.value)}`, signHeaders()); - const blob = new Blob([result], { type: 'text/csv' }); - const url = window.URL.createObjectURL(blob); - const a = document.createElement('a'); - a.href = url; - a.download = 'ReportVisits.csv'; - document.body.appendChild(a); - a.click(); - document.body.removeChild(a); - window.URL.revokeObjectURL(url); + const result = await $fetch(`/api/project/generate_csv?mode=visits&slice=${options.indexOf(selectedTimeFrom.value)}`, + signHeaders({ 'x-google-export': isGoogle ? 'true' : 'false' }) + ); + if (!isGoogle) { + const blob = new Blob([result as any], { type: 'text/csv' }); + const url = window.URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = 'ReportVisits.csv'; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + window.URL.revokeObjectURL(url); + } else { + alert(result); + } creatingCsv.value = false; } @@ -78,7 +84,6 @@ function goToUpgrade() { -