mirror of
https://github.com/Litlyx/litlyx
synced 2025-12-10 15:58:38 +01:00
add domain filter on events
This commit is contained in:
@@ -16,7 +16,7 @@ function onChange(e: string) {
|
|||||||
base: 'z-[999] hover:!bg-lyx-lightmode-widget-light dark:hover:!bg-lyx-widget-lighter cursor-pointer',
|
base: 'z-[999] hover:!bg-lyx-lightmode-widget-light dark:hover:!bg-lyx-widget-lighter cursor-pointer',
|
||||||
active: '!bg-lyx-lightmode-widget-light dark:!bg-lyx-widget-lighter'
|
active: '!bg-lyx-lightmode-widget-light dark:!bg-lyx-widget-lighter'
|
||||||
}
|
}
|
||||||
}" class="w-full" v-if="domainList" @change="onChange" :value="domain" :options="domainList">
|
}" class="w-full" searchable v-if="domainList" @change="onChange" :value="domain" :options="domainList">
|
||||||
|
|
||||||
<template #option="{ option, active, selected }">
|
<template #option="{ option, active, selected }">
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ export function getDefaultSnapshots(project_id: TProjectSnapshot['project_id'],
|
|||||||
project_id,
|
project_id,
|
||||||
_id: '___allTime' as any,
|
_id: '___allTime' as any,
|
||||||
name: 'All Time',
|
name: 'All Time',
|
||||||
from: fns.addMinutes(fns.startOfMonth(new Date(project_created_at.toString())), -new Date().getTimezoneOffset()),
|
from: fns.addMinutes(fns.startOfMonth(new Date(project_created_at.toString())), 0),
|
||||||
to: new Date(Date.now()),
|
to: new Date(Date.now()),
|
||||||
color: '#9362FF',
|
color: '#9362FF',
|
||||||
default: true
|
default: true
|
||||||
|
|||||||
@@ -1,14 +1,25 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import EventsFunnelChart from '~/components/events/EventsFunnelChart.vue';
|
import EventsFunnelChart from '~/components/events/EventsFunnelChart.vue';
|
||||||
|
import DateService, { type Slice } from '@services/DateService';
|
||||||
|
|
||||||
definePageMeta({ layout: 'dashboard' });
|
definePageMeta({ layout: 'dashboard' });
|
||||||
|
|
||||||
const selectLabelsEvents = [
|
|
||||||
|
const { snapshotDuration } = useSnapshot();
|
||||||
|
|
||||||
|
const selectedLabelIndex = ref<number>(0);
|
||||||
|
|
||||||
|
const selectLabels: { label: string, value: Slice }[] = [
|
||||||
|
{ label: 'Hour', value: 'hour' },
|
||||||
{ label: 'Day', value: 'day' },
|
{ label: 'Day', value: 'day' },
|
||||||
{ label: 'Month', value: 'month' },
|
{ label: 'Month', value: 'month' },
|
||||||
];
|
];
|
||||||
const eventsStackedSelectIndex = ref<number>(0);
|
|
||||||
|
const selectLabelsAvailable = computed<{ label: string, value: Slice, disabled: boolean }[]>(() => {
|
||||||
|
return selectLabels.map(e => {
|
||||||
|
return { ...e, disabled: !DateService.canUseSliceFromDays(snapshotDuration.value, e.value)[0] }
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
const eventsData = await useFetch(`/api/data/count`, { headers: useComputedHeaders({ custom: { 'x-schema': 'events' } }), lazy: true });
|
const eventsData = await useFetch(`/api/data/count`, { headers: useComputedHeaders({ custom: { 'x-schema': 'events' } }), lazy: true });
|
||||||
|
|
||||||
@@ -24,9 +35,6 @@ const eventsData = await useFetch(`/api/data/count`, { headers: useComputedHeade
|
|||||||
<div>
|
<div>
|
||||||
Total events: {{ eventsData.data.value?.[0]?.count || '0' }}
|
Total events: {{ eventsData.data.value?.[0]?.count || '0' }}
|
||||||
</div>
|
</div>
|
||||||
<div v-if="(eventsData.data.value?.[0]?.count || 0) === 0">
|
|
||||||
Waiting for your first event...
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<LyxUiButton type="secondary" target="_blank" to="https://docs.litlyx.com/custom-events">
|
<LyxUiButton type="secondary" target="_blank" to="https://docs.litlyx.com/custom-events">
|
||||||
@@ -45,12 +53,14 @@ const eventsData = await useFetch(`/api/data/count`, { headers: useComputedHeade
|
|||||||
<CardTitled :key="refreshKey" class="p-4 xl:flex-[4] w-full h-full" title="Events"
|
<CardTitled :key="refreshKey" class="p-4 xl:flex-[4] w-full h-full" title="Events"
|
||||||
sub="Events stacked bar chart.">
|
sub="Events stacked bar chart.">
|
||||||
<template #header>
|
<template #header>
|
||||||
<SelectButton @changeIndex="eventsStackedSelectIndex = $event"
|
|
||||||
:currentIndex="eventsStackedSelectIndex" :options="selectLabelsEvents">
|
<SelectButton class="w-fit" @changeIndex="selectedLabelIndex = $event" :currentIndex="selectedLabelIndex"
|
||||||
</SelectButton>
|
:options="selectLabelsAvailable">
|
||||||
|
</SelectButton>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
<div class="h-full">
|
<div class="h-full">
|
||||||
<EventsStackedBarChart :slice="(selectLabelsEvents[eventsStackedSelectIndex].value as any)">
|
<EventsStackedBarChart :slice="(selectLabelsAvailable[selectedLabelIndex].value as any)">
|
||||||
</EventsStackedBarChart>
|
</EventsStackedBarChart>
|
||||||
</div>
|
</div>
|
||||||
</CardTitled>
|
</CardTitled>
|
||||||
|
|||||||
@@ -1,17 +1,16 @@
|
|||||||
|
|
||||||
import { Redis } from "~/server/services/CacheService";
|
import { Redis } from "~/server/services/CacheService";
|
||||||
import { getRequestDataOld } from "~/server/utils/getRequestData";
|
|
||||||
|
|
||||||
export default defineEventHandler(async event => {
|
export default defineEventHandler(async event => {
|
||||||
|
|
||||||
|
|
||||||
const data = await getRequestDataOld(event, { requireSchema: true });
|
const data = await getRequestData(event, ['GUEST', 'DOMAIN', 'RANGE', 'SCHEMA']);
|
||||||
if (!data) return;
|
if (!data) return;
|
||||||
|
|
||||||
const { schemaName, pid, from, to, model, project_id } = data;
|
const { schemaName, pid, from, to, model, project_id, domain } = data;
|
||||||
|
|
||||||
const cacheKey = `count:${schemaName}:${pid}:${from}:${to}`;
|
const cacheKey = `count:${schemaName}:${pid}:${from}:${to}:${domain}`;
|
||||||
const cacheExp = 60;
|
const cacheExp = 20;
|
||||||
|
|
||||||
return await Redis.useCacheV2(cacheKey, cacheExp, async () => {
|
return await Redis.useCacheV2(cacheKey, cacheExp, async () => {
|
||||||
|
|
||||||
@@ -19,7 +18,8 @@ export default defineEventHandler(async event => {
|
|||||||
{
|
{
|
||||||
$match: {
|
$match: {
|
||||||
project_id,
|
project_id,
|
||||||
created_at: { $gte: new Date(from), $lte: new Date(to) }
|
created_at: { $gte: new Date(from), $lte: new Date(to) },
|
||||||
|
website: domain
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ $count: 'count' },
|
{ $count: 'count' },
|
||||||
|
|||||||
@@ -1,17 +1,16 @@
|
|||||||
|
|
||||||
import { EventModel } from "@schema/metrics/EventSchema";
|
import { EventModel } from "@schema/metrics/EventSchema";
|
||||||
import { Redis } from "~/server/services/CacheService";
|
import { Redis } from "~/server/services/CacheService";
|
||||||
import { getRequestDataOld } from "~/server/utils/getRequestData";
|
|
||||||
|
|
||||||
export default defineEventHandler(async event => {
|
export default defineEventHandler(async event => {
|
||||||
|
|
||||||
const data = await getRequestDataOld(event, { requireSchema: false });
|
const data = await getRequestData(event, ['GUEST', 'DOMAIN', 'RANGE']);
|
||||||
if (!data) return;
|
if (!data) return;
|
||||||
|
|
||||||
const { pid, from, to, project_id, limit } = data;
|
const { pid, from, to, project_id, limit, domain } = data;
|
||||||
|
|
||||||
const cacheKey = `events:${pid}:${limit}:${from}:${to}`;
|
const cacheKey = `events:${pid}:${limit}:${from}:${to}:${domain}`;
|
||||||
const cacheExp = 60;
|
const cacheExp = 20;
|
||||||
|
|
||||||
return await Redis.useCacheV2(cacheKey, cacheExp, async () => {
|
return await Redis.useCacheV2(cacheKey, cacheExp, async () => {
|
||||||
|
|
||||||
@@ -19,7 +18,8 @@ export default defineEventHandler(async event => {
|
|||||||
{
|
{
|
||||||
$match: {
|
$match: {
|
||||||
project_id,
|
project_id,
|
||||||
created_at: { $gte: new Date(from), $lte: new Date(to) }
|
created_at: { $gte: new Date(from), $lte: new Date(to) },
|
||||||
|
website: domain
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ $group: { _id: "$name", count: { $sum: 1, } } },
|
{ $group: { _id: "$name", count: { $sum: 1, } } },
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export default defineEventHandler(async event => {
|
|||||||
const timelineData = await executeTimelineAggregation({
|
const timelineData = await executeTimelineAggregation({
|
||||||
projectId: project_id,
|
projectId: project_id,
|
||||||
model: EventModel,
|
model: EventModel,
|
||||||
from, to, slice, timeOffset, domain, debug: true
|
from, to, slice, timeOffset, domain
|
||||||
});
|
});
|
||||||
return timelineData;
|
return timelineData;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,12 +4,12 @@ import { executeAdvancedTimelineAggregation } from "~/server/services/TimelineSe
|
|||||||
|
|
||||||
export default defineEventHandler(async event => {
|
export default defineEventHandler(async event => {
|
||||||
|
|
||||||
const data = await getRequestDataOld(event, { requireSchema: false, requireSlice: true });
|
const data = await getRequestData(event, ['GUEST', 'RANGE', 'SLICE', 'DOMAIN']);
|
||||||
if (!data) return;
|
if (!data) return;
|
||||||
|
|
||||||
const { from, to, slice, project_id, timeOffset } = data;
|
const { from, to, slice, project_id, timeOffset, domain } = data;
|
||||||
|
|
||||||
return await Redis.useCache({ key: `timeline:events_stacked:${project_id}:${slice}:${from || 'none'}:${to || 'none'}`, exp: TIMELINE_EXPIRE_TIME }, async () => {
|
return await Redis.useCache({ key: `timeline:events_stacked:${project_id}:${slice}:${from || 'none'}:${to || 'none'}:${domain}`, exp: TIMELINE_EXPIRE_TIME }, async () => {
|
||||||
|
|
||||||
const timelineStackedEvents = await executeAdvancedTimelineAggregation<{ name: String }>({
|
const timelineStackedEvents = await executeAdvancedTimelineAggregation<{ name: String }>({
|
||||||
model: EventModel,
|
model: EventModel,
|
||||||
@@ -17,7 +17,8 @@ export default defineEventHandler(async event => {
|
|||||||
from, to, slice,
|
from, to, slice,
|
||||||
customProjection: { name: "$_id.name" },
|
customProjection: { name: "$_id.name" },
|
||||||
customIdGroup: { name: '$name' },
|
customIdGroup: { name: '$name' },
|
||||||
timeOffset
|
timeOffset,
|
||||||
|
domain
|
||||||
})
|
})
|
||||||
|
|
||||||
return timelineStackedEvents;
|
return timelineStackedEvents;
|
||||||
|
|||||||
@@ -10,13 +10,14 @@ export default defineEventHandler(async event => {
|
|||||||
const { pid, from, to, slice, project_id, timeOffset, domain } = data;
|
const { pid, from, to, slice, project_id, timeOffset, domain } = data;
|
||||||
|
|
||||||
const cacheKey = `timeline:visits:${pid}:${slice}:${from}:${to}:${domain}`;
|
const cacheKey = `timeline:visits:${pid}:${slice}:${from}:${to}:${domain}`;
|
||||||
const cacheExp = 60;
|
const cacheExp = 20;
|
||||||
|
|
||||||
return await Redis.useCacheV2(cacheKey, cacheExp, async () => {
|
return await Redis.useCacheV2(cacheKey, cacheExp, async () => {
|
||||||
const timelineData = await executeAdvancedTimelineAggregation({
|
const timelineData = await executeAdvancedTimelineAggregation({
|
||||||
projectId: project_id,
|
projectId: project_id,
|
||||||
model: VisitModel,
|
model: VisitModel,
|
||||||
from, to, slice, timeOffset, domain
|
from, to, slice, timeOffset, domain,
|
||||||
|
debug: true
|
||||||
});
|
});
|
||||||
return timelineData;
|
return timelineData;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ export type TEvent = {
|
|||||||
metadata: Record<string, string>,
|
metadata: Record<string, string>,
|
||||||
session: string,
|
session: string,
|
||||||
flowHash: string,
|
flowHash: string,
|
||||||
|
website: string,
|
||||||
created_at: Date
|
created_at: Date
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -15,6 +16,7 @@ const EventSchema = new Schema<TEvent>({
|
|||||||
metadata: Schema.Types.Mixed,
|
metadata: Schema.Types.Mixed,
|
||||||
session: { type: String, index: 1 },
|
session: { type: String, index: 1 },
|
||||||
flowHash: { type: String },
|
flowHash: { type: String },
|
||||||
|
website: { type: String, index: 1 },
|
||||||
created_at: { type: Date, default: () => Date.now(), index: true },
|
created_at: { type: Date, default: () => Date.now(), index: true },
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ export type TSession = {
|
|||||||
session: string,
|
session: string,
|
||||||
flowHash: string,
|
flowHash: string,
|
||||||
duration: number,
|
duration: number,
|
||||||
|
website: string,
|
||||||
updated_at: Date,
|
updated_at: Date,
|
||||||
created_at: Date,
|
created_at: Date,
|
||||||
}
|
}
|
||||||
@@ -14,6 +15,7 @@ const SessionSchema = new Schema<TSession>({
|
|||||||
project_id: { type: Types.ObjectId, index: 1 },
|
project_id: { type: Types.ObjectId, index: 1 },
|
||||||
session: { type: String, required: true, index: 1 },
|
session: { type: String, required: true, index: 1 },
|
||||||
flowHash: { type: String },
|
flowHash: { type: String },
|
||||||
|
website: { type: String },
|
||||||
duration: { type: Number, required: true, default: 0 },
|
duration: { type: Number, required: true, default: 0 },
|
||||||
updated_at: { type: Date, default: () => Date.now() },
|
updated_at: { type: Date, default: () => Date.now() },
|
||||||
created_at: { type: Date, default: () => Date.now(), index: true },
|
created_at: { type: Date, default: () => Date.now(), index: true },
|
||||||
|
|||||||
Reference in New Issue
Block a user