fix ui + sessions + reactivity

This commit is contained in:
Emily
2024-08-06 15:32:46 +02:00
parent 46774bd114
commit 02db836003
14 changed files with 150 additions and 69 deletions

View File

@@ -97,6 +97,11 @@ async function process_keep_alive(data: Record<string, string>, sessionHash: str
const { pid, instant, flowHash } = data;
const existingSession = await SessionModel.findOne({ project_id: pid }, { _id: 1 });
if (!existingSession) {
await ProjectCountModel.updateOne({ project_id: pid }, { $inc: { 'sessions': 1 } }, { upsert: true });
}
if (instant == "true") {
await SessionModel.updateOne({ project_id: pid, session: sessionHash, }, {
$inc: { duration: 0 },

View File

@@ -98,6 +98,15 @@ function onLogout() {
const { projects } = useProjectsList();
const activeProject = useActiveProject();
const { data: maxProjects } = useFetch("/api/user/max_projects", {
headers: computed(() => {
return {
Authorization: authorizationHeaderComputed.value
}
})
});
const selected = ref<TProject>(activeProject.value as TProject);
watch(selected, () => {
setActiveProject(selected.value._id.toString())
@@ -112,13 +121,10 @@ watch(selected, () => {
'hidden lg:flex': !isOpen
}">
<div class="py-4 px-2 gap-6 flex flex-col w-full">
<div class="flex items-center gap-2 ml-2">
<!-- <div class="bg-black h-[2.4rem] aspect-[1/1] flex items-center justify-center rounded-lg">
<img class="h-[2rem]" :src="'/logo.png'">
</div> -->
<div class="flex px-2 flex-col">
<!-- <div class="font-bold text-[1.4rem] text-gray-300"> Litlyx </div> -->
<div class="flex items-center gap-2 w-full">
<USelectMenu :uiMenu="{
select: '!bg-lyx-widget-light !shadow-none focus:!ring-lyx-widget-lighter !ring-lyx-widget-lighter',
@@ -148,20 +154,24 @@ watch(selected, () => {
</template>
</USelectMenu>
<div class="grow flex justify-end text-[1.4rem] mr-2 lg:hidden">
<i @click="close()" class="fas fa-close"></i>
</div>
</div>
<div class="px-2 w-full flex-col">
<NuxtLink to="/project_creation" v-if="projects && (projects.length < (maxProjects || 1))"
class="flex items-center text-[.8rem] gap-1 justify-end pt-2 pr-2 text-lyx-text-dark hover:text-lyx-text cursor-pointer">
<div><i class="fas fa-plus"></i></div>
<div> Create new project </div>
</NuxtLink>
<div class="flex mb-2 px-2 items-center justify-between">
</div>
<div class="w-full flex-col px-2">
<div class="flex mb-2 items-center justify-between">
<div class="poppins text-[.8rem]">
Snapshots
</div>
@@ -196,7 +206,7 @@ watch(selected, () => {
</template>
</USelectMenu>
<div v-if="snapshot" class="flex flex-col text-[.8rem] mt-2 px-2">
<div v-if="snapshot" class="flex flex-col text-[.8rem] mt-2">
<div class="flex">
<div class="grow poppins"> From:</div>
<div class="poppins"> {{ new Date(snapshot.from).toLocaleString('it-IT').split(',')[0].trim() }}
@@ -235,7 +245,9 @@ watch(selected, () => {
</div>
</div>
<div class="flex flex-col gap-4 h-full">
<div class="bg-lyx-widget-lighter h-[2px] w-full"></div>
<div class="flex flex-col h-full">
<div v-for="section of sections" class="flex flex-col gap-1">
@@ -266,10 +278,10 @@ watch(selected, () => {
</div>
<div class="grow"></div>
<div class="text-lyx-text-dark poppins text-[.8rem] px-4">
<div class="text-lyx-text-dark poppins text-[.8rem] px-4 pb-3">
Litlyx is in Beta version.
</div>
<div class="bg-lyx-widget-lighter h-[2px] px-4 w-full"></div>
<div class="bg-lyx-widget-lighter h-[2px] px-4 w-full mb-3"></div>
<div class="flex justify-end px-2">
<div class="grow flex gap-3">

View File

@@ -12,7 +12,7 @@ const activeTabIndex = ref<number>(0);
<div class="flex">
<div v-for="(tab, index) of items" @click="activeTabIndex = index"
class="px-6 pb-3 poppins font-medium text-lyx-text-darker border-b-[1px] border-lyx-text-darker" :class="{
'!border-[#88A7FF] text-[#88A7FF]': activeTabIndex === index,
'!border-[#88A7FF] !text-[#88A7FF]': activeTabIndex === index,
'hover:border-lyx-text-dark hover:text-lyx-text-dark cursor-pointer': activeTabIndex !== index
}">
{{ tab.label }}

View File

@@ -12,6 +12,10 @@ const entries: SettingsTemplateEntry[] = [
const activeProject = useActiveProject();
const projectNameInputVal = ref<string>(activeProject.value?.name || '');
watch(activeProject, () => {
projectNameInputVal.value = activeProject.value?.name || "";
})
const canChange = computed(() => {
if (activeProject.value?.name == projectNameInputVal.value) return false;
if (projectNameInputVal.value.length === 0) return false;
@@ -19,15 +23,25 @@ const canChange = computed(() => {
});
async function changeProjectName() {
await $fetch("/api/project/change_name", {
method: 'POST',
...signHeaders({ 'Content-Type': 'application/json' }),
body: JSON.stringify({ name: projectNameInputVal.value })
});
location.reload();
}
</script>
<template>
<SettingsTemplate :entries="entries">
<SettingsTemplate :entries="entries" :key="activeProject?.name || 'NONE'">
<template #pname>
<div class="flex items-center gap-4">
<LyxUiInput class="w-full px-4 py-2" v-model="projectNameInputVal"></LyxUiInput>
<LyxUiButton :disabled="!canChange" type="primary"> Change </LyxUiButton>
<LyxUiButton @click="changeProjectName()" :disabled="!canChange" type="primary"> Change </LyxUiButton>
</div>
</template>
<template #pid>

View File

@@ -22,6 +22,7 @@ type TProjectsGrouped = {
project_name: string,
total_visits: number,
total_events: number,
total_sessions: number
}[]
}
@@ -47,7 +48,8 @@ const projectsGrouped = computed(() => {
premium: project.premium,
project_name: project.project_name,
total_events: project.total_events,
total_visits: project.total_visits
total_visits: project.total_visits,
total_sessions: project.total_sessions
});
} else {
@@ -61,7 +63,8 @@ const projectsGrouped = computed(() => {
premium_type: project.premium_type,
project_name: project.project_name,
total_events: project.total_events,
total_visits: project.total_visits
total_visits: project.total_visits,
total_sessions: project.total_sessions
}]
}
@@ -71,6 +74,12 @@ const projectsGrouped = computed(() => {
}
result.sort((sa, sb) => {
const ca = sa.projects.reduce((a, e) => a + (e.total_visits + e.total_events), 0);
const cb = sb.projects.reduce((a, e) => a + (e.total_visits + e.total_events), 0);
return cb - ca;
})
return result;
});
@@ -107,7 +116,6 @@ const totalEvents = computed(() => {
return projects.value?.reduce((a, e) => a + e.total_events, 0) || 0;
});
const details = ref<any>();
const showDetails = ref<boolean>(false);
async function getProjectDetails(project_id: string) {
@@ -188,17 +196,17 @@ async function resetCount(project_id: string) {
<div> {{ project.total_visits }} </div>
<div> Events: </div>
<div> {{ project.total_events }} </div>
<div> Sessions: </div>
<div> {{ project.total_sessions }} </div>
</div>
<div class="flex gap-4">
<div class="bg-[#272727] hover:bg-[#313131] cursor-pointer px-8 py-2 mt-3 rounded-lg"
@click="getProjectDetails(project._id)">
Get details
</div>
<div class="bg-[#272727] hover:bg-[#313131] cursor-pointer px-8 py-2 mt-3 rounded-lg"
@click="resetCount(project._id)">
Reset counts
</div>
<div class="flex gap-4 items-center mt-4">
<LyxUiButton type="secondary" @click="getProjectDetails(project._id)">
Payment details
</LyxUiButton>
<LyxUiButton type="danger" @click="resetCount(project._id)">
Refresh counts
</LyxUiButton>
</div>
</div>

View File

@@ -14,7 +14,8 @@ export type AdminProjectsList = {
created_at: Date
},
total_visits: number,
total_events: number
total_events: number,
total_sessions: number
}
export default defineEventHandler(async event => {
@@ -54,6 +55,9 @@ export default defineEventHandler(async event => {
},
total_events: {
$arrayElemAt: ["$counts.events", 0]
},
total_sessions: {
$arrayElemAt: ["$counts.sessions", 0]
}
}
}

View File

@@ -1,6 +1,7 @@
import { ProjectCountModel } from "@schema/ProjectsCounts";
import { EventModel } from "@schema/metrics/EventSchema";
import { SessionModel } from "@schema/metrics/SessionSchema";
import { VisitModel } from "@schema/metrics/VisitSchema";
export default defineEventHandler(async event => {
@@ -13,8 +14,9 @@ export default defineEventHandler(async event => {
const events = await EventModel.countDocuments({ project_id });
const visits = await VisitModel.countDocuments({ project_id });
const sessions = await SessionModel.countDocuments({ project_id });
await ProjectCountModel.updateOne({ project_id, events, visits }, {}, { upsert: true });
await ProjectCountModel.updateOne({ project_id, events, visits, sessions }, {}, { upsert: true });
return { ok: true };
});

View File

@@ -36,12 +36,12 @@ export default defineEventHandler(async event => {
$group: {
_id: "$project_id",
events: { $sum: "$events" },
visits: { $sum: "$visits" }
visits: { $sum: "$visits" },
sessions: { $sum: "$sessions" },
}
}
]);
const sessionsVisitsCount: any[] = await Redis.useCache({
key: `counts:${project_id}:sessions_count`,
exp: COUNTS_SESSIONS_EXPIRE_TIME

View File

@@ -3,9 +3,7 @@ import StripeService from '~/server/services/StripeService';
import type Event from 'stripe';
import { ProjectModel } from '@schema/ProjectSchema';
import { PREMIUM_DATA, PREMIUM_PLAN, getPlanFromId, getPlanFromPrice, getPlanFromTag } from '@data/PREMIUM';
import { ProjectCountModel } from '@schema/ProjectsCounts';
import { ProjectLimitModel } from '@schema/ProjectsLimits';
import { UserModel } from '@schema/UserSchema';

View File

@@ -0,0 +1,30 @@
import { ProjectModel } from "@schema/ProjectSchema";
import { TeamMemberModel } from "@schema/TeamMemberSchema";
import { UserModel } from "@schema/UserSchema";
import { UserSettingsModel } from "@schema/UserSettings";
export default defineEventHandler(async event => {
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);
if (!project) return setResponseStatus(event, 400, 'Project not found');
if (project.owner.toString() != userData.id) {
return setResponseStatus(event, 400, 'You are not the owner');
}
const { name } = await readBody(event);
project.name = name;
await project.save();
return { ok: true };
});

View File

@@ -39,7 +39,8 @@ export default defineEventHandler(async event => {
await ProjectCountModel.create({
project_id: project._id,
events: 0,
visits: 0
visits: 0,
sessions: 0
});
await ProjectLimitModel.updateOne({ project_id: project._id }, {
@@ -76,7 +77,8 @@ export default defineEventHandler(async event => {
await ProjectCountModel.create({
project_id: project._id,
events: 0,
visits: 0
visits: 0,
sessions: 0
});
return project.toJSON() as TProject;

View File

@@ -21,11 +21,14 @@ export class Redis {
url: runtimeConfig.REDIS_URL,
username: runtimeConfig.REDIS_USERNAME,
password: runtimeConfig.REDIS_PASSWORD,
database: process.dev ? 1 : 0
database: process.dev ? 1 : 0,
});
static async init() {
await this.client.connect();
this.client.on('error', function (err) {
console.error('Redis error:', err);
});
}
static async setString(key: string, value: string, exp: number) {

View File

@@ -1,18 +1,19 @@
<script lang="ts" setup>
export type ButtonType = 'primary' | 'secondary' | 'outline' | 'danger';
export type ButtonType = 'primary' | 'secondary' | 'outline' | 'outlined' | 'danger';
const props = defineProps<{ type: ButtonType, link?: string, target?: string }>();
const props = defineProps<{ type: ButtonType, link?: string, target?: string, disabled?: boolean }>();
</script>
<template>
<NuxtLink tag="div" :to="link" :target="target"
<NuxtLink tag="div" :to="disabled ? '' : link" :target="target"
class="poppins w-fit cursor-pointer px-4 py-1 rounded-md outline outline-[1px] text-text" :class="{
'bg-lyx-primary-dark outline-lyx-primary hover:bg-lyx-primary-hover': type === 'primary',
'bg-lyx-widget-lighter outline-lyx-widget-lighter hover:bg-lyx-widget-light': type === 'secondary',
'bg-lyx-transparent outline-lyx-widget-lighter hover:bg-lyx-widget-light': type === 'outline',
'bg-lyx-transparent outline-lyx-widget-lighter hover:bg-lyx-widget-light': (type === 'outline' || type === 'outlined'),
'bg-lyx-danger-dark outline-lyx-danger hover:bg-lyx-danger': type === 'danger',
'!bg-lyx-widget !outline-lyx-widget-lighter !cursor-not-allowed': disabled === true,
}">
<slot></slot>
</NuxtLink>

View File

@@ -5,12 +5,14 @@ export type TProjectCount = {
project_id: Schema.Types.ObjectId,
events: number,
visits: number,
sessions: number,
}
const ProjectCountSchema = new Schema<TProjectCount>({
project_id: { type: Types.ObjectId, index: true, unique: true },
events: { type: Number, required: true, default: 0 },
visits: { type: Number, required: true, default: 0 },
sessions: { type: Number, required: true, default: 0 },
});
export const ProjectCountModel = model<TProjectCount>('project_counts', ProjectCountSchema);