'use client'
import { useState } from 'react'
import { Chat } from '@/lib/types/database'
import { IoTimeOutline, IoClose, IoTrashOutline, IoPencilOutline, IoCheckmarkOutline } from 'react-icons/io5'
import DeleteConfirmationDialog from './DeleteConfirmationDialog'
interface ChatHistoryProps {
isOpen: boolean
onClose: () => void
chatHistory: Chat[]
currentChatId?: string | null
isLoading: boolean
onLoadChat: (chatId: string) => void
onDeleteChat: (chatId: string) => void
onUpdateChatTitle: (chatId: string, title: string) => void
}
export default function ChatHistory({
isOpen,
onClose,
chatHistory,
currentChatId,
isLoading,
onLoadChat,
onDeleteChat,
onUpdateChatTitle,
}: ChatHistoryProps) {
const [editingChatId, setEditingChatId] = useState<string | null>(null)
const [editingTitle, setEditingTitle] = useState('')
const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false)
const [chatToDelete, setChatToDelete] = useState<Chat | null>(null)
const handleStartEdit = (chat: Chat) => {
setEditingChatId(chat.id)
setEditingTitle(chat.title)
}
const handleSaveEdit = async (chatId: string) => {
if (editingTitle.trim()) {
await onUpdateChatTitle(chatId, editingTitle.trim())
}
setEditingChatId(null)
setEditingTitle('')
}
const handleCancelEdit = () => {
setEditingChatId(null)
setEditingTitle('')
}
const handleDeleteConfirm = () => {
if (chatToDelete) {
onDeleteChat(chatToDelete.id)
setIsDeleteDialogOpen(false)
setChatToDelete(null)
}
}
const formatDate = (dateString: string) => {
const date = new Date(dateString)
const now = new Date()
const diffTime = Math.abs(now.getTime() - date.getTime())
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24))
if (diffDays === 1) {
// Check if it's actually today (same calendar day)
const isToday = date.toDateString() === now.toDateString()
if (isToday) {
return `Today at ${date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}`
}
return 'Today'
} else if (diffDays === 2) {
return 'Yesterday'
} else if (diffDays <= 7) {
return `${diffDays - 1} days ago`
} else {
return date.toLocaleDateString()
}
}
if (!isOpen) return null
return (
<div className="fixed inset-0 z-50 bg-black/50 backdrop-blur-sm">
<div className="flex">
{/* Click outside to close */}
<div className="flex-1" onClick={onClose} />
{/* Sidebar */}
<div className="w-80 h-screen bg-slate-800 border-l border-slate-700 flex flex-col">
{/* Header */}
<div className="h-12 bg-slate-800 border-b border-slate-700 flex items-center justify-between px-4">
<div className="flex items-center gap-2">
<IoTimeOutline className="w-4 h-4 text-slate-400" />
<h2 className="text-sm font-semibold text-slate-200">Chat History</h2>
</div>
<button
onClick={onClose}
className="p-1.5 text-slate-400 hover:text-slate-200 hover:bg-slate-700 rounded-md transition-colors"
>
<IoClose className="w-4 h-4" />
</button>
</div>
{/* Chat List */}
<div className="flex-1 overflow-y-auto px-3 py-3">
{isLoading ? (
<div className="flex items-center justify-center py-8">
<div className="animate-spin rounded-full h-6 w-6 border-b-2 border-teal-500"></div>
</div>
) : chatHistory.length === 0 ? (
<div className="text-center py-8 text-slate-400">
<IoTimeOutline className="w-8 h-8 mx-auto mb-2 opacity-50" />
<p className="text-sm">No chat history yet</p>
<p className="text-xs mt-1">Start a conversation to see it here</p>
</div>
) : (
<div className="space-y-2">
{chatHistory.map((chat) => (
<div
key={chat.id}
className={`group relative p-3 rounded-lg border transition-all cursor-pointer ${
currentChatId === chat.id
? 'bg-teal-600/20 border-teal-500/50 text-teal-100'
: 'bg-slate-700/50 border-slate-600 hover:bg-slate-700 hover:border-slate-500 text-slate-200'
}`}
onClick={() => !editingChatId && onLoadChat(chat.id)}
>
{/* Chat Title */}
<div className="mb-2">
{editingChatId === chat.id ? (
<div className="flex items-center gap-2">
<input
type="text"
value={editingTitle}
onChange={(e) => setEditingTitle(e.target.value)}
className="flex-1 bg-slate-600 text-slate-200 text-sm px-2 py-1 rounded border border-slate-500 focus:outline-none focus:border-teal-500"
autoFocus
onKeyDown={(e) => {
if (e.key === 'Enter') {
handleSaveEdit(chat.id)
} else if (e.key === 'Escape') {
handleCancelEdit()
}
}}
/>
<button
onClick={(e) => {
e.stopPropagation()
handleSaveEdit(chat.id)
}}
className="p-1 text-teal-400 hover:text-teal-300"
>
<IoCheckmarkOutline className="w-4 h-4" />
</button>
</div>
) : (
<h3 className="text-sm font-medium line-clamp-2 group-hover:text-slate-100">
{chat.title}
</h3>
)}
</div>
{/* Chat Metadata */}
<div className="flex items-center justify-between text-xs">
<div className="flex items-center gap-3 text-slate-400">
<span>{chat.total_messages} messages</span>
<span>{formatDate(chat.updated_at)}</span>
</div>
{/* Action Buttons */}
{editingChatId !== chat.id && (
<div className="flex items-center gap-1 opacity-0 group-hover:opacity-100 transition-opacity">
<button
onClick={(e) => {
e.stopPropagation()
handleStartEdit(chat)
}}
className="p-1 text-slate-400 hover:text-slate-200 hover:bg-slate-600 rounded"
title="Rename chat"
>
<IoPencilOutline className="w-3 h-3" />
</button>
<button
onClick={(e) => {
e.stopPropagation()
setChatToDelete(chat)
setIsDeleteDialogOpen(true)
}}
className="p-1 text-slate-400 hover:text-red-400 hover:bg-slate-600 rounded"
title="Delete chat"
>
<IoTrashOutline className="w-3 h-3" />
</button>
</div>
)}
</div>
{/* Model Badge */}
{chat.model && (
<div className="mt-2">
<span className="inline-block px-2 py-1 bg-slate-600 text-slate-300 text-xs rounded">
{chat.model}
</span>
</div>
)}
</div>
))}
</div>
)}
</div>
</div>
</div>
{/* Delete Confirmation Dialog */}
<DeleteConfirmationDialog
isOpen={isDeleteDialogOpen}
onClose={() => {
setIsDeleteDialogOpen(false)
setChatToDelete(null)
}}
onConfirm={handleDeleteConfirm}
loading={false}
title="Delete Chat"
message={`Delete "${chatToDelete?.title}"? This action cannot be undone.`}
confirmText="Delete Chat"
/>
</div>
)
}