Spaces:
Runtime error
Runtime error
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>SecureAttend AI - Facial Recognition Attendance Control Center</title> | |
| <!-- Material Icons --> | |
| <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"> | |
| <!-- Custom Styling --> | |
| <link rel="stylesheet" href="/static/css/style.css"> | |
| </head> | |
| <body> | |
| <!-- Floating Escape button for Kiosk mode --> | |
| <button class="kiosk-escape-btn" id="btn-kiosk-escape" style="display: none;"> | |
| <i class="material-icons">exit_to_app</i> | |
| <span>Exit Kiosk</span> | |
| </button> | |
| <!-- Futuristic Glassmorphism Login Overlay --> | |
| <div id="login-overlay" class="login-overlay"> | |
| <div class="login-card glass-card"> | |
| <div class="logo-container" style="justify-content: center; margin-bottom: 24px;"> | |
| <i class="material-icons logo-icon" style="font-size: 36px;">fingerprint</i> | |
| <h2 style="font-size: 24px; font-weight: 800; background: linear-gradient(90deg, #ffffff, var(--neon-cyan)); -webkit-background-clip: text; -webkit-text-fill-color: transparent; margin: 0;">SECUREATTEND AI</h2> | |
| </div> | |
| <h3 style="text-align: center; margin-bottom: 8px; font-size: 18px; font-weight: 700;">BIOMETRIC TERMINAL ACCESS</h3> | |
| <p style="text-align: center; color: var(--text-secondary); font-size: 13px; margin-bottom: 28px;">Authorization required. Input credential keys.</p> | |
| <div class="form-group"> | |
| <label class="form-label" for="login-username">SECURITY ACCOUNT KEY</label> | |
| <input type="text" id="login-username" class="form-input" placeholder="e.g. admin, hr, kiosk"> | |
| </div> | |
| <div class="form-group" style="margin-bottom: 28px;"> | |
| <label class="form-label" for="login-password">SECURE KEYPASS</label> | |
| <input type="password" id="login-password" class="form-input" placeholder="••••••••"> | |
| </div> | |
| <button class="btn" id="btn-login-submit" style="width: 100%; justify-content: center; padding: 12px;"> | |
| <i class="material-icons">key</i> | |
| <span>Establish Security Session</span> | |
| </button> | |
| <div id="login-error-msg" style="color: var(--neon-red); text-align: center; font-size: 13px; font-weight: 600; margin-top: 16px; display: none;"> | |
| Access Denied: Invalid Keystore Combination. | |
| </div> | |
| </div> | |
| </div> | |
| <div class="app-container"> | |
| <!-- 1. Left Navigation Sidebar --> | |
| <aside class="sidebar" id="app-sidebar"> | |
| <div class="logo-container" style="margin-bottom: 10px;"> | |
| <i class="material-icons logo-icon">fingerprint</i> | |
| <div class="logo-text">SECUREATTEND AI</div> | |
| </div> | |
| <div id="role-badge-container" style="margin-bottom: 30px; padding-left: 8px;"> | |
| <span id="role-badge" style="font-size: 10px; background: rgba(0, 255, 242, 0.1); border: 1px solid var(--neon-cyan); color: var(--neon-cyan); padding: 2.5px 10px; border-radius: 20px; font-weight: 700; letter-spacing: 0.5px; text-transform: uppercase;">ADMIN NODE</span> | |
| </div> | |
| <ul class="nav-links"> | |
| <li class="nav-item active" data-tab="dashboard"> | |
| <i class="material-icons">dashboard</i> | |
| <span>Dashboard</span> | |
| </li> | |
| <li class="nav-item" data-tab="scanner"> | |
| <i class="material-icons">videocam</i> | |
| <span>Live Scanner</span> | |
| </li> | |
| <li class="nav-item" data-tab="directory"> | |
| <i class="material-icons">people</i> | |
| <span>Employee Directory</span> | |
| </li> | |
| <li class="nav-item" data-tab="settings"> | |
| <i class="material-icons">settings</i> | |
| <span>Admin Settings</span> | |
| </li> | |
| <li class="nav-item" id="nav-logout" style="margin-top: 24px; border: 1px solid rgba(231, 76, 60, 0.15); background: rgba(231, 76, 60, 0.03); color: var(--neon-red);"> | |
| <i class="material-icons" style="color: var(--neon-red);">power_settings_new</i> | |
| <span style="color: var(--neon-red); font-weight: 600;">Close Session</span> | |
| </li> | |
| </ul> | |
| <div class="sidebar-footer"> | |
| <div class="sys-status-indicator"></div> | |
| <div class="sys-status-text">SYSTEM SECURE & ACTIVE</div> | |
| </div> | |
| </aside> | |
| <!-- 2. Main Viewport Content --> | |
| <main class="main-content"> | |
| <!-- View Header --> | |
| <header class="view-header"> | |
| <div class="view-title"> | |
| <h1 id="view-title-text">Dashboard Overview</h1> | |
| <p id="view-subtitle-text">Real-time attendance metrics & engine status.</p> | |
| </div> | |
| <div class="current-time-container" style="text-align: right;"> | |
| <h3 id="live-clock" style="font-weight: 700; color: var(--neon-cyan); letter-spacing: 0.5px;">12:00:00 PM</h3> | |
| <span style="font-size: 12px; color: var(--text-secondary); font-weight: 500;">LOCAL SECURITY NODE</span> | |
| </div> | |
| </header> | |
| <!-- TAB PANEL 1: DASHBOARD OVERVIEW --> | |
| <section id="tab-dashboard" class="tab-panel active"> | |
| <!-- Metrics Counter Grid --> | |
| <div class="widgets-grid"> | |
| <div class="glass-card widget-card"> | |
| <div class="widget-icon"><i class="material-icons">people_outline</i></div> | |
| <div class="widget-info"> | |
| <h3 id="stat-total">0</h3> | |
| <p>Total Enrolled</p> | |
| </div> | |
| </div> | |
| <div class="glass-card widget-card" style="border-color: rgba(46, 204, 113, 0.15);"> | |
| <div class="widget-icon" style="background: rgba(46, 204, 113, 0.08); color: var(--neon-green); box-shadow: 0 0 15px rgba(46, 204, 113, 0.2);"><i class="material-icons">check_circle_outline</i></div> | |
| <div class="widget-info"> | |
| <h3 id="stat-present">0</h3> | |
| <p>Checked In Today</p> | |
| </div> | |
| </div> | |
| <div class="glass-card widget-card" style="border-color: rgba(241, 196, 15, 0.15);"> | |
| <div class="widget-icon" style="background: rgba(241, 196, 15, 0.08); color: var(--neon-amber); box-shadow: 0 0 15px rgba(241, 196, 15, 0.2);"><i class="material-icons">exit_to_app</i></div> | |
| <div class="widget-info"> | |
| <h3 id="stat-out">0</h3> | |
| <p>Checked Out Today</p> | |
| </div> | |
| </div> | |
| <div class="glass-card widget-card" style="border-color: rgba(231, 76, 60, 0.15);"> | |
| <div class="widget-icon" style="background: rgba(231, 76, 60, 0.08); color: var(--neon-red); box-shadow: 0 0 15px rgba(231, 76, 60, 0.2);"><i class="material-icons">error_outline</i></div> | |
| <div class="widget-info"> | |
| <h3 id="stat-absent">0</h3> | |
| <p>Absent Today</p> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Dynamic Dynamic SVG Analytics Charts --> | |
| <div class="analytics-grid" style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-bottom: 24px;"> | |
| <!-- Chart 1: Weekly spline curve --> | |
| <div class="glass-card chart-card" style="display: flex; flex-direction: column; gap: 16px; padding: 20px; min-height: 270px; position: relative;"> | |
| <div class="activity-header" style="margin-bottom: 0;"> | |
| <span style="display: flex; align-items: center; gap: 8px;"> | |
| <i class="material-icons" style="color: var(--neon-cyan); font-size: 20px;">trending_up</i> | |
| WEEKLY ATTENDANCE TRENDS | |
| </span> | |
| <span style="font-size: 11px; background: rgba(0, 255, 242, 0.1); border: 1px solid var(--neon-cyan); color: var(--neon-cyan); padding: 2.5px 10px; border-radius: 20px; font-weight: 700; text-transform: uppercase;">PAST 7 DAYS</span> | |
| </div> | |
| <div class="chart-container" style="position: relative; flex-grow: 1; width: 100%;"> | |
| <svg id="weekly-trends-svg" width="100%" height="100%" viewBox="0 0 500 200" preserveAspectRatio="none" style="overflow: visible;"> | |
| <defs> | |
| <linearGradient id="cyan-gradient" x1="0" y1="0" x2="0" y2="1"> | |
| <stop offset="0%" stop-color="var(--neon-cyan)" stop-opacity="0.3"/> | |
| <stop offset="100%" stop-color="var(--neon-cyan)" stop-opacity="0.0"/> | |
| </linearGradient> | |
| <filter id="neon-glow" x="-20%" y="-20%" width="140%" height="140%"> | |
| <feGaussianBlur stdDeviation="3" result="blur" /> | |
| <feMerge> | |
| <feMergeNode in="blur" /> | |
| <feMergeNode in="SourceGraphic" /> | |
| </feMerge> | |
| </filter> | |
| </defs> | |
| <!-- SVG path, gridlines, labels, and nodes are injected in JS --> | |
| </svg> | |
| </div> | |
| </div> | |
| <!-- Chart 2: Hourly bar columns --> | |
| <div class="glass-card chart-card" style="display: flex; flex-direction: column; gap: 16px; padding: 20px; min-height: 270px; position: relative;"> | |
| <div class="activity-header" style="margin-bottom: 0;"> | |
| <span style="display: flex; align-items: center; gap: 8px;"> | |
| <i class="material-icons" style="color: var(--neon-green); font-size: 20px;">bar_chart</i> | |
| HOURLY PEAK SCAN CLUSTERS | |
| </span> | |
| <span style="font-size: 11px; background: rgba(46, 204, 113, 0.1); border: 1px solid var(--neon-green); color: var(--neon-green); padding: 2.5px 10px; border-radius: 20px; font-weight: 700; text-transform: uppercase;">TODAY</span> | |
| </div> | |
| <div class="chart-container" style="position: relative; flex-grow: 1; width: 100%;"> | |
| <svg id="hourly-clusters-svg" width="100%" height="100%" viewBox="0 0 500 200" preserveAspectRatio="none" style="overflow: visible;"> | |
| <defs> | |
| <filter id="green-glow" x="-20%" y="-20%" width="140%" height="140%"> | |
| <feGaussianBlur stdDeviation="3" result="blur" /> | |
| <feMerge> | |
| <feMergeNode in="blur" /> | |
| <feMergeNode in="SourceGraphic" /> | |
| </feMerge> | |
| </filter> | |
| </defs> | |
| <!-- SVG bars, grids, and values are injected in JS --> | |
| </svg> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Dashboard Main Split --> | |
| <div class="dashboard-layout"> | |
| <!-- Info panel --> | |
| <div class="glass-card" style="display: flex; flex-direction: column; justify-content: center; padding: 40px; background: linear-gradient(135deg, rgba(20, 16, 38, 0.45) 0%, rgba(0, 255, 242, 0.02) 100%);"> | |
| <h2 style="font-size: 28px; font-weight: 800; margin-bottom: 16px; background: linear-gradient(90deg, #ffffff, var(--neon-cyan)); -webkit-background-clip: text; -webkit-text-fill-color: transparent;">Biometric AI Attendance Node</h2> | |
| <p style="color: var(--text-secondary); line-height: 1.6; margin-bottom: 24px; font-size: 15px;"> | |
| Welcome to your local high-accuracy face recognition terminal. The system uses a local deep learning **YuNet** detector and a neural **SFace** vectorizer to recognize registered employees instantaneously at multiple POV head angles. | |
| </p> | |
| <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 16px;"> | |
| <div class="glass-card" style="padding: 16px; background: rgba(0, 0, 0, 0.15);"> | |
| <h4 style="color: var(--neon-cyan); margin-bottom: 4px; font-size: 13px; text-transform: uppercase;">Engine Mode</h4> | |
| <p style="font-size: 16px; font-weight: 700;">ON-PREMISES CPU</p> | |
| </div> | |
| <div class="glass-card" style="padding: 16px; background: rgba(0, 0, 0, 0.15);"> | |
| <h4 style="color: var(--neon-cyan); margin-bottom: 4px; font-size: 13px; text-transform: uppercase;">Confidence Bar</h4> | |
| <p style="font-size: 16px; font-weight: 700;">36.3% COSINE</p> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Active Activity Logs panel --> | |
| <div class="glass-card activity-card"> | |
| <div class="activity-header"> | |
| <span>RECENT LOG ACTIVITY</span> | |
| <i class="material-icons" style="color: var(--neon-cyan);">history</i> | |
| </div> | |
| <div class="activity-list" id="dashboard-activity-list"> | |
| <!-- Log list dynamically filled --> | |
| <div class="activity-item" style="justify-content: center; color: var(--text-muted); border: none; background: transparent;"> | |
| <p>No scans recorded yet today.</p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- TAB PANEL 2: LIVE SCANNER --> | |
| <section id="tab-scanner" class="tab-panel"> | |
| <div class="dashboard-layout"> | |
| <!-- Live Camera Stream Frame --> | |
| <div class="glass-card" style="display: flex; flex-direction: column; gap: 16px; padding: 20px;"> | |
| <div class="activity-header" style="margin-bottom: 0;"> | |
| <span>LIVE WEB-CAMERA MONITORING</span> | |
| <span id="cam-badge" style="font-size: 11px; background: rgba(46, 204, 113, 0.15); border: 1px solid var(--neon-green); color: var(--neon-green); padding: 2px 8px; border-radius: 20px; font-weight: 700; letter-spacing: 0.5px; text-transform: uppercase;">ACTIVE</span> | |
| </div> | |
| <div class="feed-container"> | |
| <!-- Live Video stream is rendered on canvas via JS --> | |
| <canvas id="live-scanner-canvas" class="live-feed-img" width="640" height="480"></canvas> | |
| </div> | |
| <!-- Camera controls panel --> | |
| <div style="display: flex; justify-content: space-between; align-items: center; padding-top: 4px;"> | |
| <span style="font-size: 13px; color: var(--text-secondary); font-weight: 500;">Hardware Control:</span> | |
| <button class="btn" id="btn-toggle-camera" style="padding: 8px 18px; font-size: 13px; border-radius: 10px;"> | |
| <i class="material-icons" style="font-size: 18px;">videocam_off</i> | |
| <span>Disable Camera</span> | |
| </button> | |
| </div> | |
| </div> | |
| <!-- Quick Scanner Logs console --> | |
| <div class="glass-card activity-card"> | |
| <div class="activity-header"> | |
| <span>SESSION ATTENDANCE RECORDS</span> | |
| <span id="session-log-count" style="font-size: 12px; color: var(--neon-cyan); font-weight: 700;">0 LOGS</span> | |
| </div> | |
| <div class="activity-list" id="scanner-session-logs"> | |
| <!-- Dynamically filled stream --> | |
| <div class="activity-item" style="justify-content: center; color: var(--text-muted); border: none; background: transparent;"> | |
| <p>Standing in front of the camera to check-in/out.</p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- TAB PANEL 3: EMPLOYEE DIRECTORY --> | |
| <section id="tab-directory" class="tab-panel"> | |
| <div class="glass-card directory-layout"> | |
| <!-- Table Controls --> | |
| <div class="table-controls"> | |
| <div class="search-input-wrapper"> | |
| <i class="material-icons">search</i> | |
| <input type="text" id="directory-search" class="search-input" placeholder="Search employees by ID, name, or role..."> | |
| </div> | |
| <button class="btn" id="btn-open-enrollment"> | |
| <i class="material-icons">person_add</i> | |
| <span>Register Employee</span> | |
| </button> | |
| </div> | |
| <!-- Directory Table Container --> | |
| <div class="table-container"> | |
| <table> | |
| <thead> | |
| <tr> | |
| <th>Employee ID</th> | |
| <th>Employee Name</th> | |
| <th>Email Address</th> | |
| <th>Designation / Role</th> | |
| <th>Enrollment Date</th> | |
| <th style="text-align: right;">Action</th> | |
| </tr> | |
| </thead> | |
| <tbody id="directory-table-body"> | |
| <!-- Filled dynamically --> | |
| <tr> | |
| <td colspan="6" style="text-align: center; color: var(--text-muted); padding: 40px;"> | |
| Retrieving employees database directory... | |
| </td> | |
| </tr> | |
| </tbody> | |
| </table> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- TAB PANEL 4: ADMIN SETTINGS --> | |
| <section id="tab-settings" class="tab-panel"> | |
| <div class="settings-grid"> | |
| <!-- Camera Config Card --> | |
| <div class="glass-card" style="display: flex; flex-direction: column; gap: 20px;"> | |
| <h3 style="font-size: 18px; font-weight: 700; color: var(--neon-cyan); display: flex; align-items: center; gap: 8px; border-bottom: 1px solid rgba(255, 255, 255, 0.05); padding-bottom: 12px;"> | |
| <i class="material-icons">videocam</i> | |
| Webcam Input Settings | |
| </h3> | |
| <div class="form-group"> | |
| <label class="form-label" for="camera-select">ACTIVE CAMERA INPUT ID</label> | |
| <select id="camera-select" class="form-input" style="background: rgba(0,0,0,0.3); border-color: var(--glass-border);"> | |
| <option value="0">Camera Index 0 (Default Integrated Cam)</option> | |
| <option value="1">Camera Index 1 (External USB WebCam)</option> | |
| <option value="2">Camera Index 2 (Secondary Device)</option> | |
| <option value="3">Camera Index 3 (Virtual Camera / OBS)</option> | |
| </select> | |
| </div> | |
| <button class="btn" id="btn-save-camera" style="width: 100%; justify-content: center;"> | |
| <i class="material-icons">save</i> | |
| <span>Switch Active Camera Source</span> | |
| </button> | |
| </div> | |
| <!-- System Status Rules Card --> | |
| <div class="glass-card" style="display: flex; flex-direction: column; gap: 20px;"> | |
| <h3 style="font-size: 18px; font-weight: 700; color: var(--neon-cyan); display: flex; align-items: center; gap: 8px; border-bottom: 1px solid rgba(255, 255, 255, 0.05); padding-bottom: 12px;"> | |
| <i class="material-icons">security</i> | |
| Attendance Matching Rules | |
| </h3> | |
| <div style="display: flex; flex-direction: column; gap: 14px; font-size: 14px;"> | |
| <div style="display: flex; justify-content: space-between; border-bottom: 1px solid rgba(255,255,255,0.03); padding-bottom: 8px;"> | |
| <span style="color: var(--text-secondary);">Double-Scan Debounce:</span> | |
| <span style="font-weight: 700; color: var(--neon-cyan);">2 Minutes Cooldown</span> | |
| </div> | |
| <div style="display: flex; justify-content: space-between; border-bottom: 1px solid rgba(255,255,255,0.03); padding-bottom: 8px;"> | |
| <span style="color: var(--text-secondary);">Verification Strategy:</span> | |
| <span style="font-weight: 700; color: var(--neon-green);">Cosine Similarity</span> | |
| </div> | |
| <div style="display: flex; justify-content: space-between; border-bottom: 1px solid rgba(255,255,255,0.03); padding-bottom: 8px;"> | |
| <span style="color: var(--text-secondary);">Matching Threshold:</span> | |
| <span style="font-weight: 700; color: var(--neon-cyan);">0.363 (SFace Default)</span> | |
| </div> | |
| <div style="display: flex; justify-content: space-between; border-bottom: 1px solid rgba(255,255,255,0.03); padding-bottom: 8px;"> | |
| <span style="color: var(--text-secondary);">Enrollment Angles Required:</span> | |
| <span style="font-weight: 700; color: var(--neon-cyan);">5 Angles (3D POV mapping)</span> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </section> | |
| </main> | |
| </div> | |
| <!-- 3. Dynamic Multi-Angle Enrollment Wizard (Modal) --> | |
| <div class="wizard-modal" id="wizard-modal"> | |
| <div class="wizard-card glass-card"> | |
| <!-- Wizard Left Sidebar --> | |
| <aside class="wizard-sidebar"> | |
| <h3 style="font-size: 16px; font-weight: 700; margin-bottom: 24px; color: var(--neon-cyan);">Registration</h3> | |
| <ul class="wizard-steps"> | |
| <li class="step-indicator active" id="step-ind-1"> | |
| <div class="step-num">1</div> | |
| <span>Profile Details</span> | |
| </li> | |
| <li class="step-indicator" id="step-ind-2"> | |
| <div class="step-num">2</div> | |
| <span>Biometric Scans</span> | |
| </li> | |
| <li class="step-indicator" id="step-ind-3"> | |
| <div class="step-num">3</div> | |
| <span>Registration Complete</span> | |
| </li> | |
| </ul> | |
| </aside> | |
| <!-- Wizard Right Content Panel --> | |
| <div class="wizard-content"> | |
| <!-- STEP 1 PANEL: Employee Details Form --> | |
| <div class="wizard-panel active" id="wizard-panel-1"> | |
| <h2 style="font-size: 22px; font-weight: 700; margin-bottom: 6px;">Create Employee Profile</h2> | |
| <p style="color: var(--text-secondary); font-size: 14px; margin-bottom: 28px;">Input employee credentials before proceeding to biometric registration.</p> | |
| <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px; flex-grow: 1; align-content: flex-start;"> | |
| <div class="form-group"> | |
| <label class="form-label" for="emp-id-input">Employee ID</label> | |
| <input type="text" id="emp-id-input" class="form-input" placeholder="e.g. EMP-2401"> | |
| </div> | |
| <div class="form-group"> | |
| <label class="form-label" for="emp-name-input">Full Name</label> | |
| <input type="text" id="emp-name-input" class="form-input" placeholder="e.g. Kunal Katiyar"> | |
| </div> | |
| <div class="form-group"> | |
| <label class="form-label" for="emp-email-input">Work Email</label> | |
| <input type="email" id="emp-email-input" class="form-input" placeholder="e.g. kunal@office.com"> | |
| </div> | |
| <div class="form-group"> | |
| <label class="form-label" for="emp-role-input">Designation / Role</label> | |
| <input type="text" id="emp-role-input" class="form-input" placeholder="e.g. Lead Fullstack Architect"> | |
| </div> | |
| </div> | |
| <div style="display: flex; justify-content: flex-end; gap: 16px; margin-top: auto;"> | |
| <button class="btn btn-danger" id="btn-cancel-wizard">Cancel</button> | |
| <button class="btn" id="btn-step1-next"> | |
| <span>Next Step: Face Scan</span> | |
| <i class="material-icons">arrow_forward</i> | |
| </button> | |
| </div> | |
| </div> | |
| <!-- STEP 2 PANEL: Biometric Scans (Camera View + Angles Guide) --> | |
| <div class="wizard-panel" id="wizard-panel-2"> | |
| <h2 style="font-size: 20px; font-weight: 700; margin-bottom: 4px;">Biometric Facial Scanner</h2> | |
| <p style="color: var(--text-secondary); font-size: 13px; margin-bottom: 20px;">Follow the 5 POV steps below. Turn your face accordingly and scan each angle.</p> | |
| <div class="scan-area-layout"> | |
| <!-- Camera View --> | |
| <div style="display: flex; flex-direction: column; gap: 10px;"> | |
| <div class="feed-container" style="border-radius: 12px;"> | |
| <!-- Renders stream dynamically during enrollment --> | |
| <canvas id="enrollment-canvas" class="live-feed-img" width="640" height="480"></canvas> | |
| <div class="feed-overlay-text" id="enroll-cam-loading-text">Starting Camera Feed...</div> | |
| </div> | |
| <div style="font-size: 11px; color: var(--text-muted); text-align: center; font-weight: 500;"> | |
| Webcam capturing aligned 5-point facial coordinates in real-time. | |
| </div> | |
| </div> | |
| <!-- Vertical Angles Cards list --> | |
| <div class="angle-selector"> | |
| <div class="angle-card active" data-angle="center"> | |
| <div class="angle-label"> | |
| <i class="material-icons angle-status-icon">face</i> | |
| <span>1. Center Face (Straight)</span> | |
| </div> | |
| <button class="btn" style="padding: 4px 10px; font-size: 11px; border-radius: 6px;" onclick="app.captureAngle('center')">SCAN</button> | |
| </div> | |
| <div class="angle-card" data-angle="left"> | |
| <div class="angle-label"> | |
| <i class="material-icons angle-status-icon">chevron_left</i> | |
| <span>2. Look Left (Angle)</span> | |
| </div> | |
| <button class="btn" style="padding: 4px 10px; font-size: 11px; border-radius: 6px;" onclick="app.captureAngle('left')">SCAN</button> | |
| </div> | |
| <div class="angle-card" data-angle="right"> | |
| <div class="angle-label"> | |
| <i class="material-icons angle-status-icon">chevron_right</i> | |
| <span>3. Look Right (Angle)</span> | |
| </div> | |
| <button class="btn" style="padding: 4px 10px; font-size: 11px; border-radius: 6px;" onclick="app.captureAngle('right')">SCAN</button> | |
| </div> | |
| <div class="angle-card" data-angle="up"> | |
| <div class="angle-label"> | |
| <i class="material-icons angle-status-icon">expand_less</i> | |
| <span>4. Tilt Up</span> | |
| </div> | |
| <button class="btn" style="padding: 4px 10px; font-size: 11px; border-radius: 6px;" onclick="app.captureAngle('up')">SCAN</button> | |
| </div> | |
| <div class="angle-card" data-angle="down"> | |
| <div class="angle-label"> | |
| <i class="material-icons angle-status-icon">expand_more</i> | |
| <span>5. Tilt Down</span> | |
| </div> | |
| <button class="btn" style="padding: 4px 10px; font-size: 11px; border-radius: 6px;" onclick="app.captureAngle('down')">SCAN</button> | |
| </div> | |
| </div> | |
| </div> | |
| <div style="display: flex; justify-content: space-between; align-items: center; margin-top: auto; padding-top: 16px;"> | |
| <div style="font-size: 12px; color: var(--text-secondary); font-weight: 500;" id="scan-progress-label"> | |
| Scan progress: <span style="color: var(--neon-cyan); font-weight: 700;">0 / 5 angles</span> captured | |
| </div> | |
| <div style="display: flex; gap: 12px;"> | |
| <button class="btn btn-danger" style="padding: 8px 16px;" id="btn-step2-back">Back</button> | |
| <button class="btn" style="padding: 8px 20px; opacity: 0.5;" id="btn-step2-next" disabled> | |
| <span>Save & Complete</span> | |
| <i class="material-icons">arrow_forward</i> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- STEP 3 PANEL: Completion Card --> | |
| <div class="wizard-panel" id="wizard-panel-3" style="align-items: center; justify-content: center; text-align: center; gap: 20px;"> | |
| <div style="width: 80px; height: 80px; border-radius: 50%; background: rgba(46, 204, 113, 0.1); border: 2px solid var(--neon-green); display: flex; align-items: center; justify-content: center; color: var(--neon-green); box-shadow: var(--shadow-neon-success); margin-bottom: 8px;"> | |
| <i class="material-icons" style="font-size: 48px;">check</i> | |
| </div> | |
| <h2 style="font-size: 24px; font-weight: 800;">Biometric Registration Successful!</h2> | |
| <p style="color: var(--text-secondary); font-size: 15px; max-width: 480px; line-height: 1.6;"> | |
| All 5 reference angle embeddings have been processed and encrypted locally. <span id="enrolled-emp-name" style="color: var(--neon-cyan); font-weight: 700;">Kunal Katiyar</span> is now active and will be recognized instantly by the camera stream. | |
| </p> | |
| <button class="btn" id="btn-wizard-finish" style="margin-top: 24px; padding: 12px 32px;"> | |
| <span>Return to Control Directory</span> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- 4. Floating Real-Time Success Alerts Toast Box --> | |
| <div class="alerts-container" id="alerts-container"> | |
| <!-- Notifications automatically fade-in/out here --> | |
| </div> | |
| <!-- Hidden video element for client-side webcam capture --> | |
| <video id="client-webcam" style="display: none;" autoplay playsinline></video> | |
| <!-- Javascript client logic --> | |
| <script src="/static/js/app.js"></script> | |
| </body> | |
| </html> | |