234 lines
9.0 KiB
TypeScript
Raw Normal View History

"use client";
2025-04-16 14:19:14 -07:00
import React, { useState } from 'react';
import { Badge } from '@/components/ui/badge';
import { ScrollArea } from '@/components/ui/scroll-area';
import { Button } from '@/components/ui/button';
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/components/ui/accordion';
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog';
2025-04-17 20:52:18 -07:00
import { Info } from 'lucide-react';
2025-04-16 14:19:14 -07:00
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
2025-04-17 20:52:18 -07:00
import Image from 'next/image';
2025-04-16 14:19:14 -07:00
import { Document, Folder } from '@/components/types';
interface DocumentDetailProps {
selectedDocument: Document | null;
handleDeleteDocument: (documentId: string) => Promise<void>;
2025-04-16 14:19:14 -07:00
folders: Folder[];
apiBaseUrl: string;
authToken: string | null;
refreshDocuments: () => void;
refreshFolders: () => void;
loading: boolean;
2025-04-17 20:52:18 -07:00
onClose: () => void;
}
const DocumentDetail: React.FC<DocumentDetailProps> = ({
selectedDocument,
handleDeleteDocument,
2025-04-16 14:19:14 -07:00
folders,
apiBaseUrl,
authToken,
refreshDocuments,
refreshFolders,
2025-04-17 20:52:18 -07:00
loading,
onClose
}) => {
2025-04-16 14:19:14 -07:00
const [isMovingToFolder, setIsMovingToFolder] = useState(false);
if (!selectedDocument) {
return (
<div className="h-[calc(100vh-200px)] flex items-center justify-center p-8 border border-dashed rounded-lg">
<div className="text-center text-muted-foreground">
<Info className="mx-auto h-12 w-12 mb-2" />
<p>Select a document to view details</p>
</div>
</div>
);
}
2025-04-16 14:19:14 -07:00
const currentFolder = selectedDocument.system_metadata?.folder_name as string | undefined;
2025-04-16 14:19:14 -07:00
const handleMoveToFolder = async (folderName: string | null) => {
if (isMovingToFolder || !selectedDocument) return;
2025-04-16 14:19:14 -07:00
const documentId = selectedDocument.external_id;
setIsMovingToFolder(true);
2025-04-16 14:19:14 -07:00
try {
// First, get the folder ID from the name if a name is provided
if (folderName) {
// Find the target folder by name
const targetFolder = folders.find(folder => folder.name === folderName);
if (targetFolder && targetFolder.id) {
console.log(`Found folder with ID: ${targetFolder.id} for name: ${folderName}`);
2025-04-16 14:19:14 -07:00
// Add to folder using folder ID
await fetch(`${apiBaseUrl}/folders/${targetFolder.id}/documents/${documentId}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...(authToken ? { 'Authorization': `Bearer ${authToken}` } : {})
}
});
} else {
console.error(`Could not find folder with name: ${folderName}`);
}
}
2025-04-16 14:19:14 -07:00
// If there's a current folder and we're either moving to a new folder or removing from folder
if (currentFolder) {
// Find the current folder ID
const currentFolderObj = folders.find(folder => folder.name === currentFolder);
if (currentFolderObj && currentFolderObj.id) {
// Remove from current folder using folder ID
await fetch(`${apiBaseUrl}/folders/${currentFolderObj.id}/documents/${documentId}`, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
...(authToken ? { 'Authorization': `Bearer ${authToken}` } : {})
}
});
}
}
2025-04-16 14:19:14 -07:00
// Refresh folders first to get updated document_ids
await refreshFolders();
// Then refresh documents with the updated folder information
await refreshDocuments();
} catch (error) {
console.error('Error updating folder:', error);
} finally {
setIsMovingToFolder(false);
}
};
return (
<div className="border rounded-lg">
2025-04-17 20:52:18 -07:00
<div className="bg-muted px-4 py-3 border-b sticky top-0 flex justify-between items-center">
<h3 className="text-lg font-semibold">Document Details</h3>
2025-04-17 20:52:18 -07:00
<Button
variant="ghost"
size="icon"
onClick={onClose}
className="rounded-full hover:bg-background/80"
>
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<line x1="18" y1="6" x2="6" y2="18"></line>
<line x1="6" y1="6" x2="18" y2="18"></line>
</svg>
<span className="sr-only">Close panel</span>
</Button>
</div>
<ScrollArea className="h-[calc(100vh-200px)]">
<div className="p-4 space-y-4">
<div>
<h3 className="font-medium mb-1">Filename</h3>
<p>{selectedDocument.filename || 'N/A'}</p>
</div>
<div>
<h3 className="font-medium mb-1">Content Type</h3>
2025-04-16 14:19:14 -07:00
<Badge variant="secondary">{selectedDocument.content_type}</Badge>
</div>
2025-04-16 14:19:14 -07:00
<div>
<h3 className="font-medium mb-1">Folder</h3>
<div className="flex items-center gap-2">
2025-04-17 20:52:18 -07:00
<Image src="/icons/folder-icon.png" alt="Folder" width={16} height={16} />
2025-04-16 14:19:14 -07:00
<Select
value={currentFolder || "_none"}
onValueChange={(value) => handleMoveToFolder(value === "_none" ? null : value)}
disabled={isMovingToFolder}
>
<SelectTrigger className="w-full">
<SelectValue placeholder="Not in a folder" />
</SelectTrigger>
<SelectContent>
<SelectItem value="_none">Not in a folder</SelectItem>
{folders.map((folder) => (
<SelectItem key={folder.name} value={folder.name}>
{folder.name}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
</div>
<div>
<h3 className="font-medium mb-1">Document ID</h3>
<p className="font-mono text-xs">{selectedDocument.external_id}</p>
</div>
<Accordion type="single" collapsible>
<AccordionItem value="metadata">
<AccordionTrigger>Metadata</AccordionTrigger>
<AccordionContent>
<pre className="bg-muted p-2 rounded text-xs overflow-x-auto whitespace-pre-wrap">
{JSON.stringify(selectedDocument.metadata, null, 2)}
</pre>
</AccordionContent>
</AccordionItem>
<AccordionItem value="system-metadata">
<AccordionTrigger>System Metadata</AccordionTrigger>
<AccordionContent>
<pre className="bg-muted p-2 rounded text-xs overflow-x-auto whitespace-pre-wrap">
{JSON.stringify(selectedDocument.system_metadata, null, 2)}
</pre>
</AccordionContent>
</AccordionItem>
<AccordionItem value="additional-metadata">
<AccordionTrigger>Additional Metadata</AccordionTrigger>
<AccordionContent>
<pre className="bg-muted p-2 rounded text-xs overflow-x-auto whitespace-pre-wrap">
{JSON.stringify(selectedDocument.additional_metadata, null, 2)}
</pre>
</AccordionContent>
</AccordionItem>
</Accordion>
<div className="pt-4 border-t mt-4">
<Dialog>
<DialogTrigger asChild>
<Button variant="outline" size="sm" className="w-full border-red-500 text-red-500 hover:bg-red-100 dark:hover:bg-red-950">
Delete Document
</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Delete Document</DialogTitle>
<DialogDescription>
Are you sure you want to delete this document? This action cannot be undone.
</DialogDescription>
</DialogHeader>
<div className="py-3">
<p className="font-medium">Document: {selectedDocument.filename || selectedDocument.external_id}</p>
<p className="text-sm text-muted-foreground mt-1">ID: {selectedDocument.external_id}</p>
</div>
<DialogFooter>
<Button variant="outline" onClick={() => (document.querySelector('[data-state="open"] button[data-state="closed"]') as HTMLElement)?.click()}>Cancel</Button>
<Button
variant="outline"
className="border-red-500 text-red-500 hover:bg-red-100 dark:hover:bg-red-950"
onClick={() => handleDeleteDocument(selectedDocument.external_id)}
disabled={loading}
>
{loading ? 'Deleting...' : 'Delete'}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</div>
</div>
</ScrollArea>
</div>
);
};
export default DocumentDetail;