- ChatSearchDialog: CommandDialog with shouldFilter=false for server-side FTS, term highlighting via React components - ChatMessageBookmark: ghost icon button with fill-current for bookmarked state, aria-label toggle - ChatBookmarkList: scrollable list with skeleton loading, empty state, navigation callbacks - ChatBranchSelector: horizontal branch picker bar with GitBranch icon, active branch highlight
64 lines
2.4 KiB
TypeScript
64 lines
2.4 KiB
TypeScript
import { Bookmark } from "lucide-react";
|
|
import { useChatBookmarks } from "../hooks/useChatBookmarks";
|
|
import { ScrollArea } from "@/components/ui/scroll-area";
|
|
import { Skeleton } from "@/components/ui/skeleton";
|
|
|
|
interface ChatBookmarkListProps {
|
|
companyId: string;
|
|
onNavigate: (conversationId: string, messageId: string) => void;
|
|
}
|
|
|
|
function formatRelativeTime(dateStr: string): string {
|
|
const date = new Date(dateStr);
|
|
const now = new Date();
|
|
const diffMs = now.getTime() - date.getTime();
|
|
const diffMin = Math.floor(diffMs / 60000);
|
|
if (diffMin < 1) return "just now";
|
|
if (diffMin < 60) return `${diffMin}m ago`;
|
|
const diffHr = Math.floor(diffMin / 60);
|
|
if (diffHr < 24) return `${diffHr}h ago`;
|
|
const diffDays = Math.floor(diffHr / 24);
|
|
if (diffDays < 30) return `${diffDays}d ago`;
|
|
return date.toLocaleDateString();
|
|
}
|
|
|
|
export function ChatBookmarkList({ companyId, onNavigate }: ChatBookmarkListProps) {
|
|
const { data, isLoading } = useChatBookmarks(companyId);
|
|
const bookmarks = data?.items ?? [];
|
|
|
|
return (
|
|
<ScrollArea className="h-full">
|
|
<div className="p-1 space-y-0.5">
|
|
{isLoading ? (
|
|
Array.from({ length: 4 }).map((_, i) => (
|
|
<Skeleton key={i} className="h-14 w-full rounded" />
|
|
))
|
|
) : bookmarks.length === 0 ? (
|
|
<div className="flex flex-col items-center justify-center py-10 gap-2 text-muted-foreground">
|
|
<Bookmark className="h-8 w-8 opacity-30" />
|
|
<p className="text-sm">No bookmarks yet</p>
|
|
</div>
|
|
) : (
|
|
bookmarks.map((bookmark) => (
|
|
<button
|
|
key={bookmark.id}
|
|
type="button"
|
|
className="w-full text-left px-2 py-2 rounded hover:bg-accent transition-colors"
|
|
onClick={() => onNavigate(bookmark.conversationId, bookmark.message.id)}
|
|
>
|
|
<p className="text-xs text-muted-foreground truncate mb-0.5">
|
|
{bookmark.conversationTitle ?? "Untitled conversation"}
|
|
</p>
|
|
<p className="text-sm text-foreground line-clamp-2">
|
|
{bookmark.message.content.slice(0, 120)}
|
|
</p>
|
|
<p className="text-xs text-muted-foreground mt-0.5">
|
|
{formatRelativeTime(bookmark.createdAt)}
|
|
</p>
|
|
</button>
|
|
))
|
|
)}
|
|
</div>
|
|
</ScrollArea>
|
|
);
|
|
}
|