mirror of
https://github.com/Litlyx/litlyx
synced 2025-12-09 23:48:36 +01:00
add dashboard
This commit is contained in:
11
dashboard/composables/registerChartComponents.ts
Normal file
11
dashboard/composables/registerChartComponents.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
34
dashboard/composables/useAccessToken.ts
Normal file
34
dashboard/composables/useAccessToken.ts
Normal 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 }
|
||||
}
|
||||
12
dashboard/composables/useBarCardDialog.ts
Normal file
12
dashboard/composables/useBarCardDialog.ts
Normal 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 };
|
||||
}
|
||||
28
dashboard/composables/useCurrentProject.ts
Normal file
28
dashboard/composables/useCurrentProject.ts
Normal 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();
|
||||
}
|
||||
19
dashboard/composables/useCustomRequest.ts
Normal file
19
dashboard/composables/useCustomRequest.ts
Normal 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
|
||||
}
|
||||
|
||||
}
|
||||
59
dashboard/composables/useDataService.ts
Normal file
59
dashboard/composables/useDataService.ts
Normal 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;
|
||||
}
|
||||
13
dashboard/composables/useLiveDemo.ts
Normal file
13
dashboard/composables/useLiveDemo.ts
Normal 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'
|
||||
});
|
||||
}
|
||||
|
||||
30
dashboard/composables/useLoggedUser.ts
Normal file
30
dashboard/composables/useLoggedUser.ts
Normal 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 }
|
||||
}
|
||||
12
dashboard/composables/useMenu.ts
Normal file
12
dashboard/composables/useMenu.ts
Normal 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 }
|
||||
}
|
||||
34
dashboard/composables/useOnlineUsers.ts
Normal file
34
dashboard/composables/useOnlineUsers.ts
Normal 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
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user