mirror of
https://github.com/Litlyx/litlyx
synced 2025-12-09 23:48:36 +01:00
implement domain filter
This commit is contained in:
@@ -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">
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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,26 +8,28 @@ function onChange(e: string) {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
<div class="flex gap-2">
|
||||||
<USelectMenu :uiMenu="{
|
<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',
|
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',
|
base: '!bg-lyx-lightmode-widget dark:!bg-lyx-widget w-max',
|
||||||
option: {
|
option: {
|
||||||
base: 'z-[999] hover:!bg-lyx-lightmode-widget-light dark:hover:!bg-lyx-widget-lighter cursor-pointer',
|
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'
|
active: '!bg-lyx-lightmode-widget-light dark:!bg-lyx-widget-lighter'
|
||||||
}
|
},
|
||||||
}" class="w-full" searchable v-if="domainList" @change="onChange" :value="domain" :options="domainList">
|
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">
|
||||||
|
|
||||||
<template #option="{ option, active, selected }">
|
<template #option="{ option, active, selected }">
|
||||||
<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>
|
||||||
<div> {{ option }} </div>
|
<div> {{ option._id }} </div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</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">
|
||||||
@@ -38,5 +40,9 @@ function onChange(e: string) {
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</USelectMenu>
|
</USelectMenu>
|
||||||
|
<div @click="refreshDomains" v-if="!refreshingDomains"
|
||||||
|
class="flex items-center hover:rotate-[60deg] transition-all duration-200 ease-in-out cursor-pointer">
|
||||||
|
<i class="far fa-refresh"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -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 }
|
|
||||||
}
|
}
|
||||||
@@ -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,8 +57,8 @@ 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>
|
||||||
|
|||||||
@@ -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 }[];
|
||||||
|
|
||||||
});
|
});
|
||||||
@@ -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;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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');
|
||||||
|
|||||||
Reference in New Issue
Block a user