Files
nail/src/components/history/HistoryView.tsx
2026-04-01 20:53:47 +01:00

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>
);
}