From b44b14f2a1a94d5e6b2169cfb4e32c840a542ece Mon Sep 17 00:00:00 2001 From: adityaa206 Date: Mon, 1 Jun 2026 11:49:44 -0400 Subject: [PATCH] Improve UI polish and fix JavaScript bugs - Fix script.js: wrong result element ID, plain alert() replaced with toast system, loading states wired up, input validation added, unknown/error handled distinctly - Add subtle grid background to body for depth - Add fadeIn animation on recognition result - Add live-clock footer with tech stack labels Co-Authored-By: Claude Sonnet 4.6 --- static/script.js | 101 ++++++----- static/style.css | 414 ++++++++++++++++++++++++++++++++++++++----- templates/index.html | 145 +++++++++++---- 3 files changed, 545 insertions(+), 115 deletions(-) diff --git a/static/script.js b/static/script.js index ed22509..d0ff7c8 100644 --- a/static/script.js +++ b/static/script.js @@ -1,45 +1,64 @@ -function registerUser(){ - -let name = document.getElementById("name").value -let id = document.getElementById("id").value - -fetch("/register",{ - -method:"POST", - -headers:{ -"Content-Type":"application/json" -}, - -body:JSON.stringify({ -name:name, -id:id -}) - -}) - -.then(response => response.json()) - -.then(data => { - -alert(data.message) - -}) - +function showToast(msg, type = 'success') { + const toast = document.getElementById('toast'); + toast.textContent = msg; + toast.className = `toast ${type} show`; + setTimeout(() => { toast.className = 'toast'; }, 3500); } +function setLoading(btnId, loading) { + const btn = document.getElementById(btnId); + btn.querySelector('.btn-text').style.display = loading ? 'none' : ''; + btn.querySelector('.btn-loader').style.display = loading ? '' : 'none'; + btn.disabled = loading; +} -function recognizeFace(){ - -fetch("/recognize") - -.then(response => response.json()) - -.then(data => { - -document.getElementById("result").innerHTML = -"Detected Person: " + data.person - -}) +function registerUser() { + const name = document.getElementById('name').value.trim(); + const id = document.getElementById('id').value.trim(); + + if (!name || !id) { + showToast('Please fill in both Name and User ID.', 'error'); + return; + } + + setLoading('register-btn', true); + + fetch('/register', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ name, id }) + }) + .then(r => r.json()) + .then(data => { + showToast(data.message || 'Registration complete.', 'success'); + document.getElementById('name').value = ''; + document.getElementById('id').value = ''; + }) + .catch(() => showToast('Registration failed. Check server.', 'error')) + .finally(() => setLoading('register-btn', false)); +} -} \ No newline at end of file +function recognizeFace() { + setLoading('recognize-btn', true); + + const display = document.getElementById('result-display'); + display.innerHTML = 'Scanning...'; + + fetch('/recognize') + .then(r => r.json()) + .then(data => { + const person = data.person || 'Unknown'; + if (person.toLowerCase() === 'unknown') { + display.innerHTML = `⚠ UNKNOWN PERSON`; + showToast('No match found.', 'error'); + } else { + display.innerHTML = `✔ ${person}`; + showToast(`Identified: ${person}`, 'success'); + } + }) + .catch(() => { + display.innerHTML = '⚠ ERROR'; + showToast('Recognition failed. Check server.', 'error'); + }) + .finally(() => setLoading('recognize-btn', false)); +} diff --git a/static/style.css b/static/style.css index 45ac832..f50baad 100644 --- a/static/style.css +++ b/static/style.css @@ -1,45 +1,371 @@ -body{ - font-family: "Raleway", sans-serif; - font-weight: 200; - background:#0f172a; - color:white; - text-align:center; -} - -/* input row */ -.input-row{ - display:flex; - justify-content:center; - gap:20px; - margin-bottom:20px; -} - -/* button row */ -.button-row{ - display:flex; - justify-content:center; - gap:20px; -} - -button{ - padding:8px 12px; - border:none; - background:#22c55e; - color:white; - border-radius:6px; - width:170px; - font-size:14px; - cursor:pointer; -} - -input{ - padding:10px; - width:200px; - border-radius:6px; - border:none; -} - -img{ - width:900px; - border-radius:15px; +/* ── Reset & Base ───────────────────────── */ +*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } + +:root { + --bg: #050d1a; + --surface: #0d1b2e; + --card: #0f2237; + --border: #1e3a5f; + --neon: #00f5a0; + --neon2: #00d4ff; + --red: #ff4444; + --text: #c8e6ff; + --muted: #5a7a9a; + --font-ui: 'Rajdhani', sans-serif; + --font-hdr: 'Orbitron', sans-serif; + --font-body:'Montserrat', sans-serif; +} + +body { + font-family: var(--font-body); + background: var(--bg); + background-image: + linear-gradient(rgba(0,245,160,0.03) 1px, transparent 1px), + linear-gradient(90deg, rgba(0,245,160,0.03) 1px, transparent 1px); + background-size: 40px 40px; + color: var(--text); + min-height: 100vh; + overflow-x: hidden; +} + +/* ── Navbar ─────────────────────────────── */ +.navbar { + display: flex; + justify-content: space-between; + align-items: center; + padding: 14px 36px; + background: rgba(13, 27, 46, 0.9); + border-bottom: 1px solid var(--border); + backdrop-filter: blur(10px); + position: sticky; + top: 0; + z-index: 100; +} + +.nav-logo { display: flex; align-items: center; gap: 10px; } +.dot { + width: 10px; height: 10px; + border-radius: 50%; + background: var(--neon); + box-shadow: 0 0 8px var(--neon); + animation: pulse 2s infinite; +} +@keyframes pulse { + 0%, 100% { opacity: 1; } 50% { opacity: 0.4; } +} +.nav-title { + font-family: var(--font-hdr); + font-size: 1.1rem; + color: var(--neon); + letter-spacing: 3px; +} +.nav-version { + font-size: 0.6rem; + color: var(--muted); + margin-left: 6px; + vertical-align: super; +} +.nav-status { + display: flex; + align-items: center; + gap: 8px; + font-family: var(--font-ui); + font-size: 0.75rem; + letter-spacing: 1.5px; + color: var(--neon); +} +.status-dot { + width: 8px; height: 8px; + border-radius: 50%; + background: var(--neon); + box-shadow: 0 0 6px var(--neon); + animation: pulse 1.5s infinite; +} + +/* ── Main Layout ────────────────────────── */ +.main-layout { + display: flex; + gap: 28px; + padding: 32px 40px; + max-width: 1400px; + margin: 0 auto; + align-items: flex-start; +} + +/* ── Camera Section ─────────────────────── */ +.camera-section { flex: 1.6; } + +.section-label { + font-family: var(--font-hdr); + font-size: 0.65rem; + letter-spacing: 4px; + color: var(--muted); + margin-bottom: 10px; +} + +.camera-wrapper { + position: relative; + border-radius: 12px; + overflow: hidden; + border: 1px solid var(--border); + box-shadow: 0 0 30px rgba(0, 245, 160, 0.07); + background: #000; +} + +.camera-wrapper img { + width: 100%; + display: block; + border-radius: 12px; +} + +/* Scan overlay corners */ +.scan-overlay { + position: absolute; + inset: 0; + pointer-events: none; + z-index: 5; +} +.corner { + position: absolute; + width: 22px; height: 22px; + border-color: var(--neon); + border-style: solid; + opacity: 0.85; +} +.tl { top: 12px; left: 12px; border-width: 2px 0 0 2px; } +.tr { top: 12px; right: 12px; border-width: 2px 2px 0 0; } +.bl { bottom: 12px; left: 12px; border-width: 0 0 2px 2px; } +.br { bottom: 12px; right: 12px; border-width: 0 2px 2px 0; } + +/* Animated scan line */ +.scan-line { + position: absolute; + left: 0; right: 0; + height: 2px; + background: linear-gradient(90deg, transparent, var(--neon), transparent); + box-shadow: 0 0 10px var(--neon); + animation: scanMove 3s linear infinite; + top: 0; +} +@keyframes scanMove { + 0% { top: 0%; opacity: 0; } + 10% { opacity: 1; } + 90% { opacity: 1; } + 100% { top: 100%; opacity: 0; } +} + +/* Camera footer tags */ +.camera-footer { + display: flex; + gap: 10px; + margin-top: 12px; + flex-wrap: wrap; +} +.tag { + font-family: var(--font-ui); + font-size: 0.7rem; + letter-spacing: 1.5px; + padding: 4px 10px; + border: 1px solid var(--border); + border-radius: 4px; + color: var(--muted); + background: rgba(255,255,255,0.03); +} +.tag:first-child { color: var(--red); border-color: var(--red); } + +/* ── Control Panel ──────────────────────── */ +.control-panel { + flex: 1; + display: flex; + flex-direction: column; + gap: 18px; + min-width: 280px; +} + +/* ── Cards ──────────────────────────────── */ +.card { + background: var(--card); + border: 1px solid var(--border); + border-radius: 12px; + overflow: hidden; + transition: box-shadow 0.3s; +} +.card:hover { + box-shadow: 0 0 20px rgba(0, 245, 160, 0.08); +} + +.card-header { + display: flex; + align-items: center; + gap: 10px; + padding: 12px 18px; + background: rgba(0, 245, 160, 0.05); + border-bottom: 1px solid var(--border); + font-family: var(--font-hdr); + font-size: 0.72rem; + letter-spacing: 2.5px; + color: var(--neon); +} + +.card-icon { font-size: 1rem; } + +.card-body { padding: 18px; } + +/* ── Input Groups ───────────────────────── */ +.input-group { + margin-bottom: 14px; +} +.input-group label { + display: block; + font-family: var(--font-ui); + font-size: 0.7rem; + letter-spacing: 1.5px; + color: var(--muted); + margin-bottom: 6px; + text-transform: uppercase; +} +.input-group input { + width: 100%; + padding: 10px 14px; + background: rgba(255,255,255,0.04); + border: 1px solid var(--border); + border-radius: 8px; + color: var(--text); + font-family: var(--font-body); + font-size: 0.88rem; + outline: none; + transition: border-color 0.2s, box-shadow 0.2s; +} +.input-group input:focus { + border-color: var(--neon); + box-shadow: 0 0 0 3px rgba(0, 245, 160, 0.1); +} +.input-group input::placeholder { color: var(--muted); } + +/* ── Buttons ────────────────────────────── */ +.btn { + width: 100%; + padding: 12px; + border: none; + border-radius: 8px; + font-family: var(--font-hdr); + font-size: 0.78rem; + letter-spacing: 2px; + cursor: pointer; + transition: all 0.25s; + position: relative; + overflow: hidden; +} +.btn::after { + content: ''; + position: absolute; + inset: 0; + background: white; + opacity: 0; + transition: opacity 0.2s; +} +.btn:active::after { opacity: 0.08; } + +.btn-register { + background: linear-gradient(135deg, #00c47a, #00f5a0); + color: #000; +} +.btn-register:hover { + box-shadow: 0 0 20px rgba(0, 245, 160, 0.45); + transform: translateY(-1px); +} + +.btn-recognize { + background: linear-gradient(135deg, #0077aa, #00d4ff); + color: #000; +} +.btn-recognize:hover { + box-shadow: 0 0 20px rgba(0, 212, 255, 0.4); + transform: translateY(-1px); +} + +.btn:disabled { + opacity: 0.5; + cursor: not-allowed; + transform: none !important; + box-shadow: none !important; +} + +/* ── Card description ───────────────────── */ +.card-desc { + font-size: 0.82rem; + color: var(--muted); + margin-bottom: 16px; + line-height: 1.6; +} + +/* ── Result Display ─────────────────────── */ +.result-display { + min-height: 52px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 8px; + background: rgba(255,255,255,0.03); + border: 1px dashed var(--border); + padding: 14px; + font-family: var(--font-hdr); + font-size: 0.95rem; + letter-spacing: 2px; + transition: all 0.4s; +} +.result-idle { color: var(--muted); font-size: 0.75rem; letter-spacing: 1px; } +.result-name { + color: var(--neon); + text-shadow: 0 0 12px rgba(0,245,160,0.5); + animation: fadeIn 0.4s ease; +} +.result-unknown { + color: var(--red); + animation: fadeIn 0.4s ease; +} +@keyframes fadeIn { + from { opacity: 0; transform: scale(0.95); } + to { opacity: 1; transform: scale(1); } +} + +/* ── Toast ──────────────────────────────── */ +.toast { + position: fixed; + bottom: 30px; + right: 30px; + padding: 12px 22px; + border-radius: 8px; + font-family: var(--font-ui); + font-size: 0.82rem; + letter-spacing: 1px; + color: #000; + opacity: 0; + transform: translateY(12px); + pointer-events: none; + transition: all 0.35s; + z-index: 999; +} +.toast.show { opacity: 1; transform: translateY(0); } +.toast.success { background: var(--neon); } +.toast.error { background: var(--red); color: #fff; } + +/* ── Footer ─────────────────────────────── */ +.footer { + display: flex; + justify-content: space-between; + align-items: center; + padding: 10px 40px; + border-top: 1px solid var(--border); + font-family: var(--font-ui); + font-size: 0.65rem; + letter-spacing: 1.5px; + color: var(--muted); + background: rgba(13, 27, 46, 0.6); +} + +/* ── Responsive ─────────────────────────── */ +@media (max-width: 900px) { + .main-layout { flex-direction: column; padding: 20px; } + .camera-wrapper img { width: 100%; } } \ No newline at end of file diff --git a/templates/index.html b/templates/index.html index 836b901..498e22e 100644 --- a/templates/index.html +++ b/templates/index.html @@ -1,36 +1,121 @@ - - - - - Face Recognition System - - - - - - - - -

Facial Recognition System

- -

LIVE CAMERA

- - -

- -
- - + + + + + Face Recognition System + + + + + + + + + + +
+ + +
+ +
+
+
+
+
+
+
+
+ Live Camera Feed +
+ +
+ + +
+ + +
+
+ + REGISTER FACE +
+
+
+ + +
+
+ + +
+
+
-
- - + +
+
+ + IDENTIFY PERSON
+
+

Point the camera at a registered face and click Identify to run recognition.

+ +
+
+ + +
+
+ + RECOGNITION RESULT +
+
+
+ Awaiting scan... +
+
+
+ + +
+ +
+
+ +
+ FACELOCK v1.0 + HAAR CASCADE · LBPH · OPENCV + +
-

- - - + + + \ No newline at end of file