Skip to content

Commit f5edf18

Browse files
committed
update security + chart events è anomaly service
1 parent 5b7e93b commit f5edf18

File tree

18 files changed

+1417
-75
lines changed

18 files changed

+1417
-75
lines changed
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
<script setup lang="ts">
2+
import type { ChartData, ChartOptions } from 'chart.js';
3+
import { defineChartComponent } from 'vue-chart-3';
4+
registerChartComponents();
5+
6+
const FunnelChart = defineChartComponent('funnel', 'funnel');
7+
8+
const chartOptions = ref<ChartOptions<'funnel'>>({
9+
responsive: true,
10+
maintainAspectRatio: false,
11+
interaction: {
12+
intersect: false,
13+
mode: 'nearest',
14+
axis: 'x',
15+
includeInvisible: true
16+
},
17+
scales: {
18+
y: {
19+
ticks: { display: true },
20+
grid: {
21+
display: true,
22+
drawBorder: false,
23+
color: '#CCCCCC22',
24+
// borderDash: [5, 10]
25+
},
26+
},
27+
x: {
28+
ticks: { display: true },
29+
grid: {
30+
display: true,
31+
drawBorder: false,
32+
color: '#CCCCCC22',
33+
}
34+
}
35+
},
36+
plugins: {
37+
legend: { display: false },
38+
title: { display: false },
39+
tooltip: {
40+
enabled: true,
41+
backgroundColor: 'rgba(0, 0, 0, 0.8)',
42+
titleFont: { size: 16, weight: 'bold' },
43+
bodyFont: { size: 14 },
44+
padding: 10,
45+
cornerRadius: 4,
46+
boxPadding: 10,
47+
caretPadding: 20,
48+
yAlign: 'bottom',
49+
xAlign: 'center',
50+
}
51+
},
52+
});
53+
54+
55+
const chartData = ref<ChartData<'funnel'>>({
56+
labels: [],
57+
datasets: [
58+
{
59+
data: [],
60+
backgroundColor: ['#5680F8' + '77'],
61+
// borderColor: '#0000CC',
62+
// borderWidth: 4,
63+
fill: true,
64+
tension: 0.45,
65+
pointRadius: 0,
66+
pointHoverRadius: 10,
67+
hoverBackgroundColor: '#5680F8',
68+
// hoverBorderColor: 'white',
69+
// hoverBorderWidth: 2,
70+
},
71+
],
72+
});
73+
74+
75+
onMounted(async () => {
76+
77+
// const c = document.createElement('canvas');
78+
// const ctx = c.getContext("2d");
79+
// let gradient: any = `${'#0000CC'}22`;
80+
// if (ctx) {
81+
// gradient = ctx.createLinearGradient(0, 25, 0, 300);
82+
// gradient.addColorStop(0, `${'#0000CC'}99`);
83+
// gradient.addColorStop(0.35, `${'#0000CC'}66`);
84+
// gradient.addColorStop(1, `${'#0000CC'}22`);
85+
// } else {
86+
// console.warn('Cannot get context for gradient');
87+
// }
88+
89+
// chartData.value.datasets[0].backgroundColor = [gradient];
90+
91+
});
92+
93+
const activeProjectId = useActiveProjectId();
94+
const { safeSnapshotDates } = useSnapshot();
95+
96+
const eventsCount = await useFetch<{ _id: string, count: number }[]>(`/api/data/query`, {
97+
...signHeaders({
98+
'x-pid': activeProjectId.data.value || '',
99+
'x-schema': 'events',
100+
'x-from': safeSnapshotDates.value.from,
101+
'x-to': safeSnapshotDates.value.to,
102+
'x-query-limit': '1000'
103+
}), lazy: true
104+
});
105+
106+
107+
const enabledEvents = ref<string[]>([]);
108+
109+
async function onEventCheck(eventName: string) {
110+
const index = enabledEvents.value.indexOf(eventName);
111+
if (index == -1) {
112+
enabledEvents.value.push(eventName);
113+
} else {
114+
enabledEvents.value.splice(index, 1);
115+
}
116+
117+
118+
chartData.value.labels = enabledEvents.value;
119+
chartData.value.datasets[0].data = [];
120+
121+
for (const enabledEvent of enabledEvents.value) {
122+
const target = (eventsCount.data.value ?? []).find(e => e._id == enabledEvent);
123+
chartData.value.datasets[0].data.push(target?.count || 0);
124+
}
125+
}
126+
127+
</script>
128+
129+
130+
<template>
131+
<CardTitled title="Funnel" sub="Funnel events">
132+
<div class="flex gap-2 justify-between">
133+
<div>
134+
<div class="min-w-[20rem]">
135+
Select two or more events
136+
</div>
137+
<div v-for="event of eventsCount.data.value">
138+
<UCheckbox @change="onEventCheck(event._id)" :value="enabledEvents.includes(event._id)"
139+
:label="event._id">
140+
</UCheckbox>
141+
</div>
142+
</div>
143+
<div class="grow">
144+
<FunnelChart :chart-data="chartData" :options="chartOptions"> </FunnelChart>
145+
</div>
146+
</div>
147+
</CardTitled>
148+
</template>
Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,7 @@
11

