210 lines
8.0 KiB
HTML
210 lines
8.0 KiB
HTML
<!--
|
|
=============================================================================
|
|
APtool — Login Page (login.html)
|
|
=============================================================================
|
|
This is the first page a technician sees. It collects two pieces of info:
|
|
|
|
1. Site Number (4 digits) — identifies which job site the tech is at.
|
|
Stored in the session and used to organize all files into per-site
|
|
folders (e.g. uploads/5001/, Nextcloud APtool/5001/).
|
|
|
|
2. PIN — the site-specific PIN code.
|
|
Validated server-side against sites.conf (each site has its own PIN).
|
|
|
|
On successful login:
|
|
- session["authenticated"] = True
|
|
- session["site"] = "5001"
|
|
- Redirects to / (the main data collection form)
|
|
|
|
On failure:
|
|
- Re-renders this page with an error message (wrong PIN or bad site format)
|
|
|
|
Template variables (passed from Flask):
|
|
- error — error message string, or None if no error
|
|
|
|
Design:
|
|
- Centered card layout, vertically and horizontally centered on screen
|
|
- Large, touch-friendly inputs with numeric keyboard hints (inputmode)
|
|
- Site number input uses pattern="[0-9]{4}" for client-side validation
|
|
- PIN input uses type="password" to hide the digits
|
|
=============================================================================
|
|
-->
|
|
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<!-- Mobile-first: ensure proper scaling on phones/tablets -->
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>APtool - Login</title>
|
|
|
|
<style>
|
|
/* =================================================================
|
|
Reset & Base Styles
|
|
================================================================= */
|
|
*, *::before, *::after {
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
/* Full-viewport centered layout using flexbox.
|
|
The body acts as the flex container to vertically and
|
|
horizontally center the login card on all screen sizes. */
|
|
body {
|
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
margin: 0;
|
|
padding: 16px;
|
|
background: #1a1a2e;
|
|
color: #e0e0e0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: center;
|
|
align-items: center;
|
|
min-height: 100vh;
|
|
}
|
|
|
|
/* =================================================================
|
|
Login Card
|
|
================================================================= */
|
|
/* White card with shadow — max 320px wide so it looks good on
|
|
both phones (full width with padding) and desktops (compact card) */
|
|
.login-box {
|
|
background: #16213e;
|
|
border-radius: 12px;
|
|
padding: 32px 24px;
|
|
width: 100%;
|
|
max-width: 320px;
|
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.4);
|
|
text-align: center;
|
|
}
|
|
|
|
/* App title in the card */
|
|
h1 {
|
|
font-size: 1.5rem;
|
|
margin: 0 0 8px;
|
|
}
|
|
|
|
/* "Enter site number and PIN" subtitle */
|
|
.subtitle {
|
|
color: #999;
|
|
font-size: 0.9rem;
|
|
margin: 0 0 24px;
|
|
}
|
|
|
|
/* =================================================================
|
|
PIN Input
|
|
================================================================= */
|
|
/* Large centered text with letter spacing to mimic a PIN entry UI.
|
|
type="password" hides the digits as they are typed.
|
|
inputmode="numeric" brings up the number keyboard on mobile. */
|
|
input[type="password"] {
|
|
width: 100%;
|
|
padding: 14px;
|
|
font-size: 1.5rem;
|
|
text-align: center;
|
|
letter-spacing: 0.5em;
|
|
border: 2px solid #2a2a4a;
|
|
border-radius: 8px;
|
|
background: #0f3460;
|
|
color: #e0e0e0;
|
|
-moz-appearance: textfield; /* Hide Firefox number spinner */
|
|
}
|
|
|
|
/* Blue focus ring on the PIN input */
|
|
input[type="password"]:focus {
|
|
outline: none;
|
|
border-color: #2563eb;
|
|
box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.2);
|
|
}
|
|
|
|
/* Hide the increment/decrement spinner arrows that some browsers
|
|
show on inputs with inputmode="numeric" */
|
|
input::-webkit-outer-spin-button,
|
|
input::-webkit-inner-spin-button {
|
|
-webkit-appearance: none;
|
|
margin: 0;
|
|
}
|
|
|
|
/* =================================================================
|
|
Unlock Button
|
|
================================================================= */
|
|
/* Full-width blue button — same style as the main form's submit.
|
|
Large padding for easy tapping on mobile. */
|
|
button {
|
|
width: 100%;
|
|
padding: 14px;
|
|
font-size: 1.1rem;
|
|
font-weight: 600;
|
|
color: #fff;
|
|
background: #2563eb;
|
|
border: none;
|
|
border-radius: 8px;
|
|
cursor: pointer;
|
|
margin-top: 16px;
|
|
}
|
|
|
|
/* Darker blue on tap/press */
|
|
button:active {
|
|
background: #1d4ed8;
|
|
}
|
|
|
|
/* =================================================================
|
|
Error Message
|
|
================================================================= */
|
|
/* Red banner shown below the form when login fails.
|
|
Only rendered if Flask passes a non-None error variable. */
|
|
.error {
|
|
color: #fca5a5;
|
|
background: #3b1111;
|
|
padding: 10px;
|
|
border-radius: 8px;
|
|
margin-top: 16px;
|
|
font-weight: 600;
|
|
font-size: 0.9rem;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<!-- ================================================================
|
|
Login Card
|
|
================================================================ -->
|
|
<div class="login-box">
|
|
<div style="color:#999;font-size:0.8rem;margin-bottom:8px;">JCP Wifi Migration 2026</div>
|
|
<h1>APtool</h1>
|
|
<p class="subtitle">Enter site number and PIN</p>
|
|
|
|
<!-- Login form — POSTs to /login which validates and sets session -->
|
|
<form method="POST" action="/login">
|
|
|
|
<!-- Site Number Input
|
|
- type="text" with inputmode="numeric" to get the number
|
|
keyboard on mobile while allowing leading zeros
|
|
- autofocus opens the keyboard immediately on mobile
|
|
- Validated server-side against sites.conf
|
|
- Inline styles match the password input's look & feel -->
|
|
<input type="text" name="site" inputmode="numeric"
|
|
placeholder="Site Number" required autofocus
|
|
autocomplete="off" style="width:100%;padding:14px;font-size:1.2rem;text-align:center;
|
|
letter-spacing:0.3em;border:2px solid #2a2a4a;border-radius:8px;margin-bottom:12px;background:#0f3460;color:#e0e0e0;">
|
|
|
|
<!-- PIN Input
|
|
- type="password" hides the entered digits
|
|
- inputmode="numeric" brings up the number keyboard
|
|
- Validated server-side against the site's PIN in sites.conf -->
|
|
<input type="password" name="pin" inputmode="numeric" pattern="[0-9]*"
|
|
placeholder="PIN" required autocomplete="off">
|
|
|
|
<button type="submit">Unlock</button>
|
|
</form>
|
|
|
|
<!-- Error message — only shown if Flask passes error != None.
|
|
Uses Jinja2 "if" template syntax. -->
|
|
{% if error %}
|
|
<div class="error">{{ error }}</div>
|
|
{% endif %}
|
|
</div>
|
|
<div style="text-align:center;margin-top:16px;">
|
|
<a href="/admin/login" style="color:#666;font-size:0.8rem;text-decoration:none;">Admin Login</a>
|
|
</div>
|
|
<div style="text-align:center;margin-top:24px;color:#555;font-size:0.7rem;">© 2026 Mack Allison, sdAnywhere LLC (with Claude Code)</div>
|
|
</body>
|
|
</html>
|