403Webshell
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 :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /var/www/app.copyrightchain.it/copyrightchain_app/components/CertificateView.tsx
import React, { useState, useRef, useEffect, useMemo } from 'react';
import { Download, Printer, Shield, CheckCircle, Share2, FileText, Calendar, User, Hash, Loader2, ListChecks, History, Fingerprint, Award, AlertCircle, RefreshCcw, Clock, Stamp, Sparkles, X, Cpu, ExternalLink, Link as LinkIcon, EyeOff, Archive, Package, RefreshCw, Box, Building2 } from 'lucide-react';
import { Certificate, Language } from '../types';
import { translations } from '../translations';
import html2canvas from 'html2canvas';
import { jsPDF } from 'jspdf';
import QRCode from 'qrcode';
import { useNotification } from '../App';
import { PDFDocument, rgb } from 'pdf-lib';
import { usePersistedState } from '../hooks/usePersistedState';

const ABO_FOOTER_TEXT = 'ABO BANK Legal Office, Department of Intellectual Property and Copyright - Bank Association Offshore 1001 Third Avenue W, Suite 190, Bradenton, FL 34205 21. ABO BANK Harley Street, London, England, United Kingdom, W1G 9QD . Associazione Banche Offshore Corso Venezia 1 Milano Incorporata Codice Fiscale 92034960457';
const DEFAULT_CC_LOGO_URL = '/copyrightchain-logo.png';

interface CertificateViewProps {
  certificate: Certificate;
  lang: Language;
  clientToken: string | null;
}

