feat: custom node card

This commit is contained in:
dextmorgn
2025-02-17 11:44:56 +01:00
parent d86b5633ef
commit c855c124e1
4 changed files with 102 additions and 9 deletions

View File

@@ -63,7 +63,7 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({ children }) => {
</AvatarFallback>
</Avatar>
<div className={cn("flex flex-col gap-1 w-full items-start")}>
<Card className="bg-transparent border-0">
<Card className="bg-transparent shadow-none border-0">
<CardContent className="p-0">
<ReactMarkdown>{m.content}</ReactMarkdown>
</CardContent>
@@ -82,7 +82,7 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({ children }) => {
</Avatar>
<div className="flex flex-col gap-1 items-start w-full">
<span className="text-sm font-bold text-red-500">Error</span>
<Card className="max-w-[80%]">
<Card className="bg-transparent shadow-none border-0">
<CardContent className="p-3 text-red-500">
Oops, an error occurred. Make sure you provided a valid Mistral API key.
</CardContent>

View File

@@ -0,0 +1,89 @@
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
import { Button } from "@/components/ui/button"
import { Card, CardContent, CardHeader } from "@/components/ui/card"
import { Phone, User2, XIcon } from "lucide-react"
import { useState } from "react"
import { useInvestigationContext } from "../contexts/investigation-provider"
interface PhoneNumber {
id: string
country: string | null
phone_number: string
individual_id: string
investigation_id: string
}
interface Individual {
id: string
investigation_id: string
full_name: string
birth_date: string | null
gender: string
nationality: string
notes: string
image_url: string
ip_addresses: string[]
phone_numbers: PhoneNumber[]
}
interface CurrentNodeProps {
individual: Partial<Individual>
}
export default function CurrentNode({ individual }: CurrentNodeProps) {
const { handleOpenIndividualModal } = useInvestigationContext()
const [show, setShow] = useState(true)
if (!show) return
return (
<Card className="w-full max-w-sm overflow-hidden bg-background">
<div className="h-24 bg-primary relative">
<Button onClick={() => setShow(false)} className="text-white hover:text-white hover:bg-background/5 absolute top-2 right-2" size={"icon"} variant="ghost">
<XIcon className="h-4 w-4" />
</Button>
</div>
<div className="relative">
<div className="absolute -top-12 left-1/2 -translate-x-1/2">
<Avatar className="h-24 w-24 border-4 border-background">
<AvatarImage src={individual.image_url} />
<AvatarFallback className="bg-muted">
<span className="text-3xl">{individual?.full_name?.[0]}</span>
</AvatarFallback>
</Avatar>
</div>
</div>
<CardHeader className="mt-8 text-center">
<h2 className="text-2xl font-bold">{individual.full_name}</h2>
<p className="text-sm text-muted-foreground">{individual.notes || <span className="italic">No notes.</span>}</p>
</CardHeader>
<CardContent className="grid gap-4">
{individual?.phone_numbers && individual?.phone_numbers?.length > 0 && (
<div className="flex items-center gap-2 justify-center text-sm">
<Phone className="h-4 w-4 text-muted-foreground" />
{individual?.phone_numbers?.map((phone) => (
<span key={phone.id}>{phone.phone_number}</span>
))}
</div>
)}
<div className="flex gap-2 justify-center">
<Button onClick={() => handleOpenIndividualModal(individual.id)}
variant="outline">View Details</Button>
{/* <Button>Contact</Button> */}
</div>
<div className="flex justify-between gap-8 text-sm text-muted-foreground border-t pt-4">
<div className="text-center">
<p className="font-medium">Nationality</p>
<p>{individual.nationality || "N/A"}</p>
</div>
<div className="text-center">
<p className="font-medium">Gender</p>
<p>{individual.gender || "N/A"}</p>
</div>
<div className="text-center">
<p className="font-medium">IPs</p>
<p>{individual?.ip_addresses?.length}</p>
</div>
</div>
</CardContent>
</Card>
)
}

View File

@@ -30,6 +30,7 @@ import { Card } from '@/components/ui/card';
import { cn } from '@/lib/utils';
import { useInvestigationContext } from '@/components/contexts/investigation-provider';
import { useFlowStore } from '../contexts/use-flow-store';
import CurrentNode from './current-node-card';
const edgeTypes = {
"custom": FloatingEdge
@@ -139,12 +140,15 @@ const LayoutFlow = ({ theme }: { theme: ColorMode }) => {
</Button>
<NewActions addNodes={addNodes} />
</div>
{currentNode && (
<Card className='p-3'>
<>
{getNode(currentNode)?.data?.label}
</>
</Card>
{currentNode && getNode(currentNode) && (
getNode(currentNode)?.type === "individual" ?
// @ts-ignore
<CurrentNode individual={getNode(currentNode).data} /> :
<Card className='p-3 bg-background'>
<>
{getNode(currentNode)?.data.label}
</>
</Card>
)}
</div>
</Panel>

View File

@@ -79,7 +79,7 @@ function SocialNode({ data }: any) {
</div>
</ContextMenuTrigger>
<ContextMenuContent>
<ContextMenuItem onClick={() => handleOpenSearchModal(data.email)}>
<ContextMenuItem onClick={() => handleOpenSearchModal(data.label)}>
Launch search
<Zap className="ml-2 h-4 w-4 text-orange-500" />
</ContextMenuItem>