edgellm / frontend /src /components /chat /ChatSessions.tsx
wu981526092's picture
add
6a50e97
raw
history blame
5.82 kB
import React from 'react'
import { Button } from '@/components/ui/button'
import { Card } from '@/components/ui/card'
import { Badge } from '@/components/ui/badge'
import { ChatSession } from '@/types/chat'
import {
Plus,
MessageSquare,
Trash2,
Edit2,
Check,
X
} from 'lucide-react'
interface ChatSessionsProps {
sessions: ChatSession[]
currentSessionId: string | null
onSelectSession: (sessionId: string) => void
onNewSession: () => void
onDeleteSession: (sessionId: string) => void
onRenameSession: (sessionId: string, newTitle: string) => void
}
export function ChatSessions({
sessions,
currentSessionId,
onSelectSession,
onNewSession,
onDeleteSession,
onRenameSession
}: ChatSessionsProps) {
const [editingId, setEditingId] = React.useState<string | null>(null)
const [editTitle, setEditTitle] = React.useState('')
const startEditing = (session: ChatSession) => {
setEditingId(session.id)
setEditTitle(session.title)
}
const finishEditing = () => {
if (editingId && editTitle.trim()) {
onRenameSession(editingId, editTitle.trim())
}
setEditingId(null)
setEditTitle('')
}
const cancelEditing = () => {
setEditingId(null)
setEditTitle('')
}
return (
<div className="h-full flex flex-col">
{/* Header */}
<div className="p-4 border-b">
<div className="flex items-center justify-between mb-4">
<h2 className="font-semibold">Chat Sessions</h2>
<Button onClick={onNewSession} size="sm">
<Plus className="h-4 w-4 mr-1" />
New
</Button>
</div>
</div>
{/* Sessions list */}
<div className="flex-1 overflow-y-auto p-4 space-y-2">
{sessions.length === 0 ? (
<div className="text-center py-8">
<MessageSquare className="h-8 w-8 mx-auto text-muted-foreground mb-2" />
<p className="text-sm text-muted-foreground">No conversations yet</p>
<Button onClick={onNewSession} variant="outline" size="sm" className="mt-2">
Start your first chat
</Button>
</div>
) : (
sessions.map((session) => (
<Card
key={session.id}
className={`p-3 cursor-pointer transition-colors hover:bg-accent ${
currentSessionId === session.id ? 'bg-accent border-primary' : ''
}`}
onClick={() => onSelectSession(session.id)}
>
<div className="space-y-2">
{/* Title */}
{editingId === session.id ? (
<div className="flex items-center gap-1">
<input
value={editTitle}
onChange={(e) => setEditTitle(e.target.value)}
onKeyPress={(e) => {
if (e.key === 'Enter') finishEditing()
if (e.key === 'Escape') cancelEditing()
}}
className="flex-1 text-sm bg-background border rounded px-2 py-1"
autoFocus
onClick={(e) => e.stopPropagation()}
/>
<Button
size="sm"
variant="ghost"
onClick={(e) => {
e.stopPropagation()
finishEditing()
}}
>
<Check className="h-3 w-3" />
</Button>
<Button
size="sm"
variant="ghost"
onClick={(e) => {
e.stopPropagation()
cancelEditing()
}}
>
<X className="h-3 w-3" />
</Button>
</div>
) : (
<div className="flex items-start justify-between">
<h3 className="font-medium text-sm line-clamp-2">
{session.title}
</h3>
<div className="flex items-center gap-1 ml-2">
<Button
size="sm"
variant="ghost"
onClick={(e) => {
e.stopPropagation()
startEditing(session)
}}
className="h-6 w-6 p-0"
>
<Edit2 className="h-3 w-3" />
</Button>
<Button
size="sm"
variant="ghost"
onClick={(e) => {
e.stopPropagation()
onDeleteSession(session.id)
}}
className="h-6 w-6 p-0 text-destructive hover:text-destructive"
>
<Trash2 className="h-3 w-3" />
</Button>
</div>
</div>
)}
{/* Metadata */}
<div className="flex items-center justify-between text-xs text-muted-foreground">
<span>{session.messages.length} messages</span>
<span>{new Date(session.updatedAt).toLocaleDateString()}</span>
</div>
{/* Model info */}
{session.model && (
<Badge variant="outline" className="text-xs">
{session.model}
</Badge>
)}
</div>
</Card>
))
)}
</div>
</div>
)
}