add cap to dates on slices

This commit is contained in:
Emily
2024-09-23 15:01:14 +02:00
parent 3b6a202538
commit 5b7e93bcbb
8 changed files with 96 additions and 72 deletions

View File

@@ -6,7 +6,7 @@ const props = defineProps<{ title: string, sub?: string }>();
<template> <template>
<LyxUiCard> <LyxUiCard>
<div class="flex flex-col gap-4"> <div class="flex flex-col gap-4 h-full">
<div class="flex items-center"> <div class="flex items-center">
<div class="flex flex-col grow"> <div class="flex flex-col grow">
<div class="poppins font-semibold text-[1rem] md:text-[1.3rem] text-text"> <div class="poppins font-semibold text-[1rem] md:text-[1.3rem] text-text">
@@ -18,8 +18,7 @@ const props = defineProps<{ title: string, sub?: string }>();
</div> </div>
<slot name="header"></slot> <slot name="header"></slot>
</div> </div>
<div> <div class="h-full">
<slot></slot> <slot></slot>
</div> </div>
</div> </div>

View File

@@ -5,6 +5,10 @@ import type { ChartData, ChartOptions, TooltipModel } from 'chart.js';
import { useLineChart, LineChart } from 'vue-chart-3'; import { useLineChart, LineChart } from 'vue-chart-3';
registerChartComponents(); registerChartComponents();
const errorData = ref<{ errored: boolean, text: string }>({
errored: false,
text: ''
})
const chartOptions = ref<ChartOptions<'line'>>({ const chartOptions = ref<ChartOptions<'line'>>({
responsive: true, responsive: true,
@@ -130,6 +134,7 @@ function externalTooltipHandler(context: { chart: any, tooltip: TooltipModel<'li
const selectLabels: { label: string, value: Slice }[] = [ const selectLabels: { label: string, value: Slice }[] = [
{ label: 'Hour', value: 'hour' }, { label: 'Hour', value: 'hour' },
{ label: 'Day', value: 'day' }, { label: 'Day', value: 'day' },
{ label: 'Month', value: 'month' },
]; ];
const selectedLabelIndex = ref<number>(1); const selectedLabelIndex = ref<number>(1);
@@ -157,19 +162,35 @@ const body = computed(() => {
}); });
function onResponseError(e: any) {
console.log('ON RESPONSE ERROR')
errorData.value = { errored: true, text: e.response._data.message ?? 'Generic error' }
}
function onResponse(e: any) {
console.log('ON RESPONSE')
if (e.response.status != 500) errorData.value = { errored: false, text: '' }
}
const visitsData = useFetch(`/api/metrics/${activeProject.value?._id}/timeline/visits`, { const visitsData = useFetch(`/api/metrics/${activeProject.value?._id}/timeline/visits`, {
method: 'POST', ...signHeaders({ v2: 'true' }), body, transform: transformResponse, method: 'POST', ...signHeaders({ v2: 'true' }), body, transform: transformResponse,
lazy: true, immediate: false lazy: true, immediate: false,
onResponseError,
onResponse
}); });
const eventsData = useFetch(`/api/metrics/${activeProject.value?._id}/timeline/events`, { const eventsData = useFetch(`/api/metrics/${activeProject.value?._id}/timeline/events`, {
method: 'POST', ...signHeaders({ v2: 'true' }), body, transform: transformResponse, method: 'POST', ...signHeaders({ v2: 'true' }), body, transform: transformResponse,
lazy: true, immediate: false lazy: true, immediate: false,
onResponseError,
onResponse
}); });
const sessionsData = useFetch(`/api/metrics/${activeProject.value?._id}/timeline/sessions`, { const sessionsData = useFetch(`/api/metrics/${activeProject.value?._id}/timeline/sessions`, {
method: 'POST', ...signHeaders({ v2: 'true' }), body, transform: transformResponse, method: 'POST', ...signHeaders({ v2: 'true' }), body, transform: transformResponse,
lazy: true, immediate: false lazy: true, immediate: false,
onResponseError,
onResponse
}); });
@@ -310,9 +331,14 @@ const inLiveDemo = isLiveDemo();
<i class="fas fa-spinner text-[2rem] text-accent animate-[spin_1s_linear_infinite] duration-500"></i> <i class="fas fa-spinner text-[2rem] text-accent animate-[spin_1s_linear_infinite] duration-500"></i>
</div> </div>
<div class="flex flex-col items-end" v-if="readyToDisplay"> <div class="flex flex-col items-end" v-if="readyToDisplay && !errorData.errored">
<LineChart ref="lineChartRef" class="w-full h-full" v-bind="lineChartProps"> </LineChart> <LineChart ref="lineChartRef" class="w-full h-full" v-bind="lineChartProps"> </LineChart>
</div> </div>
<div v-if="errorData.errored" class="flex items-center justify-center py-8">
{{ errorData.text }}
</div>
</CardTitled> </CardTitled>
</template> </template>

View File

@@ -104,7 +104,7 @@ const headers = computed(() => {
'x-to': safeSnapshotDates.value.to, 'x-to': safeSnapshotDates.value.to,
'Authorization': authorizationHeaderComputed.value, 'Authorization': authorizationHeaderComputed.value,
'x-schema': 'events', 'x-schema': 'events',
'x-limit': "10", 'x-limit': "6",
'x-pid': activeProjectId.data.value || '' 'x-pid': activeProjectId.data.value || ''
} }
}); });

