diff --git a/pages/Documents.tsx b/pages/Documents.tsx new file mode 100644 index 0000000..d5c352d --- /dev/null +++ b/pages/Documents.tsx @@ -0,0 +1,320 @@ + +import React, { useEffect, useState } from 'react'; +import { CondoService } from '../services/mockDb'; +import { Document, Condo } from '../types'; +import { FileText, Download, Eye, Upload, Tag, Search, Trash2, X, Plus, Filter, Image as ImageIcon, File } from 'lucide-react'; + +export const DocumentsPage: React.FC = () => { + const user = CondoService.getCurrentUser(); + const isPrivileged = user?.role === 'admin' || user?.role === 'poweruser'; + + const [documents, setDocuments] = useState([]); + const [loading, setLoading] = useState(true); + const [activeCondo, setActiveCondo] = useState(undefined); + + // Filtering + const [searchTerm, setSearchTerm] = useState(''); + const [selectedTags, setSelectedTags] = useState([]); + const [allTags, setAllTags] = useState([]); + + // Upload Modal + const [showUploadModal, setShowUploadModal] = useState(false); + const [uploadForm, setUploadForm] = useState<{ + title: string; + description: string; + tags: string[]; + currentTagInput: string; + file: File | null; + }>({ title: '', description: '', tags: [], currentTagInput: '', file: null }); + const [isUploading, setIsUploading] = useState(false); + + // Preview Modal + const [previewDoc, setPreviewDoc] = useState<{doc: Document, data: string} | null>(null); + + useEffect(() => { + loadData(); + }, []); + + const loadData = async () => { + setLoading(true); + try { + const condo = await CondoService.getActiveCondo(); + setActiveCondo(condo); + const docs = await CondoService.getDocuments(); + setDocuments(docs); + + // Extract unique tags + const tags = new Set(); + docs.forEach(d => d.tags.forEach(t => tags.add(t))); + setAllTags(Array.from(tags).sort()); + } catch(e) { console.error(e); } + finally { setLoading(false); } + }; + + const handleFileUpload = (e: React.ChangeEvent) => { + if (e.target.files && e.target.files.length > 0) { + setUploadForm({ ...uploadForm, file: e.target.files[0] }); + } + }; + + const addTag = () => { + if (uploadForm.currentTagInput.trim() && !uploadForm.tags.includes(uploadForm.currentTagInput.trim())) { + setUploadForm({ + ...uploadForm, + tags: [...uploadForm.tags, uploadForm.currentTagInput.trim()], + currentTagInput: '' + }); + } + }; + + const removeTag = (tagToRemove: string) => { + setUploadForm({ + ...uploadForm, + tags: uploadForm.tags.filter(t => t !== tagToRemove) + }); + }; + + const handleUploadSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + if (!uploadForm.file) return; + + setIsUploading(true); + try { + const reader = new FileReader(); + reader.readAsDataURL(uploadForm.file); + reader.onload = async () => { + const base64 = reader.result as string; + await CondoService.uploadDocument({ + title: uploadForm.title, + description: uploadForm.description, + fileName: uploadForm.file!.name, + fileType: uploadForm.file!.type, + fileSize: uploadForm.file!.size, + tags: uploadForm.tags, + fileData: base64 + }); + setIsUploading(false); + setShowUploadModal(false); + setUploadForm({ title: '', description: '', tags: [], currentTagInput: '', file: null }); + loadData(); + }; + } catch(e) { + alert("Errore upload"); + setIsUploading(false); + } + }; + + const handleDelete = async (id: string) => { + if (!confirm("Eliminare questo documento?")) return; + try { + await CondoService.deleteDocument(id); + loadData(); + } catch(e) { alert("Errore eliminazione"); } + }; + + const handlePreview = async (doc: Document) => { + try { + const data = await CondoService.getDocumentDownload(doc.id); + if (doc.fileType === 'application/pdf' || doc.fileType.startsWith('image/')) { + setPreviewDoc({ doc, data: data.data }); + } else { + // Force download + const link = document.createElement("a"); + link.href = data.data; + link.download = data.fileName; + link.click(); + } + } catch(e) { alert("Errore apertura file"); } + }; + + const filteredDocs = documents.filter(d => { + const matchesSearch = d.title.toLowerCase().includes(searchTerm.toLowerCase()) || + (d.description && d.description.toLowerCase().includes(searchTerm.toLowerCase())); + const matchesTags = selectedTags.length === 0 || selectedTags.every(t => d.tags.includes(t)); + return matchesSearch && matchesTags; + }); + + const toggleFilterTag = (tag: string) => { + if (selectedTags.includes(tag)) { + setSelectedTags(selectedTags.filter(t => t !== tag)); + } else { + setSelectedTags([...selectedTags, tag]); + } + }; + + return ( +
+ {/* Sidebar Filters */} +
+
+

+ Filtra per Tag +

+
+ {allTags.length === 0 &&

Nessun tag disponibile.

} + {allTags.map(tag => ( + + ))} +
+ {selectedTags.length > 0 && ( + + )} +
+
+ + {/* Main Content */} +
+
+
+

Documenti

+

Archivio digitale {activeCondo?.name}

+
+ {isPrivileged && ( + + )} +
+ + {/* Search Bar */} +
+ setSearchTerm(e.target.value)} + className="w-full pl-10 pr-4 py-3 rounded-xl border border-slate-200 focus:outline-none focus:ring-2 focus:ring-blue-500 shadow-sm" + /> + +
+ + {/* Grid */} +
+ {filteredDocs.length === 0 ? ( +
+ Nessun documento trovato. +
+ ) : ( + filteredDocs.map(doc => ( +
+
+
+ {doc.fileType === 'application/pdf' ? : + doc.fileType.startsWith('image/') ? : + } +
+
+

{doc.title}

+

{new Date(doc.uploadDate).toLocaleDateString()} • {(doc.fileSize / 1024 / 1024).toFixed(2)} MB

+
+
+ +
+
+ {doc.tags.map(t => ( + + {t} + + ))} +
+ {doc.description &&

{doc.description}

} +
+ +
+ + {isPrivileged && ( + + )} +
+
+ )) + )} +
+
+ + {/* Upload Modal */} + {showUploadModal && ( +
+
+
+

Carica Documento

+ +
+
+ setUploadForm({...uploadForm, title: e.target.value})} required /> +