Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width,initial-scale=1.0"> | |
| <title>Enhanced ID Generator Tool</title> | |
| <!-- Tailwind CSS --> | |
| <link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet"> | |
| <!-- Font Awesome --> | |
| <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.5.2/css/all.min.css"> | |
| <!-- Google Fonts --> | |
| <link href="https://cdn.jsdelivr.net/npm/@fontsource/roboto@3.3.1/400.css" rel="stylesheet"> | |
| <link href="https://cdn.jsdelivr.net/npm/@fontsource/roboto@3.3.1/700.css" rel="stylesheet"> | |
| <!-- Chart.js for visualizations --> | |
| <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.3/dist/chart.umd.min.js"></script> | |
| <style> | |
| body { | |
| font-family: 'Roboto', Arial, sans-serif; | |
| } | |
| .font-logo { | |
| font-family: 'Roboto', Arial, sans-serif; | |
| font-weight:700; | |
| letter-spacing:1px; | |
| } | |
| .bg-gradient { | |
| background: linear-gradient(90deg, #007cf0 0%, #00dfd8 100%); | |
| } | |
| .fade-in { | |
| animation: fadeIn 1s ease; | |
| } | |
| @keyframes fadeIn { from{opacity:0} to{opacity:1} } | |
| </style> | |
| </head> | |
| <body class="bg-gray-100"> | |
| <header class="bg-gradient p-6 mb-8 shadow-lg"> | |
| <div class="max-w-6xl mx-auto flex flex-col sm:flex-row justify-between items-center"> | |
| <h1 class="text-3xl font-logo text-white flex items-center"><i class="fa-solid fa-id-card text-white mr-3"></i>Enhanced ID Generator Tool</h1> | |
| <div class="text-white mt-4 sm:mt-0 space-x-4"> | |
| <button id="adminPanelBtn" class="hover:underline">Admin Panel</button> | |
| <button id="marketplaceBtn" class="hover:underline">Marketplace</button> | |
| </div> | |
| </div> | |
| </header> | |
| <main class="max-w-6xl mx-auto px-4 pb-16 fade-in"> | |
| <!-- Card Generation Section --> | |
| <section class="mb-10 bg-white rounded-lg shadow p-6"> | |
| <h2 class="text-2xl font-semibold mb-4 flex items-center"><i class="fa-solid fa-credit-card mr-3 text-blue-500"></i>Card Generator</h2> | |
| <form id="cardGenForm" class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4"> | |
| <div> | |
| <label class="font-semibold">Card Holder Name</label> | |
| <input type="text" id="holderName" class="mt-1 w-full p-2 border rounded" placeholder="e.g. Sam Joe" required> | |
| </div> | |
| <div> | |
| <label class="font-semibold">Bank Name (auto/manual)</label> | |
| <div class="flex space-x-2"> | |
| <input type="text" id="bankName" class="mt-1 w-full p-2 border rounded" placeholder="e.g. Chase Bank"> | |
| <button type="button" class="bg-blue-500 text-white rounded px-2" id="autoBankBtn" title="Generate Random Bank"><i class="fas fa-dice"></i></button> | |
| </div> | |
| </div> | |
| <div> | |
| <label class="font-semibold">BIN (optional)</label> | |
| <input type="text" id="bin" class="mt-1 w-full p-2 border rounded" placeholder="e.g. 411111"> | |
| </div> | |
| <div> | |
| <label class="font-semibold">Number to Generate</label> | |
| <input type="number" min="1" max="100" value="1" id="cardCount" class="mt-1 w-full p-2 border rounded"> | |
| </div> | |
| <div> | |
| <label class="font-semibold">Email Domain (optional)</label> | |
| <input type="text" id="emailDomain" class="mt-1 w-full p-2 border rounded" placeholder="e.g. gmail.com"> | |
| </div> | |
| </form> | |
| <button id="generateBtn" | |
| class="mt-6 bg-blue-600 hover:bg-blue-700 text-white font-bold py-2 px-6 rounded shadow flex items-center"><i class="fa-solid fa-bolt mr-2"></i>Generate</button> | |
| <p id="genError" class="text-red-500 mt-2 font-semibold hidden"></p> | |
| </section> | |
| <!-- Generated Cards Table --> | |
| <section class="mb-10 bg-white rounded-lg shadow p-6"> | |
| <h2 class="text-2xl font-semibold mb-4 flex items-center"><i class="fa-solid fa-table mr-3 text-teal-600"></i>Generated Cards History</h2> | |
| <div class="overflow-auto"> | |
| <table class="min-w-full table-auto border"> | |
| <thead class="bg-gray-100 text-gray-700"> | |
| <tr> | |
| <th class="px-3 py-2 border">#</th> | |
| <th class="px-3 py-2 border">Card Holder</th> | |
| <th class="px-3 py-2 border">Number</th> | |
| <th class="px-3 py-2 border">MM/YY</th> | |
| <th class="px-3 py-2 border">CVV</th> | |
| <th class="px-3 py-2 border">Bank</th> | |
| <th class="px-3 py-2 border">BIN</th> | |
| <th class="px-3 py-2 border">Email</th> | |
| <th class="px-3 py-2 border">IP</th> | |
| <th class="px-3 py-2 border">Fraud</th> | |
| <th class="px-3 py-2 border">Status</th> | |
| </tr> | |
| </thead> | |
| <tbody id="cardsTableBody" class="bg-white"> | |
| <tr> | |
| <td colspan="11" class="text-center py-4 text-gray-400">No generated cards yet.</td> | |
| </tr> | |
| </tbody> | |
| </table> | |
| </div> | |
| </section> | |
| <!-- Card Validator & Simulators --> | |
| <section class="mb-10 grid grid-cols-1 lg:grid-cols-3 gap-6"> | |
| <!-- Card Validator --> | |
| <div class="bg-white rounded-lg shadow p-6"> | |
| <h2 class="text-xl font-semibold mb-4">Card Validator <i class="fa-solid fa-shield-check ml-2 text-green-600"></i></h2> | |
| <form id="cardValForm" class="flex flex-col space-y-2"> | |
| <input type="text" id="validateCardNum" class="p-2 border rounded" placeholder="Enter card number"> | |
| <button type="submit" class="bg-teal-600 hover:bg-teal-700 text-white px-4 py-2 rounded mt-1">Validate</button> | |
| </form> | |
| <div id="cardValResult" class="mt-2 text-sm font-semibold"></div> | |
| </div> | |
| <!-- Fraud Score Simulator --> | |
| <div class="bg-white rounded-lg shadow p-6"> | |
| <h2 class="text-xl font-semibold mb-4">Fraud Score Simulator <i class="fa-solid fa-fire text-red-600 ml-1"></i></h2> | |
| <canvas id="fraudChart" width="120" height="80"></canvas> | |
| <div id="fraudLegend" class="mt-3 text-gray-600"></div> | |
| </div> | |
| <!-- Email Analyzer Simulator --> | |
| <div class="bg-white rounded-lg shadow p-6"> | |
| <h2 class="text-xl font-semibold mb-4">Email Analyzer <i class="fa-solid fa-envelope text-blue-500 ml-1"></i></h2> | |
| <input type="text" id="analyzeEmail" class="p-2 border rounded w-full" placeholder="Enter email"> | |
| <button id="analyzeBtn" class="bg-indigo-600 hover:bg-indigo-700 text-white mt-3 px-4 py-2 rounded w-full">Analyze</button> | |
| <div id="emailAnalyzerResult" class="mt-2 text-gray-700"></div> | |
| </div> | |
| </section> | |
| <!-- SSN Tools --> | |
| <section class="mb-10 bg-white rounded-lg shadow p-6"> | |
| <h2 class="text-2xl font-semibold mb-4 flex items-center"><i class="fa-solid fa-user-shield text-pink-600 mr-3"></i>SSN Tools (Fake Data)</h2> | |
| <div class="grid grid-cols-1 sm:grid-cols-2 gap-4"> | |
| <div> | |
| <p class="font-bold mb-1">Fake SSN Generator</p> | |
| <button id="generateFakeSSN" class="bg-pink-500 hover:bg-pink-600 text-white px-4 py-2 rounded">Generate</button> | |
| <div id="fakeSSNResult" class="mt-2 text-lg font-mono"></div> | |
| </div> | |
| <div> | |
| <form id="ssnValForm"> | |
| <p class="font-bold mb-1">SSN Validator (Basic)</p> | |
| <input type="text" id="validateSSN" class="w-full p-2 border rounded" placeholder="Enter SSN"> | |
| <button type="submit" class="bg-gray-700 text-white px-4 py-2 mt-2 rounded">Validate</button> | |
| <div id="ssnValResult" class="mt-2 text-base"></div> | |
| </form> | |
| </div> | |
| </div> | |
| <p class="text-xs text-gray-500 mt-4">Generated SSNs are <b>fake</b>, follow known allocation patterns, but are not real. Validation is basic format/rule check only.</p> | |
| </section> | |
| <!-- Marketplace Section --> | |
| <section id="marketplaceSection" class="hidden mb-10 bg-white rounded-lg shadow p-6"> | |
| <h2 class="text-2xl font-semibold mb-4 flex items-center"><i class="fa-solid fa-store text-yellow-500 mr-3"></i>Generated Data Marketplace</h2> | |
| <div class="overflow-x-auto"> | |
| <table class="min-w-full table-auto border"> | |
| <thead class="bg-yellow-100 text-yellow-900"> | |
| <tr> | |
| <th class="px-3 py-2 border">#</th> | |
| <th class="px-3 py-2 border">Type</th> | |
| <th class="px-3 py-2 border">Data Preview</th> | |
| <th class="px-3 py-2 border">Source</th> | |
| <th class="px-3 py-2 border">Date</th> | |
| <th class="px-3 py-2 border">Action</th> | |
| </tr> | |
| </thead> | |
| <tbody id="marketplaceBody"> | |
| <tr> | |
| <td colspan="6" class="text-center text-gray-400 py-4">No public data yet. Generate and publish to marketplace.</td> | |
| </tr> | |
| </tbody> | |
| </table> | |
| </div> | |
| </section> | |
| <!-- Admin Panel Section --> | |
| <section id="adminPanelSection" class="hidden mb-10 bg-white rounded-lg shadow p-6"> | |
| <h2 class="text-2xl font-semibold mb-4 flex items-center"><i class="fa-solid fa-user-gear text-purple-600 mr-3"></i>Admin Panel</h2> | |
| <div> | |
| <h3 class="font-bold text-lg mb-1 flex items-center"><i class="fa-solid fa-file-upload mr-2"></i>Upload Bank Name Data</h3> | |
| <form id="bankUploadForm" class="flex items-center space-x-2 mb-4"> | |
| <input type="file" id="bankFileInput" accept=".txt,.csv" class="border px-3 py-2 rounded w-full"> | |
| <button type="submit" class="bg-purple-600 hover:bg-purple-700 text-white px-4 py-2 rounded">Upload</button> | |
| </form> | |
| <div id="bankUploadStatus" class="text-sm"></div> | |
| </div> | |
| <hr class="my-4"> | |
| <div> | |
| <h3 class="font-bold text-lg mb-3 flex items-center"><i class="fa-solid fa-list mr-2"></i>Stored Bank Names</h3> | |
| <ul id="bankList" class="list-disc pl-5 text-gray-700 mb-3"></ul> | |
| <button id="clearBankList" class="bg-red-500 hover:bg-red-600 text-white px-3 py-1 rounded">Clear All</button> | |
| </div> | |
| </section> | |
| </main> | |
| <footer class="text-center text-sm text-gray-400 p-6"> | |
| © 2024 Enhanced ID Generator Tool. Generated data for demo/testing purposes only. | |
| </footer> | |
| <!-- JavaScript Section --> | |
| <script> | |
| // GLOBALS | |
| const storedBankNamesKey = 'enhanced_id_gen_tool_banks'; | |
| const cardHistoryKey = 'enhanced_id_gen_tool_cards'; | |
| const marketplaceKey = 'enhanced_id_gen_tool_marketplace'; | |
| // Utilities | |
| function saveLocalArr(key, arr) { localStorage.setItem(key, JSON.stringify(arr)); } | |
| function loadLocalArr(key, _default=[]) { | |
| try { return JSON.parse(localStorage.getItem(key)||'') || _default; } catch{ return _default; } | |
| } | |
| // Email Generation Utility | |
| function generateRealisticEmail(name, domain, usedEmailsSet) { | |
| // Remove spaces, special chars, make lowercase. | |
| let base = name.replace(/[^a-zA-Z0-9]/g,'').toLowerCase(); | |
| if(!base) base = 'user'; | |
| const domains = domain && domain.trim() ? [domain.trim()] : ['gmail.com','yahoo.com','outlook.com','mail.com']; | |
| let email, counter=0; | |
| do { | |
| const domainIdx = counter % domains.length; | |
| const suffix = counter===0 ? '' : counter+1; | |
| email = `${base}${suffix}@${domains[domainIdx]}`; | |
| counter++; | |
| } while(usedEmailsSet.has(email)); | |
| usedEmailsSet.add(email); | |
| return email; | |
| } | |
| // Generate Random Bank Name | |
| function randomBank(bankList) { | |
| if(!bankList.length) { | |
| return ['Chase Bank','Bank of America','Wells Fargo','CitiBank','Capital One'][Math.floor(Math.random()*5)]; | |
| } | |
| return bankList[Math.floor(Math.random()*bankList.length)]; | |
| } | |
| // Generate Card Number (Simple Luhn compliant number) | |
| function generateCardNumber(bin='411111', length=16) { | |
| let num = bin.padEnd(length-1, '0'); | |
| while(num.length < length-1) num += Math.floor(Math.random()*10); | |
| // Luhn checksum | |
| let arr = num.split('').reverse().map(x=>parseInt(x)); | |
| let sum = 0; | |
| for(let i=0;i<arr.length;i++) sum += i%2==1 ? (((arr[i]*2)>9)?((arr[i]*2)-9):(arr[i]*2)) : arr[i]; | |
| let check = (10 - (sum % 10)) % 10; | |
| return num + check; | |
| } | |
| // Generate Random MM/YY | |
| function randomExpiry() { | |
| const mm = (Math.floor(Math.random()*12)+1).toString().padStart(2,'0'); | |
| const yy = (24 + Math.floor(Math.random() * 7)).toString().padStart(2,'0'); | |
| return `${mm}/${yy}`; | |
| } | |
| // Generate Random CVV | |
| function randomCVV() { | |
| return (100+Math.floor(Math.random()*900)).toString(); | |
| } | |
| // Generate Random IP | |
| function randomIP() { | |
| return [1,2,3,4].map(_=>Math.floor(Math.random()*255)).join('.'); | |
| } | |
| // Fraud Score [Simulated] | |
| function simulatedFraudScore() { | |
| return Math.ceil((Math.random()*99)+1) | |
| } | |
| // Marketplace Data Entry Preview | |
| function marketplacePreviewEntry(entry) { | |
| if(entry.type==='card') { | |
| let d = entry.data[0]; | |
| return `<div class="text-xs font-mono">${d.number} | ${d.expiry} | ${d.cvv} | ${d.cardHolder}</div>`; | |
| } | |
| if(entry.type==='ssn') { | |
| return `<span class="font-mono text-xs">${entry.data.ssn}</span>`; | |
| } | |
| return '<span>Unknown Data Type</span>'; | |
| } | |
| // Init BANKS | |
| function loadBanks() { | |
| return loadLocalArr(storedBankNamesKey,[]); | |
| } | |
| function saveBanks(arr) { | |
| saveLocalArr(storedBankNamesKey,arr); | |
| renderBankList(); | |
| } | |
| function renderBankList() { | |
| const banks = loadBanks(); | |
| const ul = document.getElementById('bankList'); | |
| ul.innerHTML = ''; | |
| if(banks.length) { | |
| banks.forEach(name=>{ | |
| let li = document.createElement('li'); | |
| li.textContent = name; | |
| ul.appendChild(li); | |
| }); | |
| } else { | |
| ul.innerHTML = `<li class="text-gray-400">No banks stored</li>`; | |
| } | |
| } | |
| document.getElementById('clearBankList').onclick = () => { | |
| saveBanks([]); | |
| }; | |
| // Handle Bank Upload | |
| document.getElementById('bankUploadForm').onsubmit = (e)=>{ | |
| e.preventDefault(); | |
| const fileInput = document.getElementById('bankFileInput'); | |
| const stat = document.getElementById('bankUploadStatus'); | |
| if(fileInput.files.length===0) { stat.textContent='No file selected.'; return; } | |
| const reader = new FileReader(); | |
| reader.onload = (event)=>{ | |
| let lines = event.target.result.split(/\r?\n/).map(l=>l.trim()).filter(Boolean); | |
| let prev = loadBanks(); | |
| let combined = Array.from(new Set([...prev,...lines])); | |
| saveBanks(combined); | |
| stat.textContent = `Added ${lines.length} banks (${combined.length} total).`; | |
| }; | |
| reader.onerror = ()=>{ stat.textContent="Failed to read file."; }; | |
| reader.readAsText(fileInput.files[0]); | |
| }; | |
| // Auto Bank Button | |
| document.getElementById('autoBankBtn').onclick = ()=>{ | |
| const banks = loadBanks(); | |
| document.getElementById('bankName').value = randomBank(banks); | |
| }; | |
| // Card Generation Handler | |
| document.getElementById('generateBtn').onclick = ()=>{ | |
| const nameEl = document.getElementById('holderName'); | |
| const bankEl = document.getElementById('bankName'); | |
| const binEl = document.getElementById('bin'); | |
| const numEl = document.getElementById('cardCount'); | |
| const emailDomEl = document.getElementById('emailDomain'); | |
| const errorEl = document.getElementById('genError'); | |
| errorEl.classList.add('hidden'); | |
| // Validation | |
| const name = nameEl.value.trim(); | |
| let bank = bankEl.value.trim(); | |
| let bin = binEl.value.trim()||'411111'; | |
| let count = Math.min(Math.max(parseInt(numEl.value)||1,1),100); // 1-100 only | |
| const emailDomain = emailDomEl.value.trim(); | |
| if(!name) { | |
| errorEl.textContent = 'Card holder name required.'; errorEl.classList.remove('hidden'); return; | |
| } | |
| if(isNaN(count)) count=1; | |
| // Try auto bank if blank | |
| if(!bank) { | |
| const banks = loadBanks(); | |
| bank = randomBank(banks); | |
| } | |
| const cards = loadLocalArr(cardHistoryKey, []); | |
| const emails = new Set(cards.map(c=>c.email)); | |
| const res = []; | |
| for(let i=0;i<count;i++) { | |
| const number = generateCardNumber(bin); | |
| const expiry = randomExpiry(); | |
| const cvv = randomCVV(); | |
| const ip = randomIP(); | |
| const email = generateRealisticEmail(name, emailDomain, emails); | |
| const fraud = simulatedFraudScore(); | |
| res.push({ | |
| number, expiry, cvv, cardHolder:name, bank, bin, email, ip, fraud, status:'Valid' | |
| }); | |
| } | |
| // Add to card history | |
| const updated = [...cards, ...res]; | |
| saveLocalArr(cardHistoryKey, updated); | |
| renderCardHistory(); | |
| // Show a success | |
| errorEl.classList.remove('hidden'); | |
| errorEl.classList.remove('text-red-500'); | |
| errorEl.classList.add('text-green-600'); | |
| errorEl.textContent = `✅ Generated ${res.length} cards!`; | |
| setTimeout(()=>{ errorEl.textContent=''; errorEl.className='hidden'}, 2000); | |
| }; | |
| function renderCardHistory() { | |
| const cards = loadLocalArr(cardHistoryKey, []); | |
| const tb = document.getElementById('cardsTableBody'); | |
| tb.innerHTML = ''; | |
| if(cards.length) { | |
| cards.slice(-100).reverse().forEach((c, i)=>{ | |
| let tr = document.createElement('tr'); | |
| tr.innerHTML = ` | |
| <td class="border px-2 py-1 text-xs">${cards.length-i}</td> | |
| <td class="border px-2 py-1 text-xs">${c.cardHolder}</td> | |
| <td class="border px-2 py-1 font-mono text-xs">${c.number}</td> | |
| <td class="border px-2 py-1 font-mono text-xs">${c.expiry}</td> | |
| <td class="border px-2 py-1 font-mono text-xs">${c.cvv}</td> | |
| <td class="border px-2 py-1 text-xs">${c.bank}</td> | |
| <td class="border px-2 py-1 font-mono text-xs">${c.bin}</td> | |
| <td class="border px-2 py-1 font-mono text-xs">${c.email}</td> | |
| <td class="border px-2 py-1 text-xs">${c.ip}</td> | |
| <td class="border px-2 py-1 text-center">${c.fraud}%</td> | |
| <td class="border px-2 py-1 text-green-600 font-bold text-xs">${c.status}</td>`; | |
| tb.appendChild(tr); | |
| }); | |
| } else { | |
| tb.innerHTML = `<tr> | |
| <td colspan="11" class="text-center py-4 text-gray-400">No generated cards yet.</td> | |
| </tr>`; | |
| } | |
| } | |
| renderCardHistory(); | |
| // --- Card Validator --- | |
| document.getElementById('cardValForm').onsubmit = (e)=>{ | |
| e.preventDefault(); | |
| const valNum = document.getElementById('validateCardNum').value.replace(/[^\d]/g,''); | |
| function luhnCheck(num) { | |
| let arr = num.split('').reverse().map(x=>parseInt(x)); | |
| let sum = 0; | |
| for(let i=0;i<arr.length;i++) sum += i%2==1 ? (((arr[i]*2)>9)?((arr[i]*2)-9):(arr[i]*2)) : arr[i]; | |
| return sum%10===0; | |
| } | |
| let text; | |
| if(valNum.length<10) text = "<span class='text-red-600'>Invalid number.</span>"; | |
| else if(luhnCheck(valNum)) text="<span class='text-green-700'>Pass Luhn validation.</span>"; | |
| else text="<span class='text-yellow-700'>Fail Luhn, possible fake.</span>"; | |
| document.getElementById('cardValResult').innerHTML = text; | |
| }; | |
| // --- Email Analyzer Simulator --- | |
| document.getElementById('analyzeBtn').onclick=()=>{ | |
| let email = document.getElementById('analyzeEmail').value.trim(); | |
| const res = document.getElementById('emailAnalyzerResult'); | |
| if(!email.match(/@/)) { | |
| res.innerHTML = "<span class='text-red-600'>Invalid email format.</span>"; return; | |
| } | |
| let domain=email.split('@')[1]; | |
| let suspected = ['ru','cn','random','test'].some(x=>domain.includes(x)); | |
| if(suspected) res.innerHTML = `<span class='text-yellow-600'>Domain is considered non-standard.</span>`; | |
| else res.innerHTML = `<span class='text-green-700'>Email appears normal.</span>`; | |
| }; | |
| // --- SSN Generator --- | |
| function generateFakeSSN() { | |
| // 3-2-4, but not using real area for demo | |
| let a = Math.floor(Math.random()*900)+100; | |
| let b = Math.floor(Math.random()*90)+10; | |
| let c = Math.floor(Math.random()*9000)+1000; | |
| return `${a}-${b}-${c}`; | |
| } | |
| document.getElementById('generateFakeSSN').onclick = ()=>{ | |
| document.getElementById('fakeSSNResult').textContent = generateFakeSSN(); | |
| }; | |
| // --- SSN Validator (basic US) --- | |
| document.getElementById('ssnValForm').onsubmit = (e)=>{ | |
| e.preventDefault(); | |
| let ssn = document.getElementById('validateSSN').value.trim(); | |
| let res = document.getElementById('ssnValResult'); | |
| if(!ssn.match(/^\d{3}-\d{2}-\d{4}$/)) { | |
| res.innerHTML = `<span class='text-red-600'>Invalid SSN format.</span>`; | |
| } else { | |
| let [a,b,c]=ssn.split('-'); | |
| if(a==='000'||b==='00'||c==='0000') res.innerHTML = `<span class='text-yellow-600'>Suspicious or invalid blocks.</span>`; | |
| else res.innerHTML = `<span class='text-green-600'>Format OK (note: fake data only).</span>`; | |
| } | |
| }; | |
| // --- Fraud Chart (simulate) --- | |
| let fraudChart; | |
| function renderFraudChart() { | |
| const ctx = document.getElementById('fraudChart').getContext('2d'); | |
| let value = 15+Math.floor(Math.random()*70); | |
| if(fraudChart) fraudChart.destroy(); | |
| fraudChart = new Chart(ctx, { | |
| type:'doughnut', | |
| data: { | |
| labels:['Fraud','Safe'], | |
| datasets:[{ | |
| data: [value,100-value], | |
| backgroundColor: ['#f87171','#10b981'], | |
| }] | |
| }, | |
| options: { | |
| responsive:false, | |
| cutout:'80%', | |
| plugins:{legend:{display:false}} | |
| } | |
| }); | |
| document.getElementById('fraudLegend').innerHTML = `<span class='font-bold text-red-600'>${value}%</span> fraud score (simulated)`; | |
| } | |
| renderFraudChart(); | |
| // --- Marketplace --- | |
| function renderMarketplace() { | |
| const mkt = loadLocalArr(marketplaceKey,[]); | |
| const tbody = document.getElementById('marketplaceBody'); | |
| tbody.innerHTML = ''; | |
| if(mkt.length) { | |
| mkt.forEach((entry,i)=>{ | |
| let tr = document.createElement('tr'); | |
| tr.innerHTML = ` | |
| <td class="border px-2 py-1">${i+1}</td> | |
| <td class="border px-2 py-1 text-xs">${entry.type}</td> | |
| <td class="border px-2 py-1">${marketplacePreviewEntry(entry)}</td> | |
| <td class="border px-2 py-1">${entry.source||'-'}</td> | |
| <td class="border px-2 py-1">${entry.date||'-'}</td> | |
| <td class="border px-2 py-1"><button class="bg-green-600 text-white px-2 py-1 rounded text-xs" onclick="alert('Sample download\\n' + JSON.stringify(entry.data,null,2))"><i class="fa-solid fa-download"></i></button></td> | |
| `; | |
| tbody.appendChild(tr); | |
| }); | |
| } else { | |
| tbody.innerHTML = `<tr> | |
| <td colspan="6" class="text-center text-gray-400 py-4">No public data yet. Generate and publish to marketplace.</td> | |
| </tr>`; | |
| } | |
| } | |
| renderMarketplace(); | |
| // Show/Hide Sections | |
| function hideAllSections() { | |
| document.getElementById('marketplaceSection').classList.add('hidden'); | |
| document.getElementById('adminPanelSection').classList.add('hidden'); | |
| } | |
| document.getElementById('adminPanelBtn').onclick=()=>{ | |
| hideAllSections(); | |
| document.getElementById('adminPanelSection').classList.remove('hidden'); | |
| }; | |
| document.getElementById('marketplaceBtn').onclick=()=>{ | |
| hideAllSections(); | |
| document.getElementById('marketplaceSection').classList.remove('hidden'); | |
| }; | |
| // Save/Pub to marketplace (add last 5) | |
| function publishLatestCardsToMarketplace() { | |
| const cards = loadLocalArr(cardHistoryKey, []); | |
| if(!cards.length) { alert('No generated cards.'); return; } | |
| const mkt = loadLocalArr(marketplaceKey,[]); | |
| let entry = { | |
| type:'card', | |
| data: cards.slice(-5), // Preview last 5 | |
| source: 'LocalGen', | |
| date: new Date().toLocaleString() | |
| }; | |
| mkt.push(entry); | |
| saveLocalArr(marketplaceKey, mkt); | |
| renderMarketplace(); | |
| alert("Published 5 latest cards to marketplace!"); | |
| } | |
| // Enable Publish Button if there are cards | |
| let genSection = document.querySelector('section'); | |
| let pubBtn = document.createElement('button'); | |
| pubBtn.textContent = 'Publish Last 5 to Marketplace'; | |
| pubBtn.className = 'mt-6 ml-3 bg-yellow-500 hover:bg-yellow-600 text-white font-bold py-2 px-4 rounded shadow'; | |
| pubBtn.onclick = publishLatestCardsToMarketplace; | |
| genSection.appendChild(pubBtn); | |
| // On load, show card table & admins, render | |
| renderBankList(); | |
| // Optional: Expose publish function for demo | |
| window.publishLatestCardsToMarketplace = publishLatestCardsToMarketplace; | |
| </script> | |
| </body> | |
| </html> | |