diff --git a/package.json b/package.json
index 5125bfc..ae8fe91 100644
--- a/package.json
+++ b/package.json
@@ -26,12 +26,15 @@
"d3-quadtree": "^3.0.1",
"framer-motion": "^12.3.1",
"intl-messageformat": "^10.5.0",
+ "lodash.debounce": "^4.0.8",
"lucide-react": "^0.474.0",
"next": "15.1.6",
"next-themes": "^0.4.4",
"postcss": "^8.5.1",
"react": "^19.0.0",
"react-dom": "^19.0.0",
+ "react-highlight-words": "^0.21.0",
+ "react-resizable-panels": "^2.1.7",
"swr": "^2.3.0",
"tailwindcss": "^4.0.3",
"usehooks-ts": "^3.1.1",
diff --git a/src/app/investigations/[investigation_id]/individuals.tsx b/src/app/investigations/[investigation_id]/individuals.tsx
index f1f299a..add6499 100644
--- a/src/app/investigations/[investigation_id]/individuals.tsx
+++ b/src/app/investigations/[investigation_id]/individuals.tsx
@@ -5,15 +5,16 @@ import { useIndividuals } from '@/src/lib/hooks/individuals/use-individuals';
import { useInvestigationContext } from '@/src/components/contexts/investigation-provider';
import { cn } from '@/src/lib/utils';
import { Pencil1Icon } from '@radix-ui/react-icons';
+import { RotateCwIcon } from 'lucide-react';
const Filters = ({ investigation_id }: { investigation_id: string }) => {
- const { individuals, isLoading } = useIndividuals(investigation_id)
+ const { individuals, isLoading, refetch } = useIndividuals(investigation_id)
const { currentNode, setCurrentNode, handleOpenIndividualModal } = useInvestigationContext()
return (
-
Profiles
-
+ Profiles refetch()} size={"1"} variant='ghost' color='gray'>
+
{isLoading && <>
@@ -21,7 +22,7 @@ const Filters = ({ investigation_id }: { investigation_id: string }) => {
>
}
{individuals?.map((individual: any) => (
-
+
setCurrentNode(individual.id)}>
- }>
+ }>
{children}
diff --git a/src/components/dashboard/seach-palette.tsx b/src/components/dashboard/seach-palette.tsx
new file mode 100644
index 0000000..70f3c94
--- /dev/null
+++ b/src/components/dashboard/seach-palette.tsx
@@ -0,0 +1,128 @@
+"use client"
+import { useState, useCallback, useEffect, SetStateAction } from 'react'
+// @ts-ignore
+import debounce from "lodash.debounce";
+import { SearchIcon } from "lucide-react"
+// @ts-ignore
+import Highlighter from "react-highlight-words";
+import Link from 'next/link';
+import { useSearchResults } from '../../lib/hooks/investigation/use-search-results';
+import { Card, Dialog, IconButton, TextField } from '@radix-ui/themes';
+import { MagnifyingGlassIcon } from '@radix-ui/react-icons';
+import { useInvestigationContext } from '../contexts/investigation-provider';
+
+const SearchModal = ({ investigation_id }: { investigation_id: string }) => {
+ const [search, setSearch] = useState("")
+ const [open, setOpen] = useState(false)
+ const { setCurrentNode } = useInvestigationContext()
+ const {
+ results,
+ error,
+ isLoading,
+ refetch,
+ } = useSearchResults(search, investigation_id);
+
+ const changeHandler = (event: { target: { value: SetStateAction; }; }) => {
+ setSearch(event.target.value);
+ refetch && refetch();
+ };
+ const debouncedChangeHandler = useCallback(debounce(changeHandler, 300), []);
+
+ const handleClose = () => {
+ () => setSearch('')
+ setOpen(false)
+ }
+
+ useEffect(() => {
+ const handleKeyPress = (event: { ctrlKey: any; key: string; }) => {
+ if (event.ctrlKey && event.key === 'k') {
+ setOpen(true)
+ }
+ };
+ window.addEventListener('keydown', handleKeyPress);
+ return () => {
+ window.removeEventListener('keydown', handleKeyPress);
+ };
+ }, []);
+
+ const SearchItem = ({ item }: any) =>
+ ( setOpen(false)}>
+ setCurrentNode(item.id)}>
+
+ Individual
+
+
+
+
+
+
+
+
+ {/*
+
+ */}
+
+
+
+ )
+ return (
+ <>
+
+
+ setOpen(true)} color="gray" size="2" variant="soft">
+
+
+
+ Search
+
+ Find the profile you're looking for.
+
+
+
+
+
+
+
+
+ {error && "An error occured."}
+ {isLoading &&
}
+ {results?.length === 0 && `No results found for "${search}".`}
+
{!error && !isLoading && Array.isArray(results) && results?.map((item) => (
+
+ ))}
+
+ {search === '' && (
+
+
+
Search for ip_addresses, domain names and other
+
+ Put you search between quotes for an exact match search.
+
+
+ )}
+
+
+
+ >
+ )
+}
+
+export default SearchModal
\ No newline at end of file
diff --git a/src/components/investigations/graph.tsx b/src/components/investigations/graph.tsx
index 75a9f14..b0ae05e 100644
--- a/src/components/investigations/graph.tsx
+++ b/src/components/investigations/graph.tsx
@@ -235,8 +235,9 @@ const LayoutFlow = ({ initialNodes, initialEdges, theme }: { initialNodes: any,
if (currentNode) {
const internalNode = getNode(currentNode)
if (!internalNode) return
- updateNode(internalNode.id, { ...internalNode, zIndex: 2000, style: { ...internalNode.style, opacity: 1 } })
- setCenter(internalNode?.position.x + 100, internalNode?.position.y, { duration: 1000, zoom: 1.5 })
+ updateNode(internalNode.id, { ...internalNode, zIndex: 5000, style: { ...internalNode.style, opacity: 1 } })
+ //@ts-ignore
+ setCenter(internalNode?.position.x + (internalNode?.measured?.width / 2), internalNode?.position.y + (internalNode?.measured?.height / 2) + 20, { duration: 1000, zoom: 1.5 })
highlightPath(internalNode);
}
}, [currentNode, highlightPath, setCenter, resetNodeStyles]);
@@ -265,6 +266,7 @@ const LayoutFlow = ({ initialNodes, initialEdges, theme }: { initialNodes: any,
onConnect={onConnect}
onNodeClick={onNodeClick}
onPaneClick={onPaneClick}
+ minZoom={0.1}
// edgesUpdatable={!isLocked}
// edgesFocusable={!isLocked}
// nodesDraggable={!isLocked}
diff --git a/src/components/investigations/layout.tsx b/src/components/investigations/layout.tsx
index a371a9f..d2545cb 100644
--- a/src/components/investigations/layout.tsx
+++ b/src/components/investigations/layout.tsx
@@ -5,35 +5,47 @@ import { ThemeSwitch } from '../theme-switch';
import MoreMenu from './more-menu';
import CaseSelector from './case-selector';
import NewCase from '../dashboard/new-case';
+import SearchModal from '../dashboard/seach-palette';
+import { ScrollArea } from '@radix-ui/themes';
+import { Panel, PanelGroup, PanelResizeHandle } from 'react-resizable-panels';
const InvestigationLayout = ({
children,
- left
+ left,
+ investigation_id
}: {
children: React.ReactNode;
left: React.ReactNode;
+ investigation_id: string
}) => {
return (
-
-
-
-
+
+
+
)
}
diff --git a/src/components/investigations/nodes/individual.tsx b/src/components/investigations/nodes/individual.tsx
index 6ed8121..de18e0e 100644
--- a/src/components/investigations/nodes/individual.tsx
+++ b/src/components/investigations/nodes/individual.tsx
@@ -38,7 +38,7 @@ function Custom(props: any) {
:
-