mirror of
https://github.com/arc53/DocsGPT.git
synced 2025-12-02 01:53:14 +00:00
Merge branch 'main' of https://github.com/utin-francis-peter/DocsGPT into fix/issue#1023
This commit is contained in:
@@ -11,6 +11,7 @@ import {
|
||||
selectStatus,
|
||||
updateQuery,
|
||||
} from './conversationSlice';
|
||||
import { selectConversationId } from '../preferences/preferenceSlice';
|
||||
import Send from './../assets/send.svg';
|
||||
import SendDark from './../assets/send_dark.svg';
|
||||
import Spinner from './../assets/spinner.svg';
|
||||
@@ -20,9 +21,13 @@ import { sendFeedback } from './conversationApi';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import ArrowDown from './../assets/arrow-down.svg';
|
||||
import RetryIcon from '../components/RetryIcon';
|
||||
import ShareIcon from '../assets/share.svg';
|
||||
import { ShareConversationModal } from '../modals/ShareConversationModal';
|
||||
|
||||
export default function Conversation() {
|
||||
const queries = useSelector(selectQueries);
|
||||
const status = useSelector(selectStatus);
|
||||
const conversationId = useSelector(selectConversationId);
|
||||
const dispatch = useDispatch<AppDispatch>();
|
||||
const endMessageRef = useRef<HTMLDivElement>(null);
|
||||
const inputRef = useRef<HTMLDivElement>(null);
|
||||
@@ -31,6 +36,7 @@ export default function Conversation() {
|
||||
const fetchStream = useRef<any>(null);
|
||||
const [eventInterrupt, setEventInterrupt] = useState(false);
|
||||
const [lastQueryReturnedErr, setLastQueryReturnedErr] = useState(false);
|
||||
const [isShareModalOpen, setShareModalState] = useState<boolean>(false);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleUserInterruption = () => {
|
||||
@@ -191,11 +197,36 @@ export default function Conversation() {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex h-screen flex-col gap-1">
|
||||
<div className="flex h-screen flex-col gap-7 pb-2">
|
||||
{conversationId && (
|
||||
<>
|
||||
<button
|
||||
title="Share"
|
||||
onClick={() => {
|
||||
setShareModalState(true);
|
||||
}}
|
||||
className="fixed top-4 right-20 z-30 rounded-full hover:bg-bright-gray dark:hover:bg-[#28292E]"
|
||||
>
|
||||
<img
|
||||
className="m-2 h-5 w-5 filter dark:invert"
|
||||
alt="share"
|
||||
src={ShareIcon}
|
||||
/>
|
||||
</button>
|
||||
{isShareModalOpen && (
|
||||
<ShareConversationModal
|
||||
close={() => {
|
||||
setShareModalState(false);
|
||||
}}
|
||||
conversationId={conversationId}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
<div
|
||||
onWheel={handleUserInterruption}
|
||||
onTouchMove={handleUserInterruption}
|
||||
className="flex h-[90%] w-full justify-center overflow-y-auto p-4 md:h-[83vh]"
|
||||
className="flex h-[90%] w-full flex-1 justify-center overflow-y-auto p-4 md:h-[83vh]"
|
||||
>
|
||||
{queries.length > 0 && !hasScrolledToLast && (
|
||||
<button
|
||||
@@ -234,8 +265,8 @@ export default function Conversation() {
|
||||
{queries.length === 0 && <Hero handleQuestion={handleQuestion} />}
|
||||
</div>
|
||||
|
||||
<div className="bottom-safe fixed flex w-11/12 flex-col items-end self-center rounded-2xl bg-opacity-0 pb-1 sm:w-6/12">
|
||||
<div className="flex h-full w-full items-center rounded-full border border-silver bg-white dark:bg-raisin-black">
|
||||
<div className="flex w-11/12 flex-col items-end self-center rounded-2xl bg-opacity-0 pb-1 sm:w-6/12">
|
||||
<div className="flex h-full w-full items-center rounded-[40px] border border-silver bg-white py-1 dark:bg-raisin-black">
|
||||
<div
|
||||
id="inputbox"
|
||||
ref={inputRef}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { forwardRef, useState } from 'react';
|
||||
import Avatar from '../components/Avatar';
|
||||
import CoppyButton from '../components/CopyButton';
|
||||
import CopyButton from '../components/CopyButton';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
import { FEEDBACK, MESSAGE_TYPE } from './conversationModels';
|
||||
import classes from './ConversationBubble.module.css';
|
||||
@@ -42,7 +42,7 @@ const ConversationBubble = forwardRef<
|
||||
bubble = (
|
||||
<div ref={ref} className={`flex flex-row-reverse self-end ${className}`}>
|
||||
<Avatar className="mt-2 text-2xl" avatar="🧑💻"></Avatar>
|
||||
<div className="ml-10 mr-2 flex items-center rounded-3xl bg-purple-30 p-3.5 text-white">
|
||||
<div className="ml-10 mr-2 flex items-center rounded-[28px] bg-purple-30 py-[14px] px-[19px] text-white">
|
||||
<ReactMarkdown className="whitespace-pre-wrap break-normal leading-normal">
|
||||
{message}
|
||||
</ReactMarkdown>
|
||||
@@ -53,7 +53,7 @@ const ConversationBubble = forwardRef<
|
||||
bubble = (
|
||||
<div
|
||||
ref={ref}
|
||||
className={`flex flex-wrap self-start ${className} group flex-col pr-20 dark:text-bright-gray`}
|
||||
className={`flex flex-wrap self-start ${className} group flex-col dark:text-bright-gray`}
|
||||
>
|
||||
<div className="flex flex-wrap self-start lg:flex-nowrap">
|
||||
<Avatar
|
||||
@@ -68,7 +68,7 @@ const ConversationBubble = forwardRef<
|
||||
/>
|
||||
|
||||
<div
|
||||
className={`ml-2 mr-5 flex max-w-[90vw] rounded-3xl bg-gray-1000 p-3.5 dark:bg-gun-metal md:max-w-[70vw] lg:max-w-[50vw] ${
|
||||
className={`ml-2 mr-5 flex max-w-[90vw] rounded-[28px] bg-gray-1000 py-[14px] px-7 dark:bg-gun-metal md:max-w-[70vw] lg:max-w-[50vw] ${
|
||||
type === 'ERROR'
|
||||
? 'relative flex-row items-center rounded-full border border-transparent bg-[#FFE7E7] p-2 py-5 text-sm font-normal text-red-3000 dark:border-red-2000 dark:text-white'
|
||||
: 'flex-col rounded-3xl'
|
||||
@@ -103,7 +103,7 @@ const ConversationBubble = forwardRef<
|
||||
className={`absolute right-3 top-3 lg:invisible
|
||||
${type !== 'ERROR' ? 'group-hover:lg:visible' : ''} `}
|
||||
>
|
||||
<CoppyButton
|
||||
<CopyButton
|
||||
text={String(children).replace(/\n$/, '')}
|
||||
/>
|
||||
</div>
|
||||
@@ -208,85 +208,89 @@ const ConversationBubble = forwardRef<
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex justify-center">
|
||||
<div
|
||||
className={`relative mr-5 block items-center justify-center lg:invisible
|
||||
</div>
|
||||
<div className="my-2 flex justify-start lg:ml-12">
|
||||
<div
|
||||
className={`relative mr-5 block items-center justify-center lg:invisible
|
||||
${type !== 'ERROR' ? 'group-hover:lg:visible' : ''}`}
|
||||
>
|
||||
<div className="absolute left-2 top-4">
|
||||
<CoppyButton text={message} />
|
||||
</div>
|
||||
>
|
||||
<div>
|
||||
<CopyButton text={message} />
|
||||
</div>
|
||||
<div
|
||||
className={`relative mr-5 flex items-center justify-center ${
|
||||
!isLikeClicked ? 'lg:invisible' : ''
|
||||
} ${
|
||||
feedback === 'LIKE' || type !== 'ERROR'
|
||||
? 'group-hover:lg:visible'
|
||||
: ''
|
||||
}`}
|
||||
>
|
||||
<div className="absolute left-6 top-4">
|
||||
<div
|
||||
className={`flex items-center justify-center rounded-full p-2 dark:bg-transparent ${
|
||||
isLikeHovered
|
||||
? 'bg-[#EEEEEE] dark:bg-purple-taupe'
|
||||
: 'bg-[#ffffff] dark:bg-transparent'
|
||||
}`}
|
||||
>
|
||||
<Like
|
||||
className={`cursor-pointer
|
||||
</div>
|
||||
{handleFeedback && (
|
||||
<>
|
||||
<div
|
||||
className={`relative mr-5 flex items-center justify-center ${
|
||||
!isLikeClicked ? 'lg:invisible' : ''
|
||||
} ${
|
||||
feedback === 'LIKE' || type !== 'ERROR'
|
||||
? 'group-hover:lg:visible'
|
||||
: ''
|
||||
}`}
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
className={`flex items-center justify-center rounded-full p-2 dark:bg-transparent ${
|
||||
isLikeHovered
|
||||
? 'bg-[#EEEEEE] dark:bg-purple-taupe'
|
||||
: 'bg-[#ffffff] dark:bg-transparent'
|
||||
}`}
|
||||
>
|
||||
<Like
|
||||
className={`cursor-pointer
|
||||
${
|
||||
isLikeClicked || feedback === 'LIKE'
|
||||
? 'fill-white-3000 stroke-purple-30 dark:fill-transparent'
|
||||
: 'fill-none stroke-gray-4000'
|
||||
}`}
|
||||
onClick={() => {
|
||||
handleFeedback?.('LIKE');
|
||||
setIsLikeClicked(true);
|
||||
setIsDislikeClicked(false);
|
||||
}}
|
||||
onMouseEnter={() => setIsLikeHovered(true)}
|
||||
onMouseLeave={() => setIsLikeHovered(false)}
|
||||
></Like>
|
||||
onClick={() => {
|
||||
handleFeedback?.('LIKE');
|
||||
setIsLikeClicked(true);
|
||||
setIsDislikeClicked(false);
|
||||
}}
|
||||
onMouseEnter={() => setIsLikeHovered(true)}
|
||||
onMouseLeave={() => setIsLikeHovered(false)}
|
||||
></Like>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className={`mr-13 relative flex items-center justify-center ${
|
||||
!isDislikeClicked ? 'lg:invisible' : ''
|
||||
} ${
|
||||
feedback === 'DISLIKE' || type !== 'ERROR'
|
||||
? 'group-hover:lg:visible'
|
||||
: ''
|
||||
}`}
|
||||
>
|
||||
<div className="absolute left-10 top-4">
|
||||
<div
|
||||
className={`flex items-center justify-center rounded-full p-2 ${
|
||||
isDislikeHovered
|
||||
? 'bg-[#EEEEEE] dark:bg-purple-taupe'
|
||||
: 'bg-[#ffffff] dark:bg-transparent'
|
||||
}`}
|
||||
>
|
||||
<Dislike
|
||||
className={`cursor-pointer ${
|
||||
isDislikeClicked || feedback === 'DISLIKE'
|
||||
? 'fill-white-3000 stroke-red-2000 dark:fill-transparent'
|
||||
: 'fill-none stroke-gray-4000'
|
||||
<div
|
||||
className={`mr-13 relative flex items-center justify-center ${
|
||||
!isDislikeClicked ? 'lg:invisible' : ''
|
||||
} ${
|
||||
feedback === 'DISLIKE' || type !== 'ERROR'
|
||||
? 'group-hover:lg:visible'
|
||||
: ''
|
||||
}`}
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
className={`flex items-center justify-center rounded-full p-2 ${
|
||||
isDislikeHovered
|
||||
? 'bg-[#EEEEEE] dark:bg-purple-taupe'
|
||||
: 'bg-[#ffffff] dark:bg-transparent'
|
||||
}`}
|
||||
onClick={() => {
|
||||
handleFeedback?.('DISLIKE');
|
||||
setIsDislikeClicked(true);
|
||||
setIsLikeClicked(false);
|
||||
}}
|
||||
onMouseEnter={() => setIsDislikeHovered(true)}
|
||||
onMouseLeave={() => setIsDislikeHovered(false)}
|
||||
></Dislike>
|
||||
>
|
||||
<Dislike
|
||||
className={`cursor-pointer ${
|
||||
isDislikeClicked || feedback === 'DISLIKE'
|
||||
? 'fill-white-3000 stroke-red-2000 dark:fill-transparent'
|
||||
: 'fill-none stroke-gray-4000'
|
||||
}`}
|
||||
onClick={() => {
|
||||
handleFeedback?.('DISLIKE');
|
||||
setIsDislikeClicked(true);
|
||||
setIsLikeClicked(false);
|
||||
}}
|
||||
onMouseEnter={() => setIsDislikeHovered(true)}
|
||||
onMouseLeave={() => setIsDislikeHovered(false)}
|
||||
></Dislike>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{sources && openSource !== null && sources[openSource] && (
|
||||
|
||||
@@ -5,11 +5,15 @@ import Exit from '../assets/exit.svg';
|
||||
import Message from '../assets/message.svg';
|
||||
import MessageDark from '../assets/message-dark.svg';
|
||||
import { useDarkTheme } from '../hooks';
|
||||
import ConfirmationModal from '../modals/ConfirmationModal';
|
||||
import CheckMark2 from '../assets/checkMark2.svg';
|
||||
import Trash from '../assets/trash.svg';
|
||||
|
||||
import Trash from '../assets/red-trash.svg';
|
||||
import Share from '../assets/share.svg';
|
||||
import threeDots from '../assets/three-dots.svg';
|
||||
import { selectConversationId } from '../preferences/preferenceSlice';
|
||||
|
||||
import { ActiveState } from '../models/misc';
|
||||
import { ShareConversationModal } from '../modals/ShareConversationModal';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
interface ConversationProps {
|
||||
name: string;
|
||||
id: string;
|
||||
@@ -32,13 +36,19 @@ export default function ConversationTile({
|
||||
const [isDarkTheme] = useDarkTheme();
|
||||
const [isEdit, setIsEdit] = useState(false);
|
||||
const [conversationName, setConversationsName] = useState('');
|
||||
|
||||
const [isOpen, setOpen] = useState<boolean>(false);
|
||||
const [isShareModalOpen, setShareModalState] = useState<boolean>(false);
|
||||
const [deleteModalState, setDeleteModalState] =
|
||||
useState<ActiveState>('INACTIVE');
|
||||
const menuRef = useRef<HTMLDivElement>(null);
|
||||
const { t } = useTranslation();
|
||||
useEffect(() => {
|
||||
setConversationsName(conversation.name);
|
||||
}, [conversation.name]);
|
||||
|
||||
function handleEditConversation() {
|
||||
setIsEdit(true);
|
||||
setOpen(false);
|
||||
}
|
||||
|
||||
function handleSaveConversation(changedConversation: ConversationProps) {
|
||||
@@ -50,6 +60,18 @@ export default function ConversationTile({
|
||||
}
|
||||
}
|
||||
|
||||
const handleClickOutside = (event: MouseEvent) => {
|
||||
if (menuRef.current && !menuRef.current.contains(event.target as Node)) {
|
||||
setOpen(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
document.addEventListener('mousedown', handleClickOutside);
|
||||
return () => {
|
||||
document.removeEventListener('mousedown', handleClickOutside);
|
||||
};
|
||||
}, []);
|
||||
function onClear() {
|
||||
setConversationsName(conversation.name);
|
||||
setIsEdit(false);
|
||||
@@ -79,7 +101,7 @@ export default function ConversationTile({
|
||||
<input
|
||||
autoFocus
|
||||
type="text"
|
||||
className="h-6 w-full px-1 text-sm font-normal leading-6 outline-[#0075FF] focus:outline-1"
|
||||
className="h-6 w-full bg-transparent px-1 text-sm font-normal leading-6 focus:outline-[#0075FF]"
|
||||
value={conversationName}
|
||||
onChange={(e) => setConversationsName(e.target.value)}
|
||||
/>
|
||||
@@ -90,36 +112,108 @@ export default function ConversationTile({
|
||||
)}
|
||||
</div>
|
||||
{conversationId === conversation.id && (
|
||||
<div className="flex text-white dark:text-[#949494]">
|
||||
<img
|
||||
src={isEdit ? CheckMark2 : Edit}
|
||||
alt="Edit"
|
||||
className="mr-2 h-4 w-4 cursor-pointer text-white hover:opacity-50"
|
||||
id={`img-${conversation.id}`}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
isEdit
|
||||
? handleSaveConversation({
|
||||
<div className="flex text-white dark:text-[#949494]" ref={menuRef}>
|
||||
{isEdit ? (
|
||||
<div className="flex gap-1">
|
||||
<img
|
||||
src={CheckMark2}
|
||||
alt="Edit"
|
||||
className="mr-2 h-4 w-4 cursor-pointer text-white hover:opacity-50"
|
||||
id={`img-${conversation.id}`}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
handleSaveConversation({
|
||||
id: conversationId,
|
||||
name: conversationName,
|
||||
})
|
||||
: handleEditConversation();
|
||||
}}
|
||||
/>
|
||||
<img
|
||||
src={isEdit ? Exit : Trash}
|
||||
alt="Exit"
|
||||
className={`mr-4 ${
|
||||
isEdit ? 'h-3 w-3' : 'h-4 w-4'
|
||||
}mt-px cursor-pointer hover:opacity-50`}
|
||||
id={`img-${conversation.id}`}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
isEdit ? onClear() : onDeleteConversation(conversation.id);
|
||||
}}
|
||||
/>
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<img
|
||||
src={isEdit ? Exit : Trash}
|
||||
alt="Exit"
|
||||
className={`mr-4 mt-px h-3 w-3 cursor-pointer hover:opacity-50`}
|
||||
id={`img-${conversation.id}`}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
onClear();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<button onClick={() => setOpen(!isOpen)}>
|
||||
<img src={threeDots} className="mr-4 w-2" />
|
||||
</button>
|
||||
)}
|
||||
{isOpen && (
|
||||
<div className="flex-start absolute flex w-32 translate-x-1 translate-y-5 flex-col rounded-xl bg-stone-100 text-sm text-black shadow-xl dark:bg-chinese-black dark:text-chinese-silver md:w-36">
|
||||
<button
|
||||
onClick={() => {
|
||||
setShareModalState(true);
|
||||
setOpen(false);
|
||||
}}
|
||||
className="flex-start flex items-center gap-4 rounded-t-xl p-3 hover:bg-bright-gray dark:hover:bg-dark-charcoal"
|
||||
>
|
||||
<img
|
||||
src={Share}
|
||||
alt="Share"
|
||||
width={14}
|
||||
height={14}
|
||||
className="cursor-pointer hover:opacity-50"
|
||||
id={`img-${conversation.id}`}
|
||||
/>
|
||||
<span>{t('convTile.share')}</span>
|
||||
</button>
|
||||
<button
|
||||
onClick={(event) => {
|
||||
handleEditConversation();
|
||||
}}
|
||||
className="flex-start flex items-center gap-4 p-3 hover:bg-bright-gray dark:hover:bg-dark-charcoal"
|
||||
>
|
||||
<img
|
||||
src={Edit}
|
||||
alt="Edit"
|
||||
width={16}
|
||||
height={16}
|
||||
className="cursor-pointer hover:opacity-50"
|
||||
id={`img-${conversation.id}`}
|
||||
/>
|
||||
<span>{t('convTile.rename')}</span>
|
||||
</button>
|
||||
<button
|
||||
onClick={(event) => {
|
||||
setDeleteModalState('ACTIVE');
|
||||
setOpen(false);
|
||||
}}
|
||||
className="flex-start flex items-center gap-3 rounded-b-xl p-2 text-red-700 hover:bg-bright-gray dark:hover:bg-dark-charcoal"
|
||||
>
|
||||
<img
|
||||
src={Trash}
|
||||
alt="Edit"
|
||||
width={24}
|
||||
height={24}
|
||||
className="cursor-pointer hover:opacity-50"
|
||||
/>
|
||||
<span>{t('convTile.delete')}</span>
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<ConfirmationModal
|
||||
message={t('convTile.deleteWarning')}
|
||||
modalState={deleteModalState}
|
||||
setModalState={setDeleteModalState}
|
||||
handleSubmit={() => onDeleteConversation(conversation.id)}
|
||||
submitLabel={t('convTile.delete')}
|
||||
/>
|
||||
{isShareModalOpen && conversationId && (
|
||||
<ShareConversationModal
|
||||
close={() => {
|
||||
setShareModalState(false);
|
||||
}}
|
||||
conversationId={conversationId}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
144
frontend/src/conversation/SharedConversation.tsx
Normal file
144
frontend/src/conversation/SharedConversation.tsx
Normal file
@@ -0,0 +1,144 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { Query } from './conversationModels';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import ConversationBubble from './ConversationBubble';
|
||||
import { Fragment } from 'react';
|
||||
const apiHost = import.meta.env.VITE_API_HOST || 'https://docsapi.arc53.com';
|
||||
const SharedConversation = () => {
|
||||
const params = useParams();
|
||||
const navigate = useNavigate();
|
||||
const { identifier } = params; //identifier is a uuid, not conversationId
|
||||
const [queries, setQueries] = useState<Query[]>([]);
|
||||
const [title, setTitle] = useState('');
|
||||
const [date, setDate] = useState('');
|
||||
const { t } = useTranslation();
|
||||
function formatISODate(isoDateStr: string) {
|
||||
const date = new Date(isoDateStr);
|
||||
|
||||
const monthNames = [
|
||||
'Jan',
|
||||
'Feb',
|
||||
'Mar',
|
||||
'Apr',
|
||||
'May',
|
||||
'June',
|
||||
'July',
|
||||
'Aug',
|
||||
'Sept',
|
||||
'Oct',
|
||||
'Nov',
|
||||
'Dec',
|
||||
];
|
||||
|
||||
const month = monthNames[date.getMonth()];
|
||||
const day = date.getDate();
|
||||
const year = date.getFullYear();
|
||||
|
||||
let hours = date.getHours();
|
||||
const minutes = date.getMinutes();
|
||||
const ampm = hours >= 12 ? 'PM' : 'AM';
|
||||
|
||||
hours = hours % 12;
|
||||
hours = hours ? hours : 12;
|
||||
const minutesStr = minutes < 10 ? '0' + minutes : minutes;
|
||||
const formattedDate = `Published ${month} ${day}, ${year} at ${hours}:${minutesStr} ${ampm}`;
|
||||
return formattedDate;
|
||||
}
|
||||
const fetchQueris = () => {
|
||||
fetch(`${apiHost}/api/shared_conversation/${identifier}`)
|
||||
.then((res) => {
|
||||
if (res.status === 404 || res.status === 400) navigate('/pagenotfound');
|
||||
return res.json();
|
||||
})
|
||||
.then((data) => {
|
||||
if (data.success) {
|
||||
setQueries(data.queries);
|
||||
setTitle(data.title);
|
||||
setDate(formatISODate(data.timestamp));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const prepResponseView = (query: Query, index: number) => {
|
||||
let responseView;
|
||||
if (query.response) {
|
||||
responseView = (
|
||||
<ConversationBubble
|
||||
className={`${index === queries.length - 1 ? 'mb-32' : 'mb-7'}`}
|
||||
key={`${index}ANSWER`}
|
||||
message={query.response}
|
||||
type={'ANSWER'}
|
||||
></ConversationBubble>
|
||||
);
|
||||
} else if (query.error) {
|
||||
responseView = (
|
||||
<ConversationBubble
|
||||
className={`${index === queries.length - 1 ? 'mb-32' : 'mb-7'} `}
|
||||
key={`${index}ERROR`}
|
||||
message={query.error}
|
||||
type="ERROR"
|
||||
></ConversationBubble>
|
||||
);
|
||||
}
|
||||
return responseView;
|
||||
};
|
||||
useEffect(() => {
|
||||
fetchQueris();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="flex h-full flex-col items-center justify-between gap-2 overflow-y-hidden dark:bg-raisin-black">
|
||||
<div className="flex w-full justify-center overflow-auto">
|
||||
<div className="mt-0 w-11/12 md:w-10/12 lg:w-6/12">
|
||||
<div className="mb-2 w-full border-b pb-2">
|
||||
<h1 className="font-semi-bold text-4xl text-chinese-black dark:text-chinese-silver">
|
||||
{title}
|
||||
</h1>
|
||||
<h2 className="font-semi-bold text-base text-chinese-black dark:text-chinese-silver">
|
||||
{t('sharedConv.subtitle')}{' '}
|
||||
<a href="/" className="text-[#007DFF]">
|
||||
DocsGPT
|
||||
</a>
|
||||
</h2>
|
||||
<h2 className="font-semi-bold text-base text-chinese-black dark:text-chinese-silver">
|
||||
{date}
|
||||
</h2>
|
||||
</div>
|
||||
<div className="">
|
||||
{queries?.map((query, index) => {
|
||||
return (
|
||||
<Fragment key={index}>
|
||||
<ConversationBubble
|
||||
className={'mb-1 last:mb-28 md:mb-7'}
|
||||
key={`${index}QUESTION`}
|
||||
message={query.prompt}
|
||||
type="QUESTION"
|
||||
sources={query.sources}
|
||||
></ConversationBubble>
|
||||
|
||||
{prepResponseView(query, index)}
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className=" flex flex-col items-center gap-4 pb-2">
|
||||
<button
|
||||
onClick={() => navigate('/')}
|
||||
className="w-fit rounded-full bg-purple-30 p-4 text-white shadow-xl transition-colors duration-200 hover:bg-purple-taupe"
|
||||
>
|
||||
{t('sharedConv.button')}
|
||||
</button>
|
||||
<span className="hidden text-xs text-dark-charcoal dark:text-silver sm:inline">
|
||||
{t('sharedConv.meta')}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SharedConversation;
|
||||
Reference in New Issue
Block a user