/**
* DocumentsPanel — Shared documents tab for the meeting right rail.
*
* Features:
* - Upload files (txt/md/pdf)
* - Add URL links
* - List documents with type icons
* - Preview text files
* - Download files
* - Delete documents
*
* Additive — lives inside MeetingRightRail as a tab.
*/
import React, { useEffect, useMemo, useState, useRef } from 'react';
import { FileText, Link as LinkIcon, Upload, Trash2, Eye, Download, Plus, X, } from 'lucide-react';
import { useTeamsDocuments } from './useTeamsDocuments';
// ---------------------------------------------------------------------------
// Component
// ---------------------------------------------------------------------------
export function DocumentsPanel({ backendUrl, apiKey, roomId, initialDocuments, onDocumentsChanged, }) {
const docsApi = useTeamsDocuments(backendUrl, apiKey);
const [docs, setDocs] = useState(initialDocuments || []);
const [selectedId, setSelectedId] = useState(null);
const [previewText, setPreviewText] = useState('');
const [busy, setBusy] = useState(false);
const [showUrlForm, setShowUrlForm] = useState(false);
const fileInputRef = useRef(null);
const selected = useMemo(() => docs.find((d) => d.id === selectedId) || null, [docs, selectedId]);
// Lazy refresh on mount
useEffect(() => {
;
(async () => {
try {
const list = await docsApi.list(roomId);
setDocs(list);
onDocumentsChanged?.(list);
}
catch {
// keep local state
}
})();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [roomId]);
const iconFor = (d) => {
if (d.kind === 'url')
return ;
const t = d.type;
if (t === 'pdf')
return ;
if (t === 'md')
return ;
return ;
};
const sizeLabel = (bytes) => {
if (typeof bytes !== 'number')
return '';
if (bytes < 1024)
return `${bytes} B`;
if (bytes < 1024 * 1024)
return `${Math.round(bytes / 1024)} KB`;
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
};
async function handleUpload(file) {
setBusy(true);
try {
const res = await docsApi.upload(roomId, file);
const list = await docsApi.list(roomId);
setDocs(list);
onDocumentsChanged?.(list);
setSelectedId(res.document.id);
}
catch (e) {
console.error('Upload failed:', e);
}
finally {
setBusy(false);
}
}
async function handlePreview(docId) {
setBusy(true);
try {
const text = await docsApi.preview(roomId, docId);
setPreviewText(text);
setSelectedId(docId);
}
catch (e) {
console.error('Preview failed:', e);
setPreviewText('[Failed to load preview]');
}
finally {
setBusy(false);
}
}
async function handleDelete(docId) {
setBusy(true);
try {
await docsApi.deleteDoc(roomId, docId);
const list = await docsApi.list(roomId);
setDocs(list);
onDocumentsChanged?.(list);
if (selectedId === docId) {
setSelectedId(null);
setPreviewText('');
}
}
catch (e) {
console.error('Delete failed:', e);
}
finally {
setBusy(false);
}
}
async function handleDownload(doc) {
setBusy(true);
try {
await docsApi.download(roomId, doc);
}
catch (e) {
console.error('Download failed:', e);
}
finally {
setBusy(false);
}
}
return (
{/* Actions row */}
{
const f = e.target.files?.[0];
if (f)
handleUpload(f);
e.currentTarget.value = '';
}}/>
{/* URL form */}
{showUrlForm && (
{
setBusy(true);
try {
await docsApi.addUrl(roomId, url, title);
const list = await docsApi.list(roomId);
setDocs(list);
onDocumentsChanged?.(list);
setShowUrlForm(false);
}
catch (e) {
console.error('Add URL failed:', e);
}
finally {
setBusy(false);
}
}} onCancel={() => setShowUrlForm(false)}/>)}
{/* Document list */}
{docs.length === 0 && (
No shared documents yet
Upload a file or add a link
)}
{docs.map((d) => (
{iconFor(d)}
{d.name}
{d.kind === 'url' ? 'Link' : (d.type || 'file').toUpperCase()}
{d.size_bytes ? ` · ${sizeLabel(d.size_bytes)}` : ''}
{d.uploaded_by ? ` · ${d.uploaded_by}` : ''}
{/* Action buttons */}
{d.kind === 'file' && ()}
))}
{/* Preview pane */}
{selected && previewText && (
{selected.name}
{selected.kind === 'file' && selected.type === 'pdf' && (
Open PDF
)}
{previewText}
)}
);
}
// ---------------------------------------------------------------------------
// Sub-component: Add URL form
// ---------------------------------------------------------------------------
function AddUrlForm({ disabled, onAdd, onCancel, }) {
const [url, setUrl] = useState('');
const [title, setTitle] = useState('');
return ();
}