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="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">

View File

@@ -106,11 +106,12 @@ const externalTooltipElement = ref<null | HTMLDivElement>(null);
function externalTooltipHandler(context: { chart: any, tooltip: TooltipModel<'line' | 'bar'> }) {
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> -->

View File

@@ -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'
}
},

View File

@@ -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>
<!-- <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,24 +154,23 @@ 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.">
<template #header>
<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">
</SelectButton>
</template>
<div>
<DashboardSessionsLineChart :slice="(selectLabels[sessionsChartSelectIndex].value as any)">
</DashboardSessionsLineChart>
</div>
</CardTitled> -->
<div>
<DashboardSessionsLineChart :slice="(selectLabels[sessionsChartSelectIndex].value as any)">
</DashboardSessionsLineChart>
</div>
</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>

View File

@@ -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,23 +130,34 @@ 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"
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 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>
</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>
Continue with Google
</div>
<div v-if="isNoAuth" @click="loginWithoutAuth"
@@ -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

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 { 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;
});

View File

@@ -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;
});

View File

@@ -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;
});

View File

@@ -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;
});

View File

@@ -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;
});
});

View File

@@ -61,4 +61,10 @@ export function fillAndMergeTimelineAggregation(timeline: { _id: string, count:
const filledDates = DateService.fillDates(timeline.map(e => e._id), slice);
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;
}