Phase 2 Step 5: Implement Basic UI Templates

Implement comprehensive web UI with dark slate theme matching HTML reports:

Templates:
- Create base.html with navigation, dark theme (#0f172a background)
- Update dashboard.html with stats cards and recent scans table
- Update scans.html with pagination, filtering, and status badges
- Update scan_detail.html with comprehensive scan results display
- Update login.html to extend base template with centered design

Features:
- AJAX-powered dynamic data loading from API endpoints
- Auto-refresh for running scans (10-15 second intervals)
- Responsive Bootstrap 5 grid layout
- Color scheme matches report_mockup.html (slate dark theme)
- Status badges (success/danger/warning/info) with proper colors
- Modal dialogs for triggering scans
- Pagination with ellipsis for large result sets
- Delete confirmation dialogs
- Loading spinners for async operations

Bug Fixes:
- Fix scanner.py imports to use 'src.' prefix for module imports
- Fix scans.py to import validate_page_params from pagination module

All templates use consistent color palette:
- Background: #0f172a, Cards: #1e293b, Accent: #60a5fa
- Success: #065f46/#6ee7b7, Danger: #7f1d1d/#fca5a5
- Warning: #78350f/#fcd34d, Info: #1e3a8a/#93c5fd
This commit is contained in:
2025-11-14 11:51:27 -06:00
parent 0791c60f60
commit a64096ece3
8 changed files with 1664 additions and 200 deletions

View File

@@ -1,95 +1,99 @@
<!DOCTYPE html>
<html lang="en" data-bs-theme="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Login - SneakyScanner</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
body {
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
}
.login-container {
width: 100%;
max-width: 400px;
padding: 2rem;
}
.card {
border: none;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
}
.brand-title {
color: #00d9ff;
font-weight: 600;
margin-bottom: 0.5rem;
}
</style>
</head>
<body>
<div class="login-container">
<div class="card">
<div class="card-body p-5">
<div class="text-center mb-4">
<h1 class="brand-title">SneakyScanner</h1>
<p class="text-muted">Network Security Scanner</p>
</div>
{% extends "base.html" %}
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
<div class="alert alert-{{ 'danger' if category == 'error' else category }} alert-dismissible fade show" role="alert">
{{ message }}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
{% endfor %}
{% endif %}
{% endwith %}
{% block title %}Login - SneakyScanner{% endblock %}
{% if password_not_set %}
<div class="alert alert-warning">
<strong>Setup Required:</strong> Please set an application password first.
<a href="{{ url_for('auth.setup') }}" class="alert-link">Go to Setup</a>
</div>
{% else %}
<form method="post" action="{{ url_for('auth.login') }}">
<div class="mb-3">
<label for="password" class="form-label">Password</label>
<input type="password"
class="form-control form-control-lg"
id="password"
name="password"
required
autofocus
placeholder="Enter your password">
</div>
{% block extra_styles %}
body {
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(135deg, #0f172a 0%, #1e293b 100%);
}
<div class="mb-3 form-check">
<input type="checkbox"
class="form-check-input"
id="remember"
name="remember">
<label class="form-check-label" for="remember">
Remember me
</label>
</div>
.container-fluid {
max-width: 450px;
padding: 0;
}
<button type="submit" class="btn btn-primary btn-lg w-100">
Login
</button>
</form>
{% endif %}
</div>
</div>
.login-card {
background-color: #1e293b;
border: 1px solid #334155;
border-radius: 12px;
padding: 3rem;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
}
<div class="text-center mt-3">
<small class="text-muted">SneakyScanner v1.0 - Phase 2</small>
</div>
.brand-title {
color: #60a5fa;
font-weight: 600;
font-size: 2rem;
margin-bottom: 0.5rem;
}
.brand-subtitle {
color: #94a3b8;
font-size: 0.95rem;
}
.btn-primary {
padding: 0.75rem;
font-size: 1rem;
font-weight: 500;
}
.footer {
position: fixed;
bottom: 20px;
left: 0;
right: 0;
margin: 0;
padding: 0;
border: none;
}
{% endblock %}
{% set hide_nav = true %}
{% block content %}
<div class="login-card">
<div class="text-center mb-4">
<h1 class="brand-title">SneakyScanner</h1>
<p class="brand-subtitle">Network Security Scanner</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
{% if password_not_set %}
<div class="alert alert-warning">
<strong>Setup Required:</strong> Please set an application password first.
<a href="{{ url_for('auth.setup') }}" class="alert-link">Go to Setup</a>
</div>
{% else %}
<form method="post" action="{{ url_for('auth.login') }}">
<div class="mb-3">
<label for="password" class="form-label">Password</label>
<input type="password"
class="form-control form-control-lg"
id="password"
name="password"
required
autofocus
placeholder="Enter your password">
</div>
<div class="mb-3 form-check">
<input type="checkbox"
class="form-check-input"
id="remember"
name="remember">
<label class="form-check-label" for="remember">
Remember me
</label>
</div>
<button type="submit" class="btn btn-primary btn-lg w-100">
Login
</button>
</form>
{% endif %}
</div>
{% endblock %}