import { useEffect, useState } from 'react'; import { useNavigate, useParams } from 'react-router-dom'; import { objectsApi } from '@/lib/api'; import type { ObjectMetadata } from '@/types'; import { Header } from '@/components/layout/header'; import { Button } from '@/components/ui/button'; import { ArrowLeft, Download, Trash, Copy, File } from 'lucide-react'; import { toast } from 'sonner'; import { formatBytes } from '@/lib/file-utils'; export function ObjectDetailsView() { const navigate = useNavigate(); const { bucketName, '*': encodedObjectKey } = useParams(); // Decode the object key from the URL const objectKey = encodedObjectKey ? decodeURIComponent(encodedObjectKey) : undefined; const [metadata, setMetadata] = useState(null); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { if (!bucketName || !objectKey) { setError('Bucket name and object key are required'); setIsLoading(false); return; } const fetchMetadata = async () => { try { setIsLoading(true); setError(null); const data = await objectsApi.getMetadata(bucketName, objectKey); setMetadata(data); } catch (err) { setError(err instanceof Error ? err.message : 'Failed to load object metadata'); console.error('Failed to fetch object metadata:', err); } finally { setIsLoading(false); } }; fetchMetadata(); }, [bucketName, objectKey]); const handleDownload = async () => { if (!bucketName || !objectKey) return; try { const blob = await objectsApi.get(bucketName, objectKey); const url = window.URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = objectKey.split('/').pop() || 'download'; document.body.appendChild(a); a.click(); window.URL.revokeObjectURL(url); document.body.removeChild(a); toast.success('Download started'); } catch (err) { console.error('Download failed:', err); } }; const handleDelete = async () => { if (!bucketName || !objectKey) return; if (!confirm(`Are you sure you want to delete "${objectKey}"?`)) { return; } try { await objectsApi.delete(bucketName, objectKey); toast.success('Object deleted successfully'); handleBackNavigation(); } catch (err) { console.error('Delete failed:', err); } }; const handleBackNavigation = () => { if (!bucketName) return; // Navigate back to the bucket explorer with the appropriate prefix // Extract the folder path from the object key (everything before the last /) const folderPath = objectKey?.split('/').slice(0, -1).join('/') || ''; const prefix = folderPath ? `${folderPath}/` : ''; // Navigate to the bucket view with the correct prefix navigate(`/buckets?bucket=${encodeURIComponent(bucketName)}${prefix ? `&prefix=${encodeURIComponent(prefix)}` : ''}`); }; const copyToClipboard = (text: string) => { navigator.clipboard.writeText(text); toast.success('Copied to clipboard'); }; const formatDate = (dateString: string) => { const date = new Date(dateString); return date.toLocaleString('en-US', { year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit', second: '2-digit', timeZoneName: 'short', }); }; if (isLoading) { return (
Loading object details...
); } if (error || !metadata) { return (
{error || 'Object not found'}
); } const fileName = objectKey?.split('/').pop() || objectKey || ''; const pathParts = objectKey?.split('/').filter(part => part) || []; const parentPath = pathParts.slice(0, -1).join('/'); return (
{/* Back Button and Actions */}
{/* File Name Header */}

{parentPath && ( /{parentPath}/ )} {fileName}

{/* Object Details Section */}

Object Details

Date Created
{formatDate(metadata.lastModified)}
Type
{metadata.contentType || 'application/octet-stream'}
Storage Class
{metadata.storageClass || 'Standard'}
Size
{formatBytes(metadata.size)}
{/* Custom Metadata Section */} {metadata.metadata && Object.keys(metadata.metadata).length > 0 && (

Custom Metadata

{Object.entries(metadata.metadata).map(([key, value]) => ( ))}
Key Value
{key} {value}
)} {/* Object Preview Section */}

Object Preview

No preview available

); }