From ec76fb63a2eaf9939f6875ef7bfc4049da9c661e Mon Sep 17 00:00:00 2001 From: dextmorgn Date: Fri, 14 Feb 2025 15:33:28 +0100 Subject: [PATCH] feat: ask ai --- src/components/assistant.tsx | 12 + src/components/contexts/chatbot-context.tsx | 232 +++++++++--------- .../contexts/investigation-provider.tsx | 6 +- src/components/contexts/node-context.tsx | 2 +- src/components/contexts/search-context.tsx | 46 ++-- src/components/contexts/use-flow-store.tsx | 8 +- src/components/copy.tsx | 7 +- src/components/dashboard/investigation.tsx | 35 ++- src/components/dashboard/search-palette.tsx | 6 +- .../investigations/case-selector.tsx | 4 +- .../investigations/floating-edge.tsx | 2 +- src/components/investigations/graph.tsx | 3 +- .../investigations/individual-modal.tsx | 5 +- src/components/investigations/layout.tsx | 24 +- src/components/investigations/nodes/email.tsx | 2 +- .../investigations/nodes/individual.tsx | 2 +- .../investigations/nodes/ip_address.tsx | 2 +- src/components/investigations/nodes/phone.tsx | 2 +- .../investigations/nodes/physical_address.tsx | 2 +- .../investigations/nodes/social.tsx | 2 +- src/components/ui/button.tsx | 8 +- src/components/ui/sheet.tsx | 2 +- styles/globals.css | 2 +- 23 files changed, 219 insertions(+), 197 deletions(-) create mode 100644 src/components/assistant.tsx diff --git a/src/components/assistant.tsx b/src/components/assistant.tsx new file mode 100644 index 0000000..0d9e437 --- /dev/null +++ b/src/components/assistant.tsx @@ -0,0 +1,12 @@ +import { Button } from "@/components/ui/button" + +import { GemIcon } from "lucide-react" +import { useChatContext } from "./contexts/chatbot-context" + +const Assistant = () => { + const { handleOpenChat } = useChatContext() + return ( + + ) +} +export default Assistant \ No newline at end of file diff --git a/src/components/contexts/chatbot-context.tsx b/src/components/contexts/chatbot-context.tsx index 48d390e..7d4e94e 100644 --- a/src/components/contexts/chatbot-context.tsx +++ b/src/components/contexts/chatbot-context.tsx @@ -6,147 +6,151 @@ import { useChat } from "@ai-sdk/react" import { Bot, User } from "lucide-react" import { cn } from "@/lib/utils" import ReactMarkdown from "react-markdown" -import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@/components/ui/dialog" +import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle } from "@/components/ui/sheet" import { Avatar, AvatarFallback } from "@/components/ui/avatar" import { Card, CardContent } from "@/components/ui/card" import { Input } from "@/components/ui/input" import { Button } from "@/components/ui/button" +import { ScrollArea } from "../ui/scroll-area" interface ChatContextType { - open: boolean - handleOpenChat: (content: any) => void + open: boolean + handleOpenChat: (content: any) => void } const ChatContext = createContext(undefined) interface ChatProviderProps { - children: ReactNode + children: ReactNode } export const ChatProvider: React.FC = ({ children }) => { - const [open, setOpen] = useState(false) - const [context, setContext] = useState(null) - const { messages, input, handleInputChange, setInput, handleSubmit, error, isLoading } = useChat() + const [open, setOpen] = useState(false) + const [context, setContext] = useState(null) + const { messages, input, handleInputChange, setInput, handleSubmit, error, isLoading } = useChat() - const handleOpenChat = (content: any) => { - setContext(content) - setInput(`What pattern can you extract from this person ?\n ${JSON.stringify(content)}`) - setOpen(true) - } + const handleOpenChat = (content: any) => { + setContext(content) + if (content) + setInput(`What pattern can you extract from this person ?\n ${JSON.stringify(content)}`) + setOpen(true) + } - return ( - - {children} - - 0 ? "sm:max-w-[730px]" : "sm:max-w-[480px]")}> - - Ask AI - Ask AI chat bot to help you understand patterns. - -
- {messages.map((m) => ( -
-
- - - {m.role === "user" ? ( - - ) : isLoading ? ( - - ) : ( - - )} - - -
- - {isLoading && m.role !== "user" ? : m.role === "user" ? "User" : "Chatbot"} - - - - {m.content} - - -
-
-
- ))} - {error && ( -
-
- - - - - -
- Error - - - Oops, an error occurred. Make sure you provided a valid Mistral API key. - - -
-
-
- )} -
-
- - -
-
-
-
- ) + return ( + + {children} + + 0 ? "sm:max-w-[730px]" : "sm:max-w-[480px]")}> + + Ask AI + Ask AI chat bot to help you understand patterns. + + + {messages.map((m) => ( +
+
+ + + {m.role === "user" ? ( + + ) : isLoading ? ( + + ) : ( + + )} + + +
+ + {isLoading && m.role !== "user" ? : m.role === "user" ? "User" : "Chatbot"} + + + + {m.content} + + +
+
+
+ ))} + {error && ( +
+
+ + + + + +
+ Error + + + Oops, an error occurred. Make sure you provided a valid Mistral API key. + + +
+
+
+ )} +
+
+ +
+ +
+
+
+
+
+ ) } export const useChatContext = (): ChatContextType => { - const context = useContext(ChatContext) - if (!context) { - throw new Error("useChatContext must be used within a ChatProvider") - } - return context + const context = useContext(ChatContext) + if (!context) { + throw new Error("useChatContext must be used within a ChatProvider") + } + return context } interface LoadingDotsProps { - speed?: number - text?: string + speed?: number + text?: string } const LoadingDots: React.FC = ({ speed = 200, text = "Thinking" }) => { - const [dots, setDots] = useState("") + const [dots, setDots] = useState("") - useEffect(() => { - const interval = setInterval(() => { - setDots((prevDots) => { - if (prevDots.length >= 3) { - return "" - } - return prevDots + "." - }) - }, speed) + useEffect(() => { + const interval = setInterval(() => { + setDots((prevDots) => { + if (prevDots.length >= 3) { + return "" + } + return prevDots + "." + }) + }, speed) - return () => clearInterval(interval) - }, [speed]) + return () => clearInterval(interval) + }, [speed]) - return ( -
- {text} - {dots} -
- ) + return ( +
+ {text} + {dots} +
+ ) } diff --git a/src/components/contexts/investigation-provider.tsx b/src/components/contexts/investigation-provider.tsx index 61f95dc..e866bd6 100644 --- a/src/components/contexts/investigation-provider.tsx +++ b/src/components/contexts/investigation-provider.tsx @@ -95,7 +95,7 @@ export const InvestigationProvider: React.FC = ({ ch return ( {children} - + Settings @@ -119,8 +119,8 @@ export const InvestigationProvider: React.FC = ({ ch
- setOpenSettingsModal(false)}> - diff --git a/src/components/contexts/node-context.tsx b/src/components/contexts/node-context.tsx index 94860bf..92f9da1 100644 --- a/src/components/contexts/node-context.tsx +++ b/src/components/contexts/node-context.tsx @@ -189,7 +189,7 @@ export const NodeProvider: React.FC = (props: any) => { }
- diff --git a/src/components/contexts/search-context.tsx b/src/components/contexts/search-context.tsx index 7caada5..120f758 100644 --- a/src/components/contexts/search-context.tsx +++ b/src/components/contexts/search-context.tsx @@ -4,13 +4,15 @@ import { createContext, useContext, type ReactNode, useState } from "react" import { Button } from "@/components/ui/button" import { Card } from "@/components/ui/card" import { - Dialog, - DialogContent, - DialogDescription, - DialogFooter, - DialogHeader, - DialogTitle, -} from "@/components/ui/dialog" + Sheet, + SheetClose, + SheetContent, + SheetDescription, + SheetFooter, + SheetHeader, + SheetTitle, + SheetTrigger, +} from "@/components/ui/sheet" import { Alert, AlertDescription } from "@/components/ui/alert" import { InfoIcon } from "lucide-react" import { investigateValue } from "@/lib/actions/search" @@ -66,12 +68,12 @@ export const SearchProvider: React.FC = ({ children }) => { return ( {children} - - - - New search - Make a new keyword associated research. - + + + + New search + Make a new keyword associated research. +
@@ -104,20 +106,22 @@ export const SearchProvider: React.FC = ({ children }) => { ) )}
- - + + + + {!results && ( )} - +
-
-
-
+ + + ) } diff --git a/src/components/contexts/use-flow-store.tsx b/src/components/contexts/use-flow-store.tsx index 8aa595c..7370092 100644 --- a/src/components/contexts/use-flow-store.tsx +++ b/src/components/contexts/use-flow-store.tsx @@ -140,18 +140,18 @@ const createStore = (initialNodes: AppNode[] = [], initialEdges: Edge[] = []) => }, onNodeClick: (_event: any, node: { id: string; }) => { set({ currentNode: node.id }); - get().resetNodeStyles() + get().resetNodeStyles(); }, onPaneClick: (_envent: any) => { set({ currentNode: null }); - get().resetNodeStyles() + get().resetNodeStyles(); }, onLayout: (direction = 'TB', fitView: () => void) => { const { nodes, edges } = getLayoutedElements(get().nodes, get().edges, { direction }); set({ nodes }); - set({ edges }) - fitView() + set({ edges }); + fitView(); }, highlightPath: (selectedNode: Node | null) => { if (!selectedNode) { diff --git a/src/components/copy.tsx b/src/components/copy.tsx index 818e5f1..e673712 100644 --- a/src/components/copy.tsx +++ b/src/components/copy.tsx @@ -1,10 +1,11 @@ "use client" import { useState, useCallback } from "react" -import { Copy } from "lucide-react" +import { CopyIcon } from "lucide-react" import { useTimeout } from "usehooks-ts" import { Tooltip, TooltipContent, TooltipTrigger } from "./ui/tooltip" import { Button } from "./ui/button" +import { cn } from "@/lib/utils" interface CopyButtonProps { content: string @@ -34,8 +35,8 @@ export function CopyButton({ content, className, delay = 2000 }: CopyButtonProps return ( - diff --git a/src/components/dashboard/investigation.tsx b/src/components/dashboard/investigation.tsx index a429393..41218d5 100644 --- a/src/components/dashboard/investigation.tsx +++ b/src/components/dashboard/investigation.tsx @@ -3,32 +3,29 @@ import React from 'react' import { Investigation } from '@/types/investigation'; import Link from 'next/link'; import Image from 'next/image'; -import { Card } from '@/components/ui/card'; const investigation = ({ investigation }: { investigation: Investigation }) => { return (
- -
-
- {investigation.title} +
+
+ {investigation.title} +
+
+
+ {investigation.title}
-
-
- {investigation.title} -
-
- {investigation.description || No description provided.} -
+
+ {investigation.description || No description provided.}
- +
) diff --git a/src/components/dashboard/search-palette.tsx b/src/components/dashboard/search-palette.tsx index 290b0c8..65f4e95 100644 --- a/src/components/dashboard/search-palette.tsx +++ b/src/components/dashboard/search-palette.tsx @@ -82,7 +82,7 @@ const SearchModal = ({ investigation_id }: { investigation_id: string }) => { - + Search @@ -99,13 +99,13 @@ const SearchModal = ({ investigation_id }: { investigation_id: string }) => { {error &&

An error occurred.

} {isLoading &&
} {results?.length === 0 &&

No results found for "{search}".

} - + {results && results?.length !== 0 &&
    {!error && !isLoading && Array.isArray(results) && results?.map((item) => ( ))}
-
+
}
diff --git a/src/components/investigations/case-selector.tsx b/src/components/investigations/case-selector.tsx index eb038c4..ffefffc 100644 --- a/src/components/investigations/case-selector.tsx +++ b/src/components/investigations/case-selector.tsx @@ -18,10 +18,10 @@ export default function CaseSelector() { router.push(`/investigations/${value}`); }; return ( -
+
{isLoading || isLoadingInvestigation ? :