diff --git a/API_INTEGRATION.md b/API_INTEGRATION.md index 5bb6c98..1e1f19e 100644 --- a/API_INTEGRATION.md +++ b/API_INTEGRATION.md @@ -38,15 +38,6 @@ Currently, the API does not require authentication. However, this may change in } ``` -### 2. Get Chat History - -- **URL**: `/chats` -- **Method**: GET -- **Description**: Retrieves the chat history (only available in non-hosted mode). -- **Response**: JSON array of chat objects. - -> Note: Disabled in hosted mode - ### 3. Get Suggestions - **URL**: `/suggestions` diff --git a/ui/app/history/page.tsx b/ui/app/history/page.tsx new file mode 100644 index 0000000..aa81e31 --- /dev/null +++ b/ui/app/history/page.tsx @@ -0,0 +1,84 @@ +'use client'; + +import { useEffect, useState } from 'react'; +import Link from 'next/link'; +import DeleteChat from '@/components/DeleteChat'; +import { StoredChat } from '@/components/ChatWindow'; + +const ChatHistory = () => { + const [chats, setChats] = useState([]); + const [searchTerm, setSearchTerm] = useState(''); + + useEffect(() => { + if (typeof window !== 'undefined') { + const storedChats = JSON.parse(localStorage.getItem('chats') || '[]'); + setChats(storedChats); + } + }, []); + + // Sort chats so the most recent (by chat.createdAt) comes first. + const sortedChats = [...chats].sort( + (a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime(), + ); + + // Filter chats based on search term. + const filteredChats = sortedChats.filter((chat) => + (chat.title || `Chat ${chat.id}`) + .toLowerCase() + .includes(searchTerm.toLowerCase()), + ); + + return ( +
+
+

Chat History

+
+ setSearchTerm(e.target.value)} + className="px-2 py-1 border border-light-200 dark:border-dark-200 rounded-md" + /> + +
+
+ {filteredChats.length === 0 ? ( +

No chats found.

+ ) : ( + + )} +
+ ); +}; + +export default ChatHistory; diff --git a/ui/app/library/page.tsx b/ui/app/library/page.tsx index fb3f90c..4a68c77 100644 --- a/ui/app/library/page.tsx +++ b/ui/app/library/page.tsx @@ -6,56 +6,22 @@ import { formatTimeDifference } from '@/lib/utils'; import { BookOpenText, ClockIcon, Delete, ScanEye } from 'lucide-react'; import Link from 'next/link'; import { useEffect, useState } from 'react'; - -export interface Chat { - id: string; - title: string; - createdAt: string; - focusMode: string; -} - const Page = () => { - const [chats, setChats] = useState([]); + const [chats, setChats] = useState([]); const [loading, setLoading] = useState(true); - const isHostedModel = process.env.NEXT_PUBLIC_HOSTED_MODE === 'true'; - useEffect(() => { const fetchChats = async () => { - if (isHostedModel) { - const storedChats = JSON.parse( - localStorage.getItem('chats') || '[]', - ) as StoredChat[]; - setChats( - storedChats.map( - (chat): Chat => ({ - id: chat.id, - title: chat.messages[0].content, - createdAt: chat.createdAt.toString(), - focusMode: chat.focusMode, - }), - ), - ); - setLoading(false); - return; - } - setLoading(true); - - const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/chats`, { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - }, - }); - - const data = await res.json(); - - setChats(data?.chats ?? []); + const storedChats = JSON.parse( + localStorage.getItem('chats') || '[]', + ) as StoredChat[]; + setChats(storedChats); setLoading(false); + return; }; fetchChats(); - }, [isHostedModel]); + }, []); return loading ? (
diff --git a/ui/components/ChatWindow.tsx b/ui/components/ChatWindow.tsx index c6cd17d..42d80a4 100644 --- a/ui/components/ChatWindow.tsx +++ b/ui/components/ChatWindow.tsx @@ -301,6 +301,7 @@ const loadMessages = async ( export type StoredChat = { id: string; + title: string; messages: Message[]; focusMode: string; createdAt: Date; @@ -319,6 +320,7 @@ const saveMessagesToLocalStorage = ( const updatedChat: StoredChat = { id: chatId, + title: messages[0].content, messages, focusMode, createdAt: new Date(), diff --git a/ui/components/DeleteChat.tsx b/ui/components/DeleteChat.tsx index 86c834a..daa7379 100644 --- a/ui/components/DeleteChat.tsx +++ b/ui/components/DeleteChat.tsx @@ -26,38 +26,13 @@ const DeleteChat = ({ const [loading, setLoading] = useState(false); const handleDelete = async () => { - if (process.env.NEXT_PUBLIC_HOSTED_MODE === 'true') { - deleteChatFromLocalStorage(chatId); - const newChats = chats.filter((chat) => chat.id !== chatId); - setChats(newChats); - setConfirmationDialogOpen(false); - return; - } setLoading(true); - try { - const res = await fetch( - `${process.env.NEXT_PUBLIC_API_URL}/chats/${chatId}`, - { - method: 'DELETE', - headers: { - 'Content-Type': 'application/json', - }, - }, - ); - - if (res.status != 200) { - throw new Error('Failed to delete chat'); - } - - const newChats = chats.filter((chat) => chat.id !== chatId); - - setChats(newChats); - } catch (err: any) { - toast.error(err.message); - } finally { - setConfirmationDialogOpen(false); - setLoading(false); - } + deleteChatFromLocalStorage(chatId); + const newChats = chats.filter((chat) => chat.id !== chatId); + setChats(newChats); + setConfirmationDialogOpen(false); + setLoading(false); + return; }; return ( diff --git a/ui/components/Sidebar.tsx b/ui/components/Sidebar.tsx index ad32ecb..95ad1ea 100644 --- a/ui/components/Sidebar.tsx +++ b/ui/components/Sidebar.tsx @@ -29,6 +29,12 @@ const Sidebar = ({ children }: { children: React.ReactNode }) => { active: segments.length === 0 || segments.includes('c'), label: 'Home', }, + { + icon: BookOpenText, + href: '/history', + active: segments.includes('history'), + label: 'History', + }, ]; return (