<script lang="ts" setup>
import LoaderOverlay from "components/LoaderOverlay.vue";
import SectionCard from "components/SectionCard.vue";
import { setRangeToFilter, usePlotlyChart } from "composables/usePlotlyChart";
import type { FilterActions } from "src/store";
import { type ChartDataPoint } from "utils/filters";
import { icons } from "utils/icons";
import { useTemplateRef, watch, type Ref } from "vue";
const props = defineProps<{
chartData: ChartDataPoint[];
localFilterActions: FilterActions;
title: string;
loading: boolean;
isPort?: boolean;
isHoneypot?: boolean;
chartLink?: string;
}>();
const chartContainer = useTemplateRef<HTMLDivElement>(
"chartContainer",
) as Ref<HTMLDivElement>;
const selectedPoints = defineModel<number>("selectedPoints", { default: 0 });
const selectedIPs = defineModel<number>("selectedIPs", { default: 0 });
const options: Record<string, any> = {};
if (props.isPort) {
options.yField = "remote_addr";
options.yTitle = "Remote Address";
options.categoricalY = true;
}
const { debouncedDrawPlot } = usePlotlyChart(
chartContainer,
() => props.chartData,
{
...options,
onRelayout(event) {
if (props.isPort) {
return;
}
setRangeToFilter(event, props.localFilterActions);
},
onSelected(event) {
selectedPoints.value = event?.points?.length ?? 0;
if (event?.points) {
const ips = new Set(
event.points.map((p) => props.chartData[p.pointIndex]?.remote_addr),
);
selectedIPs.value = ips.size;
} else {
selectedIPs.value = 0;
}
},
},
);
watch(
() => props.chartData,
() => {
debouncedDrawPlot();
},
{ deep: true },
);
</script>
<template>
<SectionCard
:title="
selectedPoints > 0
? `${title} (${selectedPoints} points, ${selectedIPs} IPs selected)`
: title
"
:icon="icons.chart"
body-class="relative h-[calc(100%-3rem)] min-h-[300px]"
>
<template #header-right>
<router-link v-if="chartLink" :to="chartLink" class="btn-secondary">
Open in Chart View
<component :is="icons.externalLink" class="text-[10px]" />
</router-link>
</template>
<LoaderOverlay v-if="loading" />
<div ref="chartContainer" class="h-full w-full"></div>
</SectionCard>
</template>