add dashboard

This commit is contained in:
Litlyx
2024-06-01 15:27:40 +02:00
parent 75f0787c3b
commit df4faf366f
201 changed files with 91267 additions and 0 deletions

View File

@@ -0,0 +1,11 @@
import { Chart, registerables } from 'chart.js';
let registered = false;
export async function registerChartComponents() {
if (registered) return;
if (process.client) {
Chart.register(...registerables);
registered = true;
}
}

View File

@@ -0,0 +1,34 @@
const ACCESS_TOKEN_STATE_KEY = 'access_token';
const ACCESS_TOKEN_COOKIE_KEY = 'access_token';
export function signHeaders(headers?: Record<string, string>) {
const { token } = useAccessToken()
return { headers: { ...(headers || {}), 'Authorization': 'Bearer ' + token.value } }
}
export function useAccessToken() {
const tokenCookie = useCookie(ACCESS_TOKEN_COOKIE_KEY, { expires: new Date(Date.now() + 1000 * 60 * 60 * 24 * 30) })
const token = useState<string | undefined | null>(ACCESS_TOKEN_STATE_KEY);
const needLoad = useState<boolean>('needAccessTokenLoad', () => true);
const readToken = () => {
token.value = tokenCookie.value;
needLoad.value = false;
}
const setToken = (newToken: string) => {
tokenCookie.value = newToken;
token.value = tokenCookie.value;
needLoad.value = false;
}
if (needLoad.value == true) readToken();
return { token, readToken, setToken, needLoad }
}

View File

@@ -0,0 +1,12 @@
export function useBarCardDialog() {
const showDialog = useState('show-bar-card-dialog', () => false);
const dialogBarData = useState<any[]>('bar-card-dialog-data', () => []);
const isDataLoading = useState('bar-card-dialog-data-loading', () => false);
function closeDialog() {
showDialog.value = false;
}
return { showDialog, dialogBarData, closeDialog, isDataLoading };
}

View File

@@ -0,0 +1,28 @@
import type { TProject } from "@schema/ProjectSchema";
const projects = useFetch<TProject[]>('/api/project/list', { key: 'projectslist', ...signHeaders() });
export function useProjectsList() { return projects; }
const activeProjectId = useFetch<string>(`/api/user/active_project`, { key: 'activeProjectId', ...signHeaders() });
export function useActiveProjectId() { return activeProjectId; }
export function useActiveProject() {
if (isLiveDemo()) {
const { data: liveDemoProject } = useLiveDemo();
return liveDemoProject;
}
const { data: projects } = useProjectsList();
const { data: activeProjectId } = useActiveProjectId();
return computed(() => {
if (!projects.value) return;
if (!activeProjectId.value) return;
return projects.value.find(e => e._id.toString() == activeProjectId.value);
});
}
export async function setActiveProject(project_id: string) {
await $fetch<string>(`/api/user/set_active_project?project_id=${project_id}`, signHeaders());
activeProjectId.refresh();
}

View File

@@ -0,0 +1,19 @@
export function createRequestOptions(method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH', sign: boolean, body?: Record<string, any>, headers: Record<string, string> = {}) {
const requestHeaders = sign ? signHeaders(headers) : headers;
let requestBody;
if (method === 'POST' || method == 'PUT' || method == 'PATCH') {
requestBody = body ? JSON.stringify(body) : undefined;
}
return {
method,
headers: requestHeaders,
body: requestBody
}
}

View File

