Skip to content

Css handling of info modal and Tooltips #418

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 18 commits into from
Jun 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions frontend/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
FROM node:20 AS build

ARG BACKEND_API_URL="http://localhost:8000"
ARG REACT_APP_SOURCES="local,youtube,wiki,s3"
ARG LLM_MODELS="Diffbot,OpenAI GPT 3.5,OpenAI GPT 4o"
ARG REACT_APP_SOURCES=""
ARG LLM_MODELS=""
ARG GOOGLE_CLIENT_ID=""
ARG BLOOM_URL="https://workspace-preview.neo4j.io/workspace/explore?connectURL={CONNECT_URL}&search=Show+me+a+graph&featureGenAISuggestions=true&featureGenAISuggestionsInternal=true"
ARG TIME_PER_CHUNK=4
Expand Down
15 changes: 9 additions & 6 deletions frontend/src/components/ButtonWithToolTip.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,33 @@
import { IconButton, Tip } from '@neo4j-ndl/react';
import { Button, Tip } from '@neo4j-ndl/react';
import React from 'react';

const ButtonWithToolTip = ({
text,
children,
onClick,
size = 'medium',
clean,
grouped,
placement = 'bottom',
disabled = false,
className = '',
label,
}: {
text: string;
text: string | React.ReactNode;
children: React.ReactNode;
onClick?: () => void;
size?: 'small' | 'medium' | 'large';
clean?: boolean;
grouped?: boolean;
placement?: 'bottom' | 'top' | 'right' | 'left';
disabled?: boolean;
className?: string;
label: string;
}) => {
return (
<Tip allowedPlacements={[placement]}>
<Tip.Trigger>
<IconButton aria-label={text} size={size} clean={clean} grouped={grouped} onClick={onClick} disabled={disabled}>
<Button aria-label={label} size={size} onClick={onClick} disabled={disabled} className={className}>
{children}
</IconButton>
</Button>
</Tip.Trigger>
<Tip.Content isPortaled={false} style={{ whiteSpace: 'nowrap' }}>
{text}
Expand Down
71 changes: 67 additions & 4 deletions frontend/src/components/Chatbot.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import React, { useEffect, useRef, useState } from 'react';
import { Button, Widget, Typography, Avatar, TextInput, IconButton, Modal } from '@neo4j-ndl/react';
import { InformationCircleIconOutline, XMarkIconOutline } from '@neo4j-ndl/react/icons';
import {
InformationCircleIconOutline,
XMarkIconOutline,
// ClipboardDocumentIconOutline,
// SpeakerWaveIconOutline,
// SpeakerXMarkIconOutline,
} from '@neo4j-ndl/react/icons';
import ChatBotAvatar from '../assets/images/chatbot-ai.png';
import { ChatbotProps, Source, UserCredentials } from '../types';
import { useCredentials } from '../context/UserCredentials';
Expand All @@ -10,6 +16,8 @@ import { useFileContext } from '../context/UsersFiles';
import InfoModal from './InfoModal';
import clsx from 'clsx';
import ReactMarkdown from 'react-markdown';
import IconButtonWithToolTip from './IconButtonToolTip';
// import { tooltips } from '../utils/Constants';
const Chatbot: React.FC<ChatbotProps> = (props) => {
const { messages: listMessages, setMessages: setListMessages, isLoading, isFullScreen } = props;
const [inputMessage, setInputMessage] = useState('');
Expand All @@ -24,6 +32,9 @@ const Chatbot: React.FC<ChatbotProps> = (props) => {
const [responseTime, setResponseTime] = useState<number>(0);
const [chunkModal, setChunkModal] = useState<string[]>([]);
const [tokensUsed, setTokensUsed] = useState<number>(0);
// const [copyMessage, setCopyMessage] = useState<string>('');
// const [speaking, setSpeaking] = useState<boolean>(false);

const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setInputMessage(e.target.value);
};
Expand Down Expand Up @@ -147,6 +158,31 @@ const Chatbot: React.FC<ChatbotProps> = (props) => {
useEffect(() => {
setLoading(() => listMessages.some((msg) => msg.isLoading || msg.isTyping));
}, [listMessages]);

// const handleCopy = async (text: string) => {
// try {
// await navigator.clipboard.writeText(text);
// setCopyMessage('copied!');
// setTimeout(() => setCopyMessage(''), 2000);
// } catch (error) {
// console.error('Failed to copy text: ', error);
// }
// };

// const handleSpeak = (text: string) => {
// if (speaking) {
// window.speechSynthesis.cancel();
// setSpeaking(false);
// } else {
// const utterance = new SpeechSynthesisUtterance(text);
// utterance.onend = () => {
// setSpeaking(false);
// };
// window.speechSynthesis.speak(utterance);
// setSpeaking(true);
// }
// };

return (
<div className='n-bg-palette-neutral-bg-weak flex flex-col justify-between min-h-full max-h-full overflow-hidden'>
<div className='flex overflow-y-auto pb-12 min-w-full chatBotContainer pl-3 pr-3'>
Expand Down Expand Up @@ -209,9 +245,11 @@ const Chatbot: React.FC<ChatbotProps> = (props) => {
</div>
{((chat.user === 'chatbot' && chat.id !== 2 && chat.sources?.length !== 0) || chat.isLoading) && (
<div className='flex'>
<IconButton
<IconButtonWithToolTip
placement='top'
clean
aria-label='Retrieval Information'
text='Retrieval Information'
label='Retrieval Information'
disabled={chat.isTyping || chat.isLoading}
onClick={() => {
setModelModal(chat.model ?? '');
Expand All @@ -223,7 +261,32 @@ const Chatbot: React.FC<ChatbotProps> = (props) => {
}}
>
<InformationCircleIconOutline className='w-4 h-4 inline-block' />
</IconButton>
</IconButtonWithToolTip>
{/* <IconButtonWithToolTip
label='copy text'
placement='top'
clean
text={copyMessage ? tooltips.copied : tooltips.copy}
onClick={() => handleCopy(chat.message)}
disabled={chat.isTyping || chat.isLoading}
>
<ClipboardDocumentIconOutline className='w-4 h-4 inline-block' />
</IconButtonWithToolTip>
{copyMessage && <span className='pt-4 text-xs'>{copyMessage}</span>}
<IconButtonWithToolTip
placement='top'
label='text to speak'
clean
text={speaking ? tooltips.stopSpeaking : tooltips.textTospeech}
onClick={() => handleSpeak(chat.message)}
disabled={chat.isTyping || chat.isLoading}
>
{speaking ? (
<SpeakerXMarkIconOutline className='w-4 h-4 inline-block' />
) : (
<SpeakerWaveIconOutline className='w-4 h-4 inline-block' />
)}
</IconButtonWithToolTip> */}
</div>
)}
</div>
Expand Down
47 changes: 28 additions & 19 deletions frontend/src/components/Content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ import { triggerStatusUpdateAPI } from '../services/ServerSideStatusUpdateAPI';
import useServerSideEvent from '../hooks/useSse';
import { useSearchParams } from 'react-router-dom';
import ConfirmationDialog from './ConfirmationDialog';
import { chunkSize } from '../utils/Constants';
import { chunkSize, tooltips } from '../utils/Constants';
import ButtonWithToolTip from './ButtonWithToolTip';
import connectAPI from '../services/ConnectAPI';

const Content: React.FC<ContentProps> = ({ isLeftExpanded, isRightExpanded }) => {
Expand Down Expand Up @@ -307,10 +308,6 @@ const Content: React.FC<ContentProps> = ({ isLeftExpanded, isRightExpanded }) =>
[selectedfileslength, completedfileNo]
);

const deleteFileClickHandler: React.MouseEventHandler<HTMLButtonElement> = () => {
setshowDeletePopUp(true);
};

const filesForProcessing = useMemo(() => {
let newstatusfiles: CustomFile[] = [];
if (selectedRows.length) {
Expand Down Expand Up @@ -455,8 +452,10 @@ const Content: React.FC<ContentProps> = ({ isLeftExpanded, isRightExpanded }) =>
>
<LlmDropdown onSelect={handleDropdownChange} isDisabled={dropdowncheck} />
<Flex flexDirection='row' gap='4' className='self-end'>
<Button
disabled={disableCheck}
<ButtonWithToolTip
text={tooltips.generateGraph}
placement='top'
label='generate graph'
onClick={() => {
if (selectedRows.length) {
let selectedLargeFiles: CustomFile[] = [];
Expand Down Expand Up @@ -514,33 +513,43 @@ const Content: React.FC<ContentProps> = ({ isLeftExpanded, isRightExpanded }) =>
}
}
}}
disabled={disableCheck}
className='mr-0.5'
>
Generate Graph {selectedfileslength && !disableCheck && newFilecheck ? `(${newFilecheck})` : ''}
</Button>
<Button
title='only completed files will be processed for graph visualization'
disabled={showGraphCheck}
</ButtonWithToolTip>
<ButtonWithToolTip
text={tooltips.showGraph}
placement='top'
onClick={handleGraphView}
disabled={showGraphCheck}
className='mr-0.5'
label='show graph'
>
Show Graph {selectedfileslength && completedfileNo ? `(${completedfileNo})` : ''}
</Button>
<Button
</ButtonWithToolTip>
<ButtonWithToolTip
text={tooltips.bloomGraph}
placement='top'
onClick={handleOpenGraphClick}
disabled={!filesData.some((f) => f?.status === 'Completed')}
className='ml-0.5'
label='Open Graph with Bloom'
>
Open Graph with Bloom
</Button>
<Button
onClick={deleteFileClickHandler}
className='ml-0.5'
title={!selectedfileslength ? 'please select a file' : 'File is still under process'}
</ButtonWithToolTip>
<ButtonWithToolTip
text={
!selectedfileslength ? tooltips.deleteFile : `${selectedfileslength} ${tooltips.deleteSelectedFiles}`
}
placement='top'
onClick={() => setshowDeletePopUp(true)}
disabled={!selectedfileslength}
className='ml-0.5'
label='Delete Files'
>
Delete Files {selectedfileslength > 0 && `(${selectedfileslength})`}
</Button>
</ButtonWithToolTip>
</Flex>
</Flex>
</div>
Expand Down
31 changes: 26 additions & 5 deletions frontend/src/components/DropZone.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import axios from 'axios';
import { Dropzone } from '@neo4j-ndl/react';
import { Dropzone, Flex, Typography } from '@neo4j-ndl/react';
import React, { useState, useEffect, FunctionComponent } from 'react';
import Loader from '../utils/Loader';
import { v4 as uuidv4 } from 'uuid';
Expand All @@ -9,6 +9,8 @@ import CustomAlert from './Alert';
import { CustomFile, CustomFileBase, UploadResponse, alertStateType } from '../types';
import { chunkSize } from '../utils/Constants';
import { url } from '../utils/Utils';
import { InformationCircleIconOutline } from '@neo4j-ndl/react/icons';
import IconButtonWithToolTip from './IconButtonToolTip';

const DropZone: FunctionComponent = () => {
const { filesData, setFilesData, model } = useFileContext();
Expand Down Expand Up @@ -224,19 +226,38 @@ const DropZone: FunctionComponent = () => {
loadingComponent={isLoading && <Loader />}
isTesting={true}
className='!bg-none dropzoneContainer'
supportedFilesDescription={'Supports: Common Types'}
supportedFilesDescription={
<Typography variant='body-small'>
Documents, Images, Unstructured
<IconButtonWithToolTip
label='Source info'
size='small'
clean
text={
<Typography variant='body-small'>
<Flex gap='5'>
<span>Microsoft Office (.docx, .pptx, .xls)</span>
<span>PDF (.pdf)</span>
<span>Images (.jpeg, .jpg, .png, .svg)</span>
<span>Text (.html, .txt , .md)</span>
</Flex>
</Typography>
}
>
<InformationCircleIconOutline className='n-size-token-7' />
</IconButtonWithToolTip>
</Typography>
}
dropZoneOptions={{
accept: {
'application/pdf': ['.pdf'],
'image/png': ['.png'],
'image/*': ['.jpeg', '.jpg', '.png', '.svg'],
'text/html': ['.html'],
'application/vnd.openxmlformats-officedocument.wordprocessingml.document': ['.docx'],
'text/plain': ['.txt'],
'application/vnd.ms-powerpoint': ['.pptx'],
'application/vnd.ms-excel': ['.xls'],
'text/markdown': ['.md'],
'image/jpeg': ['.jpeg', 'jpg'],
'image/svg+xml': ['.svg']
},
onDrop: (f: Partial<globalThis.File>[]) => {
onDropHandler(f);
Expand Down
Loading