diff --git a/src/templates/shared/components/Card.tsx b/src/templates/shared/components/Card.tsx index 6c13becb..19427ead 100644 --- a/src/templates/shared/components/Card.tsx +++ b/src/templates/shared/components/Card.tsx @@ -28,48 +28,65 @@ const Card: React.FC & { Content: React.FC; } = ({ layout, imageSrc, imageSize = 'small', iconSystem, children, className }) => { return ( - -
- {imageSrc && ( -
- Card Image -
- )} - {iconSystem && ( -
- {React.createElement(iconSystem, { className: 'w-8 h-8' })} -
- )} -
- {children} -
-
+
+ {imageSrc && ( +
+ Card Image +
+ )} + {iconSystem &&
{React.createElement(iconSystem, { className: 'w-8 h-8' })}
} +
{children}
+
); -} +}; const Header: React.FC = ({ children }) => ( - {children} + + {children} + ); const Subheader: React.FC = ({ children }) => ( - {children} + + {children} + ); const Content: React.FC = ({ children }) => ( - {children} + + {children} + ); Card.Header = Header; Card.Subheader = Subheader; Card.Content = Content; -export default Card; \ No newline at end of file +export default Card; diff --git a/src/templates/shared/components/Chatbot.tsx b/src/templates/shared/components/Chatbot.tsx index 1501c4da..29ef9bf3 100644 --- a/src/templates/shared/components/Chatbot.tsx +++ b/src/templates/shared/components/Chatbot.tsx @@ -3,7 +3,13 @@ import { useEffect, useRef, useState } from 'react'; import { Button, Widget, Typography, Avatar, TextInput, IconButton, useCopyToClipboard, Modal } from '@neo4j-ndl/react'; import ChatBotAvatar from '../assets/chatbot-ai.png'; -import { ArrowPathIconOutline, ClipboardDocumentIconOutline, HandThumbDownIconOutline, InformationCircleIconOutline, SpeakerWaveIconOutline } from '@neo4j-ndl/react/icons'; +import { + ArrowPathIconOutline, + ClipboardDocumentIconOutline, + HandThumbDownIconOutline, + InformationCircleIconOutline, + SpeakerWaveIconOutline, +} from '@neo4j-ndl/react/icons'; import RetrievalInformation from './RetrievalInformation'; type ChatbotProps = { @@ -26,7 +32,7 @@ export default function Chatbot(props: ChatbotProps) { const { messages } = props; const [listMessages, setListMessages] = useState(messages); const [inputMessage, setInputMessage] = useState(''); - const [value, copy] = useCopyToClipboard(); + const [, copy] = useCopyToClipboard(); const [isOpenModal, setIsOpenModal] = useState(false); const [timeTaken, setTimeTaken] = useState(0); const [sourcesModal, setSourcesModal] = useState([]); @@ -53,7 +59,14 @@ export default function Chatbot(props: ChatbotProps) { const datetime = `${date.toLocaleDateString()} ${date.toLocaleTimeString()}`; setListMessages((msgs) => [ ...msgs, - { id: Date.now(), user: 'chatbot', message: currentTypedText, datetime: datetime, isTyping: true, src: responseText.src }, + { + id: Date.now(), + user: 'chatbot', + message: currentTypedText, + datetime: datetime, + isTyping: true, + src: responseText.src, + }, ]); } else { setListMessages((msgs) => msgs.map((msg) => (msg.isTyping ? { ...msg, message: currentTypedText } : msg))); @@ -76,7 +89,11 @@ export default function Chatbot(props: ChatbotProps) { setListMessages((listMessages) => [...listMessages, userMessage]); setInputMessage(''); - const chatbotReply = {response: 'Hello, here is an example response with sources. To use the chatbot, plug this to your backend with a fetch containing an object response of type: {response: string, src: Array}', src: ["1:1234-abcd-efgh-ijkl-5678:2", "3:8765-zyxw-vuts-rqpo-4321:4"]}; // Replace with getting a response from your chatbot through your APIs + const chatbotReply = { + response: + 'Hello, here is an example response with sources. To use the chatbot, plug this to your backend with a fetch containing an object response of type: {response: string, src: Array}', + src: ['1:1234-abcd-efgh-ijkl-5678:2', '3:8765-zyxw-vuts-rqpo-4321:4'], + }; // Replace with getting a response from your chatbot through your APIs simulateTypingEffect(chatbotReply); }; @@ -147,31 +164,36 @@ export default function Chatbot(props: ChatbotProps) { {chat.user === 'chatbot' ? (
- <> - - - - {chat.src ? ( - { - setModelModal('OpenAI GPT 4o'); - setSourcesModal(chat.src ?? []); - setTimeTaken(50); - setIsOpenModal(true) - }}> - - - ) : <>} - copy(chat.message)} > - - - - - - - - - + <> + + + + {chat.src ? ( + { + setModelModal('OpenAI GPT 4o'); + setSourcesModal(chat.src ?? []); + setTimeTaken(50); + setIsOpenModal(true); + }} + > + + + ) : ( + <> + )} + copy(chat.message)}> + + + + + + + + +
) : ( <> @@ -192,18 +214,22 @@ export default function Chatbot(props: ChatbotProps) { onChange={handleInputChange} htmlAttributes={{ type: 'text', - "aria-label": "Chatbot Input", + 'aria-label': 'Chatbot Input', }} /> - - + }} + onClose={handleCloseModal} + isOpen={isOpenModal} + > + ); diff --git a/src/templates/shared/components/DemoCards.tsx b/src/templates/shared/components/DemoCards.tsx index ed590dd3..dfd7a49d 100644 --- a/src/templates/shared/components/DemoCards.tsx +++ b/src/templates/shared/components/DemoCards.tsx @@ -1,32 +1,36 @@ import Card from './Card'; import testImg from '../assets/cardImg.png'; import { Button, Typography } from '@neo4j-ndl/react'; -import { AcademicCapIconOutline, RocketLaunchIconOutline } from '@neo4j-ndl/react/icons'; +import { AcademicCapIconOutline, RocketLaunchIconOutline } from '@neo4j-ndl/react/icons'; export default function DemoCards() { return ( -
- +
+ Header text Subtitle or description

Some description about relatively important things but not too long since this is a card component.

-
    +
    • 1 Key information
    • 12.59 Key information
    • 3 Key information
    - - + +
    - +

    Some description about relatively important things but not too long since this is a card component.

    -
      +
      • 18 Key information
      • 12.59 Key information
      • 5 Key information
      • @@ -34,39 +38,43 @@ export default function DemoCards() { - + Header text

        Some description about relatively important things but not too long since this is a card component.

        -
          +
          • 18 Key information
          • 12.59 Key information
          • 5 Key information
          - - + + Header text Subtitle or description

          Some description about relatively important things but not too long since this is a card component.

          -
            +
            • 18 Key information
            • 12.59 Key information
            • 5 Key information
            - - + +
            - +

            Some description about relatively important things but not too long since this is a card component.

            -
              +
              • 1 Key information
              • 12.59 Key information
              • 3 Key information
              • @@ -74,18 +82,17 @@ export default function DemoCards() { - + Header text

                Some description about relatively important things but not too long since this is a card component.

                -
                  +
                  • 18 Key information
                  • 12.59 Key information
                  • 5 Key information
                  -
); -} \ No newline at end of file +} diff --git a/src/templates/shared/components/Header.tsx b/src/templates/shared/components/Header.tsx index d0895796..fb1ea5d8 100644 --- a/src/templates/shared/components/Header.tsx +++ b/src/templates/shared/components/Header.tsx @@ -14,7 +14,7 @@ export default function Header({ setConnectNeo4j = () => {}, openConnectionModal = () => {}, userHeader = true, - documentation = "", + documentation = '', }: { title: string; navItems?: string[]; @@ -103,7 +103,13 @@ export default function Header({ )} - handleURLClick(documentation)} className='hidden md:inline-flex' ariaLabel='Help' isClean size='large'> + handleURLClick(documentation)} + className='hidden md:inline-flex' + ariaLabel='Help' + isClean + size='large' + > diff --git a/src/templates/shared/components/RetrievalInformation.tsx b/src/templates/shared/components/RetrievalInformation.tsx index 68042e18..b3a9a0b2 100644 --- a/src/templates/shared/components/RetrievalInformation.tsx +++ b/src/templates/shared/components/RetrievalInformation.tsx @@ -4,82 +4,84 @@ import { Box, Flex, IconButton, Typography } from '@neo4j-ndl/react'; import { ClockIconOutline, FitToScreenIcon, ResetZoomIcon } from '@neo4j-ndl/react/icons'; import retrievalIllustration from '../assets/retrieval.png'; -import type { NVL, HitTargets, Node, Relationship } from '@neo4j-nvl/base' -import { InteractiveNvlWrapper } from '@neo4j-nvl/react' -import type { MouseEventCallbacks } from '@neo4j-nvl/react' -import { runRAGQuery, setDriver } from '../utils/Driver'; +import type { NVL, HitTargets, Node, Relationship } from '@neo4j-nvl/base'; +import { InteractiveNvlWrapper } from '@neo4j-nvl/react'; +import type { MouseEventCallbacks } from '@neo4j-nvl/react'; +// import { runRAGQuery, setDriver } from '../utils/Driver'; type RetrievalProps = { - sources: Array; - model: string; - timeTaken: number; - }; + sources: Array; + model: string; + timeTaken: number; +}; function RetrievalInformation(props: RetrievalProps) { - const nvl = useRef(null) + const nvl = useRef(null); - const [nodes, setNodes] = useState([]) - const [rels, setRels] = useState([]) + const [nodes, setNodes] = useState([]); + const [rels, setRels] = useState([]); - const mouseEventCallbacks: MouseEventCallbacks = { - onHover: (_element: Node | Relationship, _hitTargets: HitTargets, _evt: MouseEvent) => null, - onRelationshipRightClick: (_rel: Relationship, _hitTargets: HitTargets, _evt: MouseEvent) => null, - onNodeClick: (_node: Node, _hitTargets: HitTargets, _evt: MouseEvent) => null, - onNodeRightClick: (_node: Node, _hitTargets: HitTargets, _evt: MouseEvent) => null, - onNodeDoubleClick: (_node: Node, _hitTargets: HitTargets, _evt: MouseEvent) => null, - onRelationshipClick: (_rel: Relationship, _hitTargets: HitTargets, _evt: MouseEvent) => null, - onRelationshipDoubleClick: (_rel: Relationship, _hitTargets: HitTargets, _evt: MouseEvent) => null, - onCanvasClick: (_evt: MouseEvent) => null, - onCanvasDoubleClick: (_evt: MouseEvent) => null, - onCanvasRightClick: (_evt: MouseEvent) => null, - onDrag: (_nodes: Node[]) => null, - onPan: (_panning: { x: number; y: number }, _evt: MouseEvent) => null, - onZoom: (_zoomLevel: number) => null - } + const mouseEventCallbacks: MouseEventCallbacks = { + onHover: (_element: Node | Relationship, _hitTargets: HitTargets, _evt: MouseEvent) => null, + onRelationshipRightClick: (_rel: Relationship, _hitTargets: HitTargets, _evt: MouseEvent) => null, + onNodeClick: (_node: Node, _hitTargets: HitTargets, _evt: MouseEvent) => null, + onNodeRightClick: (_node: Node, _hitTargets: HitTargets, _evt: MouseEvent) => null, + onNodeDoubleClick: (_node: Node, _hitTargets: HitTargets, _evt: MouseEvent) => null, + onRelationshipClick: (_rel: Relationship, _hitTargets: HitTargets, _evt: MouseEvent) => null, + onRelationshipDoubleClick: (_rel: Relationship, _hitTargets: HitTargets, _evt: MouseEvent) => null, + onCanvasClick: (_evt: MouseEvent) => null, + onCanvasDoubleClick: (_evt: MouseEvent) => null, + onCanvasRightClick: (_evt: MouseEvent) => null, + onDrag: (_nodes: Node[]) => null, + onPan: (_panning: { x: number; y: number }, _evt: MouseEvent) => null, + onZoom: (_zoomLevel: number) => null, + }; - const fitNodes = () => { - nvl.current?.fit(nodes.map((n) => n.id)) - } - const resetZoom = () => { - nvl.current?.resetZoom() - } + const fitNodes = () => { + nvl.current?.fit(nodes.map((n) => n.id)); + }; + const resetZoom = () => { + nvl.current?.resetZoom(); + }; - function retrieveSources() { - // This is only for rendering the sources nodes. Ideally, for each of the sources, you would use your retrieval query to get the nodes and relationships - // Example: - // setDriver('bolt://localhost:7687', 'neo4j', 'password'); - // runRAGQuery(props.sources).then((nvlGraph) => { - // setNodes(nvlGraph.nodes); - // setRels(nvlGraph.relationships); - // }); - const retrievedNodes = props.sources.map((source, index) => ({ - id: `${index}`, - color: '#0A6190', - captions: [{ value: source }] - })); - setNodes(retrievedNodes); - setRels([{ id: '10', from: '0', to: '1', captions: [{ value: 'MOCKUP_DATA' }] }]); - } + function retrieveSources() { + // This is only for rendering the sources nodes. Ideally, for each of the sources, you would use your retrieval query to get the nodes and relationships + // Example: + // setDriver('bolt://localhost:7687', 'neo4j', 'password'); + // runRAGQuery(props.sources).then((nvlGraph) => { + // setNodes(nvlGraph.nodes); + // setRels(nvlGraph.relationships); + // }); + const retrievedNodes = props.sources.map((source, index) => ({ + id: `${index}`, + color: '#0A6190', + captions: [{ value: source }], + })); + setNodes(retrievedNodes); + setRels([{ id: '10', from: '0', to: '1', captions: [{ value: 'MOCKUP_DATA' }] }]); + } - useEffect(() => { - retrieveSources(); - }, []); + useEffect(() => { + retrieveSources(); + }, []); return ( - + - icon + icon - Retrieval information - + Retrieval information + To generate this response, we used the model {props.model}. - {props.timeTaken / 1000} seconds + + {props.timeTaken / 1000} seconds + - +
- - - - - - - - - null} - mouseEventCallbacks={mouseEventCallbacks} - nvlOptions={{ - initialZoom: 2, - layout: 'd3Force', - relationshipThreshold: 1, - }} - /> + }} + > + + + + + + + + + null} + mouseEventCallbacks={mouseEventCallbacks} + nvlOptions={{ + initialZoom: 2, + layout: 'd3Force', + relationshipThreshold: 1, + }} + />
diff --git a/src/templates/shared/utils/Driver.tsx b/src/templates/shared/utils/Driver.tsx index 9aba3268..cde565f3 100644 --- a/src/templates/shared/utils/Driver.tsx +++ b/src/templates/shared/utils/Driver.tsx @@ -1,6 +1,6 @@ /* eslint-disable no-console */ import neo4j, { Driver } from 'neo4j-driver'; -import { nvlResultTransformer } from "@neo4j-nvl/base"; +import { nvlResultTransformer } from '@neo4j-nvl/base'; export let driver: Driver; @@ -31,27 +31,23 @@ export async function disconnect() { export async function runRAGQuery(sources: Array) { // Customize the RETRIEVAL_QUERY to match your needs - const formattedSources = sources.map(source => `'${source}'`).join(', '); + const formattedSources = sources.map((source) => `'${source}'`).join(', '); const RETRIEVAL_QUERY = `MATCH (a)-[r]->(b) WHERE elementId(a) IN [${formattedSources}] RETURN a, r, b LIMIT 25`; - const nvlGraph = await driver.executeQuery( - RETRIEVAL_QUERY, - {}, - { resultTransformer: nvlResultTransformer } - ); + const nvlGraph = await driver.executeQuery(RETRIEVAL_QUERY, {}, { resultTransformer: nvlResultTransformer }); const nodes = nvlGraph.nodes.map((node) => { - const {properties, labels} = nvlGraph.recordObjectMap.get(node.id); + const { properties, labels } = nvlGraph.recordObjectMap.get(node.id); return { ...node, - caption: properties.name ?? labels[0] , + caption: properties.name ?? labels[0], }; }); console.log(nodes); - const relationships = nvlGraph.relationships.map(rel => { - const or = nvlGraph.recordObjectMap.get(rel.id) + const relationships = nvlGraph.relationships.map((rel) => { + const or = nvlGraph.recordObjectMap.get(rel.id); return { ...rel, - caption: or.type - } + caption: or.type, + }; }); return { nodes, relationships }; }