mirror of
https://github.com/reconurge/flowsint.git
synced 2026-03-12 01:44:42 -05:00
feat: show labels on zoom
This commit is contained in:
@@ -21,13 +21,13 @@ const DashboardLayout = async ({
|
||||
<Flex className='h-screen w-screen flex'>
|
||||
<Flex className='h-screen'>
|
||||
<div className='flex flex-col w-[300px] h-full rounded-none shadow-none border-r border-gray-400/20'>
|
||||
<div className='w-full rounded-none shadow-none h-14 border-b border-gray-400/20 flex items-center gap-1 flex-row justify-between p-2'>
|
||||
<div className='w-full rounded-none shadow-none h-12 border-b border-gray-400/20 flex items-center gap-1 flex-row justify-between p-2'>
|
||||
<Logo />
|
||||
<Flex gap={"1"}>
|
||||
<NewCase />
|
||||
</Flex>
|
||||
</div>
|
||||
<ScrollArea type="auto" scrollbars="vertical" className='p-3 w-full !h-[calc(100vh_-56px)] grow overflow-y-auto'>
|
||||
<ScrollArea type="auto" scrollbars="vertical" className='p-3 w-full !h-[calc(100vh_-48px)] grow overflow-y-auto'>
|
||||
<div className="flex flex-col">
|
||||
|
||||
</div>
|
||||
@@ -37,8 +37,8 @@ const DashboardLayout = async ({
|
||||
</Flex>
|
||||
<Flex className='grow flex flex-col'>
|
||||
<div>
|
||||
<div className='w-full rounded-none shadow-none !h-14 justify-between border-b border-gray-400/20 flex flex-row justify-between items-center p-2'>
|
||||
<TextField.Root placeholder="Search an investigation..." className='w-full max-w-xs !h-10'>
|
||||
<div className='w-full rounded-none shadow-none !h-12 justify-between border-b border-gray-400/20 flex flex-row justify-between items-center p-2'>
|
||||
<TextField.Root placeholder="Search an investigation..." className='w-full max-w-xs !h-8'>
|
||||
<TextField.Slot>
|
||||
<MagnifyingGlassIcon height="16" width="16" />
|
||||
</TextField.Slot>
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import { InvestigationProvider } from '@/src/components/contexts/investigation-provider';
|
||||
import InvestigationLayout from '@/src/components/investigations/layout';
|
||||
import { getInvestigation } from '@/src/lib/actions/investigations';
|
||||
import Individuals from './individuals';
|
||||
import { SearchProvider } from '@/src/components/contexts/search-context';
|
||||
import { notFound } from 'next/navigation';
|
||||
import { createClient } from "@/src/lib/supabase/server";
|
||||
import { redirect } from "next/navigation";
|
||||
import { ChatProvider } from '@/src/components/contexts/chatbot-context';
|
||||
@@ -21,14 +19,12 @@ const DashboardLayout = async ({
|
||||
redirect('/login')
|
||||
}
|
||||
const { investigation_id } = await (params)
|
||||
const { error } = await getInvestigation(investigation_id)
|
||||
if (error) return notFound()
|
||||
|
||||
return (
|
||||
<InvestigationProvider>
|
||||
<SearchProvider>
|
||||
<ChatProvider>
|
||||
<InvestigationLayout investigation_id={investigation_id} left={<Individuals investigation_id={investigation_id} />}>
|
||||
<InvestigationLayout user={data.user} investigation_id={investigation_id} left={<Individuals investigation_id={investigation_id} />}>
|
||||
{children}
|
||||
</InvestigationLayout>
|
||||
</ChatProvider>
|
||||
|
||||
10
src/app/investigations/[investigation_id]/loading.tsx
Normal file
10
src/app/investigations/[investigation_id]/loading.tsx
Normal file
@@ -0,0 +1,10 @@
|
||||
import { Spinner } from '@radix-ui/themes'
|
||||
import React from 'react'
|
||||
|
||||
const loading = () => {
|
||||
return (
|
||||
<div className='h-[90vh] w-full flex items-center justify-center gap-2'><Spinner /> Loading nodes and edges...</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default loading
|
||||
10
src/app/investigations/loading.tsx
Normal file
10
src/app/investigations/loading.tsx
Normal file
@@ -0,0 +1,10 @@
|
||||
import { Spinner } from '@radix-ui/themes'
|
||||
import React from 'react'
|
||||
|
||||
const loading = () => {
|
||||
return (
|
||||
<div className='h-screen w-screen flex items-center justify-center gap-2'><Spinner /> Loading investigation...</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default loading
|
||||
@@ -1,7 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import { createContext, ReactNode, useContext, useState } from 'react';
|
||||
import { Avatar, Box, Card, Dialog, Flex, Spinner, Text, TextField } from '@radix-ui/themes';
|
||||
import { Avatar, Box, Card, Dialog, Flex, Spinner, Text, TextArea, TextField } from '@radix-ui/themes';
|
||||
import { useChat } from '@ai-sdk/react';
|
||||
import { BotIcon } from 'lucide-react';
|
||||
import { cn } from '@/src/lib/utils';
|
||||
@@ -100,7 +100,7 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({ children }) => {
|
||||
<Text size="2" as='div' weight="bold" className={cn(m.role === 'user' ? 'text-right' : 'text-left')}>
|
||||
{m.role === 'user' ? 'User' : 'Chatbot'}
|
||||
</Text>
|
||||
<Card className='max-w-[70%]'>
|
||||
<Card className='max-w-[80%]'>
|
||||
<Box>
|
||||
<Text as="div" size="2" color="gray">
|
||||
<ReactMarkdown>{m.content}</ReactMarkdown>
|
||||
@@ -135,11 +135,11 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({ children }) => {
|
||||
</Flex>}
|
||||
</Flex>
|
||||
<form onSubmit={handleSubmit} className="mt-4">
|
||||
<TextField.Root
|
||||
value={input}
|
||||
placeholder="Say something..."
|
||||
onChange={handleInputChange}
|
||||
/>
|
||||
<Box width={"100%"}>
|
||||
<TextField.Root value={input}
|
||||
onChange={handleInputChange}
|
||||
size="2" placeholder="'What pattern can you extract from those relations ?'" />
|
||||
</Box>
|
||||
</form>
|
||||
</Dialog.Content>
|
||||
</Dialog.Root>
|
||||
|
||||
@@ -20,7 +20,9 @@ interface InvestigationContextType {
|
||||
handleOpenIndividualModal: any,
|
||||
handleDeleteInvestigation: any,
|
||||
currentNode: any,
|
||||
setCurrentNode: any
|
||||
setCurrentNode: any,
|
||||
panelOpen: boolean,
|
||||
setPanelOpen: any
|
||||
}
|
||||
const InvestigationContext = createContext<InvestigationContextType | undefined>(undefined);
|
||||
|
||||
@@ -33,6 +35,7 @@ export const InvestigationProvider: React.FC<InvestigationProviderProps> = ({ ch
|
||||
const router = useRouter()
|
||||
const pathname = usePathname()
|
||||
const searchParams = useSearchParams()
|
||||
const [panelOpen, setPanelOpen] = useLocalStorage('panel_open', false)
|
||||
const [filters, setFilters] = useLocalStorage('filters', {});
|
||||
const [currentNode, setCurrentNode] = useState<null | Node>(null)
|
||||
const { investigation, isLoading: isLoadingInvestigation } = useInvestigation(investigation_id)
|
||||
@@ -40,7 +43,9 @@ export const InvestigationProvider: React.FC<InvestigationProviderProps> = ({ ch
|
||||
const { confirm } = useConfirm()
|
||||
const [settings, setSettings] = useLocalStorage('settings', {
|
||||
showNodeLabel: true,
|
||||
showEdgeLabel: true
|
||||
showEdgeLabel: true,
|
||||
showMiniMap: true,
|
||||
showCopyIcon: true
|
||||
});
|
||||
|
||||
const createQueryString = useCallback(
|
||||
@@ -76,7 +81,7 @@ export const InvestigationProvider: React.FC<InvestigationProviderProps> = ({ ch
|
||||
</div>
|
||||
)
|
||||
return (
|
||||
<InvestigationContext.Provider value={{ filters, setFilters, settings, setSettings, setOpenSettingsModal, investigation, isLoadingInvestigation, handleOpenIndividualModal, handleDeleteInvestigation, currentNode, setCurrentNode }}>
|
||||
<InvestigationContext.Provider value={{ filters, setFilters, settings, setSettings, setOpenSettingsModal, investigation, isLoadingInvestigation, handleOpenIndividualModal, handleDeleteInvestigation, currentNode, setCurrentNode, panelOpen, setPanelOpen }}>
|
||||
{children}
|
||||
<Dialog.Root open={openSettingsModal}>
|
||||
<Dialog.Content maxWidth="450px">
|
||||
@@ -87,6 +92,8 @@ export const InvestigationProvider: React.FC<InvestigationProviderProps> = ({ ch
|
||||
<Flex direction="column" gap="3">
|
||||
<SettingSwitch setting={"showNodeLabel"} value={settings.showNodeLabel} title={"Show labels on nodes"} description={"Displays the labels on the nodes, like username or avatar."} />
|
||||
<SettingSwitch setting={"showEdgeLabel"} value={settings.showEdgeLabel} title={"Show labels on edges"} description={"Displays the labels on the edges, like relation type."} />
|
||||
<SettingSwitch setting={"showMiniMap"} value={settings.showMiniMap} title={"Show minimap on the canva"} description={"Displays the minimap on canva."} />
|
||||
<SettingSwitch setting={"showCopyIcon"} value={settings.showCopyIcon} title={"Show copy button on nodes"} description={"Displays a copy button on the nodes."} />
|
||||
<div className="flex items-center justify-between gap-4">
|
||||
<div className="flex flex-col gap-1">
|
||||
<p className="font-medium">Theme</p>
|
||||
|
||||
@@ -32,7 +32,7 @@ export function CopyButton({ content, delay = 2000 }: CopyButtonProps) {
|
||||
return (
|
||||
<Tooltip open={isCopied} content="Copied !">
|
||||
<IconButton size={"1"} color="gray" radius="large" variant="ghost" onClick={handleCopy} aria-label="Copy content">
|
||||
<Copy className="h-3 w-3" />
|
||||
<Copy className="h-3 w-3 opacity-50" />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
)
|
||||
|
||||
@@ -29,7 +29,7 @@ import AddressNode from './nodes/physical_address'
|
||||
import { AlignCenterHorizontal, AlignCenterVertical, LockOpenIcon, MaximizeIcon, RotateCcwIcon, ZoomInIcon, ZoomOutIcon, LockIcon } from 'lucide-react';
|
||||
import { useTheme } from 'next-themes';
|
||||
import NewActions from './new-actions';
|
||||
import { IconButton, Tooltip, Spinner, Card, Flex } from '@radix-ui/themes';
|
||||
import { IconButton, Tooltip, Spinner, Card, Flex, SegmentedControl } from '@radix-ui/themes';
|
||||
import { isNode, isEdge, getIncomers, getOutgoers } from "@xyflow/react";
|
||||
import { EdgeBase } from '@xyflow/system';
|
||||
import { useInvestigationContext } from '../contexts/investigation-provider';
|
||||
@@ -72,7 +72,7 @@ const LayoutFlow = ({ initialNodes, initialEdges, theme }: { initialNodes: any,
|
||||
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
|
||||
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
|
||||
const [isLocked, setIsLocked] = useState(false)
|
||||
const { currentNode, setCurrentNode } = useInvestigationContext()
|
||||
const { currentNode, setCurrentNode, settings } = useInvestigationContext()
|
||||
const ref = useRef(null);
|
||||
const getAllIncomers = useCallback((node: any, nodes: any[], edges: EdgeBase[], prevIncomers = []) => {
|
||||
const incomers = getIncomers(node, nodes, edges);
|
||||
@@ -293,6 +293,13 @@ const LayoutFlow = ({ initialNodes, initialEdges, theme }: { initialNodes: any,
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</Panel>
|
||||
<Panel position="top-center" className='flex items-center gap-1'>
|
||||
<SegmentedControl.Root defaultValue="graph">
|
||||
<SegmentedControl.Item value="graph">Graph</SegmentedControl.Item>
|
||||
<SegmentedControl.Item value="timeline">Timeline</SegmentedControl.Item>
|
||||
<SegmentedControl.Item value="map">Map</SegmentedControl.Item>
|
||||
</SegmentedControl.Root>
|
||||
</Panel>
|
||||
<Panel position="top-right" className='flex items-center gap-1'>
|
||||
<Flex direction={"column"} align={"end"} gap={"1"}>
|
||||
<Flex gap="1">
|
||||
@@ -337,9 +344,9 @@ const LayoutFlow = ({ initialNodes, initialEdges, theme }: { initialNodes: any,
|
||||
</Tooltip>
|
||||
</Panel>
|
||||
<Background />
|
||||
<MiniMap pannable />
|
||||
{settings.showMiniMap && <MiniMap pannable />}
|
||||
</ReactFlow>
|
||||
</div>
|
||||
</div >
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,30 +1,33 @@
|
||||
"use client"
|
||||
import { Investigation } from '@/src/types/investigation';
|
||||
import React from 'react'
|
||||
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 { Flex, IconButton, ScrollArea } from '@radix-ui/themes';
|
||||
import { Panel, PanelGroup, PanelResizeHandle } from 'react-resizable-panels';
|
||||
import { BotMessageSquareIcon } from 'lucide-react';
|
||||
import { BotMessageSquareIcon, PanelRightIcon } from 'lucide-react';
|
||||
import { useChatContext } from '../contexts/chatbot-context';
|
||||
import Logo from '../logo';
|
||||
import { useInvestigationContext } from '../contexts/investigation-provider';
|
||||
import User from '../user';
|
||||
|
||||
const InvestigationLayout = ({
|
||||
children,
|
||||
left,
|
||||
investigation_id
|
||||
investigation_id,
|
||||
user
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
left: React.ReactNode;
|
||||
investigation_id: string
|
||||
user: any
|
||||
}) => {
|
||||
const { setOpen: setOpenChat } = useChatContext()
|
||||
const { panelOpen, setPanelOpen } = useInvestigationContext()
|
||||
return (
|
||||
<PanelGroup className='h-screen w-screen flex' direction="horizontal">
|
||||
<Panel className='h-screen' defaultSize={20} minSize={20}>
|
||||
<PanelGroup autoSaveId="conditional" className='h-screen w-screen flex' direction="horizontal">
|
||||
{panelOpen && <Panel id="left" className='h-screen' defaultSize={20} minSize={10}>
|
||||
<div className='flex flex-col w-full h-full rounded-none shadow-none border-r border-gray-400/20'>
|
||||
<div className='w-full rounded-none shadow-none h-12 border-b border-gray-400/20 flex items-center gap-1 flex-row justify-between p-2'>
|
||||
<Logo />
|
||||
@@ -39,14 +42,21 @@ const InvestigationLayout = ({
|
||||
{left}
|
||||
</div>
|
||||
</ScrollArea>
|
||||
|
||||
<Flex justify={"end"} className='p-2'>
|
||||
<User user={user} />
|
||||
</Flex>
|
||||
</div>
|
||||
</Panel>
|
||||
</Panel>}
|
||||
<PanelResizeHandle />
|
||||
<Panel defaultSize={80} minSize={50} className='grow flex flex-col'>
|
||||
<Panel id="center" defaultSize={80} minSize={50} className='grow flex flex-col'>
|
||||
<div>
|
||||
<div className='w-full rounded-none shadow-none h-12 justify-between border-b border-gray-400/20 flex flex-row items-center p-2'>
|
||||
<CaseSelector />
|
||||
<Flex gap={"1"}>
|
||||
<IconButton onClick={() => setPanelOpen(!panelOpen)} variant='soft' color='gray'>
|
||||
<PanelRightIcon className='h-4 w-4' />
|
||||
</IconButton>
|
||||
<CaseSelector />
|
||||
</Flex>
|
||||
<MoreMenu />
|
||||
</div>
|
||||
{children}
|
||||
|
||||
@@ -1,25 +1,26 @@
|
||||
"use client"
|
||||
import React, { memo } from 'react';
|
||||
import { Handle, Position } from '@xyflow/react';
|
||||
import { Handle, Position, useStore } from '@xyflow/react';
|
||||
import { Card, Box, Text, ContextMenu, Badge, Flex, Inset, Tooltip, Avatar } from '@radix-ui/themes';
|
||||
import { NodeProvider, useNodeContext } from '../../contexts/node-context';
|
||||
import { AtSignIcon, ZapIcon } from 'lucide-react';
|
||||
import { cn } from '@/src/lib/utils';
|
||||
import { cn, zoomSelector } from '@/src/lib/utils';
|
||||
import { useInvestigationContext } from '../../contexts/investigation-provider';
|
||||
import { CopyButton } from '../../copy';
|
||||
import { useSearchContext } from '../../contexts/search-context';
|
||||
|
||||
function EmailNode({ data }: any) {
|
||||
const { handleDeleteNode, loading } = useNodeContext()
|
||||
const { settings } = useInvestigationContext()
|
||||
const { settings, currentNode } = useInvestigationContext()
|
||||
const { handleOpenSearchModal } = useSearchContext()
|
||||
const showContent = useStore(zoomSelector);
|
||||
|
||||
return (
|
||||
<ContextMenu.Root>
|
||||
<ContextMenu.Trigger>
|
||||
<Box className={cn(loading ? "!opacity-40" : "!opacity-100")}>
|
||||
{settings.showNodeLabel ?
|
||||
<Card>
|
||||
{settings.showNodeLabel && showContent ?
|
||||
<Card className={cn('border border-transparent hover:border-sky-400', currentNode === data.id && "border-sky-400")}>
|
||||
<Inset>
|
||||
<Flex className='items-center p-0'>
|
||||
<Badge color='green' className='!h-[24px] !rounded-r-none'>
|
||||
@@ -29,7 +30,7 @@ function EmailNode({ data }: any) {
|
||||
<Text as="div" size="1" weight="regular">
|
||||
{data.label}
|
||||
</Text>
|
||||
<CopyButton content={data.label} />
|
||||
{settings.showCopyIcon && <CopyButton content={data.label} />}
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Inset>
|
||||
@@ -63,7 +64,7 @@ function EmailNode({ data }: any) {
|
||||
Delete
|
||||
</ContextMenu.Item>
|
||||
</ContextMenu.Content>
|
||||
</ContextMenu.Root>
|
||||
</ContextMenu.Root >
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,26 +1,28 @@
|
||||
"use client"
|
||||
import React, { memo } from 'react';
|
||||
import { Handle, Position } from '@xyflow/react';
|
||||
import { Handle, Position, useStore } from '@xyflow/react';
|
||||
import { useInvestigationContext } from '../../contexts/investigation-provider';
|
||||
import { Avatar, Card, Box, Flex, Text, ContextMenu, Spinner, Badge, Tooltip } from '@radix-ui/themes';
|
||||
import { AtSignIcon, CameraIcon, FacebookIcon, InstagramIcon, LocateIcon, MessageCircleDashedIcon, PhoneIcon, SendIcon, UserIcon, MapPinHouseIcon, ZapIcon, BotIcon } from 'lucide-react';
|
||||
import { NodeProvider, useNodeContext } from '../../contexts/node-context';
|
||||
import { useSearchContext } from '../../contexts/search-context';
|
||||
import { cn } from '@/src/lib/utils';
|
||||
import { cn, zoomSelector } from '@/src/lib/utils';
|
||||
import { CopyButton } from '../../copy';
|
||||
import { useChatContext } from '../../contexts/chatbot-context';
|
||||
|
||||
|
||||
function Custom(props: any) {
|
||||
const { settings, handleOpenIndividualModal, currentNode } = useInvestigationContext()
|
||||
const { setOpenAddNodeModal, handleDuplicateNode, handleDeleteNode, loading } = useNodeContext()
|
||||
const { handleOpenSearchModal } = useSearchContext()
|
||||
const { setOpen: setOpenChat } = useChatContext()
|
||||
const showContent = useStore(zoomSelector);
|
||||
const { data } = props
|
||||
return (
|
||||
<>
|
||||
<ContextMenu.Root>
|
||||
<ContextMenu.Trigger onContextMenu={(e) => { e.stopPropagation() }}>
|
||||
<Box className={cn(loading ? "!opacity-40" : "!opacity-100")}>{settings.showNodeLabel ?
|
||||
<Box className={cn(loading ? "!opacity-40" : "!opacity-100")}>{settings.showNodeLabel && showContent ?
|
||||
<Card onDoubleClick={() => handleOpenIndividualModal(data.id)} className={cn('!py-1 border border-transparent hover:border-sky-400', currentNode === data.id && "border-sky-400")}>
|
||||
<Flex gap="2" align="center">
|
||||
<Avatar
|
||||
@@ -30,12 +32,12 @@ function Custom(props: any) {
|
||||
radius="full"
|
||||
fallback={loading ? <Spinner /> : data.full_name[0]}
|
||||
/>
|
||||
{settings.showNodeLabel &&
|
||||
{settings.showNodeLabel && showContent &&
|
||||
<Flex align={"center"} gap="2">
|
||||
<Text as="div" size="1" weight="regular">
|
||||
{data.full_name}
|
||||
</Text>
|
||||
<CopyButton content={data.full_name} />
|
||||
{settings.showCopyIcon && <CopyButton content={data.full_name} />}
|
||||
</Flex>}
|
||||
</Flex>
|
||||
</Card> :
|
||||
|
||||
@@ -1,25 +1,27 @@
|
||||
"use client"
|
||||
import React, { memo } from 'react';
|
||||
import { Handle, Position } from '@xyflow/react';
|
||||
import { Handle, Position, useStore } from '@xyflow/react';
|
||||
import { Card, Box, Text, ContextMenu, Flex, Inset, Badge, Tooltip, Avatar } from '@radix-ui/themes';
|
||||
import { NodeProvider, useNodeContext } from '../../contexts/node-context';
|
||||
import { LocateIcon, ZapIcon } from 'lucide-react';
|
||||
import { cn } from '@/src/lib/utils';
|
||||
import { cn, zoomSelector } from '@/src/lib/utils';
|
||||
import { useInvestigationContext } from '../../contexts/investigation-provider';
|
||||
import { CopyButton } from '../../copy';
|
||||
import { useSearchContext } from '../../contexts/search-context';
|
||||
|
||||
function Custom({ data }: any) {
|
||||
const { handleDeleteNode, loading } = useNodeContext()
|
||||
const { settings } = useInvestigationContext()
|
||||
const { settings, currentNode } = useInvestigationContext()
|
||||
const { handleOpenSearchModal } = useSearchContext()
|
||||
const showContent = useStore(zoomSelector);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ContextMenu.Root>
|
||||
<ContextMenu.Trigger>
|
||||
<Box className={cn(loading ? "!opacity-40" : "!opacity-100")}>
|
||||
{settings.showNodeLabel ?
|
||||
<Card>
|
||||
{settings.showNodeLabel && showContent ?
|
||||
<Card className={cn('border border-transparent hover:border-sky-400', currentNode === data.id && "border-sky-400")}>
|
||||
<Inset>
|
||||
<Flex className='items-center p-0'>
|
||||
<Badge color='orange' className='!h-[24px] !rounded-r-none'>
|
||||
@@ -29,7 +31,7 @@ function Custom({ data }: any) {
|
||||
<Text as="div" size="1" weight="regular">
|
||||
{data.label}
|
||||
</Text>
|
||||
<CopyButton content={data.label} />
|
||||
{settings.showCopyIcon && <CopyButton content={data.label} />}
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Inset>
|
||||
|
||||
@@ -1,25 +1,27 @@
|
||||
"use client"
|
||||
import React, { memo } from 'react';
|
||||
import { Handle, Position } from '@xyflow/react';
|
||||
import { Handle, Position, useStore } from '@xyflow/react';
|
||||
import { Card, Box, Text, ContextMenu, Flex, Inset, Badge, Tooltip, Avatar } from '@radix-ui/themes';
|
||||
import { NodeProvider, useNodeContext } from '../../contexts/node-context';
|
||||
import { PhoneIcon, ZapIcon } from 'lucide-react';
|
||||
import { cn } from '@/src/lib/utils';
|
||||
import { cn, zoomSelector } from '@/src/lib/utils';
|
||||
import { useInvestigationContext } from '../../contexts/investigation-provider';
|
||||
import { CopyButton } from '../../copy';
|
||||
import { useSearchContext } from '../../contexts/search-context';
|
||||
|
||||
function Custom({ data }: any) {
|
||||
const { handleDeleteNode, loading } = useNodeContext()
|
||||
const { settings } = useInvestigationContext()
|
||||
const { settings, currentNode } = useInvestigationContext()
|
||||
const { handleOpenSearchModal } = useSearchContext()
|
||||
const showContent = useStore(zoomSelector);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ContextMenu.Root>
|
||||
<ContextMenu.Trigger>
|
||||
<Box className={cn(loading ? "!opacity-40" : "!opacity-100")}>
|
||||
{settings.showNodeLabel ?
|
||||
<Card>
|
||||
{settings.showNodeLabel && showContent ?
|
||||
<Card className={cn('border border-transparent hover:border-sky-400', currentNode === data.id && "border-sky-400")}>
|
||||
<Inset>
|
||||
<Flex className='items-center p-0'>
|
||||
<Badge color='violet' className='!h-[24px] !rounded-r-none'>
|
||||
@@ -29,7 +31,7 @@ function Custom({ data }: any) {
|
||||
<Text as="div" size="1" weight="regular">
|
||||
{data.label}
|
||||
</Text>
|
||||
<CopyButton content={data.label} />
|
||||
{settings.showCopyIcon && <CopyButton content={data.label} />}
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Inset>
|
||||
|
||||
@@ -1,24 +1,26 @@
|
||||
"use client"
|
||||
import React, { memo } from 'react';
|
||||
import { Handle, Position } from '@xyflow/react';
|
||||
import { Handle, Position, useStore } from '@xyflow/react';
|
||||
import { Card, Box, Text, ContextMenu, Badge, Flex, Inset, Tooltip, Avatar } from '@radix-ui/themes';
|
||||
import { NodeProvider, useNodeContext } from '../../contexts/node-context';
|
||||
import { MapPinHouseIcon, ZapIcon } from 'lucide-react';
|
||||
import { cn } from '@/src/lib/utils';
|
||||
import { cn, zoomSelector } from '@/src/lib/utils';
|
||||
import { useInvestigationContext } from '../../contexts/investigation-provider';
|
||||
import { CopyButton } from '../../copy';
|
||||
import { useSearchContext } from '../../contexts/search-context';
|
||||
|
||||
function AddressNode({ data }: any) {
|
||||
const { handleDeleteNode, loading } = useNodeContext()
|
||||
const { settings } = useInvestigationContext()
|
||||
const { settings, currentNode } = useInvestigationContext()
|
||||
const { handleOpenSearchModal } = useSearchContext()
|
||||
const showContent = useStore(zoomSelector);
|
||||
|
||||
return (
|
||||
<ContextMenu.Root>
|
||||
<ContextMenu.Trigger>
|
||||
<Box className={cn(loading ? "!opacity-40" : "!opacity-100")}>
|
||||
{settings.showNodeLabel ?
|
||||
<Card>
|
||||
{settings.showNodeLabel && showContent ?
|
||||
<Card className={cn('border border-transparent hover:border-sky-400', currentNode === data.id && "border-sky-400")}>
|
||||
<Inset>
|
||||
<Flex className='items-center p-0'>
|
||||
<Badge color='ruby' className='!h-[24px] !rounded-r-none'>
|
||||
@@ -28,7 +30,7 @@ function AddressNode({ data }: any) {
|
||||
<Text as="div" size="1" weight="regular">
|
||||
{data.label}
|
||||
</Text>
|
||||
<CopyButton content={data.label} />
|
||||
{settings.showCopyIcon && <CopyButton content={data.label} />}
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Inset>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
"use client"
|
||||
import React, { memo } from 'react';
|
||||
import { Handle, Position } from '@xyflow/react';
|
||||
import { Handle, Position, useStore } from '@xyflow/react';
|
||||
import { Card, Box, Text, ContextMenu, Flex, Inset, Badge, Tooltip, Avatar } from '@radix-ui/themes';
|
||||
import { NodeProvider, useNodeContext } from '../../contexts/node-context';
|
||||
import { cn } from '@/src/lib/utils';
|
||||
import { cn, zoomSelector } from '@/src/lib/utils';
|
||||
import { useInvestigationContext } from '../../contexts/investigation-provider';
|
||||
import { usePlatformIcons } from '@/src/lib/hooks/use-platform-icons';
|
||||
import { CopyButton } from '../../copy';
|
||||
@@ -12,16 +12,18 @@ import { useSearchContext } from '../../contexts/search-context';
|
||||
|
||||
function Custom({ data }: any) {
|
||||
const { handleDeleteNode, loading } = useNodeContext()
|
||||
const { settings } = useInvestigationContext()
|
||||
const { settings, currentNode } = useInvestigationContext()
|
||||
const platformsIcons = usePlatformIcons()
|
||||
const { handleOpenSearchModal } = useSearchContext()
|
||||
const showContent = useStore(zoomSelector);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ContextMenu.Root>
|
||||
<ContextMenu.Trigger>
|
||||
<Box className={cn(loading ? "!opacity-40" : "!opacity-100")}>
|
||||
{settings.showNodeLabel ?
|
||||
<Card>
|
||||
{settings.showNodeLabel && showContent ?
|
||||
<Card className={cn('border border-transparent hover:border-sky-400', currentNode === data.id && "border-sky-400")}>
|
||||
<Inset>
|
||||
<Flex className='items-center p-0'>
|
||||
{/* @ts-ignore */}
|
||||
@@ -35,8 +37,7 @@ function Custom({ data }: any) {
|
||||
{data.username || data.profile_url}
|
||||
|
||||
</Text>
|
||||
<CopyButton content={data.username || data.profile_url}
|
||||
/>
|
||||
{settings.showCopyIcon && <CopyButton content={data.username || data.profile_url} />}
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Inset>
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import { Avatar, Button, DropdownMenu, Inset, Text } from '@radix-ui/themes'
|
||||
import { Avatar, Button, DropdownMenu, IconButton, Inset, Text } from '@radix-ui/themes'
|
||||
import React from 'react'
|
||||
|
||||
const User = ({ user }: any) => {
|
||||
return (
|
||||
<DropdownMenu.Root>
|
||||
<DropdownMenu.Trigger>
|
||||
<Button className='!mr-1' size={"1"} variant='ghost'>
|
||||
<IconButton radius='full' className='!mr-1' size={"1"} variant='ghost'>
|
||||
<Inset>
|
||||
<Avatar size={"2"} radius='full' src={user?.user_metadata?.avatar_url} fallback={user?.user_metadata?.user_name?.[0] || "?"} variant="soft" />
|
||||
</Inset>
|
||||
</Button>
|
||||
</IconButton>
|
||||
</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Content className='p-2'>
|
||||
<Text weight={"medium"}>{user?.user_metadata?.user_name}</Text>
|
||||
|
||||
@@ -4,3 +4,5 @@ import { twMerge } from "tailwind-merge"
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs))
|
||||
}
|
||||
|
||||
export const zoomSelector = (s: { transform: number[]; }) => s.transform[2] >= 0.6;
|
||||
|
||||
Reference in New Issue
Block a user