@@ -0,0 +1,59 @@
import type { MetricsCounts } from "~/server/api/metrics/[project_id]/counts";
import type { VisitsWebsiteAggregated } from "~/server/api/metrics/[project_id]/data/websites";
import type { MetricsTimeline } from "~/server/api/metrics/[project_id]/timeline/generic";
export function useMetricsData() {
const activeProject = useActiveProject();
const metricsInfo = useFetch<MetricsCounts>(`/api/metrics/${activeProject.value?._id}/counts`, signHeaders());
return metricsInfo;
}
export function useFirstInteractionData() {
const activeProject = useActiveProject();
const metricsInfo = useFetch<boolean>(`/api/metrics/${activeProject.value?._id}/first_interaction`, signHeaders());
return metricsInfo;
}
export async function useTimelineDataRaw(timelineEndpointName: string, slice: SliceName) {
const activeProject = useActiveProject();
const response = await $fetch<{ data: MetricsTimeline[], from: string, to: string }>(
`/api/metrics/${activeProject.value?._id}/timeline/${timelineEndpointName}`, {
method: 'POST',
...signHeaders({ 'Content-Type': 'application/json' }),
body: JSON.stringify({ slice })
});
return response;
}
export async function useTimelineData(timelineEndpointName: string, slice: SliceName) {
const response = await useTimelineDataRaw(timelineEndpointName, slice);
if (!response) return;
const fixed = fixMetrics(response, slice);
return fixed;
}
export function usePagesData(website: string, limit: number = 10) {
const activeProject = useActiveProject();
const res = useFetch<VisitsWebsiteAggregated[]>(`/api/metrics/${activeProject.value?._id}/data/pages`, {
...signHeaders({
'x-query-limit': limit.toString(),
'x-website-name': website
}),
key: `pages_data:${website}:${limit}`,
});
return res;
}
export function useWebsitesData(limit: number = 10) {
const activeProject = useActiveProject();
const res = useFetch<VisitsWebsiteAggregated[]>(`/api/metrics/${activeProject.value?._id}/data/websites`, {
...signHeaders({ 'x-query-limit': limit.toString() }),
key: `websites_data:${limit}`,
});
return res;
}

View File

@@ -0,0 +1,13 @@
export function isLiveDemo() {
const route = useRoute();
return route.path == '/live_demo';
}
export function useLiveDemo() {
return useFetch('/api/live_demo', {
key: 'live_demo_project'
});
}

View File

@@ -0,0 +1,30 @@
import type { AuthContext } from "~/server/middleware/01-authorization";
const LOGGED_USER_STATE_KEY = 'logged_user';
export function useLoggedUser() {
const loggedUserState = useState<AuthContext | undefined>(LOGGED_USER_STATE_KEY);
return loggedUserState;
}
export function setLoggedUser(authContext?: AuthContext) {
useLoggedUser().value = authContext;
}
export const isAdminHidden = ref<boolean>(false);
export function useUserRoles() {
const isPremium = computed(() => {
const loggedUser = useLoggedUser();
if (!loggedUser.value?.logged) return false;
return loggedUser.value.user.roles.includes('PREMIUM');
});
const isAdmin = computed(() => {
const loggedUser = useLoggedUser();
if (!loggedUser.value?.logged) return false;
return loggedUser.value.user.roles.includes('ADMIN');
});
return { isPremium, isAdmin }
}

View File

@@ -0,0 +1,12 @@
const isOpen = ref<boolean>(false);
function open() { isOpen.value = true; }
function close() { isOpen.value = false; return true; }
function toggle() { isOpen.value = !isOpen.value; }
function set(value: boolean) { isOpen.value = value; }
export function useMenu() {
return { isOpen, open, close, toggle, set }
}

View File

@@ -0,0 +1,34 @@
const onlineUsers = ref<number>(-1);
async function getOnlineUsers() {
const activeProject = useActiveProject();
if (!activeProject.value) return onlineUsers.value = -1;
const online = await $fetch<number>(`/api/metrics/${activeProject.value._id}/live_users`, signHeaders());
onlineUsers.value = online;
}
let watching: any;
function startWatching(instant: boolean = true) {
if (instant) getOnlineUsers();
watching = setInterval(async () => {
await getOnlineUsers();
}, 5000);
}
function stopWatching() {
if (watching) clearInterval(watching);
}
export function useOnlineUsers() {
return {
onlineUsers,
getOnlineUsers,
startWatching,
stopWatching
}
}