import React, { useState, useRef } from 'react'; import { FileText, Upload, Search, File, Loader2, CheckCircle, Link, Plus, MoreVertical, Trash2, Download, X, Camera, Image as ImageIcon, Palette, ChevronDown, FileSpreadsheet } from 'lucide-react'; import { jsPDF } from 'jspdf'; import { Document, Appliance, BrandProfile, PaintPaletteSlot } from '../types'; import { parseDocument } from '../geminiService'; import { isDarkColor } from '../constants'; interface DocumentVaultProps { docs: Document[]; setDocs: React.Dispatch >; appliances: Appliance[]; brand: BrandProfile; interiorPaint: PaintPaletteSlot[]; exteriorPaint: PaintPaletteSlot[]; onUpdatePaint: (type: 'Interior' | 'Exterior', updated: PaintPaletteSlot[]) => void; } const DocumentVault: React.FC = ({ docs, setDocs, appliances, brand, interiorPaint, exteriorPaint, onUpdatePaint }) => { const [isUploading, setIsUploading] = useState(false); const [searchTerm, setSearchTerm] = useState(''); const [selectedType, setSelectedType] = useState ('All'); const [activeMenu, setActiveMenu] = useState (null); const [showExportMenu, setShowExportMenu] = useState(false); const [paintPaletteView, setPaintPaletteView] = useState<'Interior' | 'Exterior'>('Interior'); const [editingSlot, setEditingSlot] = useState<{ type: 'Interior' | 'Exterior', index: number } | null>(null); const isDark = isDarkColor(brand.backgroundColor); const headingColor = isDark ? 'text-white' : 'text-gray-800'; const subtextColor = isDark ? 'text-gray-300' : 'text-gray-500'; const fileInputRef = useRef (null); const paintPhotoInputRef = useRef (null); const handleUpload = async (e: React.ChangeEvent ) => { const file = e.target.files?.[0]; if (!file) return; setIsUploading(true); const reader = new FileReader(); reader.onloadend = async () => { const base64 = (reader.result as string).split(',')[1]; const mimeType = file.type || 'image/jpeg'; try { const parsed = await parseDocument(base64, mimeType); const newDoc: Document = { id: `doc-${Date.now()}`, name: file.name, type: (parsed.type || 'Other') as any, uploadDate: new Date().toISOString(), fileUrl: URL.createObjectURL(file), parsedData: parsed }; setDocs(prev => [newDoc, ...prev]); } catch (err) { alert("Upload failed. Please try again."); } finally { setIsUploading(false); if (fileInputRef.current) fileInputRef.current.value = ''; } }; reader.readAsDataURL(file); }; const handleDelete = (id: string, fileUrl: string) => { if (confirm("Permanently delete this document from your vault?")) { if (fileUrl.startsWith('blob:')) { URL.revokeObjectURL(fileUrl); } setDocs(prev => prev.filter(doc => doc.id !== id)); setActiveMenu(null); } }; const handleUpdateSlotPhoto = (e: React.ChangeEvent ) => { const file = e.target.files?.[0]; if (!file || !editingSlot) return; const reader = new FileReader(); reader.onloadend = () => { const { type, index } = editingSlot; const palette = type === 'Interior' ? [...interiorPaint] : [...exteriorPaint]; palette[index] = { ...palette[index], photoUrl: reader.result as string }; onUpdatePaint(type, palette); }; reader.readAsDataURL(file); }; const updateSlotField = (field: 'name' | 'color', value: string) => { if (!editingSlot) return; const { type, index } = editingSlot; const palette = type === 'Interior' ? [...interiorPaint] : [...exteriorPaint]; palette[index] = { ...palette[index], [field]: value }; onUpdatePaint(type, palette); }; const filteredDocs = docs.filter(doc => { const matchesSearch = doc.name.toLowerCase().includes(searchTerm.toLowerCase()); const matchesType = selectedType === 'All' || doc.type === selectedType; return matchesSearch && matchesType; }); const exportToCSV = () => { let csvContent = "data:text/csv;charset=utf-8,"; csvContent += "CasaKeeper Branded Report\n"; csvContent += `Company: ${brand.companyName}\n`; csvContent += `Generated: ${new Date().toLocaleString()}\n\n`; csvContent += "APPLIANCE & SYSTEM INVENTORY\n"; csvContent += "Type,Name,Manufacturer,Model,Serial,Category,Location,Installation Date\n"; appliances.forEach(app => { csvContent += `Appliance,"${app.name}","${app.manufacturer}","${app.modelNumber}","${app.serialNumber}","${app.category}","${app.location}","${app.installationDate}"\n`; }); csvContent += "\nPAINT PALETTE (INTERIOR)\n"; csvContent += "Slot,Color Name,Hex Code\n"; interiorPaint.forEach(p => { csvContent += `"${p.label}","${p.name}","${p.color}"\n`; }); csvContent += "\nPAINT PALETTE (EXTERIOR)\n"; csvContent += "Slot,Color Name,Hex Code\n"; exteriorPaint.forEach(p => { csvContent += `"${p.label}","${p.name}","${p.color}"\n`; }); const encodedUri = encodeURI(csvContent); const link = window.document.createElement("a"); link.setAttribute("href", encodedUri); link.setAttribute("download", `CasaKeeper_Report_${new Date().toISOString().split('T')[0]}.csv`); window.document.body.appendChild(link); link.click(); window.document.body.removeChild(link); setShowExportMenu(false); }; const exportToPDF = () => { try { const doc = new jsPDF(); const timestamp = new Date().toLocaleString(); const dateStr = new Date().toISOString().split('T')[0]; // Branded Header doc.setFontSize(22); doc.setTextColor(brand.primaryColor); doc.text(brand.companyName, 20, 20); doc.setFontSize(10); doc.setTextColor(100); doc.text(brand.tagline, 20, 27); doc.text(`Generated: ${timestamp}`, 20, 32); doc.setDrawColor(brand.primaryColor); doc.line(20, 35, 190, 35); // Section 1: Appliances doc.setFontSize(16); doc.setTextColor(0); doc.text("Appliance & System Inventory", 20, 45); let y = 55; doc.setFontSize(10); appliances.forEach((app, idx) => { if (y > 270) { doc.addPage(); y = 20; } doc.setFont("helvetica", "bold"); doc.text(`${idx + 1}. ${app.name}`, 20, y); doc.setFont("helvetica", "normal"); doc.text(`Manufacturer: ${app.manufacturer} | Model: ${app.modelNumber}`, 25, y + 5); doc.text(`Serial: ${app.serialNumber} | Installed: ${app.installationDate}`, 25, y + 10); y += 18; }); // Section 2: Paint Palette if (y > 230) { doc.addPage(); y = 20; } else { y += 10; } doc.setFontSize(16); doc.text("Property Paint Palette", 20, y); y += 10; doc.setFontSize(12); doc.setFont("helvetica", "bold"); doc.text("Interior Colors", 20, y); y += 8; doc.setFontSize(10); doc.setFont("helvetica", "normal"); interiorPaint.forEach(p => { doc.text(`${p.label}: ${p.name} (${p.color})`, 25, y); y += 6; }); y += 5; doc.setFontSize(12); doc.setFont("helvetica", "bold"); doc.text("Exterior Colors", 20, y); y += 8; doc.setFontSize(10); doc.setFont("helvetica", "normal"); exteriorPaint.forEach(p => { doc.text(`${p.label}: ${p.name} (${p.color})`, 25, y); y += 6; }); // Footer doc.setFontSize(8); doc.setTextColor(150); doc.text(brand.footerMessage, 20, 285); doc.save(`CasaKeeper_Report_${dateStr}.pdf`); setShowExportMenu(false); } catch (error) { console.error("PDF Generation Error:", error); alert("There was an error generating the PDF. Try CSV export instead."); } }; const currentPalette = paintPaletteView === 'Interior' ? interiorPaint : exteriorPaint; return (

Document Vault

Secure storage for manuals, warranties, and paint colors.

{/* Search and Filters */}
setSearchTerm(e.target.value)} />
{['All', 'Manual', 'Receipt', 'Warranty', 'Plan'].map((type) => ( ))}
{/* Document Table */}
{filteredDocs.length > 0 ? ( filteredDocs.map((doc) => (

{doc.name}

{doc.parsedData && (

AI Analyzed

)}
{doc.type}
{new Date(doc.uploadDate).toLocaleDateString()}
{doc.linkedApplianceId ? ( {appliances.find(a => a.id === doc.linkedApplianceId)?.name || 'Linked'} ) : ( )}
{activeMenu === doc.id && (
e.stopPropagation()} >
)}
)) ) : (

No documents stored.

)}
{/* Paint Color Palette */}

Paint Color Palette

Property Asset Inventory

{['Interior', 'Exterior'].map((view) => ( ))}
{currentPalette.map((paint, i) => (
setEditingSlot({ type: paintPaletteView, index: i })} className="group bg-blue-50/30 rounded-2xl p-4 text-center shadow-sm border border-blue-100/50 hover:bg-blue-50 transition-all cursor-pointer relative" >
{paint.photoUrl && ( QR Label )}

{paint.label}

{paint.name || 'Empty'}

{paint.color || '#------'}

{paint.photoUrl && (
)}
))}
{/* Edit Modal */} {editingSlot && (

Edit {editingSlot.type} Color

{currentPalette[editingSlot.index].label} Slot

updateSlotField('name', e.target.value)} className="w-full p-3 bg-blue-50/50 border border-blue-100 rounded-xl outline-none focus:ring-2" style={{ '--tw-ring-color': brand.primaryColor } as any} />
updateSlotField('color', e.target.value)} className="w-12 h-12 rounded-lg border-none p-0 cursor-pointer" /> updateSlotField('color', e.target.value)} className="flex-1 p-3 bg-blue-50/50 border border-blue-100 rounded-xl font-mono text-sm" />
paintPhotoInputRef.current?.click()} className="w-full h-40 border-2 border-dashed border-blue-100 bg-blue-50/20 rounded-2xl flex flex-col items-center justify-center cursor-pointer hover:bg-blue-50 transition-colors overflow-hidden" > {currentPalette[editingSlot.index].photoUrl ? ( QR ) : ( <>

Upload Paint Can / QR Code

)}
)}
{/* Branded Report Section */}

Branded Report

{showExportMenu && (
)}

Create a professional branded insurance or home sale report. Includes all appliance data, service history, and your property paint palette.

Recent Report

Generated {new Date().toLocaleDateString()}

{activeMenu && (
setActiveMenu(null)}>
)} {showExportMenu && (
setShowExportMenu(false)}>
)}
); }; export default DocumentVault;