mirror of
https://github.com/Litlyx/litlyx
synced 2025-12-10 07:48:37 +01:00
fix dates
This commit is contained in:
@@ -134,11 +134,11 @@ const pricingDrawer = usePricingDrawer();
|
||||
}">
|
||||
<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;">
|
||||
Upgrade plan
|
||||
</LyxUiButton>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
|
||||
<div class="flex px-2 flex-col">
|
||||
|
||||
@@ -107,10 +107,11 @@ function externalTooltipHandler(context: { chart: any, tooltip: TooltipModel<'li
|
||||
const { chart, tooltip } = context;
|
||||
const tooltipEl = externalTooltipElement.value;
|
||||
|
||||
currentTooltipData.value = [0,0,0,''];
|
||||
currentTooltipData.value.push(...tooltip.dataPoints.map(e => e.raw) as number[]);
|
||||
currentTooltipData.value[2] = ((tooltip.dataPoints[2]?.raw as any)?.r2 || 0) as number;
|
||||
currentTooltipData.value[3] = new Date(allDatesFull.value[tooltip.dataPoints[0].dataIndex]).toLocaleDateString();
|
||||
currentTooltipData.value.visits = (tooltip.dataPoints.find(e=> e.datasetIndex == 0)?.raw) as number;
|
||||
currentTooltipData.value.sessions = (tooltip.dataPoints.find(e=> e.datasetIndex == 1)?.raw) as number;
|
||||
currentTooltipData.value.events = ((tooltip.dataPoints.find(e=> e.datasetIndex == 2)?.raw) as any)?.r2 as number;
|
||||
|
||||
currentTooltipData.value.date = new Date(allDatesFull.value[tooltip.dataPoints[0].dataIndex]).toLocaleDateString();
|
||||
|
||||
if (!tooltipEl) return;
|
||||
if (tooltip.opacity === 0) {
|
||||
@@ -147,7 +148,6 @@ function transformResponse(input: { _id: string, count: number }[]) {
|
||||
}
|
||||
|
||||
const body = computed(() => {
|
||||
console.log('INDEX IS', selectedLabelIndex.value, 'VALUE IS', selectLabels[selectedLabelIndex.value].value)
|
||||
return {
|
||||
from: safeSnapshotDates.value.from,
|
||||
to: safeSnapshotDates.value.to,
|
||||
@@ -214,7 +214,8 @@ function onDataReady() {
|
||||
chartData.value.datasets[0].data = visitsData.data.value.data;
|
||||
chartData.value.datasets[1].data = sessionsData.data.value.data;
|
||||
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')];
|
||||
@@ -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) {
|
||||
dataset.hidden = !checked;
|
||||
@@ -273,14 +281,14 @@ onMounted(async () => {
|
||||
<LyxUiCard>
|
||||
<div class="flex gap-2 items-center">
|
||||
<div> Date: </div>
|
||||
<div v-if="currentTooltipData"> {{ currentTooltipData[3] }}</div>
|
||||
<div v-if="currentTooltipData"> {{ currentTooltipData.date }}</div>
|
||||
</div>
|
||||
<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>
|
||||
<div> {{ dataset.label }}</div>
|
||||
<div v-if="currentTooltipData" class="grow text-right px-4">
|
||||
{{ currentTooltipData[index] }}
|
||||
{{ (currentTooltipData as any)[tooltipNameIndex[index]] }}
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div class="bg-lyx-background-lighter h-[2px] w-full my-2"> </div> -->
|
||||
|
||||
@@ -43,6 +43,8 @@ export default defineNuxtConfig({
|
||||
AUTH_JWT_SECRET: process.env.AUTH_JWT_SECRET,
|
||||
GOOGLE_AUTH_CLIENT_ID: process.env.GOOGLE_AUTH_CLIENT_ID,
|
||||
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_WH_SECRET: process.env.STRIPE_WH_SECRET,
|
||||
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_NAME: process.env.NOAUTH_USER_NAME,
|
||||
public: {
|
||||
AUTH_MODE: process.env.AUTH_MODE
|
||||
AUTH_MODE: process.env.AUTH_MODE,
|
||||
GITHUB_CLIENT_ID: process.env.GITHUB_AUTH_CLIENT_ID || 'NONE'
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
@@ -78,6 +78,10 @@ const { snapshot } = useSnapshot();
|
||||
|
||||
const refreshKey = computed(() => `${snapshot.value._id.toString() + activeProject.value?._id.toString()}`);
|
||||
|
||||
const isPremium = computed(() => {
|
||||
return activeProject.value?.premium;
|
||||
})
|
||||
|
||||
const pricingDrawer = usePricingDrawer();
|
||||
|
||||
function goToUpgrade() {
|
||||
@@ -93,9 +97,7 @@ function goToUpgrade() {
|
||||
|
||||
<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"
|
||||
class="w-full bg-[#fbbf2422] p-4 rounded-lg text-[.9rem] flex items-center">
|
||||
<div class="flex flex-col grow">
|
||||
@@ -110,23 +112,38 @@ function goToUpgrade() {
|
||||
<div>
|
||||
<LyxUiButton type="outline" @click="goToUpgrade()"> Upgrade </LyxUiButton>
|
||||
</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>
|
||||
<DashboardTopCards :key="refreshKey"></DashboardTopCards> -->
|
||||
<DashboardTopSection></DashboardTopSection>
|
||||
<DashboardTopCards :key="refreshKey"></DashboardTopCards>
|
||||
|
||||
|
||||
|
||||
<div class="mt-6 px-6 flex gap-6 flex-col 2xl:flex-row w-full">
|
||||
<DashboardActionableChart :key="refreshKey"></DashboardActionableChart>
|
||||
</div>
|
||||
|
||||
<!--
|
||||
<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.">
|
||||
<template #header>
|
||||
<SelectButton @changeIndex="mainChartSelectIndex = $event" :currentIndex="mainChartSelectIndex"
|
||||
@@ -137,10 +154,9 @@ function goToUpgrade() {
|
||||
<DashboardVisitsLineChart :slice="(selectLabels[mainChartSelectIndex].value as any)">
|
||||
</DashboardVisitsLineChart>
|
||||
</div>
|
||||
</CardTitled> -->
|
||||
</CardTitled>
|
||||
|
||||
<!-- <CardTitled :key="refreshKey" class="p-4 flex-1 w-full" title="Sessions"
|
||||
sub="Shows trends in sessions.">
|
||||
<CardTitled :key="refreshKey" class="p-4 flex-1 w-full" title="Sessions" sub="Shows trends in sessions.">
|
||||
<template #header>
|
||||
<SelectButton @changeIndex="sessionsChartSelectIndex = $event"
|
||||
:currentIndex="sessionsChartSelectIndex" :options="selectLabels">
|
||||
@@ -150,11 +166,11 @@ function goToUpgrade() {
|
||||
<DashboardSessionsLineChart :slice="(selectLabels[sessionsChartSelectIndex].value as any)">
|
||||
</DashboardSessionsLineChart>
|
||||
</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-1">
|
||||
<DashboardWebsitesBarCard :key="refreshKey"></DashboardWebsitesBarCard>
|
||||
@@ -195,7 +211,7 @@ function goToUpgrade() {
|
||||
<div class="flex-1">
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
@@ -81,6 +81,33 @@ function handleOnError(errorResponse: any) {
|
||||
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>
|
||||
|
||||
|
||||
@@ -103,18 +130,20 @@ function handleOnError(errorResponse: any) {
|
||||
</div>
|
||||
|
||||
<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>
|
||||
with one-line code setup.
|
||||
with extreme simplicity in under 30 sec.
|
||||
<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.
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
|
||||
<div class="mt-12">
|
||||
|
||||
<div v-if="!isNoAuth" @click="login"
|
||||
|
||||
<div v-if="!isNoAuth" class="flex flex-col gap-2">
|
||||
<div @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 class="flex items-center">
|
||||
<i class="fab fa-google"></i>
|
||||
@@ -122,6 +151,15 @@ function handleOnError(errorResponse: any) {
|
||||
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>
|
||||
|
||||
<div v-if="isNoAuth" @click="loginWithoutAuth"
|
||||
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">
|
||||
@@ -133,7 +171,7 @@ function handleOnError(errorResponse: any) {
|
||||
</div>
|
||||
|
||||
<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>
|
||||
our
|
||||
<a class="underline" href="https://litlyx.com/terms" target="_blank">Terms of Service</a> and
|
||||
|
||||
72
dashboard/server/api/integrations/github/oauth2/callback.ts
Normal file
72
dashboard/server/api/integrations/github/oauth2/callback.ts
Normal 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 }) }
|
||||
|
||||
});
|
||||
@@ -2,7 +2,7 @@ import { EventModel } from "@schema/metrics/EventSchema";
|
||||
import { getTimeline } from "./generic";
|
||||
import { Redis, TIMELINE_EXPIRE_TIME } from "~/server/services/CacheService";
|
||||
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 => {
|
||||
const project_id = getRequestProjectId(event);
|
||||
@@ -27,7 +27,7 @@ export default defineEventHandler(async event => {
|
||||
model: EventModel,
|
||||
from, to, slice
|
||||
});
|
||||
const timelineFilledMerged = fillAndMergeTimelineAggregation(timelineData, slice);
|
||||
const timelineFilledMerged = fillAndMergeTimelineAggregationV2(timelineData, slice, from, to);
|
||||
return timelineFilledMerged;
|
||||
});
|
||||
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import { getTimeline } from "./generic";
|
||||
import { VisitModel } from "@schema/metrics/VisitSchema";
|
||||
import DateService from "@services/DateService";
|
||||
import { Redis, TIMELINE_EXPIRE_TIME } from "~/server/services/CacheService";
|
||||
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 => {
|
||||
const project_id = getRequestProjectId(event);
|
||||
@@ -31,7 +29,7 @@ export default defineEventHandler(async event => {
|
||||
referrer
|
||||
}
|
||||
});
|
||||
const timelineFilledMerged = fillAndMergeTimelineAggregation(timelineData, slice);
|
||||
const timelineFilledMerged = fillAndMergeTimelineAggregationV2(timelineData, slice, from, to);
|
||||
return timelineFilledMerged;
|
||||
});
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { getTimeline } from "./generic";
|
||||
import { Redis, TIMELINE_EXPIRE_TIME } from "~/server/services/CacheService";
|
||||
import { getUserProjectFromId } from "~/server/LIVE_DEMO_DATA";
|
||||
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 => {
|
||||
const project_id = getRequestProjectId(event);
|
||||
@@ -28,7 +28,7 @@ export default defineEventHandler(async event => {
|
||||
model: SessionModel,
|
||||
from, to, slice
|
||||
});
|
||||
const timelineFilledMerged = fillAndMergeTimelineAggregation(timelineData, slice);
|
||||
const timelineFilledMerged = fillAndMergeTimelineAggregationV2(timelineData, slice, from, to);
|
||||
return timelineFilledMerged;
|
||||
});
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { getTimeline } from "./generic";
|
||||
import { Redis, TIMELINE_EXPIRE_TIME } from "~/server/services/CacheService";
|
||||
import { getUserProjectFromId } from "~/server/LIVE_DEMO_DATA";
|
||||
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 => {
|
||||
const project_id = getRequestProjectId(event);
|
||||
@@ -45,7 +45,7 @@ export default defineEventHandler(async event => {
|
||||
count: { $divide: ["$duration", "$count"] }
|
||||
},
|
||||
});
|
||||
const timelineFilledMerged = fillAndMergeTimelineAggregation(timelineData, slice);
|
||||
const timelineFilledMerged = fillAndMergeTimelineAggregationV2(timelineData, slice, from ,to);
|
||||
return timelineFilledMerged;
|
||||
});
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { VisitModel } from "@schema/metrics/VisitSchema";
|
||||
import { Redis, TIMELINE_EXPIRE_TIME } from "~/server/services/CacheService";
|
||||
import { getUserProjectFromId } from "~/server/LIVE_DEMO_DATA";
|
||||
import DateService from "@services/DateService";
|
||||
import { executeTimelineAggregation, fillAndMergeTimelineAggregation } from "~/server/services/TimelineService";
|
||||
import { executeTimelineAggregation, fillAndMergeTimelineAggregationV2 } from "~/server/services/TimelineService";
|
||||
|
||||
export default defineEventHandler(async event => {
|
||||
const project_id = getRequestProjectId(event);
|
||||
@@ -28,11 +28,9 @@ export default defineEventHandler(async event => {
|
||||
model: VisitModel,
|
||||
from, to, slice,
|
||||
});
|
||||
const timelineFilledMerged = fillAndMergeTimelineAggregation(timelineData, slice);
|
||||
const timelineFilledMerged = fillAndMergeTimelineAggregationV2(timelineData, slice, from, to);
|
||||
return timelineFilledMerged;
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
});
|
||||
@@ -62,3 +62,9 @@ export function fillAndMergeTimelineAggregation(timeline: { _id: string, count:
|
||||
const merged = DateService.mergeFilledDates(filledDates, timeline, '_id', slice, { count: 0 });
|
||||
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;
|
||||
}
|
||||
@@ -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) {
|
||||
const allDates: dayjs.Dayjs[] = [];
|
||||
const firstDate = dayjs(dates.at(0));
|
||||
@@ -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>) {
|
||||
const result = new Array<T>();
|
||||
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);
|
||||
}
|
||||
return result;
|
||||
|
||||
Reference in New Issue
Block a user