| Server IP : 54.37.205.81 / Your IP : 216.73.216.76 Web Server : nginx/1.22.1 System : Linux vps-249481fa 6.1.0-50-cloud-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.176-1 (2026-07-02) x86_64 User : debian ( 1000) PHP Version : 8.2.32 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : OFF Directory : /var/www/app.copyrightchain.it/copyrightchain_app/components/ |
Upload File : |
import React, { useMemo, useState } from 'react';
import { Search, Filter, Grid, List, Download, Eye, ExternalLink, Archive, ShieldCheck, MoreVertical, FolderOpen, FileText, ChevronRight, UserCheck, ShieldAlert } from 'lucide-react';
import { Certificate, Language, IdentityData } from '../types';
import { translations } from '../translations';
import IdentityPortal from './IdentityPortal';
interface LegalVaultProps {
certificates: Certificate[];
onViewCert: (cert: Certificate) => void;
lang: Language;
userIdentity: IdentityData;
onUserIdentityUpdate: (data: IdentityData) => void;
}
const LegalVault: React.FC<LegalVaultProps> = ({ certificates, onViewCert, lang, userIdentity, onUserIdentityUpdate }) => {
const t = useMemo(() => translations[lang], [lang]);
const [searchTerm, setSearchTerm] = useState('');
const [viewMode, setViewMode] = useState<'grid' | 'list'>('grid');
const [showIdentityPortal, setShowIdentityPortal] = useState(false);
const filteredCerts = certificates.filter(c =>
c.title.toLowerCase().includes(searchTerm.toLowerCase()) ||
c.fileName.toLowerCase().includes(searchTerm.toLowerCase())
);
return (
<div className="space-y-8">
{showIdentityPortal && (
<IdentityPortal
lang={lang}
onVerified={(data) => { onUserIdentityUpdate(data); setShowIdentityPortal(false); }}
onClose={() => setShowIdentityPortal(false)}
/>
)}
<div className="flex flex-col lg:flex-row justify-between items-start lg:items-center gap-6">
<div>
<h2 className="text-3xl font-black text-slate-900 tracking-tight flex items-center gap-4">
<Archive className="text-blue-600" size={32} /> {t.legalVault}
</h2>
<p className="text-slate-500 font-medium mt-1">{t.vaultDesc}</p>
</div>
<div className="flex flex-wrap items-center gap-4 w-full lg:w-auto">
{userIdentity.verified ? (
<div className="bg-emerald-50 text-emerald-700 px-6 py-3 rounded-2xl flex items-center gap-3 border border-emerald-100 shadow-sm animate-in fade-in">
<UserCheck size={20} />
<div className="flex flex-col">
<span className="text-[10px] font-black uppercase tracking-widest">{t.identityVerified}</span>
<span className="text-xs font-bold">{userIdentity.fullName}</span>
</div>
</div>
) : (
<button
onClick={() => setShowIdentityPortal(true)}
className="bg-slate-900 text-white px-6 py-3 rounded-2xl flex items-center gap-3 shadow-xl hover:-translate-y-1 transition-all"
>
<ShieldAlert size={20} className="text-orange-400" />
<span className="text-[10px] font-black uppercase tracking-widest">{t.verifyIdentity}</span>
</button>
)}
<div className="relative flex-1 md:w-80">
<Search className="absolute left-4 top-1/2 -translate-y-1/2 text-slate-400" size={18} />
<input
type="text"
placeholder="Cerca tra le tue opere..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="w-full pl-12 pr-4 py-3 rounded-2xl border border-slate-100 bg-white shadow-sm focus:ring-4 focus:ring-blue-500/10 focus:border-blue-500 outline-none transition-all font-medium text-slate-700"
/>
</div>
<div className="flex bg-white p-1 rounded-xl border border-slate-100 shadow-sm">
<button
onClick={() => setViewMode('grid')}
className={`p-2 rounded-lg transition-all ${viewMode === 'grid' ? 'bg-slate-100 text-blue-600' : 'text-slate-400'}`}
>
<Grid size={18} />
</button>
<button
onClick={() => setViewMode('list')}
className={`p-2 rounded-lg transition-all ${viewMode === 'list' ? 'bg-slate-100 text-blue-600' : 'text-slate-400'}`}
>
<List size={18} />
</button>
</div>
</div>
</div>
{filteredCerts.length === 0 ? (
<div className="bg-white border-2 border-dashed border-slate-100 rounded-[3rem] p-20 text-center">
<div className="w-24 h-24 bg-slate-50 rounded-[2.5rem] flex items-center justify-center mx-auto mb-6 text-slate-200">
<FolderOpen size={48} />
</div>
<h3 className="text-xl font-black text-slate-900 mb-2">Nessun file nel caveau</h3>
<p className="text-slate-400 font-medium max-w-xs mx-auto">Inizia a certificare le tue opere per vederle apparire qui nel tuo archivio protetto.</p>
</div>
) : viewMode === 'grid' ? (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
{filteredCerts.map(cert => (
<VaultCard key={cert.id} cert={cert} onClick={() => onViewCert(cert)} lang={lang} />
))}
</div>
) : (
<div className="bg-white rounded-[2.5rem] border border-slate-100 shadow-sm overflow-hidden">
<table className="w-full">
<thead>
<tr className="bg-slate-50/50 border-b border-slate-100">
<th className="px-6 py-4 text-left text-[10px] font-black text-slate-400 uppercase tracking-widest">Titolo Opera</th>
<th className="px-6 py-4 text-left text-[10px] font-black text-slate-400 uppercase tracking-widest">Data Deposito</th>
<th className="px-6 py-4 text-left text-[10px] font-black text-slate-400 uppercase tracking-widest">Protezione</th>
<th className="px-6 py-4 text-right text-[10px] font-black text-slate-400 uppercase tracking-widest">Azioni</th>
</tr>
</thead>
<tbody className="divide-y divide-slate-50">
{filteredCerts.map(cert => (
<tr key={cert.id} className="hover:bg-slate-50/50 transition-colors group cursor-pointer" onClick={() => onViewCert(cert)}>
<td className="px-6 py-5">
<div className="flex items-center gap-4">
<div className="w-10 h-10 bg-blue-50 rounded-xl flex items-center justify-center text-blue-600">
<FileText size={20} />
</div>
<div>
<p className="font-black text-slate-900 text-sm tracking-tight">{cert.title}</p>
<p className="text-[10px] text-slate-400 font-bold uppercase truncate max-w-[150px]">{cert.fileName}</p>
</div>
</div>
</td>
<td className="px-6 py-5">
<p className="text-xs font-bold text-slate-600">{new Date(cert.timestamp).toLocaleDateString()}</p>
</td>
<td className="px-6 py-5">
<div className="flex items-center gap-2">
<span className={`text-[9px] font-black px-2 py-0.5 rounded-full uppercase tracking-tighter ${cert.tier === 'enterprise' ? 'bg-indigo-100 text-indigo-700' : cert.tier === 'pro' ? 'bg-blue-100 text-blue-700' : 'bg-slate-100 text-slate-600'}`}>
{cert.tier || 'basic'}
</span>
</div>
</td>
<td className="px-6 py-5 text-right">
<button className="p-2 text-slate-400 hover:text-blue-600 transition">
<ChevronRight size={20} />
</button>
</td>
</tr>
))}
</tbody>
</table>
</div>
)}
</div>
);
};
const VaultCard = ({ cert, onClick, lang }: { cert: Certificate, onClick: () => void, lang: string, key?: any }) => (
<div
onClick={onClick}
className="bg-white rounded-[2rem] p-6 border border-slate-100 shadow-sm hover:shadow-xl hover:shadow-slate-100 hover:-translate-y-1 transition-all duration-500 cursor-pointer group flex flex-col h-full"
>
<div className="flex justify-between items-start mb-6">
<div className="w-12 h-12 bg-slate-50 rounded-2xl flex items-center justify-center text-slate-400 group-hover:bg-blue-600 group-hover:text-white transition-all duration-500 shadow-inner">
<FileText size={24} />
</div>
<div className="flex flex-col items-end gap-1">
<span className={`text-[8px] font-black px-2 py-0.5 rounded-full uppercase tracking-widest ${cert.paymentStatus === 'paid' ? 'bg-emerald-50 text-emerald-600' : 'bg-orange-50 text-orange-600'}`}>
{cert.paymentStatus}
</span>
{cert.bitcoinEvidence && (
<div className="bg-orange-500 text-white p-1 rounded-md" title="Bitcoin Anchored"><ShieldCheck size={12} /></div>
)}
</div>
</div>
<div className="flex-1">
<h4 className="font-black text-slate-900 text-lg tracking-tight mb-1 group-hover:text-blue-600 transition-colors uppercase leading-none">{cert.title}</h4>
<p className="text-[10px] text-slate-400 font-bold uppercase tracking-widest mb-4 truncate">{cert.fileName}</p>
</div>
<div className="pt-6 border-t border-slate-50 flex items-center justify-between">
<div className="flex flex-col">
<span className="text-[9px] font-black text-slate-300 uppercase tracking-widest">Data</span>
<span className="text-xs font-bold text-slate-700">{new Date(cert.timestamp).toLocaleDateString()}</span>
</div>
<button className="bg-slate-50 text-slate-400 p-2.5 rounded-xl group-hover:bg-blue-50 group-hover:text-blue-600 transition-all">
<Eye size={18} />
</button>
</div>
</div>
);
export default LegalVault;