| 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, { useState, useMemo, useEffect } from 'react';
import { Monitor, Globe, Loader2, Gavel, Trash2, AlertTriangle, CheckCircle, Activity, LayoutGrid, List, Zap, Coins } from 'lucide-react';
import { Certificate, Language, InfringementAlert } from '../types';
import { translations } from '../translations';
import { GoogleGenAI } from "@google/genai";
import { useNotification } from '../App';
// Added missing IPMonitorProps interface
interface IPMonitorProps {
certificates: Certificate[];
lang: Language;
onNavigate: (tab: any) => void;
}
const IPMonitor: React.FC<IPMonitorProps> = ({ certificates, lang, onNavigate }) => {
const t = useMemo(() => translations[lang], [lang]);
const { user, deductCredits, notify } = useNotification();
const [isScanning, setIsScanning] = useState(false);
const [alerts, setAlerts] = useState<InfringementAlert[]>([]);
const handleStartScan = async () => {
if (certificates.length === 0) return;
// Scansione costa 2 ABO Coins
if (!deductCredits(2)) return;
setIsScanning(true);
// Create a new instance right before the call to ensure up-to-date API key
const ai = new GoogleGenAI({ apiKey: process.env.API_KEY });
try {
const targetCert = certificates[0];
const prompt = `Simula una scansione web per violazioni copyright dell'opera "${targetCert.title}". Trova 2 potenziali violazioni e descrivile in formato JSON (url, description, riskLevel). Lingua: ${lang === 'it' ? 'Italiano' : 'English'}`;
const response = await ai.models.generateContent({
model: 'gemini-3-flash-preview',
contents: prompt,
config: { responseMimeType: "application/json" }
});
const results = JSON.parse(response.text || "[]");
const newAlerts: InfringementAlert[] = results.map((r: any) => ({
...r,
id: `ALT-${Math.random().toString(36).substr(2, 6).toUpperCase()}`,
detectedOn: new Date().toISOString(),
status: 'new'
}));
setAlerts(prev => [...newAlerts, ...prev]);
notify(`Rilevate ${newAlerts.length} potenziali violazioni. (2 ABO Usati)`, "info");
} catch (err) { notify("Errore scansione", "error"); } finally { setIsScanning(false); }
};
return (
<div className="space-y-10 pb-10">
<div className="flex flex-col md:flex-row justify-between items-start md:items-end gap-6">
<div>
<h2 className="text-4xl font-black text-slate-900 tracking-tight flex items-center gap-4 uppercase">
<Monitor size={36} className="text-indigo-600" /> IP Monitor
</h2>
<p className="text-slate-500 font-medium mt-1">Scansione web per violazioni del diritto d'autore.</p>
</div>
<button onClick={handleStartScan} disabled={isScanning || certificates.length === 0 || user.credits.balance < 2} className="bg-indigo-600 text-white px-10 py-4 rounded-2xl font-black text-xs uppercase tracking-widest flex items-center gap-3 shadow-xl disabled:opacity-50">
{isScanning ? <Loader2 size={18} className="animate-spin" /> : <Zap size={18} />}
AVVIA SCANSIONE (2 ABO)
</button>
</div>
<div className="grid grid-cols-1 lg:grid-cols-4 gap-8">
<div className="lg:col-span-1 space-y-8">
<div className="bg-white p-8 rounded-[2.5rem] border border-slate-100 shadow-sm text-center">
<h4 className="text-[10px] font-black text-slate-400 uppercase tracking-widest mb-6">Wallet Balance</h4>
<div className="text-5xl font-black text-slate-900 mb-2">{user.credits.balance}</div>
<p className="text-[10px] font-black text-indigo-600 uppercase">ABO COINS</p>
</div>
</div>
<div className="lg:col-span-3">
{alerts.length === 0 ? (
<div className="bg-white rounded-[3rem] border-4 border-dashed border-slate-50 p-20 text-center flex flex-col items-center">
<Globe size={40} className="text-slate-200 mb-6" />
<h3 className="text-xl font-black text-slate-300 uppercase">Nessuna scansione effettuata</h3>
<p className="text-xs text-slate-300 font-bold uppercase mt-2">Costo Scansione: 2 ABO COIN</p>
</div>
) : (
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{alerts.map(alert => (
<div key={alert.id} className="bg-white p-8 rounded-[2.5rem] border border-slate-100 shadow-xl">
<div className="flex justify-between mb-6">
<span className={`text-[10px] font-black px-3 py-1 rounded-full uppercase ${alert.riskLevel === 'high' ? 'bg-red-50 text-red-600' : 'bg-blue-50 text-blue-600'}`}>{alert.riskLevel} Risk</span>
</div>
<h4 className="font-black text-slate-900 text-xs truncate mb-4">{alert.url}</h4>
<p className="text-xs text-slate-500 mb-8 font-medium">{alert.description}</p>
<button onClick={() => onNavigate('defense')} className="w-full bg-slate-900 text-white py-3 rounded-xl font-black text-[10px] uppercase tracking-widest shadow-lg">AGISCI ORA</button>
</div>
))}
</div>
)}
</div>
</div>
</div>
);
};
export default IPMonitor;