add referrers bar chart

This commit is contained in:
Emily
2024-06-10 17:04:12 +02:00
parent 0c3a25b7e0
commit 7cc7a3ab8d
13 changed files with 247 additions and 18 deletions

View File

@@ -0,0 +1,109 @@
<script setup lang="ts">
import type { ChartData, ChartOptions } from 'chart.js';
import { useBarChart, BarChart } from 'vue-chart-3';
registerChartComponents();
const props = defineProps<{
data: any[],
labels: string[]
color: string,
}>();
const chartOptions = ref<ChartOptions<'bar'>>({
responsive: true,
maintainAspectRatio: false,
interaction: {
intersect: false,
mode: 'nearest',
axis: 'x',
includeInvisible: true
},
scales: {
y: {
ticks: { display: true },
grid: {
display: false,
drawBorder: false,
color: '#CCCCCC22',
},
},
x: {
ticks: { display: true },
grid: {
display: false,
drawBorder: false,
color: '#CCCCCC22',
}
}
},
plugins: {
legend: {
display: false,
position: 'right',
},
title: { display: false },
tooltip: {
enabled: true,
backgroundColor: 'rgba(0, 0, 0, 0.8)',
titleFont: { size: 16, weight: 'bold' },
bodyFont: { size: 14 },
padding: 10,
cornerRadius: 4,
boxPadding: 10,
caretPadding: 20,
yAlign: 'bottom',
xAlign: 'center',
}
},
});
const chartData = ref<ChartData<'bar'>>({
labels: props.labels,
datasets: [
{
data: props.data,
backgroundColor: [props.color + '77'],
borderColor: props.color,
borderWidth: 4,
hoverBackgroundColor: props.color,
hoverBorderColor: 'white',
hoverBorderWidth: 2,
},
],
});
const { barChartProps, barChartRef } = useBarChart({ chartData: chartData, options: chartOptions });
onMounted(async () => {
// const c = document.createElement('canvas');
// const ctx = c.getContext("2d");
// let gradient: any = `${props.color}22`;
// if (ctx) {
// gradient = ctx.createLinearGradient(0, 25, 0, 300);
// gradient.addColorStop(0, `${props.color}99`);
// gradient.addColorStop(0.35, `${props.color}66`);
// gradient.addColorStop(1, `${props.color}22`);
// } else {
// console.warn('Cannot get context for gradient');
// }
// chartData.value.datasets[0].backgroundColor = [gradient];
watch(props, () => {
console.log('UPDATE')
chartData.value.labels = props.labels;
chartData.value.datasets[0].data = props.data;
});
});
</script>
<template>
<BarChart v-bind="barChartProps"> </BarChart>
</template>

View File

@@ -36,7 +36,7 @@ const { isAdmin } = useUserRoles();
:class="{ '!w-[18rem] shadow-[0_0_20px_#000000] rounded-r-2xl': isOpen }">
<div :class="{ 'w-[18rem]': isOpen }">
<div class="flex gap-4 items-center py-6 px-[.9rem] pb-8">
<div class="bg-accent h-[2.8rem] aspect-[1/1] flex items-center justify-center rounded-lg">
<div class="bg-black h-[2.8rem] aspect-[1/1] flex items-center justify-center rounded-lg">
<img class="h-[2.4rem]" :src="'/logo.png'">
</div>
<div v-if="isOpen" class="font-bold text-[1.4rem] text-gray-300"> Litlyx </div>

View File

