Skip to content
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

Feature/f 146 integrate chatbot reports chatting #117

Merged
merged 16 commits into from
Dec 15, 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
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"@nextui-org/card": "^2.0.34",
"@nextui-org/chip": "^2.0.33",
"@nextui-org/code": "2.0.33",
"pdfjs-dist": "3.11.174",
"@nextui-org/dropdown": "^2.1.31",
"@nextui-org/image": "^2.0.32",
"@nextui-org/input": "2.2.5",
Expand Down Expand Up @@ -72,6 +73,7 @@
"react-pdf": "^9.1.1",
"rehype-sanitize": "^6.0.0",
"remark-gfm": "^4.0.0",
"sharp": "^0.33.5",
"tailwind-variants": "0.1.20",
"uuid": "^11.0.3"
},
Expand Down
6 changes: 5 additions & 1 deletion src/app/about/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Metadata } from 'next';

import HungerMapChatbot from '@/components/Chatbot/Chatbot';
import { Topbar } from '@/components/Topbar/Topbar';

export const metadata: Metadata = {
Expand All @@ -9,7 +10,10 @@ export const metadata: Metadata = {
export default function Layout({ children }: { children: React.ReactNode }) {
return (
<div>
<Topbar />
<div className="mb-20">
<Topbar />
<HungerMapChatbot />
</div>
<main className="flex flex-col gap-6 lg:gap-10 p-5 lg:p-10 text-content !pt-1">{children}</main>
</div>
);
Expand Down
6 changes: 5 additions & 1 deletion src/app/data_sources/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Metadata } from 'next';

import HungerMapChatbot from '@/components/Chatbot/Chatbot';
import { Topbar } from '@/components/Topbar/Topbar';

export const metadata: Metadata = {
Expand All @@ -9,7 +10,10 @@ export const metadata: Metadata = {
export default function Layout({ children }: { children: React.ReactNode }) {
return (
<div>
<Topbar />
<div className="mb-20">
<Topbar />
<HungerMapChatbot />
</div>
<main className="flex flex-col gap-6 lg:gap-10 p-5 lg:p-10 text-content !pt-1">{children}</main>
</div>
);
Expand Down
6 changes: 5 additions & 1 deletion src/app/disclaimer/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Metadata } from 'next';

import HungerMapChatbot from '@/components/Chatbot/Chatbot';
import { Topbar } from '@/components/Topbar/Topbar';

export const metadata: Metadata = {
Expand All @@ -9,7 +10,10 @@ export const metadata: Metadata = {
export default function Layout({ children }: { children: React.ReactNode }) {
return (
<div>
<Topbar />
<div className="mb-20">
<Topbar />
<HungerMapChatbot />
</div>
<main className="flex flex-col gap-6 lg:gap-10 p-5 lg:p-10 text-content">{children}</main>
</div>
);
Expand Down
6 changes: 5 additions & 1 deletion src/app/download-portal/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Metadata } from 'next';

import HungerMapChatbot from '@/components/Chatbot/Chatbot';
import { Topbar } from '@/components/Topbar/Topbar';

export const metadata: Metadata = {
Expand All @@ -9,7 +10,10 @@ export const metadata: Metadata = {
export default function Layout({ children }: { children: React.ReactNode }) {
return (
<div>
<Topbar />
<div className="mb-20">
<Topbar />
<HungerMapChatbot />
</div>
<main className="flex flex-col gap-6 lg:gap-10 p-5 lg:p-10 text-content">{children}</main>
</div>
);
Expand Down
7 changes: 5 additions & 2 deletions src/app/providers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ import { QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import { useRouter } from 'next/navigation';
import { ThemeProvider as NextThemesProvider } from 'next-themes';
import type { ThemeProviderProps } from 'next-themes/dist/types.d.ts';
import type { ThemeProviderProps } from 'next-themes/dist/types';
import * as React from 'react';

import { cachedQueryClient } from '@/config/queryClient';
import { AccordionsModalProvider } from '@/domain/contexts/AccodionsModalContext';
import { ChatbotProvider } from '@/domain/contexts/ChatbotContext';
import { SelectedAlertProvider } from '@/domain/contexts/SelectedAlertContext';
import { SelectedCountryIdProvider } from '@/domain/contexts/SelectedCountryIdContext';
import { SelectedMapProvider } from '@/domain/contexts/SelectedMapContext';
Expand All @@ -33,7 +34,9 @@ export function Providers({ children, themeProps }: ProvidersProps) {
<SelectedAlertProvider>
<SelectedCountryIdProvider>
<AccordionsModalProvider>
<SnackbarProvider>{children}</SnackbarProvider>
<SnackbarProvider>
<ChatbotProvider>{children}</ChatbotProvider>
</SnackbarProvider>
</AccordionsModalProvider>
</SelectedCountryIdProvider>
</SelectedAlertProvider>
Expand Down
6 changes: 5 additions & 1 deletion src/app/wiki/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Metadata } from 'next';

import HungerMapChatbot from '@/components/Chatbot/Chatbot';
import { Topbar } from '@/components/Topbar/Topbar';

export const metadata: Metadata = {
Expand All @@ -9,7 +10,10 @@ export const metadata: Metadata = {
export default function Layout({ children }: { children: React.ReactNode }) {
return (
<div>
<Topbar />
<div className="mb-20">
<Topbar />
<HungerMapChatbot />
</div>
<main className="flex flex-col gap-6 lg:gap-10 p-5 lg:p-10 text-content !pt-1">{children}</main>
</div>
);
Expand Down
59 changes: 32 additions & 27 deletions src/components/Chatbot/Chatbot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,29 +24,37 @@ import {
TYPING_PLACEHOLDER,
WELCOME_MESSAGE,
} from '@/domain/constant/chatbot/Chatbot';
import { useChatbot } from '@/domain/contexts/ChatbotContext';
import { APIError } from '@/domain/entities/chatbot/BackendCommunication';
import { IChat } from '@/domain/entities/chatbot/Chatbot';
import { SenderRole } from '@/domain/enums/SenderRole';
import ChatbotRepository from '@/domain/repositories/ChatbotRepository';
import ChatbotOperations from '@/operations/chatbot/Chatbot';
import { useMediaQuery } from '@/utils/resolution';

import TypingDots from '../TypingText/TypingDot';
import ChatbotSidebar from './ChatbotSidebar';

export default function HungerMapChatbot() {
const chatbot = container.resolve<ChatbotRepository>('ChatbotRepository');
const [isOpen, setIsOpen] = useState(false);
const {
chats,
setChats,
currentChatIndex,
setCurrentChatIndex,
startNewChat,
isOpen,
isMobile,
isSidebarOpen,
setIsOpen,
setIsSidebarOpen,
} = useChatbot();

const [isFullScreen, setIsFullScreen] = useState(false);
const [isSidebarOpen, setIsSidebarOpen] = useState(false);
const [isUserMessageSent, setIsUserMessageSent] = useState(false);
const [chats, setChats] = useState<IChat[]>([{ id: 1, title: 'Chat 1', messages: [], isTyping: false }]);
const [currentChatIndex, setCurrentChatIndex] = useState(0);
const [input, setInput] = useState('');
const [isResponseAnimated, setIsResponseAnimated] = useState(true);

const inputRef = useRef<HTMLTextAreaElement>(null);
const chatEndRef = useRef<HTMLDivElement>(null);
const isMobile = useMediaQuery('(max-width: 640px)');

const toggleChat = (): void => {
if (isMobile) {
Expand All @@ -64,15 +72,6 @@ export default function HungerMapChatbot() {
}
};

const startNewChat = (): void => {
const newChat: IChat = { id: chats.length + 1, title: `Chat ${chats.length + 1}`, messages: [], isTyping: false };
setChats([...chats, newChat]);
setCurrentChatIndex(chats.length);
if (isMobile) {
setIsSidebarOpen(false);
}
};

const setTypingStatus = (chatIndex: number, isTyping: boolean): void => {
setChats((prevChats) => prevChats.map((chat, index) => (index === chatIndex ? { ...chat, isTyping } : chat)));
};
Expand All @@ -97,8 +96,17 @@ export default function HungerMapChatbot() {
const previousMessages = chats[currentChatIndex].messages;
let aiResponse = '';
try {
const response = await chatbot.sendMessage(text, { previous_messages: previousMessages });
aiResponse = response.response;
if (chats[currentChatIndex].isReportStarter && chats[currentChatIndex].context) {
aiResponse = (
await chatbot.sendMessage(text, {
previous_messages: previousMessages,
context: chats[currentChatIndex].context,
})
).response;
chats[currentChatIndex].isReportStarter = false;
} else {
aiResponse = (await chatbot.sendMessage(text, { previous_messages: previousMessages })).response;
}
} catch (err) {
if (err instanceof APIError) {
aiResponse = `Ups! Unfortunately, it seems like there was a problem connecting to the server...\n ${err.status}: ${err.message}`;
Expand Down Expand Up @@ -165,7 +173,7 @@ export default function HungerMapChatbot() {

// use to scroll to the end of the chat when new messages are added
useEffect(() => {
chatEndRef.current?.scrollIntoView({ behavior: 'smooth' });
chatEndRef.current?.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}, [chats, currentChatIndex]);

// listen to isOpen and isMobile to set isFullScreen
Expand All @@ -185,7 +193,7 @@ export default function HungerMapChatbot() {

return (
<div
className={clsx('absolute', {
className={clsx('fixed', {
'inset-0 z-chatbotFullScreen': isFullScreen,
'top-4 right-4': !isFullScreen,
'z-chatbotExpanded': isOpen,
Expand All @@ -196,8 +204,7 @@ export default function HungerMapChatbot() {
<Tooltip text={TRIGGER_CHAT}>
<Button
onClick={toggleChat}
className="
relative flex items-center justify-center min-w-12 h-12 px-1 rounded-full bg-content1 hover:bg-content2 shadow-md"
className="relative flex items-center justify-center min-w-12 h-12 px-1 rounded-full bg-content1 hover:bg-content2 shadow-md"
>
<Bot size={24} />
</Button>
Expand Down Expand Up @@ -268,15 +275,13 @@ export default function HungerMapChatbot() {
{chats[currentChatIndex].messages.length === 0 ? (
<div className="flex flex-col items-center pt-4 space-y-4">
<p className="text-center text-xl max-w-[80%] mb-2">{WELCOME_MESSAGE}</p>
<p className="text-center text-md max-w-[80%] mb-2">{SUB_WELCOME_MESSAGE}</p>
<p className="text-center text-md max-w-[80%] mb-2text">{SUB_WELCOME_MESSAGE}</p>
<div className="flex flex-col items-center space-y-2 w-full max-w-md">
{DEFAULT_PROMPT.map((prompt) => (
<Button
key={prompt.id}
onClick={(event) => handleSubmit(event, prompt.value)}
className="
truncate w-full mb-2 max-w-[250px] sm:max-w-[400px] border border-solid border-black dark:border-white bg-transparent hover:bg-chatbotDefaultPromptHover dark:hover:bg-chatbotDefaultPromptHover
"
className="truncate w-full mb-2 max-w-[250px] sm:max-w-[400px] border border-solid border-black dark:border-white bg-transparent hover:bg-chatbotDefaultPromptHover dark:hover:bg-chatbotDefaultPromptHover"
title={prompt.value}
>
<span className="truncate">{prompt.value}</span>
Expand All @@ -297,7 +302,7 @@ export default function HungerMapChatbot() {
)}
<div
className={clsx(
'mb-5 rounded-lg max-w-[80%]',
'mb-5 rounded-lg max-w-[80%] break-words whitespace-normal',
message.role === SenderRole.USER
? 'rounded-xl p-2 bg-chatbotUserMsg dark:bg-chatbotUserMsg ml-12'
: 'bg-transparent pl-2 pr-2'
Expand Down
2 changes: 1 addition & 1 deletion src/components/Chatbot/ChatbotSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export default function ChatbotSidebar({
>
<CardBody className="p-4">
<Button
onClick={onStartNewChat}
onClick={() => onStartNewChat()}
className="bg-transparent w-full h-10 mb-4 flex justify-center items-center gap-2 border-1.5 border-solid border-black dark:border-white text-black dark:text-white hover:bg-chatbotSidebarBtnHover dark:hover:bg-chatbotSidebarBtnHover"
>
<AddCircle size={24} />
Expand Down
5 changes: 5 additions & 0 deletions src/components/DownloadPortal/CountryReports.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { useMemo, useState } from 'react';
import PdfPreview from '@/components/Pdf/PdfPreview';
import SearchBar from '@/components/Search/SearchBar';
import CustomTable from '@/components/Table/CustomTable';
import { useChatbot } from '@/domain/contexts/ChatbotContext';
import { CountryCodesData } from '@/domain/entities/country/CountryCodesData';
import CountryReportsProps from '@/domain/props/CountryReportsProps';
import { PdfFile } from '@/domain/props/PdfViewerProps';
Expand All @@ -17,6 +18,9 @@ export default function CountryReports({ countryCodesData }: CountryReportsProps
const [error, setError] = useState<string | null>(null);
const [selectedCountry, setSelectedCountry] = useState<CountryCodesData | null>(null);

const chatBot = useChatbot();
const { initiateChatAboutReport } = chatBot;

const toggleModal = () => setModalOpen((prev) => !prev);

const filteredData = useMemo(() => {
Expand All @@ -33,6 +37,7 @@ export default function CountryReports({ countryCodesData }: CountryReportsProps
data={DownloadPortalOperations.formatTableData(
filteredData,
setSelectedCountry,
initiateChatAboutReport,
setPdfFile,
setError,
toggleModal
Expand Down
13 changes: 8 additions & 5 deletions src/components/Topbar/Topbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@ export function Topbar() {
const pathname = usePathname();

return (
<Navbar onMenuOpenChange={setIsMenuOpen} maxWidth="full">
<NavbarContent className="flex-1 min-w-[200px]">
<Navbar onMenuOpenChange={setIsMenuOpen} maxWidth="full" className="fixed pr-10">
<NavbarContent className="flex-1 min-w-[200px] mt-4">
<NavbarMenuToggle aria-label={isMenuOpen ? 'Close menu' : 'Open menu'} className="lg:hidden" />
<NavbarBrand>
<LogoWithText />
</NavbarBrand>
</NavbarContent>
<NavbarContent className="hidden lg:flex gap-4" justify="center">
<NavbarContent className="hidden lg:flex gap-4 mt-4" justify="center">
{pageLinks.map((item) => (
<NavbarItem key={item.label} isActive={pathname === item.href}>
<Link href={item.href} className="text-medium" color={pathname === item.href ? 'primary' : 'foreground'}>
Expand All @@ -38,8 +38,8 @@ export function Topbar() {
</NavbarItem>
))}
</NavbarContent>
<NavbarContent justify="end">
<NavbarItem>
<NavbarContent justify="end" className="hidden lg:flex space-x-7">
<NavbarItem className="mt-4">
<ThemeSwitch isIconOnly />
</NavbarItem>
</NavbarContent>
Expand All @@ -56,6 +56,9 @@ export function Topbar() {
</Link>
</NavbarMenuItem>
))}
<NavbarMenuItem className="mt-4">
<ThemeSwitch isIconOnly />
</NavbarMenuItem>
</NavbarMenu>
</Navbar>
);
Expand Down
Loading
Loading