mirror of
https://github.com/Litlyx/litlyx
synced 2025-12-10 07:48:37 +01:00
fix ui + sessions + reactivity
This commit is contained in:
@@ -97,6 +97,11 @@ async function process_keep_alive(data: Record<string, string>, sessionHash: str
|
|||||||
|
|
||||||
const { pid, instant, flowHash } = data;
|
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") {
|
if (instant == "true") {
|
||||||
await SessionModel.updateOne({ project_id: pid, session: sessionHash, }, {
|
await SessionModel.updateOne({ project_id: pid, session: sessionHash, }, {
|
||||||
$inc: { duration: 0 },
|
$inc: { duration: 0 },
|
||||||
|
|||||||
@@ -98,6 +98,15 @@ function onLogout() {
|
|||||||
const { projects } = useProjectsList();
|
const { projects } = useProjectsList();
|
||||||
const activeProject = useActiveProject();
|
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);
|
const selected = ref<TProject>(activeProject.value as TProject);
|
||||||
watch(selected, () => {
|
watch(selected, () => {
|
||||||
setActiveProject(selected.value._id.toString())
|
setActiveProject(selected.value._id.toString())
|
||||||
@@ -112,13 +121,10 @@ watch(selected, () => {
|
|||||||
'hidden lg:flex': !isOpen
|
'hidden lg:flex': !isOpen
|
||||||
}">
|
}">
|
||||||
<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 items-center gap-2 ml-2">
|
|
||||||
|
|
||||||
<!-- <div class="bg-black h-[2.4rem] aspect-[1/1] flex items-center justify-center rounded-lg">
|
<div class="flex px-2 flex-col">
|
||||||
<img class="h-[2rem]" :src="'/logo.png'">
|
|
||||||
</div> -->
|
|
||||||
|
|
||||||
<!-- <div class="font-bold text-[1.4rem] text-gray-300"> Litlyx </div> -->
|
<div class="flex items-center gap-2 w-full">
|
||||||
|
|
||||||
<USelectMenu :uiMenu="{
|
<USelectMenu :uiMenu="{
|
||||||
select: '!bg-lyx-widget-light !shadow-none focus:!ring-lyx-widget-lighter !ring-lyx-widget-lighter',
|
select: '!bg-lyx-widget-light !shadow-none focus:!ring-lyx-widget-lighter !ring-lyx-widget-lighter',
|
||||||
@@ -148,20 +154,24 @@ watch(selected, () => {
|
|||||||
</template>
|
</template>
|
||||||
</USelectMenu>
|
</USelectMenu>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div class="grow flex justify-end text-[1.4rem] mr-2 lg:hidden">
|
<div class="grow flex justify-end text-[1.4rem] mr-2 lg:hidden">
|
||||||
<i @click="close()" class="fas fa-close"></i>
|
<i @click="close()" class="fas fa-close"></i>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</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]">
|
<div class="poppins text-[.8rem]">
|
||||||
Snapshots
|
Snapshots
|
||||||
</div>
|
</div>
|
||||||
@@ -196,7 +206,7 @@ watch(selected, () => {
|
|||||||
</template>
|
</template>
|
||||||
</USelectMenu>
|
</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="flex">
|
||||||
<div class="grow poppins"> From:</div>
|
<div class="grow poppins"> From:</div>
|
||||||
<div class="poppins"> {{ new Date(snapshot.from).toLocaleString('it-IT').split(',')[0].trim() }}
|
<div class="poppins"> {{ new Date(snapshot.from).toLocaleString('it-IT').split(',')[0].trim() }}
|
||||||
@@ -235,7 +245,9 @@ watch(selected, () => {
|
|||||||
</div>
|
</div>
|
||||||
</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">
|
<div v-for="section of sections" class="flex flex-col gap-1">
|
||||||
|
|
||||||
@@ -266,10 +278,10 @@ watch(selected, () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="grow"></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.
|
Litlyx is in Beta version.
|
||||||
</div>
|
</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="flex justify-end px-2">
|
||||||
|
|
||||||
<div class="grow flex gap-3">
|
<div class="grow flex gap-3">
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ const activeTabIndex = ref<number>(0);
|
|||||||
<div class="flex">
|
<div class="flex">
|
||||||
<div v-for="(tab, index) of items" @click="activeTabIndex = index"
|
<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="{
|
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
|
'hover:border-lyx-text-dark hover:text-lyx-text-dark cursor-pointer': activeTabIndex !== index
|
||||||
}">
|
}">
|
||||||
{{ tab.label }}
|
{{ tab.label }}
|
||||||
|
|||||||
@@ -12,6 +12,10 @@ const entries: SettingsTemplateEntry[] = [
|
|||||||
const activeProject = useActiveProject();
|
const activeProject = useActiveProject();
|
||||||
const projectNameInputVal = ref<string>(activeProject.value?.name || '');
|
const projectNameInputVal = ref<string>(activeProject.value?.name || '');
|
||||||
|
|
||||||
|
watch(activeProject, () => {
|
||||||
|
projectNameInputVal.value = activeProject.value?.name || "";
|
||||||
|
})
|
||||||
|
|
||||||
const canChange = computed(() => {
|
const canChange = computed(() => {
|
||||||
if (activeProject.value?.name == projectNameInputVal.value) return false;
|
if (activeProject.value?.name == projectNameInputVal.value) return false;
|
||||||
if (projectNameInputVal.value.length === 0) 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>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<SettingsTemplate :entries="entries">
|
<SettingsTemplate :entries="entries" :key="activeProject?.name || 'NONE'">
|
||||||
<template #pname>
|
<template #pname>
|
||||||
<div class="flex items-center gap-4">
|
<div class="flex items-center gap-4">
|
||||||
<LyxUiInput class="w-full px-4 py-2" v-model="projectNameInputVal"></LyxUiInput>
|
<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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #pid>
|
<template #pid>
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ type TProjectsGrouped = {
|
|||||||
project_name: string,
|
project_name: string,
|
||||||
total_visits: number,
|
total_visits: number,
|
||||||
total_events: number,
|
total_events: number,
|
||||||
|
total_sessions: number
|
||||||
}[]
|
}[]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,7 +48,8 @@ const projectsGrouped = computed(() => {
|
|||||||
premium: project.premium,
|
premium: project.premium,
|
||||||
project_name: project.project_name,
|
project_name: project.project_name,
|
||||||
total_events: project.total_events,
|
total_events: project.total_events,
|
||||||
total_visits: project.total_visits
|
total_visits: project.total_visits,
|
||||||
|
total_sessions: project.total_sessions
|
||||||
});
|
});
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
@@ -61,7 +63,8 @@ const projectsGrouped = computed(() => {
|
|||||||
premium_type: project.premium_type,
|
premium_type: project.premium_type,
|
||||||
project_name: project.project_name,
|
project_name: project.project_name,
|
||||||
total_events: project.total_events,
|
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;
|
return result;
|
||||||
|
|
||||||
});
|
});
|
||||||
@@ -107,7 +116,6 @@ const totalEvents = computed(() => {
|
|||||||
return projects.value?.reduce((a, e) => a + e.total_events, 0) || 0;
|
return projects.value?.reduce((a, e) => a + e.total_events, 0) || 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
const details = ref<any>();
|
const details = ref<any>();
|
||||||
const showDetails = ref<boolean>(false);
|
const showDetails = ref<boolean>(false);
|
||||||
async function getProjectDetails(project_id: string) {
|
async function getProjectDetails(project_id: string) {
|
||||||
@@ -188,17 +196,17 @@ async function resetCount(project_id: string) {
|
|||||||
<div> {{ project.total_visits }} </div>
|
<div> {{ project.total_visits }} </div>
|
||||||
<div> Events: </div>
|
<div> Events: </div>
|
||||||
<div> {{ project.total_events }} </div>
|
<div> {{ project.total_events }} </div>
|
||||||
|
<div> Sessions: </div>
|
||||||
|
<div> {{ project.total_sessions }} </div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex gap-4">
|
<div class="flex gap-4 items-center mt-4">
|
||||||
<div class="bg-[#272727] hover:bg-[#313131] cursor-pointer px-8 py-2 mt-3 rounded-lg"
|
<LyxUiButton type="secondary" @click="getProjectDetails(project._id)">
|
||||||
@click="getProjectDetails(project._id)">
|
Payment details
|
||||||
Get details
|
</LyxUiButton>
|
||||||
</div>
|
<LyxUiButton type="danger" @click="resetCount(project._id)">
|
||||||
<div class="bg-[#272727] hover:bg-[#313131] cursor-pointer px-8 py-2 mt-3 rounded-lg"
|
Refresh counts
|
||||||
@click="resetCount(project._id)">
|
</LyxUiButton>
|
||||||
Reset counts
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ export type AdminProjectsList = {
|
|||||||
created_at: Date
|
created_at: Date
|
||||||
},
|
},
|
||||||
total_visits: number,
|
total_visits: number,
|
||||||
total_events: number
|
total_events: number,
|
||||||
|
total_sessions: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export default defineEventHandler(async event => {
|
export default defineEventHandler(async event => {
|
||||||
@@ -54,6 +55,9 @@ export default defineEventHandler(async event => {
|
|||||||
},
|
},
|
||||||
total_events: {
|
total_events: {
|
||||||
$arrayElemAt: ["$counts.events", 0]
|
$arrayElemAt: ["$counts.events", 0]
|
||||||
|
},
|
||||||
|
total_sessions: {
|
||||||
|
$arrayElemAt: ["$counts.sessions", 0]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
|
|
||||||
import { ProjectCountModel } from "@schema/ProjectsCounts";
|
import { ProjectCountModel } from "@schema/ProjectsCounts";
|
||||||
import { EventModel } from "@schema/metrics/EventSchema";
|
import { EventModel } from "@schema/metrics/EventSchema";
|
||||||
|
import { SessionModel } from "@schema/metrics/SessionSchema";
|
||||||
import { VisitModel } from "@schema/metrics/VisitSchema";
|
import { VisitModel } from "@schema/metrics/VisitSchema";
|
||||||
|
|
||||||
export default defineEventHandler(async event => {
|
export default defineEventHandler(async event => {
|
||||||
@@ -13,8 +14,9 @@ export default defineEventHandler(async event => {
|
|||||||
|
|
||||||
const events = await EventModel.countDocuments({ project_id });
|
const events = await EventModel.countDocuments({ project_id });
|
||||||
const visits = await VisitModel.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 };
|
return { ok: true };
|
||||||
});
|
});
|
||||||
@@ -36,12 +36,12 @@ export default defineEventHandler(async event => {
|
|||||||
$group: {
|
$group: {
|
||||||
_id: "$project_id",
|
_id: "$project_id",
|
||||||
events: { $sum: "$events" },
|
events: { $sum: "$events" },
|
||||||
visits: { $sum: "$visits" }
|
visits: { $sum: "$visits" },
|
||||||
|
sessions: { $sum: "$sessions" },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
||||||
const sessionsVisitsCount: any[] = await Redis.useCache({
|
const sessionsVisitsCount: any[] = await Redis.useCache({
|
||||||
key: `counts:${project_id}:sessions_count`,
|
key: `counts:${project_id}:sessions_count`,
|
||||||
exp: COUNTS_SESSIONS_EXPIRE_TIME
|
exp: COUNTS_SESSIONS_EXPIRE_TIME
|
||||||
|
|||||||
@@ -3,9 +3,7 @@ import StripeService from '~/server/services/StripeService';
|
|||||||
import type Event from 'stripe';
|
import type Event from 'stripe';
|
||||||
import { ProjectModel } from '@schema/ProjectSchema';
|
import { ProjectModel } from '@schema/ProjectSchema';
|
||||||
import { PREMIUM_DATA, PREMIUM_PLAN, getPlanFromId, getPlanFromPrice, getPlanFromTag } from '@data/PREMIUM';
|
import { PREMIUM_DATA, PREMIUM_PLAN, getPlanFromId, getPlanFromPrice, getPlanFromTag } from '@data/PREMIUM';
|
||||||
import { ProjectCountModel } from '@schema/ProjectsCounts';
|
|
||||||
import { ProjectLimitModel } from '@schema/ProjectsLimits';
|
import { ProjectLimitModel } from '@schema/ProjectsLimits';
|
||||||
import { UserModel } from '@schema/UserSchema';
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
30
dashboard/server/api/project/change_name.post.ts
Normal file
30
dashboard/server/api/project/change_name.post.ts
Normal 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 };
|
||||||
|
|
||||||
|
});
|
||||||
@@ -39,7 +39,8 @@ export default defineEventHandler(async event => {
|
|||||||
await ProjectCountModel.create({
|
await ProjectCountModel.create({
|
||||||
project_id: project._id,
|
project_id: project._id,
|
||||||
events: 0,
|
events: 0,
|
||||||
visits: 0
|
visits: 0,
|
||||||
|
sessions: 0
|
||||||
});
|
});
|
||||||
|
|
||||||
await ProjectLimitModel.updateOne({ project_id: project._id }, {
|
await ProjectLimitModel.updateOne({ project_id: project._id }, {
|
||||||
@@ -76,7 +77,8 @@ export default defineEventHandler(async event => {
|
|||||||
await ProjectCountModel.create({
|
await ProjectCountModel.create({
|
||||||
project_id: project._id,
|
project_id: project._id,
|
||||||
events: 0,
|
events: 0,
|
||||||
visits: 0
|
visits: 0,
|
||||||
|
sessions: 0
|
||||||
});
|
});
|
||||||
|
|
||||||
return project.toJSON() as TProject;
|
return project.toJSON() as TProject;
|
||||||
|
|||||||
@@ -21,11 +21,14 @@ export class Redis {
|
|||||||
url: runtimeConfig.REDIS_URL,
|
url: runtimeConfig.REDIS_URL,
|
||||||
username: runtimeConfig.REDIS_USERNAME,
|
username: runtimeConfig.REDIS_USERNAME,
|
||||||
password: runtimeConfig.REDIS_PASSWORD,
|
password: runtimeConfig.REDIS_PASSWORD,
|
||||||
database: process.dev ? 1 : 0
|
database: process.dev ? 1 : 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
static async init() {
|
static async init() {
|
||||||
await this.client.connect();
|
await this.client.connect();
|
||||||
|
this.client.on('error', function (err) {
|
||||||
|
console.error('Redis error:', err);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static async setString(key: string, value: string, exp: number) {
|
static async setString(key: string, value: string, exp: number) {
|
||||||
|
|||||||
@@ -1,18 +1,19 @@
|
|||||||
<script lang="ts" setup>
|
<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>
|
</script>
|
||||||
|
|
||||||
<template>
|
<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="{
|
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-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-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-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>
|
<slot></slot>
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
|
|||||||
@@ -5,12 +5,14 @@ export type TProjectCount = {
|
|||||||
project_id: Schema.Types.ObjectId,
|
project_id: Schema.Types.ObjectId,
|
||||||
events: number,
|
events: number,
|
||||||
visits: number,
|
visits: number,
|
||||||
|
sessions: number,
|
||||||
}
|
}
|
||||||
|
|
||||||
const ProjectCountSchema = new Schema<TProjectCount>({
|
const ProjectCountSchema = new Schema<TProjectCount>({
|
||||||
project_id: { type: Types.ObjectId, index: true, unique: true },
|
project_id: { type: Types.ObjectId, index: true, unique: true },
|
||||||
events: { type: Number, required: true, default: 0 },
|
events: { type: Number, required: true, default: 0 },
|
||||||
visits: { 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);
|
export const ProjectCountModel = model<TProjectCount>('project_counts', ProjectCountSchema);
|
||||||
Reference in New Issue
Block a user