fix dates

This commit is contained in:
Emily
2024-09-13 15:25:26 +02:00
parent 525a371a6e
commit e7c2dbf237
13 changed files with 211 additions and 60 deletions

View File

@@ -134,11 +134,11 @@ const pricingDrawer = usePricingDrawer();
}"> }">
<div class="py-4 px-2 gap-6 flex flex-col w-full"> <div class="py-4 px-2 gap-6 flex flex-col w-full">
<div class="flex px-2" v-if="!isPremium"> <!-- <div class="flex px-2" v-if="!isPremium">
<LyxUiButton type="primary" class="w-full text-center text-[.8rem] font-medium" @click="pricingDrawer.visible.value = true;"> <LyxUiButton type="primary" class="w-full text-center text-[.8rem] font-medium" @click="pricingDrawer.visible.value = true;">
Upgrade plan Upgrade plan
</LyxUiButton> </LyxUiButton>
</div> </div> -->
<div class="flex px-2 flex-col"> <div class="flex px-2 flex-col">

View File

@@ -106,11 +106,12 @@ const externalTooltipElement = ref<null | HTMLDivElement>(null);
function externalTooltipHandler(context: { chart: any, tooltip: TooltipModel<'line' | 'bar'> }) { function externalTooltipHandler(context: { chart: any, tooltip: TooltipModel<'line' | 'bar'> }) {
const { chart, tooltip } = context; const { chart, tooltip } = context;
const tooltipEl = externalTooltipElement.value; const tooltipEl = externalTooltipElement.value;
currentTooltipData.value = [0,0,0,'']; currentTooltipData.value.visits = (tooltip.dataPoints.find(e=> e.datasetIndex == 0)?.raw) as number;
currentTooltipData.value.push(...tooltip.dataPoints.map(e => e.raw) as number[]); currentTooltipData.value.sessions = (tooltip.dataPoints.find(e=> e.datasetIndex == 1)?.raw) as number;
currentTooltipData.value[2] = ((tooltip.dataPoints[2]?.raw as any)?.r2 || 0) as number; currentTooltipData.value.events = ((tooltip.dataPoints.find(e=> e.datasetIndex == 2)?.raw) as any)?.r2 as number;
currentTooltipData.value[3] = new Date(allDatesFull.value[tooltip.dataPoints[0].dataIndex]).toLocaleDateString();
currentTooltipData.value.date = new Date(allDatesFull.value[tooltip.dataPoints[0].dataIndex]).toLocaleDateString();
if (!tooltipEl) return; if (!tooltipEl) return;
if (tooltip.opacity === 0) { if (tooltip.opacity === 0) {
@@ -147,7 +148,6 @@ function transformResponse(input: { _id: string, count: number }[]) {
} }
const body = computed(() => { const body = computed(() => {
console.log('INDEX IS', selectedLabelIndex.value, 'VALUE IS', selectLabels[selectedLabelIndex.value].value)
return { return {
from: safeSnapshotDates.value.from, from: safeSnapshotDates.value.from,
to: safeSnapshotDates.value.to, to: safeSnapshotDates.value.to,
@@ -214,7 +214,8 @@ function onDataReady() {
chartData.value.datasets[0].data = visitsData.data.value.data; chartData.value.datasets[0].data = visitsData.data.value.data;
chartData.value.datasets[1].data = sessionsData.data.value.data; chartData.value.datasets[1].data = sessionsData.data.value.data;
chartData.value.datasets[2].data = eventsData.data.value.data.map(e => { chartData.value.datasets[2].data = eventsData.data.value.data.map(e => {
return { x: 0, y: maxChartY + 70, r: 25 / maxEventSize * e, r2: e } const rValue = 25 / maxEventSize * e;
return { x: 0, y: maxChartY + 70, r: isNaN(rValue) ? 0 : rValue, r2: e }
}); });
chartData.value.datasets[0].backgroundColor = [createGradient('#5655d7')]; chartData.value.datasets[0].backgroundColor = [createGradient('#5655d7')];
@@ -226,7 +227,14 @@ function onDataReady() {
} }
const currentTooltipData = ref<[number, number, number, string]>([0, 0, 0, '']); const currentTooltipData = ref<{ visits: number, events: number, sessions: number, date: string }>({
visits: 0,
events: 0,
sessions: 0,
date: ''
});
const tooltipNameIndex = ['visits', 'sessions', 'events'];
function onLegendChange(dataset: any, index: number, checked: any) { function onLegendChange(dataset: any, index: number, checked: any) {
dataset.hidden = !checked; dataset.hidden = !checked;
@@ -273,14 +281,14 @@ onMounted(async () => {
<LyxUiCard> <LyxUiCard>
<div class="flex gap-2 items-center"> <div class="flex gap-2 items-center">
<div> Date: </div> <div> Date: </div>
<div v-if="currentTooltipData"> {{ currentTooltipData[3] }}</div> <div v-if="currentTooltipData"> {{ currentTooltipData.date }}</div>
</div> </div>
<div v-for="(dataset, index) of chartData.datasets" class="flex gap-2 items-center"> <div v-for="(dataset, index) of chartData.datasets" class="flex gap-2 items-center">
<div :style="`background-color: ${legendColors[index]}`" class="h-4 w-4 rounded-full"> <div :style="`background-color: ${legendColors[index]}`" class="h-4 w-4 rounded-full">
</div> </div>
<div> {{ dataset.label }}</div> <div> {{ dataset.label }}</div>
<div v-if="currentTooltipData" class="grow text-right px-4"> <div v-if="currentTooltipData" class="grow text-right px-4">
{{ currentTooltipData[index] }} {{ (currentTooltipData as any)[tooltipNameIndex[index]] }}
</div> </div>
</div> </div>
<!-- <div class="bg-lyx-background-lighter h-[2px] w-full my-2"> </div> --> <!-- <div class="bg-lyx-background-lighter h-[2px] w-full my-2"> </div> -->

View File

@@ -43,6 +43,8 @@ export default defineNuxtConfig({
AUTH_JWT_SECRET: process.env.AUTH_JWT_SECRET, AUTH_JWT_SECRET: process.env.AUTH_JWT_SECRET,
GOOGLE_AUTH_CLIENT_ID: process.env.GOOGLE_AUTH_CLIENT_ID, GOOGLE_AUTH_CLIENT_ID: process.env.GOOGLE_AUTH_CLIENT_ID,
GOOGLE_AUTH_CLIENT_SECRET: process.env.GOOGLE_AUTH_CLIENT_SECRET, GOOGLE_AUTH_CLIENT_SECRET: process.env.GOOGLE_AUTH_CLIENT_SECRET,
GITHUB_AUTH_CLIENT_ID: process.env.GITHUB_AUTH_CLIENT_ID,
GITHUB_AUTH_CLIENT_SECRET: process.env.GITHUB_AUTH_CLIENT_SECRET,
STRIPE_SECRET: process.env.STRIPE_SECRET, STRIPE_SECRET: process.env.STRIPE_SECRET,
STRIPE_WH_SECRET: process.env.STRIPE_WH_SECRET, STRIPE_WH_SECRET: process.env.STRIPE_WH_SECRET,
STRIPE_SECRET_TEST: process.env.STRIPE_SECRET_TEST, STRIPE_SECRET_TEST: process.env.STRIPE_SECRET_TEST,
@@ -50,7 +52,8 @@ export default defineNuxtConfig({
NOAUTH_USER_EMAIL: process.env.NOAUTH_USER_EMAIL, NOAUTH_USER_EMAIL: process.env.NOAUTH_USER_EMAIL,
NOAUTH_USER_NAME: process.env.NOAUTH_USER_NAME, NOAUTH_USER_NAME: process.env.NOAUTH_USER_NAME,
public: { public: {
AUTH_MODE: process.env.AUTH_MODE AUTH_MODE: process.env.AUTH_MODE,
GITHUB_CLIENT_ID: process.env.GITHUB_AUTH_CLIENT_ID || 'NONE'
} }
}, },

View File

@@ -78,6 +78,10 @@ const { snapshot } = useSnapshot();
const refreshKey = computed(() => `${snapshot.value._id.toString() + activeProject.value?._id.toString()}`); const refreshKey = computed(() => `${snapshot.value._id.toString() + activeProject.value?._id.toString()}`);
const isPremium = computed(() => {
return activeProject.value?.premium;
})
const pricingDrawer = usePricingDrawer(); const pricingDrawer = usePricingDrawer();
function goToUpgrade() { function goToUpgrade() {
@@ -93,9 +97,7 @@ function goToUpgrade() {
<div :key="'home-' + isLiveDemo()" v-if="projects && activeProject && firstInteraction.data.value"> <div :key="'home-' + isLiveDemo()" v-if="projects && activeProject && firstInteraction.data.value">
<div class="w-full px-4 py-2"> <div class="w-full px-4 py-2 gap-2 flex flex-col">
<div v-if="limitsInfo && limitsInfo.limited" <div v-if="limitsInfo && limitsInfo.limited"
class="w-full bg-[#fbbf2422] p-4 rounded-lg text-[.9rem] flex items-center"> class="w-full bg-[#fbbf2422] p-4 rounded-lg text-[.9rem] flex items-center">
<div class="flex flex-col grow"> <div class="flex flex-col grow">
@@ -110,23 +112,38 @@ function goToUpgrade() {
<div> <div>
<LyxUiButton type="outline" @click="goToUpgrade()"> Upgrade </LyxUiButton> <LyxUiButton type="outline" @click="goToUpgrade()"> Upgrade </LyxUiButton>
</div> </div>
</div> </div>
<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
</div>
<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>
<LyxUiButton type="outline" @click="goToUpgrade()"> Upgrade </LyxUiButton>
</div>
</div>
</div> </div>
<!-- <DashboardTopSection></DashboardTopSection> <DashboardTopSection></DashboardTopSection>
<DashboardTopCards :key="refreshKey"></DashboardTopCards> --> <DashboardTopCards :key="refreshKey"></DashboardTopCards>
<div class="mt-6 px-6 flex gap-6 flex-col 2xl:flex-row w-full"> <div class="mt-6 px-6 flex gap-6 flex-col 2xl:flex-row w-full">
<DashboardActionableChart :key="refreshKey"></DashboardActionableChart> <DashboardActionableChart :key="refreshKey"></DashboardActionableChart>
</div> </div>
<!--
<div class="mt-6 px-6 flex gap-6 flex-col 2xl:flex-row"> <div class="mt-6 px-6 flex gap-6 flex-col 2xl:flex-row">
<!-- <CardTitled :key="refreshKey" class="p-4 flex-1 w-full" title="Visits trends" <CardTitled :key="refreshKey" class="p-4 flex-1 w-full" title="Visits trends"
sub="Shows trends in page visits."> sub="Shows trends in page visits.">
<template #header> <template #header>
<SelectButton @changeIndex="mainChartSelectIndex = $event" :currentIndex="mainChartSelectIndex" <SelectButton @changeIndex="mainChartSelectIndex = $event" :currentIndex="mainChartSelectIndex"
@@ -137,24 +154,23 @@ function goToUpgrade() {
<DashboardVisitsLineChart :slice="(selectLabels[mainChartSelectIndex].value as any)"> <DashboardVisitsLineChart :slice="(selectLabels[mainChartSelectIndex].value as any)">
</DashboardVisitsLineChart> </DashboardVisitsLineChart>
</div> </div>
</CardTitled> --> </CardTitled>
<!-- <CardTitled :key="refreshKey" class="p-4 flex-1 w-full" title="Sessions" <CardTitled :key="refreshKey" class="p-4 flex-1 w-full" title="Sessions" sub="Shows trends in sessions.">
sub="Shows trends in sessions."> <template #header>
<template #header>
<SelectButton @changeIndex="sessionsChartSelectIndex = $event" <SelectButton @changeIndex="sessionsChartSelectIndex = $event"
:currentIndex="sessionsChartSelectIndex" :options="selectLabels"> :currentIndex="sessionsChartSelectIndex" :options="selectLabels">
</SelectButton> </SelectButton>
</template> </template>
<div> <div>
<DashboardSessionsLineChart :slice="(selectLabels[sessionsChartSelectIndex].value as any)"> <DashboardSessionsLineChart :slice="(selectLabels[sessionsChartSelectIndex].value as any)">
</DashboardSessionsLineChart> </DashboardSessionsLineChart>
</div> </div>
</CardTitled> --> </CardTitled>
</div> </div> -->
<!-- <div class="flex w-full justify-center mt-6 px-6"> <div class="flex w-full justify-center mt-6 px-6">
<div class="flex w-full gap-6 flex-col xl:flex-row"> <div class="flex w-full gap-6 flex-col xl:flex-row">
<div class="flex-1"> <div class="flex-1">
<DashboardWebsitesBarCard :key="refreshKey"></DashboardWebsitesBarCard> <DashboardWebsitesBarCard :key="refreshKey"></DashboardWebsitesBarCard>
@@ -195,7 +211,7 @@ function goToUpgrade() {
<div class="flex-1"> <div class="flex-1">
</div> </div>
</div> </div>
</div> --> </div>
</div> </div>

View File

@@ -81,6 +81,33 @@ function handleOnError(errorResponse: any) {
alert('Error' + errorResponse); alert('Error' + errorResponse);
}; };
function getRandomHex(size: number) {
const bytes = new Uint8Array(size);
window.crypto.getRandomValues(bytes);
return Array.from(bytes)
.map((byte) => byte.toString(16).padStart(2, '0'))
.join('');
}
function githubLogin() {
const client_id = config.public.GITHUB_CLIENT_ID;
const redirect_uri = window.location.origin + '/api';
console.log({ redirect_uri })
const state = getRandomHex(16);
localStorage.setItem("latestCSRFToken", state);
const link = `https://github.com/login/oauth/authorize?client_id=${client_id}&response_type=code&scope=repo&redirect_uri=${redirect_uri}/integrations/github/oauth2/callback&state=${state}`;
window.location.assign(link);
}
const route = useRoute();
onMounted(() => {
if (route.query.github_access_token) {
//TODO: Something
}
})
</script> </script>
@@ -103,23 +130,34 @@ function handleOnError(errorResponse: any) {
</div> </div>
<div class="text-text/80 text-[1.2rem] text-center w-[70%] poppins mt-2"> <div class="text-text/80 text-[1.2rem] text-center w-[70%] poppins mt-2">
Real-time analytics for 15+ JS/TS frameworks Track web analytics and custom events
<br> <br>
with one-line code setup. with extreme simplicity in under 30 sec.
<br> <br>
<div class="font-bold poppins mt-4"> <!-- <div class="font-bold poppins mt-4">
Start for Free now! Up to 3k visits/events monthly. Start for Free now! Up to 3k visits/events monthly.
</div> </div> -->
</div> </div>
<div class="mt-12"> <div class="mt-12">
<div v-if="!isNoAuth" @click="login"
class="hover:bg-accent cursor-pointer flex text-[1.3rem] gap-4 items-center border-[1px] border-gray-400 rounded-lg px-8 py-3 relative z-[2]"> <div v-if="!isNoAuth" class="flex flex-col gap-2">
<div class="flex items-center"> <div @click="login"
<i class="fab fa-google"></i> class="hover:bg-accent cursor-pointer flex text-[1.3rem] gap-4 items-center border-[1px] border-gray-400 rounded-lg px-8 py-3 relative z-[2]">
<div class="flex items-center">
<i class="fab fa-google"></i>
</div>
Continue with Google
</div>
<div
class=" opacity-35 cursor-not-allowed flex text-[1.3rem] gap-4 items-center border-[1px] border-gray-400 rounded-lg px-8 py-3 relative z-[2]">
<div class="flex items-center">
<i class="fab fa-github"></i>
</div>
Continue with GitHub
</div> </div>
Continue with Google
</div> </div>
<div v-if="isNoAuth" @click="loginWithoutAuth" <div v-if="isNoAuth" @click="loginWithoutAuth"
@@ -133,7 +171,7 @@ function handleOnError(errorResponse: any) {
</div> </div>
<div class="text-[.9rem] poppins mt-12 text-text-sub text-center relative z-[2]"> <div class="text-[.9rem] poppins mt-12 text-text-sub text-center relative z-[2]">
By continuing you are indicating that you accept By continuing you are accepting
<br> <br>
our our
<a class="underline" href="https://litlyx.com/terms" target="_blank">Terms of Service</a> and <a class="underline" href="https://litlyx.com/terms" target="_blank">Terms of Service</a> and

View File

@@ -0,0 +1,72 @@
import { createUserJwt } from '~/server/AuthManager';
import { UserModel } from '@schema/UserSchema';
import EmailService from '@services/EmailService';
const config = useRuntimeConfig();
export default defineEventHandler(async event => {
const { code } = getQuery(event);
console.log('CODE', code);
const redirect_uri = 'http://127.0.0.1:3000'
const res = await fetch(`https://github.com/login/oauth/access_token?client_id=${config.GITHUB_AUTH_CLIENT_ID}&client_secret=${config.GITHUB_AUTH_CLIENT_SECRET}&code=${code}&redirect_url=${redirect_uri}`, {
headers: {
"Accept": "application/json",
"Accept-Encoding": "application/json",
},
});
const data = await res.json();
const access_token = data.access_token;
console.log(data);
return sendRedirect(event,`http://127.0.0.1:3000/login?github_access_token=${access_token}`)
// const origin = event.headers.get('origin');
// const tokenResponse = await client.getToken({
// code: body.code,
// redirect_uri: origin || ''
// });
// const tokens = tokenResponse.tokens;
// const ticket = await client.verifyIdToken({
// idToken: tokens.id_token || '',
// audience: GOOGLE_AUTH_CLIENT_ID,
// });
// const payload = ticket.getPayload();
// if (!payload) return { error: true, access_token: '' };
// const user = await UserModel.findOne({ email: payload.email });
// if (user) return { error: false, access_token: createUserJwt({ email: user.email, name: user.name }) }
// const newUser = new UserModel({
// email: payload.email,
// given_name: payload.given_name,
// name: payload.name,
// locale: payload.locale,
// picture: payload.picture,
// created_at: Date.now()
// });
// const savedUser = await newUser.save();
// setImmediate(() => {
// console.log('SENDING WELCOME EMAIL TO', payload.email);
// if (payload.email) EmailService.sendWelcomeEmail(payload.email);
// });
// return { error: false, access_token: createUserJwt({ email: savedUser.email, name: savedUser.name }) }
});

View File

@@ -2,7 +2,7 @@ import { EventModel } from "@schema/metrics/EventSchema";
import { getTimeline } from "./generic"; import { getTimeline } from "./generic";
import { Redis, TIMELINE_EXPIRE_TIME } from "~/server/services/CacheService"; import { Redis, TIMELINE_EXPIRE_TIME } from "~/server/services/CacheService";
import { getUserProjectFromId } from "~/server/LIVE_DEMO_DATA"; import { getUserProjectFromId } from "~/server/LIVE_DEMO_DATA";
import { executeTimelineAggregation, fillAndMergeTimelineAggregation } from "~/server/services/TimelineService"; import { executeTimelineAggregation, fillAndMergeTimelineAggregation, fillAndMergeTimelineAggregationV2 } from "~/server/services/TimelineService";
export default defineEventHandler(async event => { export default defineEventHandler(async event => {
const project_id = getRequestProjectId(event); const project_id = getRequestProjectId(event);
@@ -27,7 +27,7 @@ export default defineEventHandler(async event => {
model: EventModel, model: EventModel,
from, to, slice from, to, slice
}); });
const timelineFilledMerged = fillAndMergeTimelineAggregation(timelineData, slice); const timelineFilledMerged = fillAndMergeTimelineAggregationV2(timelineData, slice, from, to);
return timelineFilledMerged; return timelineFilledMerged;
}); });

View File

@@ -1,9 +1,7 @@
import { getTimeline } from "./generic";
import { VisitModel } from "@schema/metrics/VisitSchema"; import { VisitModel } from "@schema/metrics/VisitSchema";
import DateService from "@services/DateService";
import { Redis, TIMELINE_EXPIRE_TIME } from "~/server/services/CacheService"; import { Redis, TIMELINE_EXPIRE_TIME } from "~/server/services/CacheService";
import { getUserProjectFromId } from "~/server/LIVE_DEMO_DATA"; import { getUserProjectFromId } from "~/server/LIVE_DEMO_DATA";
import { executeAdvancedTimelineAggregation, fillAndMergeTimelineAggregation } from "~/server/services/TimelineService"; import { executeAdvancedTimelineAggregation, fillAndMergeTimelineAggregationV2 } from "~/server/services/TimelineService";
export default defineEventHandler(async event => { export default defineEventHandler(async event => {
const project_id = getRequestProjectId(event); const project_id = getRequestProjectId(event);
@@ -31,7 +29,7 @@ export default defineEventHandler(async event => {
referrer referrer
} }
}); });
const timelineFilledMerged = fillAndMergeTimelineAggregation(timelineData, slice); const timelineFilledMerged = fillAndMergeTimelineAggregationV2(timelineData, slice, from, to);
return timelineFilledMerged; return timelineFilledMerged;
}); });

View File

@@ -2,7 +2,7 @@ import { getTimeline } from "./generic";
import { Redis, TIMELINE_EXPIRE_TIME } from "~/server/services/CacheService"; import { Redis, TIMELINE_EXPIRE_TIME } from "~/server/services/CacheService";
import { getUserProjectFromId } from "~/server/LIVE_DEMO_DATA"; import { getUserProjectFromId } from "~/server/LIVE_DEMO_DATA";
import { SessionModel } from "@schema/metrics/SessionSchema"; import { SessionModel } from "@schema/metrics/SessionSchema";
import { executeTimelineAggregation, fillAndMergeTimelineAggregation } from "~/server/services/TimelineService"; import { executeTimelineAggregation, fillAndMergeTimelineAggregationV2 } from "~/server/services/TimelineService";
export default defineEventHandler(async event => { export default defineEventHandler(async event => {
const project_id = getRequestProjectId(event); const project_id = getRequestProjectId(event);
@@ -28,7 +28,7 @@ export default defineEventHandler(async event => {
model: SessionModel, model: SessionModel,
from, to, slice from, to, slice
}); });
const timelineFilledMerged = fillAndMergeTimelineAggregation(timelineData, slice); const timelineFilledMerged = fillAndMergeTimelineAggregationV2(timelineData, slice, from, to);
return timelineFilledMerged; return timelineFilledMerged;
}); });

View File

@@ -2,7 +2,7 @@ import { getTimeline } from "./generic";
import { Redis, TIMELINE_EXPIRE_TIME } from "~/server/services/CacheService"; import { Redis, TIMELINE_EXPIRE_TIME } from "~/server/services/CacheService";
import { getUserProjectFromId } from "~/server/LIVE_DEMO_DATA"; import { getUserProjectFromId } from "~/server/LIVE_DEMO_DATA";
import { SessionModel } from "@schema/metrics/SessionSchema"; import { SessionModel } from "@schema/metrics/SessionSchema";
import { executeAdvancedTimelineAggregation, executeTimelineAggregation, fillAndMergeTimelineAggregation } from "~/server/services/TimelineService"; import { executeAdvancedTimelineAggregation, fillAndMergeTimelineAggregationV2 } from "~/server/services/TimelineService";
export default defineEventHandler(async event => { export default defineEventHandler(async event => {
const project_id = getRequestProjectId(event); const project_id = getRequestProjectId(event);
@@ -45,7 +45,7 @@ export default defineEventHandler(async event => {
count: { $divide: ["$duration", "$count"] } count: { $divide: ["$duration", "$count"] }
}, },
}); });
const timelineFilledMerged = fillAndMergeTimelineAggregation(timelineData, slice); const timelineFilledMerged = fillAndMergeTimelineAggregationV2(timelineData, slice, from ,to);
return timelineFilledMerged; return timelineFilledMerged;
}); });

View File

@@ -2,7 +2,7 @@ import { VisitModel } from "@schema/metrics/VisitSchema";
import { Redis, TIMELINE_EXPIRE_TIME } from "~/server/services/CacheService"; import { Redis, TIMELINE_EXPIRE_TIME } from "~/server/services/CacheService";
import { getUserProjectFromId } from "~/server/LIVE_DEMO_DATA"; import { getUserProjectFromId } from "~/server/LIVE_DEMO_DATA";
import DateService from "@services/DateService"; import DateService from "@services/DateService";
import { executeTimelineAggregation, fillAndMergeTimelineAggregation } from "~/server/services/TimelineService"; import { executeTimelineAggregation, fillAndMergeTimelineAggregationV2 } from "~/server/services/TimelineService";
export default defineEventHandler(async event => { export default defineEventHandler(async event => {
const project_id = getRequestProjectId(event); const project_id = getRequestProjectId(event);
@@ -28,11 +28,9 @@ export default defineEventHandler(async event => {
model: VisitModel, model: VisitModel,
from, to, slice, from, to, slice,
}); });
const timelineFilledMerged = fillAndMergeTimelineAggregation(timelineData, slice); const timelineFilledMerged = fillAndMergeTimelineAggregationV2(timelineData, slice, from, to);
return timelineFilledMerged; return timelineFilledMerged;
}); });
}); });

View File

@@ -61,4 +61,10 @@ export function fillAndMergeTimelineAggregation(timeline: { _id: string, count:
const filledDates = DateService.fillDates(timeline.map(e => e._id), slice); const filledDates = DateService.fillDates(timeline.map(e => e._id), slice);
const merged = DateService.mergeFilledDates(filledDates, timeline, '_id', slice, { count: 0 }); const merged = DateService.mergeFilledDates(filledDates, timeline, '_id', slice, { count: 0 });
return merged; return merged;
}
export function fillAndMergeTimelineAggregationV2(timeline: { _id: string, count: number }[], slice: Slice, from: string, to: string) {
const filledDates = DateService.createBetweenDates(from, to, slice);
const merged = DateService.mergeFilledDates(filledDates.dates, timeline, '_id', slice, { count: 0 });
return merged;
} }

View File

@@ -102,6 +102,18 @@ class DateService {
} }
} }
createBetweenDates(from: string, to: string, slice: Slice) {
let start = dayjs(from);
const end = dayjs(to);
const filledDates: dayjs.Dayjs[] = [];
while (start.isBefore(end) || start.isSame(end)) {
filledDates.push(start);
start = start.add(1, slice);
}
return { dates: filledDates, from, to };
}
fillDates(dates: string[], slice: Slice) { fillDates(dates: string[], slice: Slice) {
const allDates: dayjs.Dayjs[] = []; const allDates: dayjs.Dayjs[] = [];
const firstDate = dayjs(dates.at(0)); const firstDate = dayjs(dates.at(0));
@@ -109,7 +121,7 @@ class DateService {
let currentDate = firstDate.clone(); let currentDate = firstDate.clone();
allDates.push(currentDate); allDates.push(currentDate);
while (currentDate.isBefore(lastDate, slice)) { while (currentDate.isBefore(lastDate, slice)) {
currentDate = currentDate.add(1, slice); currentDate = currentDate.add(1, slice);
allDates.push(currentDate); allDates.push(currentDate);
@@ -121,7 +133,7 @@ class DateService {
mergeFilledDates<T extends Record<string, any>, K extends keyof T>(dates: dayjs.Dayjs[], items: T[], dateField: K, slice: Slice, fillData: Omit<T, K>) { mergeFilledDates<T extends Record<string, any>, K extends keyof T>(dates: dayjs.Dayjs[], items: T[], dateField: K, slice: Slice, fillData: Omit<T, K>) {
const result = new Array<T>(); const result = new Array<T>();
for (const date of dates) { for (const date of dates) {
const item = items.find(e => dayjs(e[dateField]).isSame(date), slice); const item = items.find(e => dayjs(e[dateField]).isSame(date, slice));
result.push(item ?? { ...fillData, [dateField]: date.format() } as T); result.push(item ?? { ...fillData, [dateField]: date.format() } as T);
} }
return result; return result;