Add built-in dashboard demo samples
Browse files- index.html +87 -27
index.html
CHANGED
|
@@ -151,6 +151,34 @@
|
|
| 151 |
background: rgba(255, 255, 255, 0.01);
|
| 152 |
}
|
| 153 |
.drop-zone:hover { border-color: var(--accent); background: rgba(0, 255, 170, 0.03); transform: scale(0.99); }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 154 |
|
| 155 |
.terminal {
|
| 156 |
background: rgba(0, 0, 0, 0.4);
|
|
@@ -363,6 +391,11 @@
|
|
| 363 |
<div class="meta-item"><span class="label" style="margin-bottom:0; font-size: 8px;" data-i18n="meta_issuer">Issuer</span><div class="meta-val" id="m-issuer">--</div></div>
|
| 364 |
<div class="meta-item"><span class="label" style="margin-bottom:0; font-size: 8px;" data-i18n="meta_status">Status</span><div class="meta-val" id="m-status">--</div></div>
|
| 365 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 366 |
</div>
|
| 367 |
</div>
|
| 368 |
<div class="modal" id="modal"><div class="report" id="rep-body"></div></div>
|
|
@@ -427,34 +460,47 @@
|
|
| 427 |
cons.appendChild(d); cons.scrollTop = cons.scrollHeight;
|
| 428 |
}
|
| 429 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 430 |
document.getElementById('f-input').onchange = (e) => {
|
| 431 |
if(e.target.files.length) {
|
| 432 |
file = e.target.files[0];
|
| 433 |
-
|
| 434 |
-
dropZone.replaceChildren();
|
| 435 |
-
|
| 436 |
-
const check = document.createElement('div');
|
| 437 |
-
check.style.color = 'var(--accent)';
|
| 438 |
-
check.innerText = '✓';
|
| 439 |
-
dropZone.appendChild(check);
|
| 440 |
-
|
| 441 |
-
const name = document.createElement('p');
|
| 442 |
-
name.style.fontSize = '10px';
|
| 443 |
-
name.innerText = file.name;
|
| 444 |
-
dropZone.appendChild(name);
|
| 445 |
-
|
| 446 |
-
const note = document.createElement('p');
|
| 447 |
-
note.style.fontSize = '9px';
|
| 448 |
-
note.style.color = '#ffcc66';
|
| 449 |
-
note.style.marginTop = '8px';
|
| 450 |
-
note.innerText = 'Demo intake only; no browser-side approval will be issued.';
|
| 451 |
-
dropZone.appendChild(note);
|
| 452 |
-
|
| 453 |
log(`INGESTED: ${file.name}`);
|
| 454 |
}
|
| 455 |
};
|
| 456 |
document.getElementById('drop-zone').onclick = () => document.getElementById('f-input').click();
|
| 457 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 458 |
btn.onclick = async () => {
|
| 459 |
if(!file) {
|
| 460 |
log("SYSTEM: Select a PDF, PNG, or JPEG credential before starting the demo audit.");
|
|
@@ -484,6 +530,9 @@
|
|
| 484 |
|
| 485 |
function inferDemoOutcome(uploadedFile) {
|
| 486 |
const name = uploadedFile.name.toLowerCase();
|
|
|
|
|
|
|
|
|
|
| 487 |
const demoBlacklist = [
|
| 488 |
{ pattern: 'graham', issuer: 'Graham International University' },
|
| 489 |
{ pattern: 'giu', issuer: 'Graham International University' },
|
|
@@ -493,7 +542,11 @@
|
|
| 493 |
{ pattern: 'belford', issuer: 'Belford University' },
|
| 494 |
{ pattern: 'almeda', issuer: 'Almeda University' }
|
| 495 |
];
|
| 496 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 497 |
}
|
| 498 |
|
| 499 |
async function finish() {
|
|
@@ -513,11 +566,18 @@
|
|
| 513 |
log('SYSTEM: REJECTED - Unsupported file type for audit intake.');
|
| 514 |
} else if (inferDemoOutcome(file)) {
|
| 515 |
const fixture = inferDemoOutcome(file);
|
| 516 |
-
verdict = 'REJECTED';
|
| 517 |
-
status_code = 'DEMO_BLACKLIST_MATCH';
|
| 518 |
issuer = fixture.issuer;
|
| 519 |
-
|
| 520 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 521 |
} else {
|
| 522 |
log('SYSTEM: DEMO ONLY - File content was not sent to a trusted audit service.');
|
| 523 |
log('SYSTEM: NEEDS REVIEW - No browser-side approval is issued.');
|
|
@@ -530,8 +590,8 @@
|
|
| 530 |
document.getElementById('m-issuer').innerText = issuer;
|
| 531 |
document.getElementById('m-status').innerText = status_code;
|
| 532 |
|
| 533 |
-
const verdictTitle = verdict === 'REJECTED' ? t.verdict_no : 'SERVER AUDIT REQUIRED';
|
| 534 |
-
const verdictColor = verdict === 'REJECTED' ? '#ff3366' : '#ffcc66';
|
| 535 |
|
| 536 |
const report = document.getElementById('rep-body');
|
| 537 |
report.replaceChildren();
|
|
|
|
| 151 |
background: rgba(255, 255, 255, 0.01);
|
| 152 |
}
|
| 153 |
.drop-zone:hover { border-color: var(--accent); background: rgba(0, 255, 170, 0.03); transform: scale(0.99); }
|
| 154 |
+
|
| 155 |
+
.sample-grid {
|
| 156 |
+
display: grid;
|
| 157 |
+
grid-template-columns: repeat(3, 1fr);
|
| 158 |
+
gap: 8px;
|
| 159 |
+
margin-top: 16px;
|
| 160 |
+
width: 100%;
|
| 161 |
+
}
|
| 162 |
+
|
| 163 |
+
.sample-btn {
|
| 164 |
+
border: 1px solid var(--border);
|
| 165 |
+
background: rgba(255, 255, 255, 0.04);
|
| 166 |
+
color: var(--text);
|
| 167 |
+
border-radius: 8px;
|
| 168 |
+
padding: 10px 8px;
|
| 169 |
+
font-size: 10px;
|
| 170 |
+
font-weight: 700;
|
| 171 |
+
font-family: 'Outfit';
|
| 172 |
+
letter-spacing: 0.5px;
|
| 173 |
+
cursor: pointer;
|
| 174 |
+
min-height: 44px;
|
| 175 |
+
}
|
| 176 |
+
|
| 177 |
+
.sample-btn:hover {
|
| 178 |
+
border-color: var(--accent);
|
| 179 |
+
color: var(--accent);
|
| 180 |
+
background: rgba(0, 255, 170, 0.06);
|
| 181 |
+
}
|
| 182 |
|
| 183 |
.terminal {
|
| 184 |
background: rgba(0, 0, 0, 0.4);
|
|
|
|
| 391 |
<div class="meta-item"><span class="label" style="margin-bottom:0; font-size: 8px;" data-i18n="meta_issuer">Issuer</span><div class="meta-val" id="m-issuer">--</div></div>
|
| 392 |
<div class="meta-item"><span class="label" style="margin-bottom:0; font-size: 8px;" data-i18n="meta_status">Status</span><div class="meta-val" id="m-status">--</div></div>
|
| 393 |
</div>
|
| 394 |
+
<div class="sample-grid">
|
| 395 |
+
<button class="sample-btn" data-sample="aclas_alumni_2025.png">ACLAS</button>
|
| 396 |
+
<button class="sample-btn" data-sample="fake_degree_sample.png">FAKE</button>
|
| 397 |
+
<button class="sample-btn" data-sample="graham_university_transcript.pdf">GRAHAM</button>
|
| 398 |
+
</div>
|
| 399 |
</div>
|
| 400 |
</div>
|
| 401 |
<div class="modal" id="modal"><div class="report" id="rep-body"></div></div>
|
|
|
|
| 460 |
cons.appendChild(d); cons.scrollTop = cons.scrollHeight;
|
| 461 |
}
|
| 462 |
|
| 463 |
+
function showSelectedFile(name, noteText) {
|
| 464 |
+
const dropZone = document.getElementById('drop-zone');
|
| 465 |
+
dropZone.replaceChildren();
|
| 466 |
+
|
| 467 |
+
const check = document.createElement('div');
|
| 468 |
+
check.style.color = 'var(--accent)';
|
| 469 |
+
check.innerText = '✓';
|
| 470 |
+
dropZone.appendChild(check);
|
| 471 |
+
|
| 472 |
+
const selectedName = document.createElement('p');
|
| 473 |
+
selectedName.style.fontSize = '10px';
|
| 474 |
+
selectedName.innerText = name;
|
| 475 |
+
dropZone.appendChild(selectedName);
|
| 476 |
+
|
| 477 |
+
const note = document.createElement('p');
|
| 478 |
+
note.style.fontSize = '9px';
|
| 479 |
+
note.style.color = '#ffcc66';
|
| 480 |
+
note.style.marginTop = '8px';
|
| 481 |
+
note.innerText = noteText;
|
| 482 |
+
dropZone.appendChild(note);
|
| 483 |
+
}
|
| 484 |
+
|
| 485 |
document.getElementById('f-input').onchange = (e) => {
|
| 486 |
if(e.target.files.length) {
|
| 487 |
file = e.target.files[0];
|
| 488 |
+
showSelectedFile(file.name, 'Demo intake only; no browser-side approval will be issued.');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 489 |
log(`INGESTED: ${file.name}`);
|
| 490 |
}
|
| 491 |
};
|
| 492 |
document.getElementById('drop-zone').onclick = () => document.getElementById('f-input').click();
|
| 493 |
|
| 494 |
+
document.querySelectorAll('.sample-btn').forEach(sampleBtn => {
|
| 495 |
+
sampleBtn.onclick = () => {
|
| 496 |
+
const sampleName = sampleBtn.getAttribute('data-sample');
|
| 497 |
+
const mimeType = sampleName.endsWith('.pdf') ? 'application/pdf' : 'image/png';
|
| 498 |
+
file = { name: sampleName, type: mimeType, demoSample: true };
|
| 499 |
+
showSelectedFile(sampleName, 'Built-in demo fixture selected.');
|
| 500 |
+
log(`SAMPLE LOADED: ${sampleName}`);
|
| 501 |
+
};
|
| 502 |
+
});
|
| 503 |
+
|
| 504 |
btn.onclick = async () => {
|
| 505 |
if(!file) {
|
| 506 |
log("SYSTEM: Select a PDF, PNG, or JPEG credential before starting the demo audit.");
|
|
|
|
| 530 |
|
| 531 |
function inferDemoOutcome(uploadedFile) {
|
| 532 |
const name = uploadedFile.name.toLowerCase();
|
| 533 |
+
const demoApprovals = [
|
| 534 |
+
{ pattern: 'aclas', issuer: 'Atlanta College of Liberal Arts and Sciences' }
|
| 535 |
+
];
|
| 536 |
const demoBlacklist = [
|
| 537 |
{ pattern: 'graham', issuer: 'Graham International University' },
|
| 538 |
{ pattern: 'giu', issuer: 'Graham International University' },
|
|
|
|
| 542 |
{ pattern: 'belford', issuer: 'Belford University' },
|
| 543 |
{ pattern: 'almeda', issuer: 'Almeda University' }
|
| 544 |
];
|
| 545 |
+
const rejected = demoBlacklist.find(item => name.includes(item.pattern));
|
| 546 |
+
if (rejected) return { ...rejected, verdict: 'REJECTED' };
|
| 547 |
+
const approved = demoApprovals.find(item => name.includes(item.pattern));
|
| 548 |
+
if (approved) return { ...approved, verdict: 'APPROVED' };
|
| 549 |
+
return null;
|
| 550 |
}
|
| 551 |
|
| 552 |
async function finish() {
|
|
|
|
| 566 |
log('SYSTEM: REJECTED - Unsupported file type for audit intake.');
|
| 567 |
} else if (inferDemoOutcome(file)) {
|
| 568 |
const fixture = inferDemoOutcome(file);
|
|
|
|
|
|
|
| 569 |
issuer = fixture.issuer;
|
| 570 |
+
if (fixture.verdict === 'REJECTED') {
|
| 571 |
+
verdict = 'REJECTED';
|
| 572 |
+
status_code = 'DEMO_BLACKLIST_MATCH';
|
| 573 |
+
reason = `${fixture.issuer} matches the public demo blacklist fixture. This is a browser-side demo result only; production rejection still requires a server-signed audit trail.`;
|
| 574 |
+
log(`SYSTEM: DEMO REJECTED - ${fixture.issuer} matched the local demo blacklist fixture.`);
|
| 575 |
+
} else {
|
| 576 |
+
verdict = 'APPROVED';
|
| 577 |
+
status_code = 'DEMO_VERIFIED';
|
| 578 |
+
reason = `${fixture.issuer} matches the built-in verified demo fixture. This browser-side status is only a UI demonstration, not a production credential approval.`;
|
| 579 |
+
log(`SYSTEM: DEMO VERIFIED - ${fixture.issuer} matched the local verified fixture.`);
|
| 580 |
+
}
|
| 581 |
} else {
|
| 582 |
log('SYSTEM: DEMO ONLY - File content was not sent to a trusted audit service.');
|
| 583 |
log('SYSTEM: NEEDS REVIEW - No browser-side approval is issued.');
|
|
|
|
| 590 |
document.getElementById('m-issuer').innerText = issuer;
|
| 591 |
document.getElementById('m-status').innerText = status_code;
|
| 592 |
|
| 593 |
+
const verdictTitle = verdict === 'REJECTED' ? t.verdict_no : (verdict === 'APPROVED' ? 'DEMO VERIFIED' : 'SERVER AUDIT REQUIRED');
|
| 594 |
+
const verdictColor = verdict === 'REJECTED' ? '#ff3366' : (verdict === 'APPROVED' ? '#00ffaa' : '#ffcc66');
|
| 595 |
|
| 596 |
const report = document.getElementById('rep-body');
|
| 597 |
report.replaceChildren();
|