const CertificateView: React.FC<CertificateViewProps> = ({ certificate, lang, clientToken }) => {
  const t = useMemo(() => translations[lang], [lang]);
  const { notify } = useNotification();
  const [isGeneratingPDF, setIsGeneratingPDF] = useState(false);
  const [isGeneratingStamp, setIsGeneratingStamp] = useState(false);
  const [qrCodeUrl, setQrCodeUrl] = useState<string>('');
  const [isBundling, setIsBundling] = useState(false);
  const [publicVerifyData, setPublicVerifyData] = useState<any>(null);
  
  const certificateRef = useRef<HTMLDivElement>(null);
  const stampPreviewRef = useRef<HTMLDivElement>(null);

  const certAny = certificate as any;
  const timestampEvidence = (certAny.timestampEvidence || {}) as any;
  const isTimestampCert =
    String(certificate.tier || '').toLowerCase() === 'timestamp' ||
    Boolean(timestampEvidence.status || timestampEvidence.provider || timestampEvidence.transaction || timestampEvidence.issuedAt || timestampEvidence.bodyUrl);

  const formatInfoCertStampDate = (value: any) => {
    if (!value) return 'data marca disponibile';
    const d = new Date(String(value));
    if (Number.isNaN(d.getTime())) return String(value);
    const pad = (n: number) => String(n).padStart(2, '0');
    return `${pad(d.getUTCDate())}/${pad(d.getUTCMonth() + 1)}/${d.getUTCFullYear()} ore ${pad(d.getUTCHours())}:${pad(d.getUTCMinutes())} UTC`;
  };
  const publicAny = publicVerifyData || {};
  const verifyUrl =
    (certAny.verifyUrl && String(certAny.verifyUrl).trim()) ||
    (certificate.legalAttestation && String(certificate.legalAttestation).match(/https?:\/\/\S+/)?.[0]) ||
    `https://copyrightchain.it/verifica-certificato/?id=${encodeURIComponent(certificate.id || "")}&hash=${encodeURIComponent(certificate.fileHash || certificate.hash || "")}`;

  const otsProofUrl = String(certAny.otsProofUrl || '').trim();
  const otsManifestUrl = String(certAny.otsManifestUrl || '').trim();
  const anchoredAt = String(certAny.anchoredAt || '').trim();
  const confirmedAt = String(certAny.confirmedAt || '').trim();


  // Branding: persisted (non-sensitive) settings.
  const [branding] = usePersistedState('cc_branding', {
    companyName: 'ABO BANK LEGAL DIVISION',
    logoUrl: '',
    vatNumber: '',
    footerText: 'Documento tecnico di certificazione digitale emesso da CopyrightChain. Il certificato documenta hash, data di deposito, verifica pubblica e dati tecnici associati.',
  });

  const officialLogoUrl = (String((branding as any).logoUrl || '').trim()) || DEFAULT_CC_LOGO_URL;

  const rawVideoRetained = certAny.videoRetainedByPlatform ?? certAny.video_retained_by_platform ?? publicAny.video_retained_by_platform ?? false;
  const videoRetainedByPlatform = rawVideoRetained === true || String(rawVideoRetained).toLowerCase() === 'true';

  const videoSha256 = String(certAny.videoSha256 || certAny.video_sha256 || publicAny.video_sha256 || '').trim();
  const videoFileName = String(certAny.videoFileName || certAny.video_file_name || publicAny.video_file_name || '').trim();
  const videoDeclarationText = String(certAny.videoDeclarationText || certAny.video_declaration_text || publicAny.video_declaration_text || '').trim();
  const videoProofPresent = Boolean(certAny.videoProof || certAny.video_proof || publicAny.video_proof || videoSha256 || videoFileName || videoDeclarationText);

  const displayVideoDeclarationText = lang === 'en' && videoProofPresent
    ? `The depositor declares in the video that they are making this deposit on CopyrightChain, confirms that the uploaded file "${certificate.title}" is the work to be certified, associated with the SHA-256 hash ${certificate.fileHash || (certificate as any).hash || 'N/A'}, and declares that they will keep the original video file downloaded at the end of the recording.`
    : videoDeclarationText;

  const displayLegalAttestation = lang === 'en'
    ? `Certificate issued through CopyrightChain AI Gateway. Certificate ID: ${certificate.id}. Public verification: ${verifyUrl || 'N/A'}`
    : String(certificate.legalAttestation || `Certificato emesso via CopyrightChain AI Gateway. ID: ${certificate.id}. Verifica: ${verifyUrl || ''}`);

  const aiAssisted = Boolean(
    certAny.aiAssisted ||
    certAny.ai_assisted ||
    publicAny.ai_assisted ||
    certAny.contentType === 'ai_assisted' ||
    certAny.content_type === 'ai_assisted' ||
    publicAny.content_type === 'ai_assisted'
  );

  const aiToolUsed = String(certAny.aiToolUsed || certAny.ai_tool_used || publicAny.ai_tool_used || '').trim();
  const aiPrompt = String(certAny.aiPrompt || certAny.ai_prompt || publicAny.ai_prompt || '').trim();
  const humanContribution = String(certAny.humanContribution || certAny.human_contribution || publicAny.human_contribution || '').trim();
  const aiProcessNotes = String(certAny.aiProcessNotes || certAny.ai_process_notes || publicAny.ai_process_notes || '').trim();

  const rawAiDeclarationAccepted = certAny.aiDeclarationAccepted ?? certAny.ai_declaration_accepted ?? publicAny.ai_declaration_accepted ?? false;
  const aiDeclarationAccepted = rawAiDeclarationAccepted === true || String(rawAiDeclarationAccepted).toLowerCase() === 'true';

  useEffect(() => {
    let cancelled = false;
    const certId = String(certificate.id || '').trim();
    if (!certId) return;

    fetch(`https://app.copyrightchain.it/api/ai/verify/public?id=${encodeURIComponent(certId)}`)
      .then((res) => res.ok ? res.json() : null)
      .then((data) => {
        if (!cancelled && data?.ok) setPublicVerifyData(data);
      })
      .catch(() => {});

    return () => {
      cancelled = true;
    };
  }, [certificate.id]);

  useEffect(() => {
    const generateQR = async () => {
      try {
        const verifyUrl =
    (certificate.verifyUrl && String(certificate.verifyUrl).trim()) ||
    (certificate.legalAttestation && String(certificate.legalAttestation).match(/https?:\/\/\S+/)?.[0]) ||
    `https://copyrightchain.it/verifica-certificato/?id=${encodeURIComponent(certificate.id || "")}&hash=${encodeURIComponent(certificate.fileHash || certificate.hash || "")}`;
        const url = await QRCode.toDataURL(verifyUrl, {
          width: 256,
          margin: 1,
          color: { dark: '#0f172a', light: '#ffffff' },
        });
        setQrCodeUrl(url);
      } catch (err) { console.error('QR Error:', err); }
    };
    generateQR();
  }, [certificate.id]);

  const handlePrint = () => window.print();

  const handleDownloadPDF = async () => {
    const element = certificateRef.current;
    if (!element) return;

    setIsGeneratingPDF(true);
    notify(lang === 'it' ? "Generazione documento ufficiale..." : "Generating official document...", "info");

    try {
      const canvas = await html2canvas(element, {
        scale: 2,
        useCORS: true,
        allowTaint: true,
        backgroundColor: '#ffffff',
        logging: false,
        width: element.scrollWidth,
        height: element.scrollHeight
      });

      const imgData = canvas.toDataURL('image/jpeg', 0.9);
      const pdf = new jsPDF({
        orientation: 'portrait',
        unit: 'mm',
        format: 'a4'
      });

      const pdfWidth = pdf.internal.pageSize.getWidth();
      const pdfHeight = pdf.internal.pageSize.getHeight();
      pdf.addImage(imgData, 'JPEG', 0, 0, pdfWidth, pdfHeight);
      pdf.save(`Copyright_Cert_${certificate.id}.pdf`);
      
      notify(lang === 'it' ? "Documento scaricato con successo." : "Document downloaded successfully.", "success");
    } catch (error) {
      console.error('PDF Generation Error:', error);
      notify(lang === 'it' ? "Errore durante il download. Riprova." : "Download failed. Please try again.", "error");
    } finally {
      setIsGeneratingPDF(false);
    }
  };

  const handleDownloadStamped = async () => {
    if (!certificate.fileData) {
      notify(lang === 'it' ? "File originale non trovato." : "Original file not found.", "error");
      return;
    }

    setIsGeneratingStamp(true);
    notify(lang === 'it' ? "Applicazione timbro su ogni pagina..." : "Applying stamp to every page...", "info");

    try {
      if (!stampPreviewRef.current) return;
      
      const stampCanvas = await html2canvas(stampPreviewRef.current, { 
        scale: 2, 
        useCORS: true, 
        backgroundColor: null,
        logging: false 
      });
      const stampImgData = stampCanvas.toDataURL('image/png');

      if (certificate.mimeType === 'application/pdf') {
        const existingPdfBytes = await fetch(certificate.fileData).then((res) => res.arrayBuffer());
        const pdfDoc = await PDFDocument.load(existingPdfBytes);
        const stampImage = await pdfDoc.embedPng(stampImgData);
        
        const pages = pdfDoc.getPages();
        pages.forEach((page) => {
          const { width, height } = page.getSize();
          const stampWidth = 140;
          const stampHeight = (stampCanvas.height / stampCanvas.width) * stampWidth;
          page.drawImage(stampImage, {
            x: width - stampWidth - 30,
            y: height - stampHeight - 30,
            width: stampWidth,
            height: stampHeight,
            opacity: 0.9,
          });
        });

        const pdfBytes = await pdfDoc.save();
        const blob = new Blob([pdfBytes], { type: 'application/pdf' });
        const link = document.createElement('a');
        link.href = URL.createObjectURL(blob);
        link.download = `STAMPED_${certificate.fileName}`;
        link.click();
      } else if (certificate.mimeType.startsWith('image/')) {
        const img = new Image();
        img.src = certificate.fileData;
        await new Promise((resolve) => (img.onload = resolve));

        const canvas = document.createElement('canvas');
        canvas.width = img.width;
        canvas.height = img.height;
        const ctx = canvas.getContext('2d');
        if (ctx) {
          ctx.drawImage(img, 0, 0);
          const stamp = new Image();
          stamp.src = stampImgData;
          await new Promise((resolve) => (stamp.onload = resolve));
          const stampWidth = canvas.width * 0.25;
          const stampHeight = (stamp.height / stamp.width) * stampWidth;
          ctx.globalAlpha = 0.9;
          ctx.drawImage(stamp, canvas.width - stampWidth - 40, 40, stampWidth, stampHeight);
          const link = document.createElement('a');
          link.href = canvas.toDataURL(certificate.mimeType);
          link.download = `STAMPED_${certificate.fileName}`;
          link.click();
        }
      }
      notify(lang === 'it' ? "File timbrato scaricato." : "Stamped file downloaded.", "success");
    } catch (e) {
      notify(lang === 'it' ? "Errore durante lo stamping." : "Error during stamping.", "error");
    } finally {
      setIsGeneratingStamp(false);
    }
  };

  const handleDownloadBundle = async () => {
    setIsBundling(true);
    notify(lang === 'it' ? "Compilazione Fascicolo Probatorio..." : "Compiling Evidence Bundle...", "info");
    // Nessun delay artificiale: se in futuro il bundle verrà generato via API,
    // qui andrà una chiamata reale con progress/streaming.
    const manifest = [
      'COPYRIGHTCHAIN DIGITAL CERTIFICATE REGISTRY',
      'OFFICIAL EVIDENCE BUNDLE',
      '',
      `Certificate ID: ${certificate.id}`,
      `Title: ${certificate.title}`,
      `Original file: ${certificate.fileName}`,
      `Author / Registrant: ${certificate.author}`,
      `Deposit date: ${new Date(certificate.timestamp).toISOString()}`,
      `File SHA-256: ${certificate.fileHash || (certificate as any).hash || 'N/A'}`,
      `Public verification: ${verifyUrl || 'N/A'}`,
      '',
      'BLOCKCHAIN / TIMESTAMP EVIDENCE',
      `Status: ${certificate.bitcoinEvidence?.status || 'N/A'}`,
      `Bitcoin TxID: ${certificate.bitcoinEvidence?.txId || 'N/A'}`,
      `Block height: ${typeof certificate.bitcoinEvidence?.blockHeight === 'number' ? certificate.bitcoinEvidence.blockHeight : 'N/A'}`,
      `Anchored at: ${anchoredAt || 'N/A'}`,
      `Confirmed at: ${confirmedAt || 'N/A'}`,
      '',
      'VIDEO PROOF',
      `Status: ${videoProofPresent ? 'PRESENTE / PRESENT' : 'NON ASSOCIATO / NOT ASSOCIATED'}`,
      `Video SHA-256: ${videoSha256 || 'N/A'}`,
      `Video file: ${videoFileName || 'N/A'}`,
      `Video retained by platform: ${videoRetainedByPlatform ? 'YES' : 'NO'}`,
      `Declaration: ${displayVideoDeclarationText || 'N/A'}`,
      '',
      `Issued by: ${branding.companyName}`,
      '',
      'Note: CopyrightChain stores the technical evidence and hash references. When video proof is used, the video file remains with the user unless expressly stated otherwise.'
    ].join('\n');
    const blob = new Blob([manifest], { type: 'text/plain' });
    const link = document.createElement('a');
    link.href = URL.createObjectURL(blob);
    link.download = `Evidence_Bundle_${certificate.id}.txt`;
    link.click();
    setIsBundling(false);
  };


  const handleProtectedDownload = async (url: string, fallbackName: string) => {
    if (!url) return;

    if (!clientToken) {
      notify(lang === 'it' ? 'Sessione cliente non valida.' : 'Invalid client session.', 'error');
      return;
    }

    try {
      const res = await fetch(url, {
        headers: {
          'X-CCP-Token': clientToken
        }
      });

      if (!res.ok) {
        throw new Error(`download_failed_${res.status}`);
      }

      const blob = await res.blob();

      let filename = fallbackName;
      const cd = res.headers.get('content-disposition') || '';
      const m = cd.match(/filename="?([^"]+)"?/i);
      if (m && m[1]) {
        filename = m[1];
      }

      const a = document.createElement('a');
      const blobUrl = URL.createObjectURL(blob);
      a.href = blobUrl;
      a.download = filename;
      document.body.appendChild(a);
      a.click();
      a.remove();
      URL.revokeObjectURL(blobUrl);

      notify(lang === 'it' ? 'Download completato.' : 'Download completed.', 'success');
    } catch (e) {
      notify(lang === 'it' ? 'Download non autorizzato o non disponibile.' : 'Unauthorized or unavailable download.', 'error');
    }
  };

  return (
    <div className="space-y-8 print:p-0">
      <div className="flex flex-wrap gap-2 md:gap-3 print:hidden">
        <button onClick={handlePrint} className="flex-1 md:flex-none justify-center flex items-center gap-2 bg-slate-100 hover:bg-slate-200 text-slate-700 px-4 py-2.5 rounded-xl transition text-xs md:text-sm font-bold">
          <Printer size={18} /> <span className="hidden sm:inline">{t.printCert}</span>
        </button>
        <button onClick={handleDownloadPDF} disabled={isGeneratingPDF} className="flex-1 md:flex-none justify-center flex items-center gap-2 bg-blue-600 hover:bg-blue-700 text-white px-5 py-2.5 rounded-xl transition text-xs md:text-sm font-bold shadow-lg shadow-blue-100">
          {isGeneratingPDF ? <Loader2 size={18} className="animate-spin" /> : <Download size={18} />} 
          <span>{t.downloadPdf}</span>
        </button>
        <button onClick={handleDownloadBundle} disabled={isBundling} className="flex-1 md:flex-none justify-center flex items-center gap-2 bg-emerald-600 text-white px-4 py-2.5 rounded-xl transition text-xs md:text-sm font-bold shadow-lg shadow-emerald-100">
           {isBundling ? <Loader2 size={18} className="animate-spin" /> : <Package size={18} />} {t.evidenceBundle}
        </button>
        {!isTimestampCert && (
        <button onClick={handleDownloadStamped} disabled={isGeneratingStamp} className="flex-1 md:flex-none justify-center flex items-center gap-2 bg-slate-900 text-white px-4 py-2.5 rounded-xl transition text-xs md:text-sm font-bold">
          {isGeneratingStamp ? <Loader2 size={18} className="animate-spin" /> : <Stamp size={18} />} 
          {lang === 'it' ? 'Scarica Timbrato' : 'Download Stamped'}
        </button>
        )}
        {otsProofUrl && (
          <button onClick={() => handleProtectedDownload(otsProofUrl, `proof_${certificate.id}.ots`)} className="flex-1 md:flex-none justify-center flex items-center gap-2 bg-violet-600 text-white px-4 py-2.5 rounded-xl transition text-xs md:text-sm font-bold shadow-lg shadow-violet-100">
            <Archive size={18} /> OTS Proof
          </button>
        )}
        {otsManifestUrl && (
          <button onClick={() => handleProtectedDownload(otsManifestUrl, `manifest_${certificate.id}.json`)} className="flex-1 md:flex-none justify-center flex items-center gap-2 bg-slate-700 text-white px-4 py-2.5 rounded-xl transition text-xs md:text-sm font-bold">
            <FileText size={18} /> Manifest
          </button>
        )}
        {verifyUrl && (
          <a href={verifyUrl} target="_blank" rel="noreferrer" className="flex-1 md:flex-none justify-center flex items-center gap-2 bg-white border border-slate-200 text-slate-700 px-4 py-2.5 rounded-xl transition text-xs md:text-sm font-bold">
            <ExternalLink size={18} /> {lang === 'it' ? 'Verifica pubblica' : 'Public verify'}
          </a>
        )}
      </div>

      {/* Timbro visivo - FUORI SCHERMO */}
      <div className="fixed -left-[2000px] top-0 pointer-events-none">
          <div ref={stampPreviewRef} className="p-8 bg-white border-4 border-slate-900 rounded-[3rem] w-[500px] flex items-center gap-8 shadow-2xl">
              <div className="w-24 h-24 bg-white p-2 rounded-2xl shadow-sm border border-slate-100">
                {qrCodeUrl && <img src={qrCodeUrl} alt="QR" className="w-full h-full" />}
              </div>
              <div className="text-left border-l-4 border-slate-900 pl-8 flex-1">
                  <p className="text-lg font-black text-slate-900 leading-none mb-1 uppercase tracking-tighter">{branding.companyName} REGISTERED</p>
                  <p className="text-xs font-bold text-slate-600 mb-3 uppercase">DATA CERTA: {new Date(certificate.timestamp).toLocaleDateString()}</p>
                  <p className="text-[9px] font-mono font-black text-slate-400 uppercase break-all">ID: {certificate.id}</p>
              </div>
          </div>
      </div>

      <div 
        ref={certificateRef} 
        className="certificate-container bg-white border-[3px] border-slate-900 p-10 md:p-14 relative overflow-hidden shadow-2xl print:shadow-none rounded-sm min-h-[1120px]"
      >
        {/* Filigrana */}
        <div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 opacity-[0.025] pointer-events-none">
          <Shield size={420} className="md:w-[760px] md:h-[760px]" />
        </div>

        <div className="relative z-10 text-center space-y-8 md:space-y-10">
          <div className="border-b-4 border-slate-900 pb-8">
            <div className="flex flex-col md:flex-row items-center justify-between gap-6 text-left">
              <div className="flex items-center gap-5">
                <div className="bg-white border border-slate-200 rounded-2xl p-4 shadow-sm">
                  <img src={officialLogoUrl} crossOrigin="anonymous" alt="CopyrightChain Logo" className="h-16 md:h-24 object-contain" />
                </div>
                <div>
                  <p className="text-[10px] font-black uppercase tracking-[0.35em] text-slate-500">CopyrightChain</p>
                  <h1 className="serif-font text-3xl md:text-4xl font-black text-slate-950 tracking-tight uppercase leading-none">
                    {lang === 'it' ? 'Registro Digitale dei Certificati' : 'Digital Certificate Registry'}
                  </h1>
                  <p className="mt-2 text-[10px] md:text-xs font-black uppercase tracking-[0.22em] text-slate-500">
                    {isTimestampCert
                      ? (lang === 'it' ? 'Certificato marca temporale Openapi / InfoCert' : 'Openapi / InfoCert timestamp certificate')
                      : (lang === 'it' ? 'Certificato di deposito digitale e prova di integrità SHA-256' : 'Digital deposit certificate and SHA-256 integrity proof')}
                  </p>
                </div>
              </div>
              <div className="text-center md:text-right border border-slate-900 rounded-2xl px-6 py-4 bg-slate-50">
                <p className="text-[9px] font-black uppercase tracking-[0.3em] text-slate-500">Official Record</p>
                <p className="text-lg font-mono font-black text-slate-950 mt-1">{certificate.id}</p>
                <p className="text-[9px] font-black uppercase tracking-widest text-slate-400 mt-1">{branding.companyName}</p>
              </div>
            </div>
          </div>

          {/* Registro tecnico */}
          <div className="p-6 rounded-2xl border border-slate-200 bg-slate-50 text-left">
              <div className="flex items-start gap-4">
                <Box className={certificate.bitcoinEvidence?.status === 'pending' ? 'text-orange-500 flex-shrink-0 mt-1' : 'text-blue-700 flex-shrink-0 mt-1'} size={26} />
                <div className="min-w-0">
                  <p className="text-xs font-black uppercase tracking-widest text-slate-900">
                    {lang === 'it' ? 'Registro tecnico di integrità e verifica' : 'Technical integrity and verification registry'}
                  </p>
                  <p className="text-[10px] font-bold uppercase text-slate-500 mt-1">
                    {lang === 'it' ? 'Hash SHA-256, data deposito, verifica pubblica e dati di ancoraggio quando disponibili.' : 'SHA-256 hash, deposit date, public verification and anchoring data when available.'}
                  </p>
                  <p className="mt-4 text-[10px] font-black uppercase tracking-widest text-slate-400">File SHA-256</p>
                  <p className="font-mono text-[10px] md:text-xs font-black text-slate-900 break-all">{certificate.fileHash || (certificate as any).hash || '—'}</p>
              </div>
            </div>
          </div>

          <div className="grid grid-cols-1 md:grid-cols-2 gap-8 md:gap-14 text-left">
              <div className="space-y-1 border-l-4 border-slate-900 pl-6">
                <p className="text-[11px] text-slate-400 font-black uppercase tracking-widest">{t.certTitle}</p>
                <p className="text-xl md:text-2xl font-black text-slate-900 break-words">{certificate.title}</p>
              </div>
              <div className="space-y-1 border-l-4 border-blue-600 pl-6">
                <p className="text-[11px] text-slate-400 font-black uppercase tracking-widest">{t.certId}</p>
                <p className="text-xl md:text-2xl font-mono font-black text-blue-700 break-all">{certificate.id}</p>
              </div>
          </div>

          <div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-8 text-left">
              <div className="flex items-start gap-4">
                <Calendar className="text-slate-300 mt-1 flex-shrink-0" size={24} />
                <div className="min-w-0">
                  <p className="text-[10px] text-slate-400 font-black uppercase tracking-widest">{t.depositDate}</p>
                  <p className="text-sm md:text-base font-black text-slate-800 leading-tight">
                    {new Date(certificate.timestamp).toLocaleDateString(lang === 'it' ? 'it-IT' : 'en-US')}
                  </p>
                </div>
              </div>
              <div className="flex items-start gap-4">
                <User className="text-slate-300 mt-1 flex-shrink-0" size={24} />
                <div className="min-w-0">
                  <p className="text-[10px] text-slate-400 font-black uppercase tracking-widest">{t.authorRegistrant}</p>
                  <p className="text-sm md:text-base font-black text-slate-800 leading-tight break-words">{certificate.author}</p>
                </div>
              </div>
              <div className="flex items-start gap-4">
                <FileText className="text-slate-300 mt-1 flex-shrink-0" size={24} />
                <div className="min-w-0 flex-1">
                  <p className="text-[10px] text-slate-400 font-black uppercase tracking-widest">{t.originalFile}</p>
                  <p className="text-sm md:text-base font-black text-slate-800 leading-tight break-all">{certificate.fileName}</p>
                </div>
              </div>
          </div>

          {isTimestampCert && (
            <div className="mt-8 bg-indigo-50 border border-indigo-100 rounded-[2rem] p-6">
              <div className="flex items-center gap-3 mb-5">
                <div className="w-10 h-10 rounded-xl bg-indigo-600 text-white flex items-center justify-center text-xs font-black">TS</div>
                <div>
                  <p className="text-[10px] text-indigo-500 font-black uppercase tracking-widest">Marca temporale</p>
                  <h3 className="text-lg font-black text-slate-900">Openapi / InfoCert</h3>
                  <p className="mt-1 inline-flex rounded-full bg-white border border-indigo-200 px-3 py-1 text-[10px] font-black text-indigo-700 uppercase tracking-widest">
                    Timbro InfoCert: {formatInfoCertStampDate(timestampEvidence.issuedAt)}
                  </p>
                </div>
              </div>

              <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
                <div>
                  <p className="text-[10px] text-slate-400 font-black uppercase tracking-widest">Status</p>
                  <p className="font-mono text-sm font-black text-indigo-700 break-all">{String(timestampEvidence.status || 'issued')}</p>
                </div>
                <div>
                  <p className="text-[10px] text-slate-400 font-black uppercase tracking-widest">Provider</p>
                  <p className="font-mono text-sm font-black text-slate-900 break-all">{String(timestampEvidence.provider || 'Openapi / InfoCert')}</p>
                </div>
                <div>
                  <p className="text-[10px] text-slate-400 font-black uppercase tracking-widest">Ambiente</p>
                  <p className="font-mono text-sm font-black text-slate-900 break-all">{String(timestampEvidence.environment || '—')}</p>
                </div>
                <div>
                  <p className="text-[10px] text-slate-400 font-black uppercase tracking-widest">Transaction</p>
                  <p className="font-mono text-sm font-black text-slate-900 break-all">{String(timestampEvidence.transaction || '—')}</p>
                </div>
                <div className="md:col-span-2">
                  <p className="text-[10px] text-slate-400 font-black uppercase tracking-widest">Data emissione marca</p>
                  <p className="font-mono text-sm font-black text-slate-900 break-all">{formatInfoCertStampDate(timestampEvidence.issuedAt)}</p>
                </div>
              </div>

              <div className="mt-4">
                <p className="text-[10px] text-slate-400 font-black uppercase tracking-widest">Hash SHA-256 marcato</p>
                <p className="font-mono text-[10px] md:text-xs font-black text-slate-900 break-all">{String(timestampEvidence.hash || certificate.fileHash || '—')}</p>
              </div>

              {timestampEvidence.bodyUrl && (
                <div className="mt-4">
                  <p className="text-[10px] text-slate-400 font-black uppercase tracking-widest">Timestamp body / M7M</p>
                  <p className="font-mono text-[10px] md:text-xs font-black text-indigo-700 break-all">{String(timestampEvidence.bodyUrl)}</p>
                </div>
              )}

              <div className="mt-4 rounded-2xl bg-white/80 border border-indigo-100 p-4">
                <p className="text-xs font-bold text-slate-600 leading-relaxed">
                  Marca temporale InfoCert associata all’impronta SHA-256 del deposito CopyrightChain e collegata al presente certificato verificabile.
                </p>
              </div>
            </div>
          )}

          {certificate.bitcoinEvidence && (
            <div className="space-y-6 pt-10 border-t border-slate-100 text-left">
              <p className="text-[11px] text-slate-400 font-black uppercase tracking-widest">
                {lang === 'it' ? 'Dettagli ancoraggio blockchain' : 'Blockchain anchoring details'}
              </p>

              <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
                <div className="bg-slate-50 border border-slate-100 rounded-2xl p-5">
                  <p className="text-[10px] text-slate-400 font-black uppercase tracking-widest">Status</p>
                  <p className="text-base font-black text-slate-900 mt-1 uppercase">
                    {String(certificate.bitcoinEvidence?.status || 'pending')}
                  </p>
                </div>

                <div className="bg-slate-50 border border-slate-100 rounded-2xl p-5">
                  <p className="text-[10px] text-slate-400 font-black uppercase tracking-widest">Bitcoin TxID</p>
                  <p className="text-sm font-mono font-black text-slate-900 mt-1 break-all">
                    {String(certificate.bitcoinEvidence?.txId || '—')}
                  </p>
                </div>

                <div className="bg-slate-50 border border-slate-100 rounded-2xl p-5">
                  <p className="text-[10px] text-slate-400 font-black uppercase tracking-widest">Block Height</p>
                  <p className="text-base font-black text-slate-900 mt-1">
                    {typeof certificate.bitcoinEvidence?.blockHeight === 'number' ? certificate.bitcoinEvidence.blockHeight : '—'}
                  </p>
                </div>

                <div className="bg-slate-50 border border-slate-100 rounded-2xl p-5">
                  <p className="text-[10px] text-slate-400 font-black uppercase tracking-widest">
                    {lang === 'it' ? 'Verifica pubblica' : 'Public verification'}
                  </p>
                  {verifyUrl ? (
                    <a href={verifyUrl} target="_blank" rel="noreferrer" className="mt-1 inline-flex items-center gap-2 text-blue-600 font-black break-all">
                      <ExternalLink size={16} /> {verifyUrl}
                    </a>
                  ) : (
                    <p className="text-base font-black text-slate-900 mt-1">—</p>
                  )}
                </div>

                <div className="bg-slate-50 border border-slate-100 rounded-2xl p-5">
                  <p className="text-[10px] text-slate-400 font-black uppercase tracking-widest">Anchored At</p>
                  <p className="text-sm font-black text-slate-900 mt-1 break-all">
                    {anchoredAt ? new Date(anchoredAt).toLocaleString(lang === 'it' ? 'it-IT' : 'en-US') : '—'}
                  </p>
                </div>

                <div className="bg-slate-50 border border-slate-100 rounded-2xl p-5">
                  <p className="text-[10px] text-slate-400 font-black uppercase tracking-widest">Confirmed At</p>
                  <p className="text-sm font-black text-slate-900 mt-1 break-all">
                    {confirmedAt ? new Date(confirmedAt).toLocaleString(lang === 'it' ? 'it-IT' : 'en-US') : '—'}
                  </p>
                </div>
              </div>
            </div>
          )}

          {videoProofPresent && (
            <div className="space-y-5 pt-10 border-t border-slate-100 text-left">
              <div className="flex items-center gap-3">
                <div className="bg-slate-900 text-white p-2 rounded-xl">
                  <Fingerprint size={20} />
                </div>
                <div>
                  <p className="text-[11px] text-slate-900 font-black uppercase tracking-widest">
                    {lang === 'it' ? 'Prova video associata al deposito' : 'Video proof associated with this deposit'}
                  </p>
                  <p className="text-[10px] text-slate-400 font-black uppercase tracking-widest">
                    {lang === 'it' ? 'Collegamento tramite hash SHA-256 del file video' : 'Linked through the SHA-256 hash of the video file'}
                  </p>
                </div>
              </div>

              <div className="grid grid-cols-1 md:grid-cols-2 gap-5">
                <div className="bg-slate-50 border border-slate-100 rounded-2xl p-5">
                  <p className="text-[10px] text-slate-400 font-black uppercase tracking-widest">Video Proof</p>
                  <p className="text-base font-black text-emerald-700 mt-1 uppercase">{lang === 'it' ? 'Presente' : 'Present'}</p>
                </div>
                <div className="bg-slate-50 border border-slate-100 rounded-2xl p-5">
                  <p className="text-[10px] text-slate-400 font-black uppercase tracking-widest">
                    {lang === 'it' ? 'Conservazione piattaforma' : 'Platform retention'}
                  </p>
                  <p className="text-base font-black text-slate-900 mt-1 uppercase">
                    {videoRetainedByPlatform ? 'YES' : 'NO'}
                  </p>
                  <p className="text-[9px] font-bold text-slate-400 mt-1">
                    {lang === 'it' ? 'Il video resta nella disponibilità dell’utente.' : 'The video remains under the user’s control.'}
                  </p>
                </div>
                <div className="bg-slate-50 border border-slate-100 rounded-2xl p-5 md:col-span-2">
                  <p className="text-[10px] text-slate-400 font-black uppercase tracking-widest">Video SHA-256</p>
                  <p className="text-[10px] md:text-xs font-mono font-black text-slate-900 mt-1 break-all">{videoSha256 || '—'}</p>
                </div>
                <div className="bg-slate-50 border border-slate-100 rounded-2xl p-5 md:col-span-2">
                  <p className="text-[10px] text-slate-400 font-black uppercase tracking-widest">
                    {lang === 'it' ? 'File video dichiarato' : 'Declared video file'}
                  </p>
                  <p className="text-sm font-black text-slate-900 mt-1 break-all">{videoFileName || '—'}</p>
                </div>
                {videoDeclarationText && (
                  <div className="bg-white border border-slate-200 rounded-2xl p-5 md:col-span-2">
                    <p className="text-[10px] text-slate-400 font-black uppercase tracking-widest">
                      {lang === 'it' ? 'Dichiarazione guidata' : 'Guided declaration'}
                    </p>
                    <p className="text-xs md:text-sm font-bold text-slate-700 mt-2 leading-relaxed">{displayVideoDeclarationText}</p>
                  </div>
                )}
              </div>
            </div>
          )}

          {aiAssisted && (
            <div className="space-y-5 pt-10 border-t border-slate-100 text-left">
              <div className="flex items-center gap-3">
                <div className="bg-indigo-900 text-white p-2 rounded-xl">
                  <Sparkles size={20} />
                </div>
                <div>
                  <p className="text-[11px] text-slate-900 font-black uppercase tracking-widest">
                    {lang === 'it' ? 'Contenuto assistito da AI' : 'AI-assisted content'}
                  </p>
                  <p className="text-[10px] text-slate-400 font-black uppercase tracking-widest">
                    {lang === 'it' ? 'Dichiarazione contributo umano associata al certificato' : 'Human contribution declaration linked to this certificate'}
                  </p>
                </div>
              </div>

              <div className="bg-indigo-50/60 border border-indigo-100 rounded-2xl p-6 space-y-5">
                <div className="grid grid-cols-1 md:grid-cols-2 gap-5">
                  <div className="bg-white border border-indigo-100 rounded-2xl p-5">
                    <p className="text-[10px] text-slate-400 font-black uppercase tracking-widest">
                      {lang === 'it' ? 'Strumento AI usato' : 'AI tool used'}
                    </p>
                    <p className="text-sm md:text-base font-black text-slate-900 mt-1 break-words">{aiToolUsed || '—'}</p>
                  </div>

                  <div className="bg-white border border-indigo-100 rounded-2xl p-5">
                    <p className="text-[10px] text-slate-400 font-black uppercase tracking-widest">
                      {lang === 'it' ? 'Dichiarazione contributo umano' : 'Human contribution declaration'}
                    </p>
                    <p className={`text-sm md:text-base font-black mt-1 uppercase ${aiDeclarationAccepted ? 'text-emerald-700' : 'text-slate-600'}`}>
                      {aiDeclarationAccepted
                        ? (lang === 'it' ? 'Accettata' : 'Accepted')
                        : (lang === 'it' ? 'Non indicata' : 'Not declared')}
                    </p>
                  </div>
                </div>

                {aiPrompt && (
                  <div className="bg-white border border-indigo-100 rounded-2xl p-5">
                    <p className="text-[10px] text-slate-400 font-black uppercase tracking-widest">
                      {lang === 'it' ? 'Prompt principale' : 'Main prompt'}
                    </p>
                    <p className="text-xs md:text-sm font-bold text-slate-700 mt-2 leading-relaxed break-words">{aiPrompt}</p>
                  </div>
                )}

                {humanContribution && (
                  <div className="bg-white border border-indigo-100 rounded-2xl p-5">
                    <p className="text-[10px] text-slate-400 font-black uppercase tracking-widest">
                      {lang === 'it' ? 'Contributo umano dichiarato' : 'Declared human contribution'}
                    </p>
                    <p className="text-xs md:text-sm font-bold text-slate-700 mt-2 leading-relaxed break-words">{humanContribution}</p>
                  </div>
                )}

                {aiProcessNotes && (
                  <div className="bg-white border border-indigo-100 rounded-2xl p-5">
                    <p className="text-[10px] text-slate-400 font-black uppercase tracking-widest">
                      {lang === 'it' ? 'Note sul processo creativo' : 'Creative process notes'}
                    </p>
                    <p className="text-xs md:text-sm font-bold text-slate-700 mt-2 leading-relaxed break-words">{aiProcessNotes}</p>
                  </div>
                )}

                <p className="text-[9px] text-slate-500 font-bold leading-relaxed">
                  {lang === 'it'
                    ? 'CopyrightChain documenta il file, la data, l’hash SHA-256 e la dichiarazione del contributo umano associata al processo creativo. Non utilizza rilevatori AI e non dichiara automaticamente la tutela dell’opera.'
                    : 'CopyrightChain documents the file, date, SHA-256 hash and the human contribution declaration associated with the creative process. It does not use AI detectors and does not automatically declare copyright protection.'}
                </p>
              </div>
            </div>
          )}

          <div className="space-y-4 pt-10 border-t border-slate-100 text-left">
              <p className="text-[11px] text-slate-400 font-black uppercase tracking-widest">{t.legalAttestation}</p>
              <div className="serif-font text-sm md:text-base text-slate-800 leading-relaxed italic bg-slate-50 p-8 rounded-2xl border border-slate-100">
                "{displayLegalAttestation}"
              </div>
          </div>

          <div className="flex flex-col sm:flex-row justify-between items-center sm:items-end pt-12 border-t-2 border-slate-100 gap-10">
            <div className="text-center sm:text-left space-y-4 w-full sm:w-auto">
              <div className="inline-flex items-center gap-3 text-green-700 bg-green-50 px-5 py-2.5 rounded-full text-[10px] font-black uppercase tracking-widest border border-green-100">
                <CheckCircle size={18} /> {t.verifiedAuth}
              </div>
              <p className="text-[9px] text-slate-500 max-w-2xl leading-relaxed font-bold">
                {ABO_FOOTER_TEXT}
              </p>
              <p className="text-[8px] text-slate-400 max-w-2xl leading-relaxed font-bold uppercase tracking-widest">
                {lang === 'it'
                  ? 'Documento tecnico di deposito digitale: conserva il file originale, il video proof e il presente certificato.'
                  : 'Technical digital deposit document: keep the original file, the video proof and this certificate.'}
              </p>
            </div>
            <div className="text-center bg-white p-3 border border-slate-100 rounded-3xl shadow-xl">
              <div className="w-28 h-28 md:w-36 md:h-36 flex items-center justify-center">
                 {qrCodeUrl && <img src={qrCodeUrl} alt="QR" className="w-full h-full" />}
              </div>
              <p className="text-[9px] text-slate-400 font-black uppercase tracking-widest mt-3">{t.scanVerify}</p>
            </div>
          </div>
        </div>
      </div>
    </div>
  );
};

export default CertificateView;

Youez - 2016 - github.com/yon3zu
LinuXploit