diff --git a/frontend/nginx/nginx.prod.conf b/frontend/nginx/nginx.prod.conf index d9c369326..7fe2f81fe 100644 --- a/frontend/nginx/nginx.prod.conf +++ b/frontend/nginx/nginx.prod.conf @@ -3,7 +3,7 @@ server { add_header X-Frame-Options "DENY"; add_header X-Content-Type-Options "nosniff"; add_header Content-Security-Policy "connect-src 'self' ${VITE_BACKEND_API_URL} ${VITE_SEGMENT_API_URL}; - frame-src 'self' *.youtube.com *.wikipedia.org; + frame-src 'self' *.youtube.com *.wikipedia.org ${AUT0_DOMAIN}; script-src 'self' 'unsafe-inline' https://accounts.google.com/gsi/client; default-src 'self' *.${VITE_FRONTEND_HOSTNAME} data:; style-src 'self' *.googleapis.com 'unsafe-inline';" always ; diff --git a/frontend/package.json b/frontend/package.json index cc23f21f6..77c6f32fb 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,5 +1,5 @@ { - "name": "neo4j-needle-starterkit", + "name": "neo4j-llm-graph-builder", "private": true, "version": "1.0.0", "type": "module", @@ -26,7 +26,6 @@ "axios": "^1.6.5", "clsx": "^2.1.1", "eslint-plugin-react": "^7.33.2", - "neo4j-driver": "^5.14.0", "re-resizable": "^6.9.16", "react": "^18.3.1", "react-dom": "^18.3.1", diff --git a/frontend/src/API/Index.ts b/frontend/src/API/Index.ts index d3144379e..d0a82e888 100644 --- a/frontend/src/API/Index.ts +++ b/frontend/src/API/Index.ts @@ -9,11 +9,21 @@ const api = axios.create({ export const createDefaultFormData = (userCredentials: UserCredentials) => { const formData = new FormData(); - if (userCredentials?.uri) formData.append('uri', userCredentials?.uri); - if (userCredentials?.database) formData.append('database', userCredentials?.database); - if (userCredentials?.userName) formData.append('userName', userCredentials?.userName); - if (userCredentials?.password) formData.append('password', userCredentials?.password); - if (userCredentials?.email) formData.append('email', userCredentials?.email); + if (userCredentials?.uri) { + formData.append('uri', userCredentials?.uri); + } + if (userCredentials?.database) { + formData.append('database', userCredentials?.database); + } + if (userCredentials?.userName) { + formData.append('userName', userCredentials?.userName); + } + if (userCredentials?.password) { + formData.append('password', userCredentials?.password); + } + if (userCredentials?.email) { + formData.append('email', userCredentials?.email); + } api.interceptors.request.use( (config) => { if (config.data instanceof FormData) { diff --git a/frontend/src/components/ChatBot/ChatInfoModal.tsx b/frontend/src/components/ChatBot/ChatInfoModal.tsx index d14c48be6..d547a36ec 100644 --- a/frontend/src/components/ChatBot/ChatInfoModal.tsx +++ b/frontend/src/components/ChatBot/ChatInfoModal.tsx @@ -107,7 +107,6 @@ const ChatInfoModal: React.FC = ({ ? false : null ); - const actions: React.ComponentProps>[] = useMemo( () => [ { @@ -132,6 +131,7 @@ const ChatInfoModal: React.FC = ({ ); useEffect(() => { + const abortcontroller = new AbortController(); if ( (mode != chatModeLables.graph || error?.trim() !== '') && (!nodes.length || !infoEntities.length || !chunks.length) @@ -139,11 +139,7 @@ const ChatInfoModal: React.FC = ({ (async () => { toggleInfoLoading(); try { - const response = await chunkEntitiesAPI( - nodeDetails, - entities_ids, - mode, - ); + const response = await chunkEntitiesAPI(nodeDetails, entities_ids, mode, abortcontroller.signal); if (response.data.status === 'Failure') { throw new Error(response.data.error); } @@ -195,6 +191,7 @@ const ChatInfoModal: React.FC = ({ if (metricsLoading) { toggleMetricsLoading(); } + abortcontroller.abort(); }; }, [nodeDetails, mode, error, metricsLoading]); diff --git a/frontend/src/components/ChatBot/ChatOnlyComponent.tsx b/frontend/src/components/ChatBot/ChatOnlyComponent.tsx index 33ee19bd9..323fdf2dd 100644 --- a/frontend/src/components/ChatBot/ChatOnlyComponent.tsx +++ b/frontend/src/components/ChatBot/ChatOnlyComponent.tsx @@ -43,8 +43,7 @@ const ChatContent: React.FC = ({ chatMessages }) => { } else { setOpenConnection((prev) => ({ ...prev, openPopUp: true })); } - } - else { + } else { const credentialsForAPI: UserCredentials = { uri, userName: user, diff --git a/frontend/src/components/Content.tsx b/frontend/src/components/Content.tsx index 93c9e42ab..62964cb93 100644 --- a/frontend/src/components/Content.tsx +++ b/frontend/src/components/Content.tsx @@ -72,6 +72,7 @@ const Content: React.FC = ({ const [currentPage, setCurrentPage] = useState(0); const [totalPageCount, setTotalPageCount] = useState(null); const [textChunks, setTextChunks] = useState([]); + const chunksTextAbortController = useRef(); const [alertStateForRetry, setAlertStateForRetry] = useState({ showAlert: false, @@ -103,9 +104,9 @@ const Content: React.FC = ({ additionalInstructions, setAdditionalInstructions, } = useFileContext(); - const [viewPoint, setViewPoint] = useState<'tableView' | 'showGraphView' | 'chatInfoView' | 'neighborView'|'showSchemaView'>( - 'tableView' - ); + const [viewPoint, setViewPoint] = useState< + 'tableView' | 'showGraphView' | 'chatInfoView' | 'neighborView' | 'showSchemaView' + >('tableView'); const [showDeletePopUp, setShowDeletePopUp] = useState(false); const [deleteLoading, setIsDeleteLoading] = useState(false); @@ -221,8 +222,9 @@ const Content: React.FC = ({ }); }; const getChunks = async (name: string, pageNo: number) => { + chunksTextAbortController.current = new AbortController(); toggleChunksLoading(); - const response = await getChunkText(name, pageNo); + const response = await getChunkText(name, pageNo, chunksTextAbortController.current.signal); setTextChunks(response.data.data.pageitems); if (!totalPageCount) { setTotalPageCount(response.data.data.total_pages); @@ -271,11 +273,7 @@ const Content: React.FC = ({ }); if (fileItem.name != undefined && userCredentials != null) { const { name } = fileItem; - triggerStatusUpdateAPI( - name as string, - userCredentials, - updateStatusForLargeFiles - ); + triggerStatusUpdateAPI(name as string, userCredentials, updateStatusForLargeFiles); } const apiResponse = await extractAPI( @@ -544,12 +542,14 @@ const Content: React.FC = ({ let finalUrl = bloomUrl; if (userCredentials?.database && userCredentials.uri && userCredentials.userName) { const uriCoded = userCredentials.uri.replace(/:\d+$/, ''); - const connectURL = `${uriCoded.split('//')[0]}//${userCredentials.userName}@${uriCoded.split('//')[1]}:${userCredentials.port ?? '7687'}`; + const connectURL = `${uriCoded.split('//')[0]}//${userCredentials.userName}@${uriCoded.split('//')[1]}:${ + userCredentials.port ?? '7687' + }`; const encodedURL = encodeURIComponent(connectURL); finalUrl = bloomUrl?.replace('{CONNECT_URL}', encodedURL); } window.open(finalUrl, '_blank'); - }; + }; const handleGraphView = () => { setOpenGraphView(true); @@ -855,7 +855,10 @@ const Content: React.FC = ({ {showChunkPopup && ( toggleChunkPopup()} + onClose={() => { + chunksTextAbortController.current?.abort(); + toggleChunkPopup(); + }} showChunkPopup={showChunkPopup} chunks={textChunks} incrementPage={incrementPage} @@ -885,11 +888,7 @@ const Content: React.FC = ({
Neo4j connection {isReadOnlyUser ? '(Read only Mode)' : ''} - +
{!hasSelections ? : }
diff --git a/frontend/src/components/Dropdown.tsx b/frontend/src/components/Dropdown.tsx index 2782addd9..9570ee127 100644 --- a/frontend/src/components/Dropdown.tsx +++ b/frontend/src/components/Dropdown.tsx @@ -3,6 +3,8 @@ import { OptionType, ReusableDropdownProps } from '../types'; import { memo, useMemo } from 'react'; import { capitalize, capitalizeWithUnderscore } from '../utils/Utils'; import { prodllms } from '../utils/Constants'; +import { InformationCircleIconOutline } from '@neo4j-ndl/react/icons'; + const DropdownComponent: React.FC = ({ options, placeholder, @@ -29,7 +31,14 @@ const DropdownComponent: React.FC = ({