refactor App, add /shared/id page

This commit is contained in:
ManishMadan2882
2024-07-14 03:29:06 +05:30
parent 02187fed4e
commit 81d7fe3fdb
4 changed files with 232 additions and 19 deletions

View File

@@ -1,4 +1,5 @@
import { Routes, Route } from 'react-router-dom';
import { ReactElement, useEffect } from 'react';
import Navigation from './Navigation';
import Conversation from './conversation/Conversation';
import About from './About';
@@ -8,29 +9,93 @@ import { useMediaQuery } from './hooks';
import { useState } from 'react';
import Setting from './settings';
import './locale/i18n';
import SharedConversation from './conversation/SharedConversation';
import { useDarkTheme } from './hooks';
inject();
export default function App() {
function MainLayout({ children }: { children: ReactElement }) {
const { isMobile } = useMediaQuery();
const [navOpen, setNavOpen] = useState(!isMobile);
return (
<div className="min-h-full min-w-full dark:bg-raisin-black">
<>
<Navigation navOpen={navOpen} setNavOpen={setNavOpen} />
<div
className={`transition-all duration-200 ${
className={` h-full dark:bg-raisin-black ${
!isMobile
? `ml-0 ${!navOpen ? 'md:mx-auto lg:mx-auto' : 'md:ml-72'}`
: 'ml-0 md:ml-16'
}`}
>
<Routes>
<Route path="/" element={<Conversation />} />
<Route path="/about" element={<About />} />
<Route path="*" element={<PageNotFound />} />
<Route path="/settings" element={<Setting />} />
</Routes>
{children}
</div>
</div>
</>
);
}
function Layout({ children }: { children: ReactElement }) {
return (
<>
<div className={`h-full dark:bg-raisin-black`}>{children}</div>
</>
);
}
export default function App() {
const [isDarkTheme] = useDarkTheme();
useEffect(() => {
localStorage.setItem('selectedTheme', isDarkTheme ? 'Dark' : 'Light');
if (isDarkTheme) {
document
.getElementById('root')
?.classList.add('dark', 'dark:bg-raisin-black');
} else {
document.getElementById('root')?.classList.remove('dark');
}
}, [isDarkTheme]);
return (
<>
<Routes>
<Route
path="/"
element={
<MainLayout>
<Conversation />
</MainLayout>
}
/>
<Route
path="/about"
element={
<MainLayout>
<About />
</MainLayout>
}
/>
<Route
path="/settings"
element={
<MainLayout>
<Setting />
</MainLayout>
}
/>
<Route
path="/share/:identifier"
element={
<Layout>
<SharedConversation />
</Layout>
}
/>
{/* default page */}
<Route
path="/*"
element={
<Layout>
<PageNotFound />
</Layout>
}
/>
</Routes>
</>
);
}

View File

@@ -0,0 +1,146 @@
import { useState, useEffect } from 'react';
import { useParams } from 'react-router-dom';
import { useNavigate } from 'react-router-dom';
import { Query } from './conversationModels';
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('');
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="">
<div className="flex h-screen flex-col items-center justify-between">
{queries.length > 0 && (
<div className="flex w-full justify-center overflow-auto">
<div className="mt-0 w-11/12 md:w-6/12">
<div className="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">
Created with{' '}
<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="pt-2">
{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 p-4">
<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"
>
Get Started with DocsGPT
</button>
<span className="hidden text-xs text-dark-charcoal dark:text-silver sm:inline">
This is a chatbot that uses the GPT-3, Faiss and LangChain to answer
questions.
</span>
</div>
</div>
</div>
);
};
export default SharedConversation;

View File

@@ -77,21 +77,23 @@ export function useDarkTheme() {
// Set dark mode based on local storage preference
if (savedMode === 'Dark') {
setIsDarkTheme(true);
document.documentElement.classList.add('dark');
document.documentElement.classList.add('dark:bg-raisin-black');
document
.getElementById('root')
?.classList.add('dark', 'dark:bg-raisin-black');
} else {
// If no preference found, set to default (light mode)
setIsDarkTheme(false);
document.documentElement.classList.remove('dark');
document.getElementById('root')?.classList.remove('dark');
}
}, []);
useEffect(() => {
localStorage.setItem('selectedTheme', isDarkTheme ? 'Dark' : 'Light');
if (isDarkTheme) {
document.documentElement.classList.add('dark');
document.documentElement.classList.add('dark:bg-raisin-black');
document
.getElementById('root')
?.classList.add('dark', 'dark:bg-raisin-black');
} else {
document.documentElement.classList.remove('dark');
document.getElementById('root')?.classList.remove('dark');
}
}, [isDarkTheme]);
//method to toggle theme

View File

@@ -57,13 +57,13 @@ export const ShareConversationModal = ({
remain private
</p>
<div className="flex items-baseline justify-between gap-2">
<span className="no-scrollbar w-full overflow-x-auto whitespace-nowrap rounded-full border-2 p-3 shadow-inner">{`${domain}/shared/${
<span className="no-scrollbar w-full overflow-x-auto whitespace-nowrap rounded-full border-2 p-3 shadow-inner">{`${domain}/share/${
identifier ?? '....'
}`}</span>
{status === 'fetched' ? (
<button
className="my-1 h-10 w-36 rounded-full border border-solid border-purple-30 p-2 text-sm text-purple-30 hover:bg-purple-30 hover:text-white"
onClick={() => handleCopyKey(`${domain}/shared/${identifier}`)}
onClick={() => handleCopyKey(`${domain}/share/${identifier}`)}
>
{isCopied
? t('modals.saveKey.copied')