This commit is contained in:
Emily
2024-10-09 15:30:59 +02:00
parent 126296d28f
commit e953af2c1b
27 changed files with 271 additions and 209 deletions

View File

@@ -37,6 +37,6 @@ async function showMore() {
<BarCardBase @showMore="showMore()" @showRawData="goToView()"
desc="Most frequent user events triggered in this project" @dataReload="eventsData.refresh()"
:data="eventsData.data.value || []" :loading="eventsData.pending.value" label="Top Events"
sub-label="Events" :rawButton="!isLiveDemo()"></BarCardBase>
sub-label="Events" :rawButton="!isLiveDemo"></BarCardBase>
</div>
</template>

View File

@@ -51,7 +51,7 @@ function goToView() {
:loading="currentData.pending.value" :label="isPagesView ? 'Top pages' : 'Top Domains'"
:sub-label="isPagesView ? 'Page' : 'Domains'"
:desc="isPagesView ? 'Most visited pages' : 'Most visited domains in this project'"
:interactive="!isPagesView" :rawButton="!isLiveDemo()" :isDetailView="isPagesView">
:interactive="!isPagesView" :rawButton="!isLiveDemo" :isDetailView="isPagesView">
</BarCardBase>
</div>
</template>

View File

@@ -4,14 +4,8 @@ import type { TProject } from '@schema/ProjectSchema';
const { user } = useLoggedUser()
const { projectList, guestProjectList, actions, project } = useProject();
const { projectList, guestProjectList,allProjectList, actions, project } = useProject();
const selectorProjects = computed(() => {
const result: TProject[] = [];
if (projectList.value) result.push(...projectList.value);
if (guestProjectList.value) result.push(...guestProjectList.value);
return result;
});
function isProjectMine(owner?: string) {
if (!owner) return false;
@@ -34,7 +28,7 @@ function onChange(e: TProject) {
base: 'hover:!bg-lyx-widget-lighter cursor-pointer',
active: '!bg-lyx-widget-lighter'
}
}" class="w-full" v-if="selectorProjects" @change="onChange" :value="project" :options="selectorProjects">
}" class="w-full" v-if="allProjectList" @change="onChange" :value="project" :options="allProjectList">
<template #option="{ option, active, selected }">
<div class="flex items-center gap-2">

View File

@@ -241,8 +241,6 @@ const legendClasses = ref<string[]>([
])
const inLiveDemo = isLiveDemo();
</script>
<template>
@@ -254,7 +252,7 @@ const inLiveDemo = isLiveDemo();
</template>
<div class="flex gap-6 w-full justify-between">
<LyxUiButton type="secondary" :to="inLiveDemo ? '#' : '/analyst'" :disabled="inLiveDemo">
<LyxUiButton type="secondary" :to="isLiveDemo ? '#' : '/analyst'" :disabled="isLiveDemo">
<div class="flex items-center gap-2 px-10">
<i class="far fa-sparkles text-yellow-400"></i>
<div class="poppins text-lyx-text"> Ask AI </div>

View File

@@ -99,7 +99,7 @@ function transformResponse(input: CustomEventsAggregated[]) {
}
const eventsData = useFetch(`/api/data/events`, {
method: 'POST', headers: useComputedHeaders({ limit: 6 }), lazy: true, immediate: false, transform: transformResponse
headers: useComputedHeaders({ limit: 6 }), lazy: true, immediate: false, transform: transformResponse
});
onMounted(() => {

View File

@@ -56,14 +56,35 @@ const chartData = ref<ChartData<'funnel'>>({
datasets: [
{
data: [],
backgroundColor: ['#5680F8' + '77'],
backgroundColor: [
'#5680F877',
"#6bbbe377",
"#a6d5cb77",
"#fae0b977",
"#f28e8e77",
"#e3a7e477",
"#c4a8e177",
"#8cc1d877",
"#f9c2cd77",
"#b4e3b277",
"#ffdfba77",
"#e9c3b577",
"#d5b8d677",
"#add7f677",
"#ffd1dc77",
"#ffe7a177",
"#a8e6cf77",
"#d4a5a577",
"#f3d6e477",
"#c3aed677"
],
// borderColor: '#0000CC',
// borderWidth: 4,
fill: true,
tension: 0.45,
pointRadius: 0,
pointHoverRadius: 10,
hoverBackgroundColor: '#5680F8',
hoverBackgroundColor: '#26262677',
// hoverBorderColor: 'white',
// hoverBorderWidth: 2,
},
@@ -89,16 +110,10 @@ onMounted(async () => {
});
const eventsCount = await useFetch<{ _id: string, count: number }[]>(`/api/data/query`, {
lazy: true, headers: useComputedHeaders({
limit: 1000,
custom: {
'schema': 'events'
}
})
const eventsData = useFetch(`/api/data/events`, {
headers: useComputedHeaders(), lazy: true, immediate: false
});
const enabledEvents = ref<string[]>([]);
async function onEventCheck(eventName: string) {
@@ -114,7 +129,7 @@ async function onEventCheck(eventName: string) {
chartData.value.datasets[0].data = [];
for (const enabledEvent of enabledEvents.value) {
const target = (eventsCount.data.value ?? []).find(e => e._id == enabledEvent);
const target = (eventsData.data.value ?? []).find(e => e._id == enabledEvent);
chartData.value.datasets[0].data.push(target?.count || 0);
}
}
@@ -123,16 +138,19 @@ async function onEventCheck(eventName: string) {
<template>
<CardTitled title="Funnel" sub="Funnel events">
<CardTitled title="Funnel"
sub="Monitor and analyze the actions your users are performing on your platform to gain insights into their behavior and optimize the user experience">
<div class="flex gap-2 justify-between">
<div>
<div class="min-w-[20rem]">
<div class="flex flex-col gap-1">
<div class="min-w-[20rem] text-lyx-text-darker">
Select two or more events
</div>
<div v-for="event of eventsCount.data.value">
<UCheckbox @change="onEventCheck(event._id)" :value="enabledEvents.includes(event._id)"
:label="event._id">
</UCheckbox>
<div class="flex flex-col gap-1">
<div v-for="event of eventsData.data.value">
<UCheckbox color="secondary" @change="onEventCheck(event._id)"
:value="enabledEvents.includes(event._id)" :label="event._id">
</UCheckbox>
</div>
</div>
</div>
<div class="grow">

View File

@@ -20,7 +20,7 @@ watch(selectedMetadataField, () => {
});
async function getMetadataFields() {
metadataFields.value = await $fetch<string[]>(`/api/metrics/events_data/metadata_fields?name=${selectedEventName.value}`, {
metadataFields.value = await $fetch<string[]>(`/api/data/events_data/metadata_fields?name=${selectedEventName.value}`, {
headers: useComputedHeaders().value
});
selectedMetadataField.value = undefined;
@@ -42,7 +42,7 @@ async function getMetadataFieldGrouped() {
const queryParamsString = Object.keys(queryParams).map((key) => `${key}=${queryParams[key]}`).join('&');
metadataFieldGrouped.value = await $fetch<string[]>(`/api/metrics/events_data/metadata_field_group?${queryParamsString}`, {
metadataFieldGrouped.value = await $fetch<string[]>(`/api/data/events_data/metadata_field_group?${queryParamsString}`, {
headers: useComputedHeaders().value
});
}
@@ -74,7 +74,76 @@ const canSearch = computed(() => {
<CardTitled title="Event metadata analyzer" sub="Filter events metadata fields to analyze them" class="w-full p-4">
<div class="p-2 flex flex-col">
<div class="">
<LyxUiCard class="h-full w-full flex gap-2">
<div class="flex-[2]">
<div class="flex flex-col gap-2">
<USelectMenu :uiMenu="{
select: '!bg-lyx-widget-light !shadow-none focus:!ring-lyx-widget-lighter !ring-lyx-widget-lighter',
base: '!bg-lyx-widget',
option: {
base: 'hover:!bg-lyx-widget-lighter cursor-pointer',
active: '!bg-lyx-widget-lighter'
}
}" searchable searchable-placeholder="Search an event..." class="w-full"
placeholder="Select an event" :options="eventNames.data.value || []"
v-model="selectedEventName">
</USelectMenu>
<USelectMenu :uiMenu="{
select: '!bg-lyx-widget-light !shadow-none focus:!ring-lyx-widget-lighter !ring-lyx-widget-lighter',
base: '!bg-lyx-widget',
option: {
base: 'hover:!bg-lyx-widget-lighter cursor-pointer',
active: '!bg-lyx-widget-lighter'
}
}" searchable searchable-placeholder="Search a field..." class="w-full"
placeholder="Select a field" :options="metadataFields" v-model="selectedMetadataField">
</USelectMenu>
</div>
<div class="text-lyx-text-darker poppins mt-4 flex items-center gap-4">
<div class="w-[10rem]">
Search results: {{ metadataFieldGroupedFiltered.length }}
</div>
<div v-if="canSearch" class="h-full flex items-center text-[1.2rem]">
<div class="bg-lyx-widget-light flex items-center rounded-md pl-4">
<div><i class="far fa-search"></i></div>
<input class="bg-transparent px-4 py-2 text-[1rem] outline-none" type="text"
placeholder="Filter by metadata name" v-model="currentSearchText">
</div>
</div>
</div>
<div class="flex flex-wrap gap-2 mt-4">
<div class="bg-lyx-widget-light text-lyx-text-dark px-3 py-2 rounded-md w-fit"
v-for="item of metadataFieldGroupedFiltered">
<div class="flex gap-2">
<div> {{ item._id || 'OLD_EVENTS' }} </div>
<div> {{ item.count }} </div>
</div>
</div>
</div>
</div>
<!-- <div class="border-solid border-[#212121] border-l-[1px]"></div> -->
<!-- <div class="flex-[1]">
<div class="poppins font-semibold"> </div>
</div> -->
</LyxUiCard>
</div>
<!-- <div class="p-2 flex flex-col">
<div class="flex flex-col gap-2">
<USelectMenu searchable searchable-placeholder="Search an event..." class="w-full"
@@ -103,17 +172,11 @@ const canSearch = computed(() => {
Search results: {{ metadataFieldGroupedFiltered.length }}
</div>
<div class="flex flex-col">
<div v-for="item of metadataFieldGroupedFiltered">
<div class="flex gap-2">
<div> {{ item._id || 'OLD_EVENTS' }} </div>
<div> {{ item.count }} </div>
</div>
</div>
</div>
</div>
</div>
</div> -->
</CardTitled>

View File

@@ -42,23 +42,30 @@ async function analyzeEvent() {
sub="Track your user's journey from external links to in-app events, maintaining a complete view of their path from entry to engagement."
class="w-full p-4">
<div class="p-2 flex flex-col gap-3">
<USelectMenu searchable searchable-placeholder="Search an event..." class="w-full"
placeholder="Select an event" :options="eventNames.data.value || []" v-model="selectedEventName">
</USelectMenu>
<div v-if="selectedEventName && !analyzing" class="flex justify-center">
<div @click="analyzeEvent()"
class="bg-bg w-fit px-8 py-2 poppins rounded-lg hover:bg-bg/80 cursor-pointer">
Analyze
<div class="flex flex-col gap-4">
<div class="py-2 flex items-center gap-3">
<USelectMenu :uiMenu="{
select: '!bg-lyx-widget-light !shadow-none focus:!ring-lyx-widget-lighter !ring-lyx-widget-lighter',
base: '!bg-lyx-widget',
option: {
base: 'hover:!bg-lyx-widget-lighter cursor-pointer',
active: '!bg-lyx-widget-lighter'
}
}" searchable searchable-placeholder="Search an event..." class="w-full" placeholder="Select an event"
:options="eventNames.data.value || []" v-model="selectedEventName">
</USelectMenu>
<div v-if="selectedEventName && !analyzing" class="flex justify-center">
<LyxUiButton @click="analyzeEvent()" type="primary" class="w-fit px-8 py-1">
Analyze
</LyxUiButton>
</div>
</div>
<div v-if="analyzing">
Analyzing...
</div>
<div v-if="analyzing"> Analyzing... </div>
<div class="flex flex-col gap-2" v-if="userFlowData">
<div class="flex gap-4 items-center bg-bg py-1 px-2 rounded-lg"
<div class="flex gap-4 items-center bg-bg py-2 px-2 bg-lyx-widget-light rounded-lg"
v-for="(count, referrer) in userFlowData">
<div class="w-5 h-5 flex items-center justify-center">
<img :src="`https://s2.googleusercontent.com/s2/favicons?domain=${referrer}&sz=64`"
@@ -69,8 +76,8 @@ async function analyzeEvent() {
<div> {{ count.toFixed(2).replace('.', ',') }} % </div>
</div>
</div>
</div>
</CardTitled>
</template>

View File

@@ -86,6 +86,9 @@ async function changeProjectName() {
location.reload();
}
const router = useRouter();
async function deleteProject() {
if (!project.value) return;
const sure = confirm(`Are you sure to delete the project ${project.value.name} ?`);
@@ -108,6 +111,7 @@ async function deleteProject() {
const firstProjectId = projectList.value?.[0]?._id.toString();
if (firstProjectId) {
await actions.setActiveProject(firstProjectId);
router.push('/')
}
@@ -153,14 +157,15 @@ function copyProjectId() {
<SettingsTemplate :entries="entries" :key="project?.name || 'NONE'">
<template #pname>
<div class="flex items-center gap-4">
<LyxUiInput class="w-full px-4 py-2" v-model="projectNameInputVal"></LyxUiInput>
<LyxUiInput class="w-full px-4 py-2" :disabled="isGuest" v-model="projectNameInputVal"></LyxUiInput>
<LyxUiButton v-if="!isGuest" @click="changeProjectName()" :disabled="!canChange" type="primary"> Change
</LyxUiButton>
</div>
</template>
<template #api>
<div class="flex items-center gap-4" v-if="apiKeys && apiKeys.length < 5">
<LyxUiInput class="grow px-4 py-2" placeholder="ApiKeyName" v-model="newApiKeyName"></LyxUiInput>
<LyxUiInput class="grow px-4 py-2" :disabled="isGuest" placeholder="ApiKeyName" v-model="newApiKeyName">
</LyxUiInput>
<LyxUiButton v-if="!isGuest" @click="createApiKey()" :disabled="newApiKeyName.length < 3"
type="primary">
<i class="far fa-plus"></i>
@@ -172,7 +177,7 @@ function copyProjectId() {
<div class="flex gap-8 items-center">
<div class="grow">Name: {{ apiKey.apiName }}</div>
<div>{{ apiKey.apiKey }}</div>
<div class="flex justify-end">
<div class="flex justify-end" v-if="!isGuest">
<i class="far fa-trash cursor-pointer" @click="deleteApiKey(apiKey._id.toString())"></i>
</div>
</div>

View File

@@ -126,7 +126,7 @@ const { visible } = usePricingDrawer();
<SettingsTemplate v-if="!invoicesPending && !planPending" :entries="entries">
<template #info>
<div>
<div v-if="!isGuest">
<div class="flex flex-col gap-2">
<LyxUiInput class="px-2 py-1" placeholder="Address line 1" v-model="currentBillingInfo.line1">
</LyxUiInput>