2-
import { Chart, registerables } from 'chart.js';
3-
import annotaionPlugin from 'chartjs-plugin-annotation';
4-
52
let registered = false;
63
export async function registerChartComponents() {
74
if (registered) return;
8-
if (process.client) {
9-
Chart.register(...registerables, annotaionPlugin);
10-
registered = true;
11-
}
5+
console.log('registerChartComponents is deprecated. Plugin is now used');
6+
registered = true;
127
}

dashboard/nuxt.config.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export default defineNuxtConfig({
1212
postcss: {
1313
plugins: {
1414
tailwindcss: {},
15-
autoprefixer: {}
15+
autoprefixer: {},
1616
}
1717
},
1818
colorMode: {
@@ -60,6 +60,9 @@ export default defineNuxtConfig({
6060
nitro: {
6161
plugins: ['~/server/init.ts']
6262
},
63+
plugins: [
64+
{ src: '~/plugins/chartjs.ts', mode: 'client' }
65+
],
6366
...gooleSignInConfig,
6467
modules: ['@nuxt/ui', 'nuxt-vue3-google-signin'],
6568
devServer: {

dashboard/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"@getbrevo/brevo": "^2.2.0",
1717
"@nuxtjs/tailwindcss": "^6.12.0",
1818
"chart.js": "^3.9.1",
19+
"chartjs-chart-funnel": "^4.2.1",
1920
"chartjs-plugin-annotation": "^2.2.1",
2021
"date-fns": "^3.6.0",
2122
"dayjs": "^1.11.11",

dashboard/pages/events.vue

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
<script lang="ts" setup>
2+
import EventsFunnelChart from '~/components/events/EventsFunnelChart.vue';
3+
24
35
definePageMeta({ layout: 'dashboard' });
46
@@ -22,7 +24,8 @@ const refreshKey = computed(() => `${snapshot.value._id.toString() + activeProje
2224

2325
<div class="flex gap-6 flex-col xl:flex-row h-full">
2426

25-
<CardTitled :key="refreshKey" class="p-4 flex-[4] w-full h-full" title="Events" sub="Events stacked bar chart.">
27+
<CardTitled :key="refreshKey" class="p-4 flex-[4] w-full h-full" title="Events"
28+
sub="Events stacked bar chart.">
2629
<template #header>
2730
<SelectButton @changeIndex="eventsStackedSelectIndex = $event"
2831
:currentIndex="eventsStackedSelectIndex" :options="selectLabelsEvents">
@@ -41,6 +44,10 @@ const refreshKey = computed(() => `${snapshot.value._id.toString() + activeProje
4144

4245
</div>
4346

47+
<div class="flex">
48+
<EventsFunnelChart :key="refreshKey" class="w-full"></EventsFunnelChart>
49+
</div>
50+
4451
<div class="flex">
4552
<EventsUserFlow :key="refreshKey"></EventsUserFlow>
4653
</div>

dashboard/pages/index.vue

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ const limitsInfo = ref<{
2020
percent: number
2121
}>();
2222
23+
const justLogged = computed(() => {
24+
return route.query.just_logged;
25+
})
26+
2327
2428
onMounted(async () => {
2529
if (route.query.just_logged) return location.href = '/';
@@ -68,7 +72,8 @@ function goToUpgrade() {
6872

6973
<div class="dashboard w-full h-full overflow-y-auto pb-20 md:pt-4 lg:pt-0">
7074

71-
<div :key="'home-' + isLiveDemo()" v-if="projects && activeProject && (firstInteraction.data.value === true)">
75+
<div :key="'home-' + isLiveDemo()"
76+
v-if="projects && activeProject && (firstInteraction.data.value === true) && !justLogged">
7277

7378
<div class="w-full px-4 py-2 gap-2 flex flex-col">
7479
<div v-if="limitsInfo && limitsInfo.limited"
@@ -188,13 +193,17 @@ function goToUpgrade() {
188193

189194
</div>
190195

196+
<FirstInteraction v-if="!justLogged" :refresh-interaction="firstInteraction.refresh"
197+
:first-interaction="(firstInteraction.data.value || false)"></FirstInteraction>
191198

192-
<FirstInteraction :refresh-interaction="firstInteraction.refresh" :first-interaction="(firstInteraction.data.value || false)"></FirstInteraction>
193-
194-
<div class="text-text/85 mt-8 ml-8 poppis text-[1.2rem]" v-if="projects && projects.length == 0">
199+
<div class="text-text/85 mt-8 ml-8 poppis text-[1.2rem]" v-if="projects && projects.length == 0 && !justLogged">
195200
Create your first project...
196201
</div>
197202

203+
<div v-if="justLogged" class="text-[2rem]">
204+
The page will refresh soon
205+
</div>
206+
198207
</div>
199208

200209
</template>

dashboard/pages/security.vue

Lines changed: 55 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
<script setup lang="ts">
22
3+
import type { } from '#ui/types/tabs'
4+
35
definePageMeta({ layout: 'dashboard' });
46
const activeProjectId = useActiveProjectId();
57
@@ -25,6 +27,15 @@ function showAnomalyInfoAlert() {
2527
)
2628
}
2729
30+
31+
const rows = computed(() => reportList.data.value || [])
32+
33+
const columns = [
34+
{ key: 'scan', label: 'Scan date' },
35+
{ key: 'type', label: 'Type' },
36+
{ key: 'data', label: 'Data' },
37+
];
38+
2839
</script>
2940

3041

@@ -41,35 +52,72 @@ function showAnomalyInfoAlert() {
4152
</div>
4253
</div>
4354

55+
<div class="pb-[10rem]">
56+
<UTable :rows="rows" :columns="columns">
57+
58+
59+
<template #scan-data="{ row }">
60+
<div class="text-lyx-text-dark">
61+
{{ new Date(row.data.created_at).toLocaleString() }}
62+
</div>
63+
</template>
64+
65+
<template #type-data="{ row }">
66+
<UBadge color="white" class="w-[4rem] flex justify-center">
67+
{{ row.type }}
68+
</UBadge>
69+
</template>
4470

71+
<template #data-data="{ row }">
72+
<div class="text-lyx-text-dark">
73+
<div v-if="row.type === 'domain'">
74+
{{ row.data.domain }}
75+
</div>
76+
<div v-if="row.type === 'visit'">
77+
{{ row.data.visit }}
78+
</div>
79+
<div v-if="row.type === 'event'">
80+
{{ row.data.event }}
81+
</div>
82+
</div>
83+
</template>
4584

46-
<div class="w-full h-full py-8 px-12">
85+
<!-- <template #actions-data="{ row }">
86+
<UDropdown :items="items(row)">
87+
<UButton color="gray" variant="ghost" icon="i-heroicons-ellipsis-horizontal-20-solid" />
88+
</UDropdown>
89+
</template> -->
90+
91+
</UTable>
92+
</div>
93+
94+
<!-- <div class="w-full py-8 px-12 pb-[10rem]">
4795
<div v-if="reportList.data.value" class="flex flex-col gap-2">
48-
<div v-for="entry of reportList.data.value">
49-
<div v-if="entry.type === 'event'" class="flex gap-2">
96+
<div v-for="entry of reportList.data.value" class="flex flex-col gap-4">
97+
<div v-if="entry.type === 'event'" class="flex gap-2 flex-col lg:flex-row items-center lg:items-start">
5098
<div class="text-lyx-text-darker">{{ new Date(entry.data.created_at).toLocaleString() }}</div>
5199
<UBadge class="w-[4rem] flex justify-center"> {{ entry.type }} </UBadge>
52100
<div class="text-lyx-text-dark">
53101
Event date: {{ new Date(entry.data.eventDate).toLocaleString() }}
54102
</div>
55103
</div>
56-
<div v-if="entry.type === 'visit'" class="flex gap-2">
104+
<div v-if="entry.type === 'visit'" class="flex gap-2 flex-col lg:flex-row items-center lg:items-start">
57105
<div class="text-lyx-text-darker">{{ new Date(entry.data.created_at).toLocaleString() }}</div>
58106
<UBadge class="w-[4rem] flex justify-center"> {{ entry.type }} </UBadge>
59107
<div class="text-lyx-text-dark">
60108
Visit date: {{ new Date(entry.data.visitDate).toLocaleString() }}
61109
</div>
62110
</div>
63-
<div v-if="entry.type === 'domain'" class="flex gap-2">
111+
<div v-if="entry.type === 'domain'" class="flex gap-2 flex-col py-2 lg:flex-row items-center lg:items-start">
64112
<div class="text-lyx-text-darker">{{ new Date(entry.data.created_at).toLocaleString() }}</div>
65113
<UBadge class="w-[4rem] flex justify-center"> {{ entry.type }} </UBadge>
66114
<div class="text-lyx-text-dark">
67-
Domain found: {{ entry.data.domain }}
115+
{{ entry.data.domain }}
68116
</div>
69117
</div>
70118
</div>
71119
</div>
72-
</div>
120+
</div> -->
73121

74122
</div>
75123

dashboard/plugins/chartjs.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
2+
import { Chart, registerables } from 'chart.js';
3+
import annotaionPlugin from 'chartjs-plugin-annotation';
4+
import 'chartjs-chart-funnel';
5+
6+
import { FunnelController, FunnelChart, TrapezoidElement } from 'chartjs-chart-funnel';
7+
8+
export default defineNuxtPlugin(() => {
9+
Chart.register(...registerables, annotaionPlugin, FunnelController, FunnelChart, TrapezoidElement);
10+
})

0 commit comments

Comments
 (0)