Spaces:
Running
Running
| /** | |
| * Context Document Card Component | |
| * | |
| * Displays individual context documents with preview and actions | |
| */ | |
| import React from "react"; | |
| import { Card, CardHeader, CardTitle } from "@/components/ui/card"; | |
| import { Button } from "@/components/ui/button"; | |
| import { Badge } from "@/components/ui/badge"; | |
| import { Trash2, FileText, Edit } from "lucide-react"; | |
| import { ContextDocument } from "@/types/context"; | |
| import { shouldRenderAsMarkdown } from "@/lib/markdown-utils"; | |
| import ReactMarkdown from "react-markdown"; | |
| interface ContextDocumentCardProps { | |
| document: ContextDocument; | |
| onDelete: () => void; | |
| onViewEdit: () => void; | |
| } | |
| export function ContextDocumentCard({ | |
| document, | |
| onDelete, | |
| onViewEdit, | |
| }: ContextDocumentCardProps) { | |
| const formatDate = (dateString: string) => { | |
| return new Date(dateString).toLocaleDateString("en-US", { | |
| year: "numeric", | |
| month: "short", | |
| day: "numeric", | |
| hour: "2-digit", | |
| minute: "2-digit", | |
| }); | |
| }; | |
| const getPreviewContent = (content: string, maxLength: number = 150) => { | |
| if (content.length <= maxLength) return content; | |
| return content.substring(0, maxLength) + "..."; | |
| }; | |
| return ( | |
| <Card className="transition-all duration-200 hover:shadow-md"> | |
| <CardHeader className="pb-3"> | |
| <div className="space-y-3"> | |
| <div className="flex items-center justify-between"> | |
| <div className="flex items-center gap-2 flex-1 min-w-0"> | |
| <FileText className="h-5 w-5 text-primary" /> | |
| <CardTitle className="text-base truncate"> | |
| {document.title} | |
| </CardTitle> | |
| </div> | |
| {/* Action buttons in top right */} | |
| <div className="flex items-center gap-2"> | |
| <Button | |
| variant="outline" | |
| size="sm" | |
| onClick={onViewEdit} | |
| className="gap-1" | |
| title="View and edit context document" | |
| > | |
| <Edit className="h-4 w-4" /> | |
| View/Edit | |
| </Button> | |
| <Button | |
| variant="ghost" | |
| size="sm" | |
| onClick={onDelete} | |
| className="h-8 w-8 p-0 text-muted-foreground hover:text-destructive" | |
| title="Delete context document" | |
| > | |
| <Trash2 className="h-4 w-4" /> | |
| </Button> | |
| </div> | |
| </div> | |
| <div className="flex flex-wrap gap-2 text-sm text-muted-foreground items-center"> | |
| <span>{formatDate(document.created_at)}</span> | |
| {/* Document metadata */} | |
| <Badge | |
| variant="outline" | |
| className="text-xs h-5 flex items-center gap-1" | |
| > | |
| <FileText className="h-3 w-3" /> | |
| Type: {document.document_type.replace(/_/g, " ")} | |
| </Badge> | |
| {/* Character count */} | |
| <div className="text-xs text-muted-foreground"> | |
| {document.content.length.toLocaleString()} chars | |
| </div> | |
| </div> | |
| {/* Content preview */} | |
| <div className="text-sm text-muted-foreground bg-muted/30 rounded-md p-2 border-l-2 border-primary/20"> | |
| {shouldRenderAsMarkdown(document.content, document.file_name) ? ( | |
| <div className="prose prose-sm max-w-none [&>*]:my-1 [&>*:first-child]:mt-0 [&>*:last-child]:mb-0 [&>h1]:text-sm [&>h2]:text-sm [&>h3]:text-sm [&>h4]:text-sm [&>h5]:text-sm [&>h6]:text-sm [&>p]:text-xs [&>ul]:text-xs [&>ol]:text-xs [&>li]:text-xs [&>blockquote]:text-xs [&>code]:text-xs [&>pre]:text-xs"> | |
| <ReactMarkdown> | |
| {getPreviewContent(document.content, 200)} | |
| </ReactMarkdown> | |
| </div> | |
| ) : ( | |
| <div className="whitespace-pre-wrap"> | |
| {getPreviewContent(document.content)} | |
| </div> | |
| )} | |
| </div> | |
| </div> | |
| </CardHeader> | |
| </Card> | |
| ); | |
| } | |