Spaces:
Sleeping
Sleeping
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Cyber Guider AI | Scam Protection</title> | |
| <link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;600;800&display=swap" rel="stylesheet"> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <style> | |
| :root { | |
| --primary: #818cf8; | |
| --primary-glow: rgba(129, 140, 248, 0.5); | |
| --danger: #ef4444; | |
| --success: #10b981; | |
| --bg: #0f172a; | |
| --glass: rgba(30, 41, 59, 0.7); | |
| --glass-border: rgba(255, 255, 255, 0.1); | |
| } | |
| * { margin: 0; padding: 0; box-sizing: border-box; } | |
| body { | |
| font-family: 'Outfit', sans-serif; | |
| background: var(--bg); | |
| color: white; | |
| min-height: 100vh; | |
| overflow-x: hidden; | |
| background: radial-gradient(circle at top right, #1e1b4b, #0f172a); | |
| } | |
| .container { max-width: 1000px; margin: 0 auto; padding: 40px 20px; } | |
| header { text-align: center; margin-bottom: 60px; animation: fadeInDown 0.8s ease-out; } | |
| .logo { font-size: 4rem; font-weight: 800; letter-spacing: -2px; margin-bottom: 10px; background: linear-gradient(to right, #818cf8, #c084fc); -webkit-background-clip: text; -webkit-text-fill-color: transparent; filter: drop-shadow(0 0 10px var(--primary-glow)); } | |
| .subtitle { color: #94a3b8; font-size: 1.1rem; } | |
| .main-card { | |
| background: var(--glass); | |
| backdrop-filter: blur(12px); | |
| border: 1px solid var(--glass-border); | |
| border-radius: 24px; | |
| padding: 30px; | |
| box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5); | |
| } | |
| .tabs { display: flex; gap: 10px; margin-bottom: 30px; background: rgba(15, 23, 42, 0.5); padding: 5px; border-radius: 12px; } | |
| .tab-btn { | |
| flex: 1; padding: 12px; border: none; background: transparent; color: #94a3b8; | |
| font-weight: 600; cursor: pointer; border-radius: 8px; transition: 0.3s; | |
| } | |
| .tab-btn.active { background: var(--primary); color: white; box-shadow: 0 4px 12px var(--primary-glow); } | |
| .input-section { display: none; } | |
| .input-section.active { display: block; animation: fadeIn 0.4s ease-out; } | |
| textarea { | |
| width: 100%; height: 150px; background: rgba(15, 23, 42, 0.8); border: 1px solid var(--glass-border); | |
| border-radius: 16px; padding: 20px; color: white; font-size: 1rem; resize: none; margin-bottom: 20px; | |
| transition: 0.3s; | |
| } | |
| textarea:focus { outline: none; border-color: var(--primary); box-shadow: 0 0 15px var(--primary-glow); } | |
| .file-upload { | |
| border: 2px dashed var(--glass-border); border-radius: 16px; padding: 40px; text-align: center; | |
| cursor: pointer; transition: 0.3s; | |
| } | |
| .file-upload:hover { border-color: var(--primary); background: rgba(129, 140, 248, 0.05); } | |
| .file-upload i { font-size: 3rem; color: var(--primary); margin-bottom: 15px; } | |
| .btn-analyze { | |
| width: 100%; padding: 18px; border: none; border-radius: 16px; background: var(--primary); | |
| color: white; font-size: 1.1rem; font-weight: 700; cursor: pointer; transition: 0.3s; | |
| box-shadow: 0 10px 20px -5px var(--primary-glow); | |
| } | |
| .btn-analyze:hover { transform: translateY(-2px); filter: brightness(1.1); } | |
| .btn-analyze:active { transform: translateY(0); } | |
| /* Results Area */ | |
| .results-container { margin-top: 40px; display: none; } | |
| .loading { text-align: center; padding: 40px; } | |
| .scanner { | |
| width: 60px; height: 60px; border: 4px solid var(--primary); border-top-color: transparent; | |
| border-radius: 50%; margin: 0 auto 20px; animation: spin 1s linear infinite; | |
| } | |
| .result-card { | |
| background: rgba(15, 23, 42, 0.9); border-radius: 20px; padding: 30px; | |
| border-left: 6px solid var(--primary); animation: slideUp 0.5s ease-out; | |
| } | |
| .result-card.danger { border-left-color: var(--danger); } | |
| .result-card.success { border-left-color: var(--success); } | |
| .status-badge { | |
| display: inline-block; padding: 6px 16px; border-radius: 99px; font-weight: 700; font-size: 0.9rem; | |
| margin-bottom: 15px; text-transform: uppercase; | |
| } | |
| .status-badge.scam { background: rgba(239, 68, 68, 0.2); color: var(--danger); } | |
| .status-badge.safe { background: rgba(16, 185, 129, 0.2); color: var(--success); } | |
| .grid-info { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-bottom: 25px; } | |
| .info-box { background: rgba(255, 255, 255, 0.03); padding: 15px; border-radius: 12px; } | |
| .info-label { font-size: 0.8rem; color: #94a3b8; text-transform: uppercase; margin-bottom: 5px; } | |
| .info-value { font-weight: 600; font-size: 1rem; } | |
| /* Self-Learning Badge */ | |
| .learned-badge { | |
| background: linear-gradient(135deg, #6366f1, #a855f7); | |
| color: white; | |
| padding: 4px 12px; | |
| border-radius: 99px; | |
| font-size: 0.75rem; | |
| font-weight: 800; | |
| display: inline-flex; | |
| align-items: center; | |
| gap: 5px; | |
| margin-top: 5px; | |
| box-shadow: 0 0 15px rgba(168, 85, 247, 0.4); | |
| animation: pulse 2s infinite; | |
| } | |
| @keyframes pulse { | |
| 0% { box-shadow: 0 0 0 0 rgba(168, 85, 247, 0.4); } | |
| 70% { box-shadow: 0 0 0 10px rgba(168, 85, 247, 0); } | |
| 100% { box-shadow: 0 0 0 0 rgba(168, 85, 247, 0); } | |
| } | |
| .analysis-text { line-height: 1.6; color: #cbd5e1; margin-bottom: 20px; } | |
| .action-plan { background: rgba(129, 140, 248, 0.05); padding: 20px; border-radius: 16px; } | |
| .action-title { font-weight: 700; margin-bottom: 12px; display: flex; align-items: center; gap: 10px; color: var(--primary); } | |
| .action-list { list-style: none; } | |
| .action-list li { margin-bottom: 8px; display: flex; gap: 10px; align-items: flex-start; } | |
| .action-list li::before { content: '✓'; color: var(--primary); font-weight: bold; } | |
| /* New Action Buttons */ | |
| .action-btn-primary { | |
| background: var(--primary); color: white; border: none; padding: 12px; border-radius: 12px; | |
| font-weight: 700; cursor: pointer; transition: 0.3s; display: flex; align-items: center; justify-content: center; gap: 8px; | |
| } | |
| .action-btn-success { | |
| background: var(--success); color: white; border: none; padding: 12px; border-radius: 12px; | |
| font-weight: 700; cursor: pointer; transition: 0.3s; display: flex; align-items: center; justify-content: center; gap: 8px; | |
| } | |
| .secondary-btn { | |
| background: transparent; border: 1px solid var(--danger); color: var(--danger); padding: 10px 20px; | |
| border-radius: 12px; cursor: pointer; font-weight: 600; transition: 0.3s; | |
| } | |
| .action-btn-primary:hover, .action-btn-success:hover { transform: translateY(-2px); filter: brightness(1.1); box-shadow: 0 5px 15px rgba(0,0,0,0.3); } | |
| /* Notification Toast */ | |
| #toast { | |
| position: fixed; bottom: 30px; left: 50%; transform: translateX(-50%); | |
| background: #1e293b; color: white; padding: 12px 24px; border-radius: 99px; | |
| box-shadow: 0 10px 25px rgba(0,0,0,0.5); border: 1px solid var(--primary); | |
| display: none; z-index: 1000; animation: slideUp 0.3s ease-out; | |
| } | |
| @keyframes spin { to { transform: rotate(360deg); } } | |
| @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } | |
| @keyframes fadeInDown { from { opacity: 0; transform: translateY(-20px); } to { opacity: 1; transform: translateY(0); } } | |
| @keyframes slideUp { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } } | |
| @media (max-width: 600px) { | |
| .grid-info { grid-template-columns: 1fr; } | |
| .logo { font-size: 2.5rem; } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <header> | |
| <div class="logo">CYBER GUIDER</div> | |
| <p class="subtitle">Agentic Fraud Detection for Pakistani Citizens</p> | |
| </header> | |
| <main class="main-card"> | |
| <div class="tabs"> | |
| <button class="tab-btn active" onclick="switchTab('text')"><i class="fa-solid fa-message"></i> SMS</button> | |
| <button class="tab-btn" onclick="switchTab('image')"><i class="fa-solid fa-image"></i> Screenshot</button> | |
| <button class="tab-btn" onclick="switchTab('audio')"><i class="fa-solid fa-microphone"></i> Voice</button> | |
| </div> | |
| <div id="text-section" class="input-section active"> | |
| <textarea id="text-input" placeholder="Aapka 25000 ka inam nikla hai, is link par click karein..."></textarea> | |
| </div> | |
| <div id="image-section" class="input-section"> | |
| <div class="file-upload" onclick="document.getElementById('image-file').click()"> | |
| <i class="fa-solid fa-cloud-arrow-up"></i> | |
| <h3>Upload Screenshot</h3> | |
| <p id="image-status">Drop E-Challan or WhatsApp screenshot here</p> | |
| <input type="file" id="image-file" hidden accept="image/*" onchange="updateFileStatus('image')"> | |
| </div> | |
| </div> | |
| <div id="audio-section" class="input-section"> | |
| <div class="file-upload" onclick="document.getElementById('audio-file').click()"> | |
| <i class="fa-solid fa-music"></i> | |
| <h3>Upload Voice Note</h3> | |
| <p id="audio-status">Upload WhatsApp .m4a or .opus voice note</p> | |
| <input type="file" id="audio-file" hidden accept="audio/*" onchange="updateFileStatus('audio')"> | |
| </div> | |
| </div> | |
| <button class="btn-analyze" onclick="analyze()"><i class="fa-solid fa-shield-halved"></i> ANALYZE FOR THREATS</button> | |
| <div id="results" class="results-container"> | |
| <div id="loading" class="loading"> | |
| <div class="scanner"></div> | |
| <h3>Scanning for Scam Patterns...</h3> | |
| <p>Agentic reasoning in progress</p> | |
| </div> | |
| <div id="result-content" style="display: none;"> | |
| <div id="result-card" class="result-card"> | |
| <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;"> | |
| <div id="status-badge" class="status-badge">SCAM DETECTED</div> | |
| <div id="threat-meter" style="width: 100px; height: 10px; background: #334155; border-radius: 99px; overflow: hidden;"> | |
| <div id="meter-fill" style="width: 0%; height: 100%; background: var(--danger); transition: 1s ease-out;"></div> | |
| </div> | |
| </div> | |
| <div class="grid-info"> | |
| <div class="info-box"> | |
| <div class="info-label">Risk Level</div> | |
| <div id="risk-level" class="info-value">High</div> | |
| </div> | |
| <div class="info-box"> | |
| <div class="info-label">Detection Source</div> | |
| <div id="source" class="info-value">Local Intelligence</div> | |
| <div id="learned-tag" class="learned-badge" style="display:none;"> | |
| <i class="fa-solid fa-brain"></i> SELF-LEARNED | |
| </div> | |
| </div> | |
| <div id="geo-box" class="info-box" style="display:none;"> | |
| <div class="info-label">Scammer Location</div> | |
| <div id="geo-location" class="info-value">Unknown</div> | |
| </div> | |
| </div> | |
| <div class="info-label">AI Forensic Analysis</div> | |
| <p id="explanation" class="analysis-text"></p> | |
| <div id="transcript-box" style="display:none; margin-bottom: 20px;"> | |
| <div class="info-label">Extracted Content</div> | |
| <p id="transcript" style="font-style: italic; color: #94a3b8;"></p> | |
| </div> | |
| <div id="forensics-box" style="display:none; margin-bottom: 25px;"> | |
| <div class="info-label">Advanced Link Forensics</div> | |
| <div id="forensics-details" style="background: rgba(0,0,0,0.3); padding: 15px; border-radius: 12px; font-size: 0.9rem; border-left: 3px solid var(--primary);"></div> | |
| </div> | |
| <div class="action-plan"> | |
| <div class="action-title"><i class="fa-solid fa-list-check"></i> Next Action Plan</div> | |
| <ul id="next-steps" class="action-list"></ul> | |
| </div> | |
| <div id="chat-section" style="margin-top: 25px; border-top: 1px solid var(--glass-border); padding-top: 20px;"> | |
| <div class="action-title"><i class="fa-solid fa-comments"></i> Ask the Expert</div> | |
| <div id="chat-history" style="max-height: 200px; overflow-y: auto; margin-bottom: 15px; font-size: 0.9rem; line-height: 1.5;"></div> | |
| <div style="display: flex; gap: 10px;"> | |
| <input type="text" id="chat-input" placeholder="E.g. Kya main ne link khola to data chori ho gaya?" style="flex: 1; background: rgba(0,0,0,0.3); border: 1px solid var(--glass-border); padding: 10px; border-radius: 8px; color: white;"> | |
| <button onclick="sendChat()" style="background: var(--primary); border: none; color: white; padding: 10px 15px; border-radius: 8px; cursor: pointer;"><i class="fa-solid fa-paper-plane"></i></button> | |
| </div> | |
| </div> | |
| <div id="report-section" style="margin-top: 25px; display: flex; flex-direction: column; gap: 15px;"> | |
| <div style="display: flex; gap: 10px; justify-content: center;"> | |
| <button id="btn-report" onclick="reportScam()" class="secondary-btn"> | |
| <i class="fa-solid fa-triangle-exclamation"></i> Blacklist Scam | |
| </button> | |
| </div> | |
| <div id="action-buttons" style="display: grid; grid-template-columns: 1fr 1fr; gap: 10px;"> | |
| <button id="btn-copy-report" onclick="copyAndReport()" class="action-btn-primary"> | |
| <i class="fa-solid fa-copy"></i> Draft & Report FIA | |
| </button> | |
| <button id="btn-pdf" onclick="downloadLegalPDF()" class="action-btn-success"> | |
| <i class="fa-solid fa-file-pdf"></i> Download Legal PDF | |
| </button> | |
| </div> | |
| </div> | |
| <p id="report-msg" style="margin-top: 10px; font-size: 0.8rem; color: var(--success); text-align: center; display: none;">Reported successfully! Thank you for protecting others.</p> | |
| </div> | |
| </div> | |
| </div> | |
| </main> | |
| </div> | |
| <div id="toast">Draft copied to clipboard!</div> | |
| <script> | |
| let currentTab = 'text'; | |
| function switchTab(tab) { | |
| currentTab = tab; | |
| document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active')); | |
| document.querySelectorAll('.input-section').forEach(s => s.classList.remove('active')); | |
| event.currentTarget.classList.add('active'); | |
| document.getElementById(tab + '-section').classList.add('active'); | |
| } | |
| function updateFileStatus(type) { | |
| const input = document.getElementById(type + '-file'); | |
| const status = document.getElementById(type + '-status'); | |
| if (input.files.length > 0) { | |
| status.innerText = "Selected: " + input.files[0].name; | |
| status.style.color = "#818cf8"; | |
| } | |
| } | |
| async function analyze() { | |
| const results = document.getElementById('results'); | |
| const loading = document.getElementById('loading'); | |
| const content = document.getElementById('result-content'); | |
| results.style.display = 'block'; | |
| loading.style.display = 'block'; | |
| content.style.display = 'none'; | |
| results.scrollIntoView({ behavior: 'smooth' }); | |
| const formData = new FormData(); | |
| if (currentTab === 'text') { | |
| const text = document.getElementById('text-input').value; | |
| if (!text) { alert("Please enter some text"); return; } | |
| formData.append('text_content', text); | |
| } else if (currentTab === 'image') { | |
| const file = document.getElementById('image-file').files[0]; | |
| if (!file) { alert("Please select an image"); return; } | |
| formData.append('file', file); | |
| } else if (currentTab === 'audio') { | |
| const file = document.getElementById('audio-file').files[0]; | |
| if (!file) { alert("Please select an audio file"); return; } | |
| formData.append('file', file); | |
| } | |
| try { | |
| const response = await fetch('/analyze-media', { | |
| method: 'POST', | |
| body: formData | |
| }); | |
| const data = await response.json(); | |
| displayResults(data); | |
| } catch (error) { | |
| console.error(error); | |
| alert("Error connecting to backend. Make sure api.py is running on port 8000."); | |
| } finally { | |
| loading.style.display = 'none'; | |
| } | |
| } | |
| function displayResults(data) { | |
| const content = document.getElementById('result-content'); | |
| const card = document.getElementById('result-card'); | |
| const badge = document.getElementById('status-badge'); | |
| content.style.display = 'block'; | |
| // Set styles based on scam status | |
| if (data.is_scam) { | |
| card.className = "result-card danger"; | |
| badge.className = "status-badge scam"; | |
| badge.innerText = "SCAM DETECTED"; | |
| } else { | |
| card.className = "result-card success"; | |
| badge.className = "status-badge safe"; | |
| badge.innerText = "SAFE / SECURE"; | |
| } | |
| document.getElementById('risk-level').innerText = data.risk_level; | |
| document.getElementById('source').innerText = data.source; | |
| const learnedTag = document.getElementById('learned-tag'); | |
| if (data.source.includes("Learned") || data.source.includes("Local")) { | |
| learnedTag.style.display = 'inline-flex'; | |
| } else { | |
| learnedTag.style.display = 'none'; | |
| } | |
| document.getElementById('explanation').innerText = data.agent_explanation; | |
| // Threat Meter | |
| const meter = document.getElementById('meter-fill'); | |
| if (data.risk_level === 'High' || data.risk_level === 'Critical') { | |
| meter.style.width = '100%'; | |
| meter.style.background = 'var(--danger)'; | |
| } else if (data.risk_level === 'Medium') { | |
| meter.style.width = '60%'; | |
| meter.style.background = '#f59e0b'; | |
| } else { | |
| meter.style.width = '20%'; | |
| meter.style.background = 'var(--success)'; | |
| } | |
| if (data.transcription && data.transcription !== "Image content analysis requested.") { | |
| document.getElementById('transcript-box').style.display = 'block'; | |
| document.getElementById('transcript').innerText = '"' + data.transcription + '"'; | |
| } else { | |
| document.getElementById('transcript-box').style.display = 'none'; | |
| } | |
| // Display Forensics | |
| const forensicsBox = document.getElementById('forensics-box'); | |
| const geoBox = document.getElementById('geo-box'); | |
| if (data.forensics) { | |
| forensicsBox.style.display = 'block'; | |
| geoBox.style.display = 'block'; | |
| const f = data.forensics; | |
| document.getElementById('geo-location').innerText = (f.geolocation.country || 'Unknown') + ' (' + (f.geolocation.isp || '') + ')'; | |
| let html = `<div style="display:grid; grid-template-columns: 1fr 1fr; gap:10px;"> | |
| <div>📅 Age: <b>${f.age_days} days</b></div> | |
| <div>🔒 SSL: <b>${f.ssl_info.valid ? 'Valid' : 'None'}</b></div> | |
| <div>🌍 IP: <b>${f.geolocation.ip || 'Unknown'}</b></div> | |
| <div>🛡️ VT: <b>${f.virustotal.risk || 'Unknown'}</b></div> | |
| </div>`; | |
| if (f.state_impersonation) { | |
| html += `<div style="margin-top:10px; color:var(--danger); font-weight:bold;"><i class="fa-solid fa-triangle-exclamation"></i> CRITICAL: Impersonating Government Body!</div>`; | |
| } | |
| document.getElementById('forensics-details').innerHTML = html; | |
| } else { | |
| forensicsBox.style.display = 'none'; | |
| } | |
| const stepsList = document.getElementById('next-steps'); | |
| stepsList.innerHTML = ''; | |
| data.next_steps.split('\n').forEach(step => { | |
| if (step.trim()) { | |
| const li = document.createElement('li'); | |
| li.innerText = step.replace(/^\d+\.\s*/, ''); | |
| stepsList.appendChild(li); | |
| } | |
| }); | |
| // Reset report button | |
| document.getElementById('btn-report').style.display = data.is_scam ? 'inline-block' : 'none'; | |
| document.getElementById('report-msg').style.display = 'none'; | |
| // Store current data for reporting/downloading | |
| window.currentReportText = data.transcription || document.getElementById('text-input').value; | |
| window.currentComplaint = data.fia_complaint; | |
| window.currentAgentExplanation = data.agent_explanation; | |
| window.currentScamType = data.category || (data.is_scam ? "Cyber Fraud" : "General Inquiry"); | |
| window.currentEvidence = `Forensics: ${data.source}\nRisk: ${data.risk_level}\nExplanation: ${data.agent_explanation}`; | |
| if (data.forensics) { | |
| window.currentEvidence += `\nLink Details: Age ${data.forensics.age_days} days, SSL ${data.forensics.ssl_info.valid}`; | |
| } | |
| // Clear chat | |
| document.getElementById('chat-history').innerHTML = ''; | |
| } | |
| async function sendChat() { | |
| const input = document.getElementById('chat-input'); | |
| const history = document.getElementById('chat-history'); | |
| const question = input.value; | |
| if (!question) return; | |
| history.innerHTML += `<div style="margin-bottom:10px; color:var(--primary);"><b>Aap:</b> ${question}</div>`; | |
| input.value = ''; | |
| const formData = new FormData(); | |
| formData.append('question', question); | |
| formData.append('context', window.currentAgentExplanation); | |
| try { | |
| const response = await fetch('/chat', { | |
| method: 'POST', | |
| body: formData | |
| }); | |
| const data = await response.json(); | |
| history.innerHTML += `<div style="margin-bottom:15px; background:rgba(255,255,255,0.05); padding:10px; border-radius:8px;"><b>Expert:</b> ${data.response}</div>`; | |
| history.scrollTop = history.scrollHeight; | |
| } catch (error) { | |
| console.error(error); | |
| history.innerHTML += `<div style="color:var(--danger);">Error connecting to expert.</div>`; | |
| } | |
| } | |
| async function reportScam() { | |
| const btn = document.getElementById('btn-report'); | |
| const msg = document.getElementById('report-msg'); | |
| btn.innerText = "Reporting..."; | |
| btn.disabled = true; | |
| const formData = new FormData(); | |
| formData.append('text_content', window.currentReportText); | |
| try { | |
| await fetch('/report-scam', { | |
| method: 'POST', | |
| body: formData | |
| }); | |
| btn.style.display = 'none'; | |
| msg.style.display = 'block'; | |
| } catch (error) { | |
| console.error(error); | |
| alert("Error reporting scam."); | |
| btn.innerText = "Report Scam"; | |
| btn.disabled = false; | |
| } | |
| } | |
| function showToast(msg) { | |
| const toast = document.getElementById('toast'); | |
| toast.innerText = msg; | |
| toast.style.display = 'block'; | |
| setTimeout(() => { toast.style.display = 'none'; }, 3000); | |
| } | |
| async function copyAndReport() { | |
| if (!window.currentComplaint) return; | |
| try { | |
| await navigator.clipboard.writeText(window.currentComplaint); | |
| showToast("Legal draft copied! Opening FIA Portal..."); | |
| setTimeout(() => { | |
| window.open('https://complaint.fia.gov.pk/', '_blank'); | |
| }, 1500); | |
| } catch (err) { | |
| console.error('Failed to copy: ', err); | |
| } | |
| } | |
| async function downloadLegalPDF() { | |
| if (!window.currentComplaint) return; | |
| const btn = document.getElementById('btn-pdf'); | |
| const originalHtml = btn.innerHTML; | |
| btn.innerHTML = '<i class="fa-solid fa-spinner fa-spin"></i> Generating...'; | |
| btn.disabled = true; | |
| const formData = new FormData(); | |
| formData.append('complaint_text', window.currentComplaint); | |
| formData.append('scam_type', window.currentScamType); | |
| formData.append('evidence', window.currentEvidence); | |
| try { | |
| const response = await fetch('/generate-pdf', { | |
| method: 'POST', | |
| body: formData | |
| }); | |
| if (!response.ok) throw new Error("PDF generation failed"); | |
| const blob = await response.blob(); | |
| const url = window.URL.createObjectURL(blob); | |
| const a = document.createElement('a'); | |
| a.href = url; | |
| a.download = `FIA_Complaint_${new Date().getTime()}.pdf`; | |
| document.body.appendChild(a); | |
| a.click(); | |
| window.URL.revokeObjectURL(url); | |
| showToast("PDF Downloaded Successfully!"); | |
| } catch (error) { | |
| console.error(error); | |
| alert("Error generating PDF."); | |
| } finally { | |
| btn.innerHTML = originalHtml; | |
| btn.disabled = false; | |
| } | |
| } | |
| function downloadComplaint() { | |
| // Deprecated in favor of PDF | |
| downloadLegalPDF(); | |
| } | |
| </script> | |
| </body> | |
| </html> | |