mirror of
https://github.com/Litlyx/litlyx
synced 2025-12-10 07:48:37 +01:00
implementing snapshots
This commit is contained in:
@@ -29,6 +29,13 @@ const debugMode = process.dev;
|
||||
|
||||
const { isOpen, close } = useMenu();
|
||||
|
||||
const { snapshots, snapshot } = useSnapshot();
|
||||
|
||||
const snapshotsItems = computed(() => {
|
||||
if (!snapshots.data.value) return []
|
||||
return snapshots.data.value as any[];
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -49,7 +56,32 @@ const { isOpen, close } = useMenu();
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div class="px-4 w-full flex-col">
|
||||
|
||||
<USelectMenu class="w-full" v-model="snapshot" :options="snapshotsItems">
|
||||
<template #label>
|
||||
<div class="flex items-center gap-2">
|
||||
<div :style="'background-color:' + snapshot?.color" class="w-2 h-2 rounded-full">
|
||||
</div>
|
||||
<div> {{ snapshot?.name }} </div>
|
||||
</div>
|
||||
</template>
|
||||
<template #option="{ option }">
|
||||
<div class="flex items-center gap-2">
|
||||
<div :style="'background-color:' + option.color" class="w-2 h-2 rounded-full">
|
||||
</div>
|
||||
<div> {{ option.name }} </div>
|
||||
</div>
|
||||
</template>
|
||||
</USelectMenu>
|
||||
|
||||
<div v-if="snapshot">
|
||||
<div> {{ new Date(snapshot.from).toLocaleString('it-IT') }} </div>
|
||||
<div> {{ new Date(snapshot.to).toLocaleString('it-IT') }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-4">
|
||||
|
||||
<div v-for="section of sections" class="flex flex-col gap-1">
|
||||
|
||||
@@ -17,7 +17,7 @@ const props = defineProps<{
|
||||
<template>
|
||||
|
||||
<Card class="flex flex-col overflow-hidden relative max-h-[12rem] aspect-[2/1] w-full">
|
||||
<div class="flex p-4 items-start">
|
||||
<div v-if="ready" class="flex p-4 items-start">
|
||||
<div class="flex items-center mt-2 mr-4">
|
||||
<i :style="`color: ${props.color}`" :class="icon" class="text-[1.6rem] 2xl:text-[2rem]"></i>
|
||||
</div>
|
||||
@@ -40,7 +40,7 @@ const props = defineProps<{
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="absolute bottom-0 left-0 w-full h-[50%] flex items-end" v-if="(props.data?.length || 0) > 0">
|
||||
<div class="absolute bottom-0 left-0 w-full h-[50%] flex items-end" v-if="((props.data?.length || 0) > 0) && ready">
|
||||
<DashboardEmbedChartCard v-if="ready" :data="props.data || []" :labels="props.labels || []"
|
||||
:color="props.color">
|
||||
</DashboardEmbedChartCard>
|
||||
|
||||
@@ -9,8 +9,22 @@ const ready = ref<boolean>(false);
|
||||
|
||||
const props = defineProps<{ slice: Slice }>();
|
||||
|
||||
const { snapshot } = useSnapshot();
|
||||
|
||||
const snapshotFrom = computed(() => {
|
||||
return new Date(snapshot.value?.from || '0').toISOString();
|
||||
});
|
||||
|
||||
const snapshotTo = computed(() => {
|
||||
return new Date(snapshot.value?.to || Date.now()).toISOString();
|
||||
});
|
||||
|
||||
async function loadData() {
|
||||
const response = await useTimeline('sessions', props.slice);
|
||||
ready.value = false;
|
||||
const response = await useTimeline('sessions', props.slice,
|
||||
snapshotFrom.value.toString(),
|
||||
snapshotTo.value.toString()
|
||||
);
|
||||
if (!response) return;
|
||||
data.value = response.map(e => e.count);
|
||||
labels.value = response.map(e => DateService.getChartLabelFromISO(e._id, navigator.language, props.slice));
|
||||
@@ -20,6 +34,7 @@ async function loadData() {
|
||||
onMounted(async () => {
|
||||
await loadData();
|
||||
watch(props, async () => { await loadData(); });
|
||||
watch(snapshot, async () => { await loadData(); });
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
@@ -4,24 +4,50 @@ import DateService from '@services/DateService';
|
||||
|
||||
const { data: metricsInfo } = useMetricsData();
|
||||
|
||||
|
||||
|
||||
|
||||
type Data = {
|
||||
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(() => {
|
||||
if (!metricsInfo.value) return '0.00';
|
||||
const days = (Date.now() - (metricsInfo.value?.firstViewDate || 0)) / 1000 / 60 / 60 / 24;
|
||||
const avg = metricsInfo.value.visitsCount / Math.max(days, 1);
|
||||
const days = (snapshotTo.value - snapshotFrom.value) / 1000 / 60 / 60 / 24;
|
||||
const counts = visitsData.data.reduce((a, e) => e + a, 0);
|
||||
const avg = counts / Math.max(days, 1);
|
||||
return avg.toFixed(2);
|
||||
});
|
||||
|
||||
const avgEventsDay = computed(() => {
|
||||
if (!metricsInfo.value) return '0.00';
|
||||
const days = (Date.now() - (metricsInfo.value?.firstEventDate || 0)) / 1000 / 60 / 60 / 24;
|
||||
const avg = metricsInfo.value.eventsCount / Math.max(days, 1);
|
||||
const days = (snapshotTo.value - snapshotFrom.value) / 1000 / 60 / 60 / 24;
|
||||
const counts = eventsData.data.reduce((a, e) => e + a, 0);
|
||||
const avg = counts / Math.max(days, 1);
|
||||
return avg.toFixed(2);
|
||||
});
|
||||
|
||||
const avgSessionsDay = computed(() => {
|
||||
if (!metricsInfo.value) return '0.00';
|
||||
const days = (Date.now() - (metricsInfo.value?.firstViewDate || 0)) / 1000 / 60 / 60 / 24;
|
||||
const avg = metricsInfo.value.sessionsVisitsCount / Math.max(days, 1);
|
||||
const days = (snapshotTo.value - snapshotFrom.value) / 1000 / 60 / 60 / 24;
|
||||
const counts = sessionsData.data.reduce((a, e) => e + a, 0);
|
||||
const avg = counts / Math.max(days, 1);
|
||||
return avg.toFixed(2);
|
||||
});
|
||||
|
||||
@@ -49,23 +75,19 @@ const avgSessionDuration = computed(() => {
|
||||
return `${hours > 0 ? hours + 'h ' : ''}${minutes}m ${seconds.toFixed()}s`
|
||||
});
|
||||
|
||||
type Data = {
|
||||
data: number[],
|
||||
labels: string[],
|
||||
trend: number,
|
||||
ready: boolean
|
||||
}
|
||||
|
||||
|
||||
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 });
|
||||
|
||||
async function loadData(timelineEndpointName: string, target: Data) {
|
||||
|
||||
const response = await useTimeline(timelineEndpointName as any, 'day');
|
||||
target.ready = false;
|
||||
|
||||
const response = await useTimeline(timelineEndpointName as any, 'day',
|
||||
snapshot.value?.from.toString() || "0",
|
||||
snapshot.value?.to.toString() || Date.now().toString()
|
||||
);
|
||||
|
||||
console.log(timelineEndpointName,response);
|
||||
|
||||
if (!response) return;
|
||||
|
||||
target.data = response.map(e => e.count);
|
||||
target.labels = response.map(e => DateService.getChartLabelFromISO(e._id, navigator.language, 'day'));
|
||||
|
||||
@@ -80,12 +102,23 @@ async function loadData(timelineEndpointName: string, target: Data) {
|
||||
target.ready = true;
|
||||
}
|
||||
|
||||
|
||||
async function loadAllData() {
|
||||
console.log('LOAD ALL DATA')
|
||||
await Promise.all([
|
||||
loadData('visits', visitsData),
|
||||
loadData('events', eventsData),
|
||||
loadData('sessions', sessionsData),
|
||||
loadData('sessions_duration', sessionsDurationData),
|
||||
])
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
|
||||
await loadData('visits', visitsData);
|
||||
await loadData('events', eventsData);
|
||||
await loadData('sessions', sessionsData);
|
||||
await loadData('sessions_duration', sessionsDurationData);
|
||||
await loadAllData();
|
||||
watch(snapshot, async () => {
|
||||
await loadAllData();
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
@@ -98,17 +131,18 @@ onMounted(async () => {
|
||||
<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"
|
||||
:value="formatNumberK(metricsInfo.visitsCount)" :avg="formatNumberK(avgVisitDay) + '/day'"
|
||||
:trend="visitsData.trend" :data="visitsData.data" :labels="visitsData.labels" color="#5655d7">
|
||||
:value="formatNumberK(visitsData.data.reduce((a, e) => a + e, 0))"
|
||||
:avg="formatNumberK(avgVisitDay) + '/day'" :trend="visitsData.trend" :data="visitsData.data"
|
||||
:labels="visitsData.labels" color="#5655d7">
|
||||
</DashboardCountCard>
|
||||
|
||||
<DashboardCountCard :ready="eventsData.ready" icon="far fa-flag" text="Total custom events"
|
||||
:value="formatNumberK(metricsInfo.eventsCount)" :avg="formatNumberK(avgEventsDay) + '/day'"
|
||||
:value="formatNumberK(eventsData.data.reduce((a, e) => a + e, 0))" :avg="formatNumberK(avgEventsDay) + '/day'"
|
||||
:trend="eventsData.trend" :data="eventsData.data" :labels="eventsData.labels" color="#1e9b86">
|
||||
</DashboardCountCard>
|
||||
|
||||
<DashboardCountCard :ready="sessionsData.ready" icon="far fa-user" text="Unique visits sessions"
|
||||
:value="formatNumberK(metricsInfo.sessionsVisitsCount)" :avg="formatNumberK(avgSessionsDay) + '/day'"
|
||||
:value="formatNumberK(sessionsData.data.reduce((a, e) => a + e, 0))" :avg="formatNumberK(avgSessionsDay) + '/day'"
|
||||
:trend="sessionsData.trend" :data="sessionsData.data" :labels="sessionsData.labels" color="#4abde8">
|
||||
</DashboardCountCard>
|
||||
|
||||
|
||||
@@ -8,8 +8,23 @@ const ready = ref<boolean>(false);
|
||||
|
||||
const props = defineProps<{ slice: Slice }>();
|
||||
|
||||
const { snapshot } = useSnapshot();
|
||||
|
||||
const snapshotFrom = computed(() => {
|
||||
return new Date(snapshot.value?.from || '0').toISOString();
|
||||
});
|
||||
|
||||
const snapshotTo = computed(() => {
|
||||
return new Date(snapshot.value?.to || Date.now()).toISOString();
|
||||
});
|
||||
|
||||
async function loadData() {
|
||||
const response = await useTimeline('visits', props.slice);
|
||||
ready.value = false;
|
||||
const response = await useTimeline('visits', props.slice,
|
||||
snapshotFrom.value.toString(),
|
||||
snapshotTo.value.toString()
|
||||
);
|
||||
|
||||
if (!response) return;
|
||||
data.value = response.map(e => e.count);
|
||||
labels.value = response.map(e => DateService.getChartLabelFromISO(e._id, navigator.language, props.slice));
|
||||
@@ -19,6 +34,7 @@ async function loadData() {
|
||||
onMounted(async () => {
|
||||
await loadData();
|
||||
watch(props, async () => { await loadData(); });
|
||||
watch(snapshot, async () => { await loadData(); });
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
@@ -6,13 +6,24 @@ const { data: websites, pending, refresh } = useWebsitesData();
|
||||
|
||||
const currentViewData = ref<(VisitsWebsiteAggregated[] | null)>(websites.value);
|
||||
|
||||
watch(pending, () => {
|
||||
currentViewData.value = websites.value;
|
||||
})
|
||||
|
||||
const isPagesView = ref<boolean>(false);
|
||||
const isLoading = ref<boolean>(false);
|
||||
|
||||
|
||||
const { snapshot } = useSnapshot()
|
||||
|
||||
watch(pending, () => {
|
||||
isLoading.value = true;
|
||||
currentViewData.value = websites.value;
|
||||
isLoading.value = false;
|
||||
});
|
||||
|
||||
watch(snapshot, () => {
|
||||
refresh();
|
||||
});
|
||||
|
||||
|
||||
async function showDetails(website: string) {
|
||||
if (isPagesView.value == true) return;
|
||||
isLoading.value = true;
|
||||
@@ -21,6 +32,7 @@ async function showDetails(website: string) {
|
||||
const { data: pagesData, pending } = usePagesData(website, 10);
|
||||
|
||||
watch(pending, () => {
|
||||
isLoading.value = true;
|
||||
currentViewData.value = pagesData.value;
|
||||
isLoading.value = false;
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user