@@ -43,11 +43,11 @@ function showDetails(id: string) {
<template>
<div class="flex">
<div class="flex h-full">
<div class="text-text flex flex-col items-start gap-4 w-full relative">
<div class="w-full p-4 flex flex-col bg-menu rounded-xl gap-8 card-shadow">
<div class="w-full h-full p-4 flex flex-col bg-menu rounded-xl gap-8 card-shadow">
<div class="flex justify-between mb-3">
<div class="flex flex-col gap-1">
@@ -67,8 +67,9 @@ function showDetails(id: string) {
</div>
<div v-if="rawButton" class="hidden lg:flex">
<div @click="$emit('showRawData')"
class="cursor-pointer hover:bg-accent/60 flex items-center justify-center poppins bg-accent rounded-lg py-2 px-8">
Raw data
class="cursor-pointer flex gap-1 items-center justify-center font-semibold poppins rounded-lg text-[#5680f8] hover:text-[#5681f8ce]">
<div> Raw data </div>
<div class="flex items-center"> <i class="fas fa-arrow-up-right"></i> </div>
</div>
</div>
@@ -87,7 +88,8 @@ function showDetails(id: string) {
</div>
<div class="flex flex-col gap-1">
<div v-if="props.data.length > 0" class="flex justify-between items-center" v-for="element of props.data">
<div v-if="props.data.length > 0" class="flex justify-between items-center"
v-for="element of props.data">
<div class="w-10/12 relative" @click="showDetails(element._id)"
:class="{ 'cursor-pointer line-active': interactive }">
<div class="absolute rounded-sm w-full h-full bg-[#92abcf38]"
@@ -99,13 +101,13 @@ function showDetails(id: string) {
:src="iconProvider(element._id)?.[1]">
<i v-else :class="iconProvider(element._id)?.[1]"></i>
</div>
<span
class="text-ellipsis line-clamp-1 ui-font z-[20] text-[.95rem] text-text/70">
<span class="text-ellipsis line-clamp-1 ui-font z-[20] text-[.95rem] text-text/70">
{{ elementTextTransformer?.(element._id) || element._id }}
</span>
</div>
</div>
<div class="text-text font-semibold text-[.9rem] md:text-[1rem] manrope"> {{ formatNumberK(element.count) }} </div>
<div class="text-text font-semibold text-[.9rem] md:text-[1rem] manrope"> {{
formatNumberK(element.count) }} </div>
</div>
<div v-if="props.data.length == 0"
class="flex justify-center text-text-sub font-bold text-[1.1rem]">

View File

@@ -34,7 +34,7 @@ function showMore() {
<template>
<div class="flex flex-col gap-2">
<div class="flex flex-col gap-2 h-full">
<DashboardBarsCard @showMore="showMore()" @showRawData="goToView()" desc="Most frequent user events triggered in this project" @dataReload="refresh" :data="events || []" :loading="pending" label="Top Events"
sub-label="Events" :rawButton="!isLiveDemo()"></DashboardBarsCard>
</div>

View File

@@ -2,6 +2,7 @@
import type { ReferrersAggregated } from '~/server/api/metrics/[project_id]/data/referrers';
import type { IconProvider } from './BarsCard.vue';
import ReferrerBarChart from '../referrer/ReferrerBarChart.vue';
const activeProject = await useActiveProject();
const { data: events, pending, refresh } = await useFetch<ReferrersAggregated[]>(`/api/metrics/${activeProject.value?._id}/data/referrers`, signHeaders());
@@ -20,6 +21,16 @@ function elementTextTransformer(element: string) {
const { showDialog, dialogBarData, isDataLoading } = useBarCardDialog();
const customDialog = useCustomDialog();
function onShowDetails(referrer: string) {
customDialog.openDialog(ReferrerBarChart, { slice: 'day', referrer });
}
function showMore() {
@@ -43,9 +54,9 @@ function showMore() {
<template>
<div class="flex flex-col gap-2">
<DashboardBarsCard @showMore="showMore()" :elementTextTransformer="elementTextTransformer"
:iconProvider="iconProvider" @dataReload="refresh" :data="events || []"
desc="Where users find your website." :dataIcons="true" :loading="pending" label="Top Referrers"
sub-label="Referrers"></DashboardBarsCard>
<DashboardBarsCard @showDetails="onShowDetails" @showMore="showMore()"
:elementTextTransformer="elementTextTransformer" :iconProvider="iconProvider" @dataReload="refresh"
:data="events || []" :interactive="true" desc="Where users find your website." :dataIcons="true"
:loading="pending" label="Top Referrers" sub-label="Referrers"></DashboardBarsCard>
</div>
</template>

View File

@@ -49,7 +49,7 @@ async function dataReload() {
<template>
<div class="flex flex-col gap-2">
<div class="flex flex-col gap-2 h-full">
<DashboardBarsCard :hideShowMore="true" @showGeneral="setDefaultData()" @showRawData="goToView()"
@dataReload="dataReload()" @showDetails="showDetails" :data="currentViewData || []"
:loading="pending || isLoading" :label="isPagesView ? 'Top pages' : 'Top Websites'"

View File

@@ -0,0 +1,41 @@
<script lang="ts" setup>
import { onMounted } from 'vue';
const data = ref<number[]>([]);
const labels = ref<string[]>([]);
const ready = ref<boolean>(false);
const props = defineProps<{ slice: SliceName, referrer: string }>();
const activeProject = useActiveProject();
async function loadData() {
const response = await $fetch(`/api/metrics/${activeProject.value?._id.toString()}/timeline/referrers`, {
method: 'POST',
...signHeaders({ 'Content-Type': 'application/json' }),
body: JSON.stringify({ slice: 'day', referrer: props.referrer })
});
if (!response) return;
const fixed = fixMetrics(response, props.slice);
console.log(fixed);
data.value = fixed.data;
labels.value = fixed.labels;
ready.value = true;
}
onMounted(async () => {
await loadData();
watch(props, async () => { await loadData(); });
})
</script>
<template>
<div>
<AdvancedBarChart v-if="ready" :data="data" :labels="labels" color="#5680f8">
</AdvancedBarChart>
</div>
</template>