mirror of
https://github.com/Litlyx/litlyx
synced 2025-12-10 15:58:38 +01:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f86a399840 | ||
|
|
36c4406af2 | ||
|
|
b2afd585bb | ||
|
|
24ae9d0e0d | ||
|
|
b479ca1bbf | ||
|
|
0a748346c5 | ||
|
|
fa7880552a | ||
|
|
06fb8bfab0 | ||
|
|
a876d77d42 | ||
|
|
e6bb58693f | ||
|
|
00e63cc80b | ||
|
|
e43f138945 |
@@ -43,7 +43,7 @@ You can install Litlyx using `npm`, `pnpm`, `yarn` or any modern package manager
|
||||
npm i litlyx-js
|
||||
```
|
||||
|
||||
Litlyx natively works with all JavaScript / TypeScript frameworks. You can use Litlyx in all WordPress Websites by injecting JS code using a plug-in. Litlyx also works in serverless enviroments with Cloud (or Edge) Functions.
|
||||
Litlyx natively works with all JavaScript / TypeScript frameworks. You can use Litlyx in all WordPress Websites by injecting JS code using a plug-in. Litlyx also works in serverless environments with Cloud (or Edge) Functions.
|
||||
|
||||
<p align="center">
|
||||
<img src="assets/tech.png" />
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 127 KiB After Width: | Height: | Size: 123 KiB |
@@ -125,11 +125,10 @@ function openExternalLink(link: string) {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-text font-semibold text-[.9rem] md:text-[1rem] manrope"> {{
|
||||
formatNumberK(element.count) }} </div>
|
||||
</div>
|
||||
<div v-if="props.data.length == 0" class="flex justify-center text-text-sub font-bold text-[1.1rem]">
|
||||
<div v-if="props.data.length == 0" class="flex justify-center text-text-sub font-light text-[1.1rem]">
|
||||
No data yet
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -56,7 +56,7 @@ const { createAlert } = useAlert()
|
||||
async function deleteSnapshot(close: () => any) {
|
||||
await $fetch("/api/snapshot/delete", {
|
||||
method: 'DELETE',
|
||||
...signHeaders({ 'Content-Type': 'application/json' }),
|
||||
headers: useComputedHeaders({ useSnapshotDates: false }).value,
|
||||
body: JSON.stringify({
|
||||
id: snapshot.value._id.toString(),
|
||||
})
|
||||
@@ -71,11 +71,7 @@ async function generatePDF() {
|
||||
|
||||
try {
|
||||
const res = await $fetch<Blob>('/api/project/generate_pdf', {
|
||||
...signHeaders({
|
||||
'x-snapshot-name': snapshot.value.name,
|
||||
'x-from': snapshot.value.from.toISOString(),
|
||||
'x-to': snapshot.value.to.toISOString(),
|
||||
}),
|
||||
headers: useComputedHeaders({ useSnapshotDates: false, custom: { 'x-snapshot-name': snapshot.value.name } }).value,
|
||||
responseType: 'blob'
|
||||
});
|
||||
|
||||
@@ -149,6 +145,15 @@ const pricingDrawer = usePricingDrawer();
|
||||
</div>
|
||||
</LyxUiButton>
|
||||
|
||||
<LyxUiButton v-if="projectList && (projectList.length >= (maxProjects || 1))" type="outlined"
|
||||
class="w-full py-1 mt-2 text-[.7rem]">
|
||||
<div class="flex items-center gap-2 justify-center">
|
||||
<div><i class="text-lyx-text-darker far fa-lock"></i></div>
|
||||
<div class="text-lyx-text-darker"> Projects limit reached </div>
|
||||
</div>
|
||||
</LyxUiButton>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
@@ -203,12 +208,12 @@ const pricingDrawer = usePricingDrawer();
|
||||
<div v-if="snapshot" class="flex flex-col text-[.7rem] mt-2">
|
||||
<div class="flex gap-1 items-center justify-center text-lyx-text-dark">
|
||||
<div class="poppins">
|
||||
{{ new Date(snapshot.from).toLocaleString('it-IT').split(',')[0].trim().replace(/\//g, '-')
|
||||
{{ new Date(snapshot.from).toLocaleString().split(',')[0].trim()
|
||||
}}
|
||||
</div>
|
||||
<div class="poppins"> to </div>
|
||||
<div class="poppins">
|
||||
{{ new Date(snapshot.to).toLocaleString('it-IT').split(',')[0].trim().replace(/\//g, '-') }}
|
||||
{{ new Date(snapshot.to).toLocaleString().split(',')[0].trim() }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -243,7 +248,7 @@ const pricingDrawer = usePricingDrawer();
|
||||
|
||||
<div v-for="entry of section.entries" :class="{ 'grow flex items-end': entry.grow }">
|
||||
|
||||
<div v-if="(!entry.adminOnly || (userRoles.isAdmin && !isAdminHidden))"
|
||||
<div v-if="(!entry.adminOnly || (userRoles.isAdmin.value && !isAdminHidden))"
|
||||
class="bg-lyx-background w-full cursor-pointer text-lyx-text-dark py-[.35rem] px-2 rounded-lg text-[.95rem] flex items-center"
|
||||
:class="{
|
||||
'!text-lyx-text-darker pointer-events-none': entry.disabled,
|
||||
@@ -259,7 +264,7 @@ const pricingDrawer = usePricingDrawer();
|
||||
<div class="manrope grow">
|
||||
{{ entry.label }}
|
||||
</div>
|
||||
<div v-if="entry.premiumOnly && !userRoles.isPremium" class="flex items-center">
|
||||
<div v-if="entry.premiumOnly && !userRoles.isPremium.value" class="flex items-center">
|
||||
<i class="fal fa-lock"></i>
|
||||
</div>
|
||||
</NuxtLink>
|
||||
@@ -293,7 +298,8 @@ const pricingDrawer = usePricingDrawer();
|
||||
class="cursor-pointer hover:text-lyx-text text-lyx-text-dark">
|
||||
<i class="fab fa-dev"></i>
|
||||
</NuxtLink> -->
|
||||
<NuxtLink to="/admin" v-if="userRoles.isAdmin"
|
||||
|
||||
<NuxtLink to="/admin" v-if="userRoles.isAdmin.value"
|
||||
class="cursor-pointer hover:text-lyx-text text-lyx-text-dark">
|
||||
<i class="fas fa-cat"></i>
|
||||
</NuxtLink>
|
||||
|
||||
@@ -76,26 +76,32 @@ function reloadPage() {
|
||||
|
||||
<div class="flex items-center justify-center mt-10">
|
||||
<div class="flex flex-col gap-6">
|
||||
<div class="flex gap-6">
|
||||
<div>
|
||||
<CardTitled class="h-full" title="Tutorial" sub="Coming soon. For now enjoy our launch video.">
|
||||
<div class="flex items-center justify-center h-full">
|
||||
<iframe width="560" height="315"
|
||||
|
||||
<div class="flex gap-6 xl:flex-row flex-col">
|
||||
|
||||
<div class="h-full w-full">
|
||||
<CardTitled class="h-full w-full xl:min-w-[500px]" title="Tutorial"
|
||||
sub="Coming soon. For now enjoy our launch video.">
|
||||
|
||||
<div class="flex items-center justify-center h-full w-full">
|
||||
|
||||
<iframe class="w-full h-full min-h-[400px]"
|
||||
src="https://www.youtube.com/embed/GntyWMR7jsY?si=YGGkQwrk6-Iqmn8w" title="Litlyx"
|
||||
frameborder="0"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
|
||||
referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
|
||||
</div>
|
||||
|
||||
</CardTitled>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-6">
|
||||
|
||||
<div>
|
||||
<div class="w-full">
|
||||
<CardTitled title="Quick Integration"
|
||||
sub="Start tracking web analytics in one line. (works everywhere js is supported)">
|
||||
<div class="flex flex-col items-end gap-4">
|
||||
<div class="w-full">
|
||||
<div class="w-full xl:text-[1rem] text-[.8rem]">
|
||||
<pre><code class="language-html">{{ scriptText }}</code></pre>
|
||||
</div>
|
||||
<LyxUiButton type="secondary" @click="copyScript()">
|
||||
@@ -122,7 +128,7 @@ function reloadPage() {
|
||||
<CardTitled class="w-full h-full" title="Documentation"
|
||||
sub="Learn how to use Litlyx in every tech stack">
|
||||
<div class="flex flex-col items-end">
|
||||
<div class="flex justify-center w-full">
|
||||
<div class="justify-center w-full hidden xl:flex">
|
||||
<svg width="680" height="100" viewBox="0 0 680 100" fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<mask id="path-1-inside-1_473_1361" fill="white">
|
||||
@@ -263,7 +269,7 @@ function reloadPage() {
|
||||
|
||||
|
||||
|
||||
<!-- <div class="flex justify-center gap-10 flex-col lg:flex-row items-center lg:items-stretch px-10">
|
||||
<!-- <div class="flex justify-center gap-10 flex-col xl:flex-row items-center xl:items-stretch px-10">
|
||||
|
||||
<div class="bg-menu p-6 rounded-xl flex flex-col gap-2 w-full">
|
||||
<div class="poppins font-semibold"> Copy your project_id: </div>
|
||||
@@ -273,7 +279,7 @@ function reloadPage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-menu p-6 rounded-xl flex flex-col gap-2 w-full lg:max-w-[40vw]">
|
||||
<div class="bg-menu p-6 rounded-xl flex flex-col gap-2 w-full xl:max-w-[40vw]">
|
||||
<div class="poppins font-semibold">
|
||||
Start logging visits in 1 click | Plug anywhere !
|
||||
</div>
|
||||
|
||||
@@ -20,15 +20,16 @@ const isPremium = computed(() => {
|
||||
<div v-if="!isPremium" class="w-full bg-[#5680f822] p-4 rounded-lg text-[.9rem] flex items-center">
|
||||
<div class="flex flex-col grow">
|
||||
<div class="poppins font-semibold text-lyx-primary">
|
||||
Launch offer: 25% off
|
||||
Launch offer: 25% off forever with code <span class="text-white font-bold text-[1rem]">LIT25</span> at checkout
|
||||
from Acceleration Plan and beyond.
|
||||
</div>
|
||||
<div class="poppins text-lyx-primary">
|
||||
<!-- <div class="poppins text-lyx-primary">
|
||||
We're offering an exclusive 25% discount forever on all plans starting from the Acceleration
|
||||
Plan for our first 100 users who believe in our project.
|
||||
<br>
|
||||
Redeem Code: <span class="text-white font-bold text-[1rem]">LIT25</span> at checkout to
|
||||
claim your discount.
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
<div>
|
||||
<LyxUiButton type="outline" @click="goToUpgrade()"> Upgrade </LyxUiButton>
|
||||
|
||||
@@ -251,7 +251,7 @@ const legendClasses = ref<string[]>([
|
||||
</SelectButton>
|
||||
</template>
|
||||
|
||||
<div class="flex gap-6 w-full justify-between">
|
||||
<div class="flex gap-6 w-full justify-between lg:flex-row flex-col">
|
||||
<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>
|
||||
|
||||
@@ -21,14 +21,24 @@ const chartSlice = computed(() => {
|
||||
|
||||
|
||||
function transformResponse(input: { _id: string, count: number }[]) {
|
||||
|
||||
const data = input.map(e => e.count || 0);
|
||||
|
||||
const labels = input.map(e => DateService.getChartLabelFromISO(e._id, navigator.language, chartSlice.value));
|
||||
|
||||
const pool = [...input.map(e => e.count || 0)];
|
||||
pool.pop();
|
||||
|
||||
const avg = pool.reduce((a, e) => a + e, 0) / pool.length;
|
||||
const diffPercent: number = (100 / avg * (input.at(-1)?.count || 0)) - 100;
|
||||
|
||||
const targets = input.slice(Math.floor(input.length / 4 * 3));
|
||||
const targetAvg = targets.reduce((a, e) => a + e.count, 0) / targets.length;
|
||||
|
||||
const diffPercent: number = (100 / avg * (targetAvg)) - 100;
|
||||
|
||||
const trend = Math.max(Math.min(diffPercent, 99), -99);
|
||||
|
||||
return { data, labels, trend }
|
||||
|
||||
}
|
||||
|
||||
const visitsData = useFetch('/api/timeline/visits', {
|
||||
|
||||
@@ -42,7 +42,7 @@ const { createAlert } = useAlert()
|
||||
async function confirmSnapshot() {
|
||||
await $fetch("/api/snapshot/create", {
|
||||
method: 'POST',
|
||||
...signHeaders({ 'Content-Type': 'application/json' }),
|
||||
headers: useComputedHeaders({ useSnapshotDates: false }).value,
|
||||
body: JSON.stringify({
|
||||
name: snapshotName.value,
|
||||
color: currentColor.value,
|
||||
|
||||
@@ -111,7 +111,7 @@ onMounted(async () => {
|
||||
});
|
||||
|
||||
const eventsData = useFetch(`/api/data/events`, {
|
||||
headers: useComputedHeaders(), lazy: true, immediate: false
|
||||
headers: useComputedHeaders(), lazy: true
|
||||
});
|
||||
|
||||
const enabledEvents = ref<string[]>([]);
|
||||
@@ -140,7 +140,7 @@ async function onEventCheck(eventName: string) {
|
||||
<template>
|
||||
<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 class="flex gap-2 justify-between lg:flex-row flex-col">
|
||||
<div class="flex flex-col gap-1">
|
||||
<div class="min-w-[20rem] text-lyx-text-darker">
|
||||
Select two or more events
|
||||
|
||||
@@ -104,7 +104,7 @@ const canSearch = computed(() => {
|
||||
</USelectMenu>
|
||||
</div>
|
||||
|
||||
<div class="text-lyx-text-darker poppins mt-4 flex items-center gap-4">
|
||||
<div class="text-lyx-text-darker poppins mt-4 flex items-center gap-4 lg:flex-row flex-col">
|
||||
<div class="w-[10rem]">
|
||||
Search results: {{ metadataFieldGroupedFiltered.length }}
|
||||
</div>
|
||||
@@ -119,13 +119,13 @@ const canSearch = computed(() => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap gap-2 mt-4">
|
||||
<div class="flex flex-wrap gap-2 lg:mt-4 mt-10">
|
||||
|
||||
<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 class="flex gap-2 items-center">
|
||||
<div> {{ item._id || 'OLD_EVENTS' }} </div>
|
||||
<div> {{ item.count }} </div>
|
||||
<div class="px-1"> {{ item.count }} </div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -14,10 +14,9 @@ function transformResponse(input: { _id: string, name: string, count: number }[]
|
||||
data: input,
|
||||
from: input[0]._id,
|
||||
to: safeSnapshotDates.value.to
|
||||
}, slice.value, {
|
||||
advanced: true,
|
||||
advancedGroupKey: 'name'
|
||||
});
|
||||
},
|
||||
slice.value,
|
||||
{ advanced: true, advancedGroupKey: 'name' });
|
||||
|
||||
const parsedDatasets: any[] = [];
|
||||
|
||||
@@ -62,6 +61,7 @@ function transformResponse(input: { _id: string, name: string, count: number }[]
|
||||
datasets: parsedDatasets,
|
||||
labels: fixed.labels
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const errorData = ref<{ errored: boolean, text: string }>({
|
||||
@@ -83,7 +83,7 @@ function onResponse(e: any) {
|
||||
const eventsStackedData = useFetch(`/api/timeline/events_stacked`, {
|
||||
lazy: true, immediate: false,
|
||||
transform: transformResponse,
|
||||
headers: useComputedHeaders({slice}),
|
||||
headers: useComputedHeaders({ slice }),
|
||||
onResponseError,
|
||||
onResponse
|
||||
});
|
||||
|
||||
@@ -62,7 +62,13 @@ async function analyzeEvent() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="analyzing"> Analyzing... </div>
|
||||
<div v-if="analyzing">
|
||||
<div
|
||||
class="backdrop-blur-[1px] z-[20] w-full h-full flex items-center justify-center font-bold rockmann">
|
||||
<i
|
||||
class="fas fa-spinner text-[2rem] text-accent animate-[spin_1s_linear_infinite] duration-500"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-2" v-if="userFlowData">
|
||||
<div class="flex gap-4 items-center bg-bg py-2 px-2 bg-lyx-widget-light rounded-lg"
|
||||
|
||||
@@ -47,7 +47,7 @@ async function onUpgradeClick() {
|
||||
|
||||
<div class="flex flex-col gap-3 text-center pt-3">
|
||||
<div v-if="data.active"
|
||||
class="absolute right-6 top-3 poppins text-[.75rem] bg-[#222A42] outline outline-[1px] outline-[#5680F8] px-3 py-[.1rem] rounded-sm">
|
||||
class="absolute right-6 top-3 poppins text-[.75rem] bg-transparent border-[#262626] border-solid border-[1px] px-3 py-[.1rem] rounded-sm">
|
||||
Active
|
||||
</div>
|
||||
<div v-if="!data.active && data.title === 'Growth'"
|
||||
|
||||
@@ -198,11 +198,16 @@ function copyProjectId() {
|
||||
<script defer data-project="${project?._id}"
|
||||
src="https://cdn.jsdelivr.net/gh/litlyx/litlyx-js/browser/litlyx.js"></script>` }}
|
||||
</div>
|
||||
<div><i class="far fa-copy" @click="copyScript()"></i></div>
|
||||
<div class="hidden lg:flex"><i class="far fa-copy" @click="copyScript()"></i></div>
|
||||
</LyxUiCard>
|
||||
<div class="flex justify-end w-full">
|
||||
<LyxUiButton type="outline" class="flex lg:hidden mt-4">
|
||||
Copy script
|
||||
</LyxUiButton>
|
||||
</div>
|
||||
</template>
|
||||
<template #pdelete>
|
||||
<div class="flex justify-end" v-if="!isGuest">
|
||||
<div class="flex lg:justify-end" v-if="!isGuest">
|
||||
<LyxUiButton type="danger" @click="deleteProject()">
|
||||
Delete project
|
||||
</LyxUiButton>
|
||||
|
||||
@@ -16,10 +16,10 @@ const props = defineProps<SettingsTemplateProp>();
|
||||
|
||||
|
||||
<template>
|
||||
<div class="mt-10 px-4">
|
||||
<div class="mt-10 px-4 xl:pb-0 pb-[10rem]">
|
||||
<div v-for="(entry, index) of props.entries" class="flex flex-col">
|
||||
<div class="flex">
|
||||
<div class="flex-[2]">
|
||||
<div class="flex xl:flex-row flex-col gap-4 xl:gap-0">
|
||||
<div class="xl:flex-[2]">
|
||||
<div class="poppins font-medium text-lyx-text">
|
||||
{{ entry.title }}
|
||||
</div>
|
||||
@@ -27,7 +27,7 @@ const props = defineProps<SettingsTemplateProp>();
|
||||
{{ entry.text }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-[3]">
|
||||
<div class="xl:flex-[3]">
|
||||
<slot :name="entry.id"></slot>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -164,7 +164,7 @@ const { visible } = usePricingDrawer();
|
||||
{{ planData.premium ? 'Premium plan' : 'Basic plan' }}
|
||||
</div>
|
||||
<div
|
||||
class="flex lato text-[.7rem] bg-accent/25 border-accent/40 border-[1px] px-[.6rem] rounded-sm">
|
||||
class="flex lato text-[.7rem] bg-transparent border-[#262626] border-[1px] px-[.6rem] rounded-sm">
|
||||
{{ planData.premium ? getPremiumName(planData.premium_type) : 'FREE' }}
|
||||
</div>
|
||||
</div>
|
||||
@@ -190,7 +190,7 @@ const { visible } = usePricingDrawer();
|
||||
</div>
|
||||
<div class="my-4 w-full bg-gray-400/30 h-[1px]">
|
||||
</div>
|
||||
<div class="flex justify-between px-8 flex-col sm:flex-row">
|
||||
<div class="flex justify-between px-8 flex-col lg:flex-row gap-2 lg:gap-0 items-center">
|
||||
<div class="flex gap-2 text-text-sub text-[.9rem]">
|
||||
<div class="poppins"> Expire date:</div>
|
||||
<div> {{ prettyExpireDate }}</div>
|
||||
|
||||
@@ -138,7 +138,7 @@ async function resetCount(project_id: string) {
|
||||
<div @click="onHideClicked()" v-if="!isAdminHidden"
|
||||
class="bg-menu hover:bg-menu/70 cursor-pointer flex gap-2 rounded-lg w-fit px-6 py-4 text-text-sub">
|
||||
<div class="text-text-sub/90"> <i class="far fa-eye"></i> </div>
|
||||
<div> Nascondi dalla barra </div>
|
||||
<div> Hide from the bar </div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
@@ -148,15 +148,15 @@ const { visible: pricingDrawerVisible } = usePricingDrawer()
|
||||
|
||||
<div class="flex-[5] py-8 flex h-full flex-col items-center relative overflow-y-hidden">
|
||||
|
||||
<div class="flex flex-col items-center lg:mt-[20vh] px-8 lg:px-28"
|
||||
<div class="flex flex-col items-center xl:mt-[20vh] px-8 xl:px-28"
|
||||
v-if="currentChatMessages.length == 0">
|
||||
<div class="w-[7rem] lg:w-[10rem]">
|
||||
<div class="w-[7rem] xl:w-[10rem]">
|
||||
<img :src="'analyst.png'" class="w-full h-full">
|
||||
</div>
|
||||
<div class="poppins text-[1.2rem] text-center">
|
||||
Ask me anything about your data
|
||||
</div>
|
||||
<div class="flex flex-col lg:grid lg:grid-cols-2 gap-4 mt-6">
|
||||
<div class="flex flex-col xl:grid xl:grid-cols-2 gap-4 mt-6">
|
||||
<div v-for="prompt of defaultPrompts" @click="currentText = prompt"
|
||||
class="bg-lyx-widget-light hover:bg-lyx-widget-lighter cursor-pointer p-4 rounded-lg poppins text-center whitespace-pre-wrap flex items-center justify-center text-[.9rem]">
|
||||
{{ prompt }}
|
||||
@@ -216,7 +216,7 @@ const { visible: pricingDrawerVisible } = usePricingDrawer()
|
||||
<i class="far fa-arrow-up"></i>
|
||||
</div>
|
||||
<div @click="menuOpen = !menuOpen"
|
||||
class="bg-lyx-widget-light lg:hidden hhover:bg-lyx-widget-light cursor-pointer px-4 py-2 rounded-full">
|
||||
class="bg-lyx-widget-light xl:hidden hhover:bg-lyx-widget-light cursor-pointer px-4 py-2 rounded-full">
|
||||
<i class="far fa-message"></i>
|
||||
</div>
|
||||
</div>
|
||||
@@ -225,12 +225,12 @@ const { visible: pricingDrawerVisible } = usePricingDrawer()
|
||||
|
||||
|
||||
<div :class="{
|
||||
'absolute': menuOpen,
|
||||
'hidden lg:flex': !menuOpen
|
||||
'absolute top-0 left-0 w-full': menuOpen,
|
||||
'hidden xl:flex': !menuOpen
|
||||
}" class="flex-[2] bg-lyx-background-light p-6 flex flex-col gap-4 h-full overflow-hidden">
|
||||
|
||||
<div class="gap-2 flex flex-col">
|
||||
<div class="lg:hidden absolute right-4 top-4 text-[1.5rem]">
|
||||
<div class="xl:hidden absolute right-6 top-2 text-[1.5rem]">
|
||||
<i @click="menuOpen = false" class="fas fa-close cursor-pointer"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -19,7 +19,7 @@ const eventsData = await useFetch(`/api/data/count`, { headers: useComputedHeade
|
||||
<div class="w-full h-full overflow-y-auto pb-20 p-6 gap-6 flex flex-col">
|
||||
|
||||
|
||||
<LyxUiCard class="w-full flex justify-between items-center">
|
||||
<LyxUiCard class="w-full flex justify-between items-center lg:flex-row flex-col gap-6 lg:gap-0">
|
||||
<div class="flex flex-col gap-1">
|
||||
<div>
|
||||
Total events: {{ eventsData.data.value?.[0]?.count || '0' }}
|
||||
@@ -29,7 +29,7 @@ const eventsData = await useFetch(`/api/data/count`, { headers: useComputedHeade
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<LyxUiButton type="secondary" to="https://docs.litlyx.com/custom-events">
|
||||
<LyxUiButton type="secondary" target="_blank" to="https://docs.litlyx.com/custom-events">
|
||||
Trigger your first event
|
||||
</LyxUiButton>
|
||||
</div>
|
||||
@@ -40,9 +40,9 @@ const eventsData = await useFetch(`/api/data/count`, { headers: useComputedHeade
|
||||
<BarCardEvents :key="refreshKey"></BarCardEvents>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-6 flex-col xl:flex-row h-full">
|
||||
<div class="flex gap-6 flex-col xl:flex-row xl:h-full">
|
||||
|
||||
<CardTitled :key="refreshKey" class="p-4 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.">
|
||||
<template #header>
|
||||
<SelectButton @changeIndex="eventsStackedSelectIndex = $event"
|
||||
@@ -55,7 +55,7 @@ const eventsData = await useFetch(`/api/data/count`, { headers: useComputedHeade
|
||||
</div>
|
||||
</CardTitled>
|
||||
|
||||
<CardTitled :key="refreshKey" class="p-4 flex-[2] w-full h-full" title="Top events"
|
||||
<CardTitled :key="refreshKey" class="p-4 xl:flex-[2] w-full h-full" title="Top events"
|
||||
sub="Displays key events.">
|
||||
<DashboardEventsChart class="w-full"> </DashboardEventsChart>
|
||||
</CardTitled>
|
||||
|
||||
@@ -35,7 +35,7 @@ const showDashboard = computed(() => project.value && firstInteraction.data.valu
|
||||
|
||||
<template>
|
||||
|
||||
<div class="dashboard w-full h-full overflow-y-auto pb-20 md:pt-4 lg:pt-0">
|
||||
<div class="dashboard w-full h-full overflow-y-auto overflow-x-hidden pb-[7rem] md:pt-4 lg:pt-0">
|
||||
|
||||
<div v-if="showDashboard">
|
||||
|
||||
@@ -97,8 +97,11 @@ const showDashboard = computed(() => project.value && firstInteraction.data.valu
|
||||
Create your first project...
|
||||
</div>
|
||||
|
||||
<div v-if="justLogged" class="text-[2rem]">
|
||||
The page will refresh soon
|
||||
<div v-if="justLogged" class="text-[2rem] w-full h-full flex items-center justify-center">
|
||||
<div
|
||||
class="backdrop-blur-[1px] z-[20] left-0 top-0 w-full h-full flex items-center justify-center font-bold rockmann absolute">
|
||||
<i class="fas fa-spinner text-[2rem] text-[#727272] animate-[spin_1s_linear_infinite] duration-500"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -89,7 +89,7 @@ const selectLabelsEvents = [
|
||||
<DashboardTopCards :key="refreshKey"></DashboardTopCards>
|
||||
</div>
|
||||
|
||||
<div class="mt-6 px-6 flex gap-6 flex-col 2xl:flex-row w-full">
|
||||
<div class="mt-6 px-6 hidden lg:flex gap-6 flex-col 2xl:flex-row w-full">
|
||||
<DashboardActionableChart :key="refreshKey"></DashboardActionableChart>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -159,7 +159,7 @@ async function signInWithCredentials() {
|
||||
|
||||
<div class="flex h-full">
|
||||
|
||||
<div class="flex-1 flex flex-col items-center pt-20 lg:pt-[22vh]">
|
||||
<div class="flex-1 flex flex-col items-center pt-20 xl:pt-[22vh]">
|
||||
|
||||
<div class="rotating-thing absolute top-0"></div>
|
||||
|
||||
@@ -171,9 +171,8 @@ async function signInWithCredentials() {
|
||||
Sign in
|
||||
</div>
|
||||
|
||||
<div class="text-text/80 text-[1.2rem] text-center w-[70%] poppins mt-2">
|
||||
<div class="text-text/80 text-[1.2rem] font-light text-center w-[70%] poppins mt-2">
|
||||
Track web analytics and custom events
|
||||
<br>
|
||||
with extreme simplicity in under 30 sec.
|
||||
<br>
|
||||
<!-- <div class="font-bold poppins mt-4">
|
||||
@@ -221,7 +220,7 @@ async function signInWithCredentials() {
|
||||
<div class="flex items-center">
|
||||
<i class="far fa-envelope"></i>
|
||||
</div>
|
||||
Continue with Email
|
||||
Sign in with Email
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
|
||||
definePageMeta({ layout: 'none' });
|
||||
|
||||
const activeProject = useActiveProject();
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ async function registerAccount() {
|
||||
|
||||
<div class="flex h-full">
|
||||
|
||||
<div class="flex-1 flex flex-col items-center pt-20 lg:pt-[22vh]">
|
||||
<div class="flex-1 flex flex-col items-center pt-20 xl:pt-[22vh]">
|
||||
|
||||
<div class="rotating-thing absolute top-0"></div>
|
||||
|
||||
@@ -57,9 +57,8 @@ async function registerAccount() {
|
||||
Sign up
|
||||
</div>
|
||||
|
||||
<div class="text-text/80 text-[1.2rem] text-center w-[70%] poppins mt-2">
|
||||
<div class="text-text/80 text-[1.2rem] font-light text-center w-[70%] poppins mt-2">
|
||||
Track web analytics and custom events
|
||||
<br>
|
||||
with extreme simplicity in under 30 sec.
|
||||
<br>
|
||||
<!-- <div class="font-bold poppins mt-4">
|
||||
@@ -114,7 +113,7 @@ async function registerAccount() {
|
||||
</RouterLink>
|
||||
</div>
|
||||
|
||||
<div v-if="!emailSended" class="text-[.9rem] poppins mt-20 text-text-sub text-center relative z-[2]">
|
||||
<div v-if="!emailSended" class="text-[.9rem] poppins mt-5 xl:mt-20 text-text-sub text-center relative z-[2]">
|
||||
By continuing you are accepting
|
||||
<br>
|
||||
our
|
||||
|
||||
@@ -13,8 +13,9 @@ const items = [
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="px-10 py-8 h-dvh overflow-y-auto hide-scrollbars">
|
||||
<div class="poppins font-semibold text-[1.3rem]"> Settings </div>
|
||||
<div class="lg:px-10 lg:py-8 h-dvh overflow-y-auto overflow-x-hidden hide-scrollbars">
|
||||
|
||||
<div class="poppins font-semibold text-[1.3rem] lg:px-0 px-4 lg:py-0 py-4"> Settings </div>
|
||||
|
||||
<CustomTab :items="items" class="mt-8">
|
||||
<template #general>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
|
||||
import { getUserProjectFromId } from "~/server/LIVE_DEMO_DATA";
|
||||
import { ApiSettingsModel, TApiSettings } from "@schema/ApiSettingsSchema";
|
||||
import { UserSettingsModel } from "@schema/UserSettings";
|
||||
import { ProjectModel } from "@schema/ProjectSchema";
|
||||
|
||||
import crypto from 'crypto';
|
||||
|
||||
@@ -4,7 +4,6 @@ import pdfkit from 'pdfkit';
|
||||
import { PassThrough } from 'node:stream';
|
||||
|
||||
import { ProjectModel } from "@schema/ProjectSchema";
|
||||
import { UserSettingsModel } from "@schema/UserSettings";
|
||||
import { VisitModel } from '@schema/metrics/VisitSchema';
|
||||
import { EventModel } from '@schema/metrics/EventSchema';
|
||||
|
||||
@@ -82,15 +81,13 @@ function createPdf(data: PDFGenerationData) {
|
||||
|
||||
export default defineEventHandler(async event => {
|
||||
|
||||
const data = await getRequestData(event, { requireSchema: false, allowGuests: true, requireRange: false });
|
||||
if (!data) return;
|
||||
|
||||
const userData = getRequestUser(event);
|
||||
if (!userData?.logged) return setResponseStatus(event, 400, 'NotLogged');
|
||||
|
||||
const currentActiveProject = await UserSettingsModel.findOne({ user_id: userData.id });
|
||||
if (!currentActiveProject) return setResponseStatus(event, 400, 'You need to select a project');
|
||||
|
||||
const project_id = currentActiveProject.active_project_id;
|
||||
|
||||
const project = await ProjectModel.findById(project_id);
|
||||
const project = await ProjectModel.findById(data.project_id);
|
||||
if (!project) return setResponseStatus(event, 400, 'Project not found');
|
||||
|
||||
const snapshotHeader = getHeader(event, 'x-snapshot-name');
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { ProjectModel } from "@schema/ProjectSchema";
|
||||
import { TeamMemberModel } from "@schema/TeamMemberSchema";
|
||||
import { UserModel } from "@schema/UserSchema";
|
||||
import { UserSettingsModel } from "@schema/UserSettings";
|
||||
import StripeService from '~/server/services/StripeService';
|
||||
|
||||
|
||||
export default defineEventHandler(async event => {
|
||||
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { ProjectModel } from "@schema/ProjectSchema";
|
||||
import { ProjectSnapshotModel } from "@schema/ProjectSnapshot";
|
||||
import { UserSettingsModel } from "@schema/UserSettings";
|
||||
|
||||
|
||||
export default defineEventHandler(async event => {
|
||||
|
||||
const data = await getRequestData(event, { requireSchema: false, allowGuests: true, requireRange: false });
|
||||
if (!data) return;
|
||||
|
||||
const body = await readBody(event);
|
||||
|
||||
const { name: newSnapshotName, from, to, color: snapshotColor } = body;
|
||||
@@ -19,13 +21,8 @@ export default defineEventHandler(async event => {
|
||||
const userData = getRequestUser(event);
|
||||
if (!userData?.logged) return setResponseStatus(event, 400, 'NotLogged');
|
||||
|
||||
const userSettings = await UserSettingsModel.findOne({ user_id: userData.id }, { active_project_id: 1 });
|
||||
|
||||
if (!userSettings) return setResponseStatus(event, 500, 'Unkwnown error');
|
||||
|
||||
const currentProjectId = userSettings.active_project_id;
|
||||
|
||||
const project = await ProjectModel.findById(currentProjectId);
|
||||
const project = await ProjectModel.findById(data.project_id);
|
||||
if (!project) return setResponseStatus(event, 400, 'Project not found');
|
||||
|
||||
|
||||
@@ -34,7 +31,7 @@ export default defineEventHandler(async event => {
|
||||
from: new Date(from),
|
||||
to: new Date(to),
|
||||
color: snapshotColor,
|
||||
project_id: currentProjectId
|
||||
project_id: data.project_id
|
||||
});
|
||||
|
||||
return newSnapshot.id;
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { ProjectModel } from "@schema/ProjectSchema";
|
||||
import { ProjectSnapshotModel } from "@schema/ProjectSnapshot";
|
||||
import { UserSettingsModel } from "@schema/UserSettings";
|
||||
|
||||
|
||||
export default defineEventHandler(async event => {
|
||||
|
||||
const data = await getRequestData(event, { requireSchema: false, allowGuests: false, requireRange: false });
|
||||
if (!data) return;
|
||||
|
||||
const body = await readBody(event);
|
||||
|
||||
const { id: snapshotId } = body;
|
||||
@@ -14,18 +16,11 @@ export default defineEventHandler(async event => {
|
||||
const userData = getRequestUser(event);
|
||||
if (!userData?.logged) return setResponseStatus(event, 400, 'NotLogged');
|
||||
|
||||
const userSettings = await UserSettingsModel.findOne({ user_id: userData.id }, { active_project_id: 1 });
|
||||
|
||||
if (!userSettings) return setResponseStatus(event, 500, 'Unkwnown error');
|
||||
|
||||
const currentProjectId = userSettings.active_project_id;
|
||||
|
||||
const project = await ProjectModel.findById(currentProjectId);
|
||||
const project = await ProjectModel.findById(data.project_id);
|
||||
if (!project) return setResponseStatus(event, 400, 'Project not found');
|
||||
|
||||
|
||||
const deletation = await ProjectSnapshotModel.deleteOne({
|
||||
project_id: currentProjectId,
|
||||
project_id: data.project_id,
|
||||
_id: snapshotId
|
||||
});
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ export function fixMetrics(result: { data: MetricsTimeline[], from: string, to:
|
||||
|
||||
const allKeys = !options.advanced ? [] : Array.from(new Set(result.data.map((e: any) => e[options.advancedGroupKey])).values());
|
||||
|
||||
console.log({allKeys})
|
||||
console.log({ allKeys, allDates })
|
||||
|
||||
const fixed: any[] = allDates.map(matchDate => {
|
||||
|
||||
@@ -102,9 +102,9 @@ console.log({allKeys})
|
||||
if (slice == 'hour') {
|
||||
return `${e._id.getHours().toString().padStart(2, '0')}:00`
|
||||
} else if (slice == 'day') {
|
||||
return `${e._id.getDate().toString().padStart(2, '0')}/${e._id.getMonth().toString().padStart(2, '0')}`
|
||||
return `${e._id.getDate().toString().padStart(2, '0')}/${(e._id.getMonth() + 1).toString().padStart(2, '0')}`
|
||||
} else if (slice == 'month') {
|
||||
return `${e._id.getMonth().toString().padStart(2, '0')}/${e._id.getFullYear().toString()}`
|
||||
return `${(e._id.getMonth() + 1).toString().padStart(2, '0')}/${e._id.getFullYear().toString()}`
|
||||
} else if (slice == 'year') {
|
||||
return `${e._id.getFullYear().toString()}`
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user