136 lines
6.2 KiB
TypeScript
136 lines
6.2 KiB
TypeScript
import type { ChatSession, Theme } from "@/types";
|
|
import { THEMES } from "@/lib/constants";
|
|
|
|
interface HistoryViewProps {
|
|
theme: (typeof THEMES)[Theme];
|
|
sessions: ChatSession[];
|
|
sortedSessions: ChatSession[];
|
|
viewingHistory: ChatSession | null;
|
|
onView: (session: ChatSession) => void;
|
|
onBack: () => void;
|
|
onOpen: (id: string) => void;
|
|
onDelete: (id: string) => void;
|
|
onPin: (id: string) => void;
|
|
onClearAll: () => void;
|
|
}
|
|
|
|
export function HistoryView({
|
|
theme, sessions, sortedSessions, viewingHistory,
|
|
onView, onBack, onOpen, onDelete, onPin, onClearAll,
|
|
}: HistoryViewProps) {
|
|
|
|
// ── Drill-down: single session reader ─────────────────────────────────────
|
|
if (viewingHistory) {
|
|
return (
|
|
<div className="flex flex-col h-full">
|
|
<div className="flex items-center gap-3 px-6 py-3.5 border-b shrink-0"
|
|
style={{ borderColor: theme.border }}>
|
|
<button onClick={onBack}
|
|
className="text-white/25 hover:text-white/60 transition-colors flex items-center gap-1.5 text-[13px]">
|
|
← Back
|
|
</button>
|
|
<div className="min-w-0 flex-1 px-2">
|
|
<p className="text-[13px] font-semibold text-white/70 truncate">{viewingHistory.title}</p>
|
|
<p className="text-[11px] text-white/25 mt-0.5">
|
|
{new Date(viewingHistory.updatedAt).toLocaleString()} · {viewingHistory.messages.length} messages
|
|
</p>
|
|
</div>
|
|
<div className="flex gap-2 shrink-0">
|
|
<button onClick={() => onOpen(viewingHistory.id)}
|
|
className="text-[12px] px-3 py-1.5 rounded-lg font-medium transition-all"
|
|
style={{ background: `${theme.accent}20`, color: theme.accent }}>
|
|
Open
|
|
</button>
|
|
<button onClick={() => onDelete(viewingHistory.id)}
|
|
className="text-[12px] text-red-400/40 hover:text-red-400 transition-colors px-2">
|
|
Delete
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex-1 overflow-y-auto px-6 py-5 space-y-4">
|
|
{viewingHistory.messages.map((msg) => (
|
|
<div key={msg.id} className={`flex ${msg.role === "user" ? "justify-end" : "justify-start"}`}>
|
|
<div className={`max-w-[70%] rounded-2xl px-5 py-3 text-[14px] leading-relaxed
|
|
${msg.role === "user" ? "text-white rounded-br-sm" : "text-white/60 rounded-bl-sm border"}`}
|
|
style={msg.role === "user"
|
|
? { background: `${theme.accent}90` }
|
|
: { background: "rgba(255,255,255,0.03)", borderColor: theme.border }}>
|
|
{msg.content}
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
// ── Session list ──────────────────────────────────────────────────────────
|
|
return (
|
|
<div className="px-6 py-6">
|
|
<div className="flex items-center justify-between mb-5">
|
|
<div>
|
|
<p className="text-[15px] font-semibold text-white/70">Conversation History</p>
|
|
<p className="text-[12px] text-white/25 mt-0.5">
|
|
{sessions.length} saved session{sessions.length !== 1 ? "s" : ""}
|
|
</p>
|
|
</div>
|
|
{sessions.length > 0 && (
|
|
<button onClick={onClearAll}
|
|
className="text-[12px] text-red-400/40 hover:text-red-400 transition-colors">
|
|
Clear all
|
|
</button>
|
|
)}
|
|
</div>
|
|
|
|
{/* Empty state */}
|
|
{sessions.length === 0 ? (
|
|
<div className="flex flex-col items-center py-28 gap-3 text-center">
|
|
<span className="text-5xl opacity-20">🕐</span>
|
|
<p className="text-sm text-white/25">No history yet</p>
|
|
<p className="text-xs text-white/15">Conversations are saved automatically</p>
|
|
</div>
|
|
) : (
|
|
<div className="space-y-2">
|
|
{sortedSessions.map((session) => (
|
|
<div key={session.id} onClick={() => onView(session)}
|
|
className="group relative rounded-xl p-4 cursor-pointer transition-all border hover:bg-white/[0.04]"
|
|
style={{ background: "rgba(255,255,255,0.02)", borderColor: theme.border }}>
|
|
<div className="flex items-start gap-3">
|
|
{session.pinned && <span className="text-[11px] text-amber-400/60 mt-0.5 shrink-0">📌</span>}
|
|
<div className="min-w-0 flex-1">
|
|
<p className="text-[13px] font-medium text-white/70 truncate">{session.title}</p>
|
|
<p className="text-[12px] text-white/25 mt-0.5 line-clamp-1">{session.preview}</p>
|
|
<div className="flex items-center gap-2 text-[11px] text-white/20 mt-2">
|
|
<span>
|
|
{new Date(session.updatedAt).toLocaleDateString("en-GB", { day: "numeric", month: "short" })}
|
|
</span>
|
|
<span>·</span>
|
|
<span>{session.messages.length} messages</span>
|
|
</div>
|
|
</div>
|
|
{/* Hover actions */}
|
|
<div className="flex gap-1 opacity-0 group-hover:opacity-100 transition-all shrink-0">
|
|
<button onClick={(e) => { e.stopPropagation(); onOpen(session.id); }}
|
|
className="text-[11px] px-2 py-1 rounded-lg transition-all"
|
|
style={{ background: `${theme.accent}20`, color: theme.accent }}>
|
|
Open
|
|
</button>
|
|
<button onClick={(e) => { e.stopPropagation(); onPin(session.id); }}
|
|
className="text-[11px] px-2 py-1 rounded-lg bg-white/5 text-white/30 hover:text-white/60 transition-all">
|
|
{session.pinned ? "Unpin" : "Pin"}
|
|
</button>
|
|
<button onClick={(e) => { e.stopPropagation(); onDelete(session.id); }}
|
|
className="text-[11px] px-2 py-1 rounded-lg text-red-400/30 hover:text-red-400 transition-all">
|
|
✕
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|