Repository: keephq/keep
Pull request: fix: improve topology ux
<!-- 
Thanks for creating this pull request 🤗

Please make sure that the pull request is limited to one type (docs, feature, etc.) and keep it as small as possible. You can open multiple prs instead of opening a huge one.
-->

<!-- If this pull request closes an issue, please mention the issue number below -->
Closes #2597

## 📑 Description
<!-- Add a brief description of the pr -->

<!-- You can also choose to add a list of changes and if they have been completed or not by using the markdown to-do list syntax
- [ ] Not Completed
- [x] Completed
-->

## ✅ Checks
<!-- Make sure your pr passes the CI checks and do check the following fields as needed - -->
- [ ] My pull request adheres to the code style of this project
- [ ] My code requires changes to the documentation
- [ ] I have updated the documentation as required
- [ ] All the tests have passed

## ℹ Additional Information
<!-- Any additional information like breaking changes, dependencies added, screenshots, comparisons between new and old behavior, etc. -->


Problem statement:
[🐛 Bug]: Topology page issues
1. When hovering over an item in topology view, if there's no space in the bottom, open the hover card up
2. Replace number of firing alerts (which could be big) with number of incidents associated with this service (more interesting)
3. We can add more context based on the category of the service, for example, if it's Kafka, include the queue name, cloud provider, etc.

Hints from issue discussion:

Commit message: fix: build

Patch to review:
diff --git a/keep-ui/app/(keep)/topology/ui/map/service-node.tsx b/keep-ui/app/(keep)/topology/ui/map/service-node.tsx
index 41560005b3..d1b840a50b 100644
--- a/keep-ui/app/(keep)/topology/ui/map/service-node.tsx
+++ b/keep-ui/app/(keep)/topology/ui/map/service-node.tsx
@@ -1,13 +1,12 @@
-import React, { useEffect, useState } from "react";
+import React, { useEffect, useRef, useState } from "react";
 import { Handle, NodeProps, NodeToolbar, Position } from "@xyflow/react";
-import { useAlerts } from "@/utils/hooks/useAlerts";
-import { useAlertPolling } from "@/utils/hooks/usePusher";
 import { useRouter } from "next/navigation";
 import { ServiceNodeType, TopologyService } from "../../model/models";
 import { Badge } from "@tremor/react";
 import { getColorForUUID } from "@/app/(keep)/topology/lib/badge-colors";
 import { clsx } from "clsx";
 import Image from "next/image";
+import { useIncidents, usePollIncidents } from "@/utils/hooks/useIncidents";
 
 const THRESHOLD = 5;
 
@@ -73,34 +72,52 @@ function ServiceDetailsTooltip({ data }: { data: TopologyService }) {
 }
 
 export function ServiceNode({ data, selected }: NodeProps<ServiceNodeType>) {
-  const { useAllAlerts } = useAlerts();
-  const { data: alerts, mutate } = useAllAlerts("feed");
-  const { data: pollAlerts } = useAlertPolling();
+  const { data: incidents, mutate: incidentsMutate } = useIncidents(
+    true,
+    25,
+    0,
+    {
+      id: "creation_time",
+      desc: false,
+    },
+    { affected_services: [data.display_name] }
+  );
+  usePollIncidents(incidentsMutate);
   const router = useRouter();
   const [showDetails, setShowDetails] = useState(false);
+  const [tooltipDirection, setTooltipDirection] = useState<Position>(
+    Position.Bottom
+  );
+  const nodeRef = useRef<HTMLDivElement | null>(null);
 
   useEffect(() => {
-    if (pollAlerts) {
-      mutate();
-    }
-  }, [pollAlerts, mutate]);
+    if (!showDetails || !nodeRef.current) return;
 
-  const relevantAlerts = alerts?.filter(
-    (alert) => alert.service === data.service
-  );
+    const rect = nodeRef.current.getBoundingClientRect();
+    const viewportHeight = window.innerHeight;
+    console.log(rect.bottom, viewportHeight, rect);
+
+    if (rect.bottom + 150 > viewportHeight) {
+      setTooltipDirection(Position.Top);
+    } else {
+      setTooltipDirection(Position.Bottom);
+    }
+  }, [showDetails]);
 
   const handleClick = () => {
     router.push(
-      `/alerts/feed?cel=service%3D%3D${encodeURIComponent(`"${data.service}"`)}`
+      `/incidents?services={encodeURIComponent("${data.display_name}")}`
     );
   };
 
-  const alertCount = relevantAlerts?.length || 0;
-  const badgeColor = alertCount < THRESHOLD ? "bg-orange-500" : "bg-red-500";
+  const incidentsCount = incidents?.count || 0;
+  const badgeColor =
+    incidentsCount < THRESHOLD ? "bg-orange-500" : "bg-red-500";
 
   return (
     <>
       <div
+        ref={nodeRef}
         className={clsx(
           "flex flex-col gap-1 bg-white p-4 border-2 border-gray-200 rounded-xl shadow-lg relative transition-colors",
           selected && "border-tremor-brand"
@@ -121,12 +138,12 @@ export function ServiceNode({ data, selected }: NodeProps<ServiceNodeType>) {
           </div>
         )}
         <strong className="text-lg">{data.display_name || data.service}</strong>
-        {alertCount > 0 && (
+        {incidentsCount > 0 && (
           <span
             className={`absolute top-[-20px] right-[-20px] mt-2 mr-2 px-2 py-1 text-white text-xs font-bold rounded-full ${badgeColor} hover:cursor-pointer`}
             onClick={handleClick}
           >
-            {alertCount}
+            {incidentsCount}
           </span>
         )}
         <div className="flex flex-wrap gap-1">
@@ -141,7 +158,7 @@ export function ServiceNode({ data, selected }: NodeProps<ServiceNodeType>) {
         </div>
       </div>
 
-      <NodeToolbar isVisible={showDetails} position={Position.Bottom}>
+      <NodeToolbar isVisible={showDetails} position={tooltipDirection}>
         <ServiceDetailsTooltip data={data} />
       </NodeToolbar>
 


Code review comments:
diff_hunk: @@ -73,34 +72,52 @@ function ServiceDetailsTooltip({ data }: { data: TopologyService }) {
 }
 
 export function ServiceNode({ data, selected }: NodeProps<ServiceNodeType>) {
-  const { useAllAlerts } = useAlerts();
-  const { data: alerts, mutate } = useAllAlerts("feed");
-  const { data: pollAlerts } = useAlertPolling();
+  const { data: incidents, mutate: incidentsMutate } = useIncidents(
+    true,
+    25,
+    0,
+    {
+      id: "creation_time",
+      desc: false,
+    },
+    { affected_services: [data.display_name] }
+  );
path: keep-ui/app/(keep)/topology/ui/map/service-node.tsx
text: @user1:
This sends out a request to get incidents for *every* service in the topology map. Sounds a bit problematic in terms of performance

@author:
fixing this.

@author:
Fixed.