implement domain filter

This commit is contained in:
Emily
2025-02-06 15:23:55 +01:00
parent 4e2c8468f8
commit 0292829805
8 changed files with 69 additions and 49 deletions

View File

@@ -80,7 +80,7 @@ function reloadPage() {
<div class="flex items-center justify-center mt-10"> <div class="flex items-center justify-center mt-10">
<div class="flex flex-col-reverse gap-6"> <div class="flex flex-col gap-6">
<div class="flex gap-6 xl:flex-row flex-col"> <div class="flex gap-6 xl:flex-row flex-col">
@@ -135,8 +135,8 @@ function reloadPage() {
</div> </div>
<div> <div>
<div> <div>
<CardTitled class="w-full h-full" title="Documentation" <CardTitled class="w-full h-full" title="Modules"
sub="Learn how to use Litlyx in every tech stack"> sub="Get started with your favorite framework.">
<template #header> <template #header>
<LyxUiButton @click="Lit.event('no_visit_goto_docs')" type="secondary" <LyxUiButton @click="Lit.event('no_visit_goto_docs')" type="secondary"
to="https://docs.litlyx.com"> to="https://docs.litlyx.com">

View File

@@ -253,8 +253,6 @@ watch(readyToDisplay, () => {
}) })
function onDataReady() { function onDataReady() {
if (!visitsData.data.value) return; if (!visitsData.data.value) return;
if (!eventsData.data.value) return; if (!eventsData.data.value) return;

View File

@@ -1,6 +1,6 @@
<script lang="ts" setup> <script lang="ts" setup>
const { domainList, domain, setActiveDomain } = useDomain(); const { domainList, domain, setActiveDomain, refreshDomains, refreshingDomains } = useDomain();
function onChange(e: string) { function onChange(e: string) {
setActiveDomain(e); setActiveDomain(e);
@@ -8,35 +8,41 @@ function onChange(e: string) {
</script> </script>
<template> <template>
<div class="flex gap-2">
<USelectMenu :uiMenu="{
select: 'bg-lyx-lightmode-widget-light !ring-lyx-lightmode-widget dark:!bg-lyx-widget-light !shadow-none focus:!ring-lyx-widget-lighter dark:!ring-lyx-widget-lighter',
base: '!bg-lyx-lightmode-widget dark:!bg-lyx-widget w-max',
option: {
base: 'z-[990] 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'
},
input: 'z-[999] !bg-lyx-lightmode-widget dark:!bg-lyx-widget-light'
}" class="w-full" searchable searchable-placeholder="Search domain..." v-if="domainList" @change="onChange"
:value="domain" value-attribute="_id" :options="domainList">
<USelectMenu :uiMenu="{ <template #option="{ option, active, selected }">
select: 'bg-lyx-lightmode-widget-light !ring-lyx-lightmode-widget dark:!bg-lyx-widget-light !shadow-none focus:!ring-lyx-widget-lighter dark:!ring-lyx-widget-lighter', <div class="flex items-center gap-2">
base: '!bg-lyx-lightmode-widget dark:!bg-lyx-widget w-max', <div>
option: { <img class="h-5 bg-black rounded-full" :src="'/logo_32.png'" alt="Litlyx logo">
base: 'z-[999] hover:!bg-lyx-lightmode-widget-light dark:hover:!bg-lyx-widget-lighter cursor-pointer', </div>
active: '!bg-lyx-lightmode-widget-light dark:!bg-lyx-widget-lighter' <div> {{ option._id }} </div>
}
}" class="w-full" searchable v-if="domainList" @change="onChange" :value="domain" :options="domainList">
<template #option="{ option, active, selected }">
<div class="flex items-center gap-2">
<div>
<img class="h-5 bg-black rounded-full" :src="'/logo_32.png'" alt="Litlyx logo">
</div> </div>
<div> {{ option }} </div> </template>
</div>
</template>
<template #label> <template #label="e">
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<div> <div>
<img class="h-5 bg-black rounded-full" :src="'/logo_32.png'" alt="Litlyx logo"> <img class="h-5 bg-black rounded-full" :src="'/logo_32.png'" alt="Litlyx logo">
</div>
<div>
{{ domain || '-' }}
</div>
</div> </div>
<div> </template>
{{ domain || '-' }} </USelectMenu>
</div> <div @click="refreshDomains" v-if="!refreshingDomains"
</div> class="flex items-center hover:rotate-[60deg] transition-all duration-200 ease-in-out cursor-pointer">
</template> <i class="far fa-refresh"></i>
</USelectMenu> </div>
</div>
</template> </template>

View File

@@ -3,7 +3,7 @@
const { token } = useAccessToken(); const { token } = useAccessToken();
const { projectId } = useProject(); const { projectId } = useProject();
const domainsRequest = useFetch<{ _id: string }[]>('/api/domains/list', { const domainsRequest = useFetch<{ _id: string, visits: number }[]>('/api/domains/list', {
headers: computed(() => { headers: computed(() => {
return { return {
'Authorization': `Bearer ${token.value}`, 'Authorization': `Bearer ${token.value}`,
@@ -12,18 +12,30 @@ const domainsRequest = useFetch<{ _id: string }[]>('/api/domains/list', {
}) })
}); });
function refreshDomains() {
domainsRequest.refresh();
}
const refreshingDomains = computed(() => domainsRequest.pending.value);
const domainList = computed(() => { const domainList = computed(() => {
return domainsRequest.data.value?.map(e => e._id); return [
{
_id: 'ALL DOMAINS', visits: domainsRequest.data.value?.reduce((a, e) => a + e.visits, 0)
},
...(domainsRequest.data.value?.sort((a, b) => b.visits - a.visits) || [])
]
}) })
const activeDomain = ref<string>(); const activeDomain = ref<string>();
const domain = computed(() => { const domain = computed(() => {
if (activeDomain.value) return activeDomain.value; if (activeDomain.value) return activeDomain.value;
if (!domainList.value) return; if (!domainList.value) return;
if (domainList.value.length == 0) return; if (domainList.value.length == 0) return;
activeDomain.value = domainList.value[0]; setActiveDomain(domainList.value[0]._id);
return domainList.value[0]; return domainList.value[0]._id;
}) })
function setActiveDomain(domain: string) { function setActiveDomain(domain: string) {
@@ -31,6 +43,5 @@ function setActiveDomain(domain: string) {
} }
export function useDomain() { export function useDomain() {
return { domainList, domain, setActiveDomain, refreshDomains, refreshingDomains }
return { domainList, domain, setActiveDomain }
} }

View File

@@ -7,7 +7,7 @@ definePageMeta({ layout: 'dashboard' });
const { snapshotDuration } = useSnapshot(); const { snapshotDuration } = useSnapshot();
const selectedLabelIndex = ref<number>(0); const selectedLabelIndex = ref<number>(1);
const selectLabels: { label: string, value: Slice }[] = [ const selectLabels: { label: string, value: Slice }[] = [
{ label: 'Hour', value: 'hour' }, { label: 'Hour', value: 'hour' },
@@ -21,7 +21,10 @@ const selectLabelsAvailable = computed<{ label: string, value: Slice, disabled:
}); });
}) })
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
});
</script> </script>
@@ -54,9 +57,9 @@ const eventsData = await useFetch(`/api/data/count`, { headers: useComputedHeade
sub="Events stacked bar chart."> sub="Events stacked bar chart.">
<template #header> <template #header>
<SelectButton class="w-fit" @changeIndex="selectedLabelIndex = $event" :currentIndex="selectedLabelIndex" <SelectButton class="w-fit" @changeIndex="selectedLabelIndex = $event"
:options="selectLabelsAvailable"> :currentIndex="selectedLabelIndex" :options="selectLabelsAvailable">
</SelectButton> </SelectButton>
</template> </template>
<div class="h-full"> <div class="h-full">

View File

@@ -10,9 +10,9 @@ export default defineEventHandler(async event => {
const result = await VisitModel.aggregate([ const result = await VisitModel.aggregate([
{ $match: { project_id, } }, { $match: { project_id, } },
{ $group: { _id: "$website" } }, { $group: { _id: "$website", visits: { $sum: 1 } } },
]); ]);
return result as { _id: string }[]; return result as { _id: string, visits: number }[];
}); });

View File

@@ -16,8 +16,7 @@ export default defineEventHandler(async event => {
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;
}); });

View File

@@ -56,10 +56,13 @@ export async function getRequestData(event: H3Event<EventHandlerRequest>, requir
const pid = getHeader(event, 'x-pid'); const pid = getHeader(event, 'x-pid');
if (!pid) return setResponseStatus(event, 400, 'x-pid is required'); if (!pid) return setResponseStatus(event, 400, 'x-pid is required');
const domain = getHeader(event, 'x-domain'); let domain: any = getHeader(event, 'x-domain');
if (requireDomain) { if (requireDomain) {
if (domain == null || domain == undefined || domain.length == 0) return setResponseStatus(event, 400, 'x-domain is required'); if (domain == null || domain == undefined || domain.length == 0) return setResponseStatus(event, 400, 'x-domain is required');
} }
if (domain === 'ALL DOMAINS') {
domain = { $ne: '_NODOMAIN_' }
}
const slice = getHeader(event, 'x-slice') as Slice; const slice = getHeader(event, 'x-slice') as Slice;
if (!slice && requireSlice) return setResponseStatus(event, 400, 'x-slice is required'); if (!slice && requireSlice) return setResponseStatus(event, 400, 'x-slice is required');