View File

@@ -74,8 +74,26 @@ function transformResponse(input: { _id: string, name: string, count: number }[]
} }
} }
const errorData = ref<{ errored: boolean, text: string }>({
errored: false,
text: ''
})
function onResponseError(e: any) {
console.log('ON RESPONSE ERROR')
errorData.value = { errored: true, text: e.response._data.message ?? 'Generic error' }
}
function onResponse(e: any) {
console.log('ON RESPONSE')
if (e.response.status != 500) errorData.value = { errored: false, text: '' }
}
const eventsStackedData = useFetch(`/api/metrics/${activeProject.value?._id}/timeline/events_stacked`, { const eventsStackedData = useFetch(`/api/metrics/${activeProject.value?._id}/timeline/events_stacked`, {
method: 'POST', body, lazy: true, immediate: false, transform: transformResponse, ...signHeaders() method: 'POST', body, lazy: true, immediate: false, transform: transformResponse, ...signHeaders(),
onResponseError,
onResponse
}); });
@@ -86,13 +104,17 @@ onMounted(async () => {
</script> </script>
<template> <template>
<div> <div class="h-full">
<div v-if="eventsStackedData.pending.value" class="flex justify-center py-40"> <div v-if="eventsStackedData.pending.value" class="flex justify-center py-40">
<i class="fas fa-spinner text-[2rem] text-accent animate-[spin_1s_linear_infinite] duration-500"></i> <i class="fas fa-spinner text-[2rem] text-accent animate-[spin_1s_linear_infinite] duration-500"></i>
</div> </div>
<AdvancedStackedBarChart v-if="!eventsStackedData.pending.value" <AdvancedStackedBarChart v-if="!eventsStackedData.pending.value && !errorData.errored"
:datasets="eventsStackedData.data.value?.datasets || []" :datasets="eventsStackedData.data.value?.datasets || []"
:labels="eventsStackedData.data.value?.labels || []"> :labels="eventsStackedData.data.value?.labels || []">
</AdvancedStackedBarChart> </AdvancedStackedBarChart>
<div v-if="errorData.errored" class="flex items-center justify-center py-8 h-full">
{{ errorData.text }}
</div>
</div> </div>
</template> </template>

View File

@@ -20,15 +20,15 @@ const refreshKey = computed(() => `${snapshot.value._id.toString() + activeProje
<template> <template>
<div class="w-full h-full overflow-y-auto pb-20 p-6 gap-6 flex flex-col"> <div class="w-full h-full overflow-y-auto pb-20 p-6 gap-6 flex flex-col">
<div class="flex gap-6 flex-col xl:flex-row"> <div class="flex gap-6 flex-col xl:flex-row h-full">
<CardTitled :key="refreshKey" class="p-4 flex-[4] w-full" title="Events" sub="Events stacked bar chart."> <CardTitled :key="refreshKey" class="p-4 flex-[4] w-full h-full" title="Events" sub="Events stacked bar chart.">
<template #header> <template #header>
<SelectButton @changeIndex="eventsStackedSelectIndex = $event" <SelectButton @changeIndex="eventsStackedSelectIndex = $event"
:currentIndex="eventsStackedSelectIndex" :options="selectLabelsEvents"> :currentIndex="eventsStackedSelectIndex" :options="selectLabelsEvents">
</SelectButton> </SelectButton>
</template> </template>
<div> <div class="h-full">
<EventsStackedBarChart :slice="(selectLabelsEvents[eventsStackedSelectIndex].value as any)"> <EventsStackedBarChart :slice="(selectLabelsEvents[eventsStackedSelectIndex].value as any)">
</EventsStackedBarChart> </EventsStackedBarChart>
</div> </div>

View File

@@ -1,36 +0,0 @@
class PerformanceThing {
public min: number = Infinity;
public max: number = -Infinity;
private things: number[] = [];
private slice: number = 0;
constructor(public id: string, private maxThings: number) { }
start() { this.slice = performance.now(); }
stop() {
const time = performance.now() - this.slice;
if (time > this.max) this.max = time;
if (time < this.min) this.min = time;
this.things.push(time);
if (this.things.length > this.maxThings) {
this.things.shift();
}
return time;
}
avg() {
return this.things.reduce((a, e) => a + e, 0) / this.things.length;
}
print() {
console.log(`${this.id} | Avg: ${this.avg().toFixed(0)} ms | Min: ${this.min.toFixed(0)} ms | Max: ${this.max.toFixed(0)} ms`)
}
get data() { return this.things; }
}
export class PerformanceService {
static create(id: string, maxThings: number = 100) {
const thing = new PerformanceThing(id, maxThings);
return thing;
}
}

View File

@@ -29,6 +29,17 @@ export async function executeAdvancedTimelineAggregation<T = {}>(options: Advanc
const { group, sort, fromParts } = DateService.getQueryDateRange(options.slice); const { group, sort, fromParts } = DateService.getQueryDateRange(options.slice);
if (!sort) throw Error('Slice is probably not correct');
const dateDistDays = (new Date(options.to).getTime() - new Date(options.from).getTime()) / (1000 * 60 * 60 * 24)
// 15 Days
if (options.slice === 'hour' && (dateDistDays > 15)) throw Error('Date gap too big for this slice');
// 1 Year
if (options.slice === 'day' && (dateDistDays > 365)) throw Error('Date gap too big for this slice');
// 3 Years
if (options.slice === 'month' && (dateDistDays > 365 * 3)) throw Error('Date gap too big for this slice');
const aggregation = [ const aggregation = [
{ {
$match: { $match: {

View File

@@ -25,8 +25,10 @@ class DateService {
getChartLabelFromISO(iso: string, locale: string, slice: Slice) { getChartLabelFromISO(iso: string, locale: string, slice: Slice) {
const date = dayjs(iso).locale(locale); const date = dayjs(iso).locale(locale);
if (slice === 'hour') return date.format('HH:mm') if (slice === 'hour') return date.format('HH:mm');
if (slice === 'day') return date.format('DD/MM') if (slice === 'day') return date.format('DD/MM');
if (slice === 'month') return date.format('MM MMMM');
if (slice === 'year') return date.format('YYYY');
return date.format(); return date.format();
} }