chore: import local project into Gitea
This commit is contained in:
272
WiFiX-Enhanced/data/index.html
Normal file
272
WiFiX-Enhanced/data/index.html
Normal file
@@ -0,0 +1,272 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang=\"en\">
|
||||
<head>
|
||||
<meta charset=\"UTF-8\">
|
||||
<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">
|
||||
<title>Public WiFi Portal</title>
|
||||
<style>
|
||||
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; background: #f5f6f8; color: #333; margin: 0; }
|
||||
.container { max-width: 900px; margin: 30px auto; padding: 0 20px; }
|
||||
.header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; }
|
||||
.logo { display: flex; align-items: center; gap: 12px; }
|
||||
.logo-icon { width: 38px; height: 38px; background: #e6f2fb; border-radius: 10px; display: flex; align-items: center; justify-content: center; }
|
||||
.logo-icon svg { width: 22px; height: 22px; fill: #0078d4; }
|
||||
.logo-text { display: flex; flex-direction: column; }
|
||||
.logo-title { font-size: 18px; font-weight: 700; color: #222; }
|
||||
.logo-subtitle { font-size: 12px; color: #777; }
|
||||
.connection-status { display: flex; align-items: center; gap: 8px; }
|
||||
.status-dot { width: 10px; height: 10px; background: #22c55e; border-radius: 50%; box-shadow: 0 0 0 3px rgba(34,197,94,0.2); }
|
||||
h1 { font-size: 24px; margin: 10px 0 5px; }
|
||||
.welcome-text { background: #fff; border: 1px solid #eaeaea; border-radius: 8px; padding: 15px; margin-bottom: 15px; }
|
||||
.network-info { background: #fff; border: 1px solid #eaeaea; border-radius: 8px; margin-bottom: 20px; }
|
||||
.network-info-header { display: flex; align-items: center; gap: 10px; padding: 12px 15px; border-bottom: 1px solid #f0f0f0; }
|
||||
.network-info-icon { width: 20px; height: 20px; fill: #0078d4; }
|
||||
.network-details { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; padding: 15px; }
|
||||
.detail-item { display: flex; align-items: center; gap: 10px; }
|
||||
.detail-label { width: 140px; font-size: 13px; color: #666; }
|
||||
.detail-value { font-size: 14px; font-weight: 600; color: #222; }
|
||||
.signal-strength { display: flex; align-items: flex-end; gap: 3px; height: 16px; }
|
||||
.signal-bar { width: 6px; background: #0078d4; border-radius: 3px; }
|
||||
.signal-bar.empty { background: #e0e0e0; }
|
||||
.auth-section { background: #fff; border: 1px solid #eaeaea; border-radius: 8px; margin-top: 20px; }
|
||||
.tabs { display: flex; border-bottom: 1px solid #f0f0f0; }
|
||||
.tab { flex: 1; text-align: center; padding: 12px; cursor: pointer; font-weight: 600; color: #666; transition: all 0.2s ease; }
|
||||
.tab.active { color: #0078d4; border-bottom: 2px solid #0078d4; background: #f7fbff; }
|
||||
.tab-content { display: none; }
|
||||
.tab-content.active { display: block; }
|
||||
.form-row { display: flex; gap: 12px; }
|
||||
.form-group { margin-bottom: 12px; }
|
||||
.form-label { display: block; font-size: 13px; color: #555; margin-bottom: 5px; }
|
||||
.form-control { width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 4px; font-size: 14px; transition: all 0.2s ease; }
|
||||
.form-control:focus { border-color: #0078d4; outline: none; box-shadow: 0 0 0 3px rgba(0, 120, 212, 0.1); }
|
||||
.btn { display: inline-block; padding: 10px 16px; font-size: 14px; font-weight: 500; text-align: center; border-radius: 4px; cursor: pointer; transition: all 0.2s ease; border: none; width: 100%; }
|
||||
.btn-primary { background: #0078d4; color: #fff; }
|
||||
.btn-primary:hover { background: #006cc1; }
|
||||
.social-login { margin-top: 20px; text-align: center; display: none; }
|
||||
.policy-notice { margin-top: 25px; padding-top: 20px; border-top: 1px solid #eaeaea; font-size: 12px; color: #888; line-height: 1.5; }
|
||||
.policy-link { color: #0078d4; text-decoration: none; }
|
||||
.policy-link:hover { text-decoration: underline; }
|
||||
.footer { margin-top: 30px; display: flex; justify-content: space-between; align-items: center; font-size: 12px; color: #888; }
|
||||
.loading-overlay { position: fixed; inset: 0; background: rgba(255,255,255,0.8); display: flex; align-items: center; justify-content: center; flex-direction: column; z-index: 1000; opacity: 0; visibility: hidden; transition: all 0.3s ease; }
|
||||
.loading-overlay.show { opacity: 1; visibility: visible; }
|
||||
.spinner { width: 40px; height: 40px; border: 4px solid rgba(0,120,212,0.2); border-radius: 50%; border-top-color: #0078d4; animation: spin 1s linear infinite; margin-bottom: 15px; }
|
||||
.loading-text { font-size: 16px; color: #333; font-weight: 500; }
|
||||
@keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
|
||||
.error-message { color: #d32f2f; font-size: 14px; margin-top: 5px; display: none; }
|
||||
.error-message.show { display: block; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class=\"loading-overlay\" id=\"loadingOverlay\">
|
||||
<div class=\"spinner\"></div>
|
||||
<div class=\"loading-text\">Authenticating...</div>
|
||||
</div>
|
||||
<div class=\"container\">
|
||||
<div class=\"header\">
|
||||
<div class=\"logo\">
|
||||
<div class=\"logo-icon\">
|
||||
<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M12,21L15.6,16.2C14.6,15.45 13.35,15 12,15C10.65,15 9.4,15.45 8.4,16.2L12,21M12,3C7.95,3 4.21,4.34 1.2,6.6L3,9C5.5,7.12 8.62,6 12,6C15.38,6 18.5,7.12 21,9L22.8,6.6C19.79,4.34 16.05,3 12,3M12,9C9.3,9 6.81,9.89 4.8,11.4L6.6,13.8C8.1,12.67 9.97,12 12,12C14.03,12 15.9,12.67 17.4,13.8L19.2,11.4C17.19,9.89 14.7,9 12,9Z\"/></svg>
|
||||
</div>
|
||||
<div class=\"logo-text\">
|
||||
<span class=\"logo-title\" id=\"brandTitle\">Public WiFi</span>
|
||||
<span class=\"logo-subtitle\" id=\"brandSubtitle\">Wireless Network</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class=\"connection-status\">
|
||||
<span class=\"status-dot\"></span>
|
||||
<span>Connected</span>
|
||||
</div>
|
||||
</div>
|
||||
<h1>Authentication Required</h1>
|
||||
<div class=\"welcome-text\">
|
||||
<p>Welcome to this public WiFi portal. Please authenticate to access the internet. This secure portal helps maintain network quality and security for all users.</p>
|
||||
</div>
|
||||
<div class=\"network-info\">
|
||||
<div class=\"network-info-header\">
|
||||
<svg class=\"network-info-icon\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M12,21L15.6,16.2C14.6,15.45 13.35,15 12,15C10.65,15 9.4,15.45 8.4,16.2L12,21M12,3C7.95,3 4.21,4.34 1.2,6.6L3,9C5.5,7.12 8.62,6 12,6C15.38,6 18.5,7.12 21,9L22.8,6.6C19.79,4.34 16.05,3 12,3M12,9C9.3,9 6.81,9.89 4.8,11.4L6.6,13.8C8.1,12.67 9.97,12 12,12C14.03,12 15.9,12.67 17.4,13.8L19.2,11.4C17.19,9.89 14.7,9 12,9Z\"/></svg>
|
||||
<span class=\"network-info-title\">Network Information</span>
|
||||
</div>
|
||||
<div class=\"network-details\">
|
||||
<div class=\"detail-item\">
|
||||
<span class=\"detail-label\">SSID:</span>
|
||||
<span class=\"detail-value\" id=\"ssidName\">Unknown</span>
|
||||
</div>
|
||||
<div class=\"detail-item\">
|
||||
<span class=\"detail-label\">Signal Strength:</span>
|
||||
<div class=\"signal-strength\">
|
||||
<div class=\"signal-bar\" style=\"height:5px\"></div>
|
||||
<div class=\"signal-bar\" style=\"height:8px\"></div>
|
||||
<div class=\"signal-bar\" style=\"height:12px\"></div>
|
||||
<div class=\"signal-bar\" style=\"height:16px\"></div>
|
||||
<div class=\"signal-bar empty\" style=\"height:16px\"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class=\"detail-item\">
|
||||
<span class=\"detail-label\">Security Type:</span>
|
||||
<span class=\"detail-value\" id=\"securityType\">Unknown</span>
|
||||
</div>
|
||||
<div class=\"detail-item\">
|
||||
<span class=\"detail-label\">Portal IP:</span>
|
||||
<span class=\"detail-value\" id=\"ipAddress\">Loading...</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class=\"auth-section\">
|
||||
<div class=\"tabs\">
|
||||
<div class=\"tab active\" data-tab=\"emailTab\">Email Login</div>
|
||||
<div class=\"tab\" data-tab=\"residentTab\">Resident ID</div>
|
||||
<div class=\"tab\" data-tab=\"businessTab\">Business Account</div>
|
||||
</div>
|
||||
<div class=\"tab-content active\" id=\"emailTab\">
|
||||
<div class=\"form-group\">
|
||||
<label for=\"email\" class=\"form-label\">Email</label>
|
||||
<input type=\"email\" id=\"email\" class=\"form-control\" placeholder=\"you@example.com\" required />
|
||||
</div>
|
||||
<div class=\"form-group\">
|
||||
<label for=\"password\" class=\"form-label\">Password</label>
|
||||
<input type=\"password\" id=\"password\" class=\"form-control\" placeholder=\"Enter your password\" required />
|
||||
</div>
|
||||
<div id=\"error-message\" class=\"error-message\"></div>
|
||||
<button class=\"btn btn-primary\" onclick=\"authenticate()\">Sign In</button>
|
||||
<div class=\"policy-notice\">
|
||||
By signing in, you agree to the <a class=\"policy-link\" href=\"#\">acceptable use policy</a> and <a class=\"policy-link\" href=\"#\">privacy policy</a>.
|
||||
</div>
|
||||
</div>
|
||||
<div class=\"tab-content\" id=\"residentTab\">
|
||||
<div class=\"form-row\">
|
||||
<div class=\"form-group\" style=\"flex:1\">
|
||||
<label for=\"resident-id\" class=\"form-label\">Resident ID</label>
|
||||
<input type=\"text\" id=\"resident-id\" class=\"form-control\" placeholder=\"ID Number\" />
|
||||
</div>
|
||||
<div class=\"form-group\" style=\"flex:1\">
|
||||
<label for=\"resident-password\" class=\"form-label\">Password</label>
|
||||
<input type=\"password\" id=\"resident-password\" class=\"form-control\" placeholder=\"Enter password\" />
|
||||
</div>
|
||||
</div>
|
||||
<button class=\"btn btn-primary\" onclick=\"authenticateResident()\">Sign In</button>
|
||||
</div>
|
||||
<div class=\"tab-content\" id=\"businessTab\">
|
||||
<div class=\"form-row\">
|
||||
<div class=\"form-group\" style=\"flex:1\">
|
||||
<label for=\"business-id\" class=\"form-label\">Business ID</label>
|
||||
<input type=\"text\" id=\"business-id\" class=\"form-control\" placeholder=\"Account ID\" />
|
||||
</div>
|
||||
<div class=\"form-group\" style=\"flex:1\">
|
||||
<label for=\"business-password\" class=\"form-label\">Password</label>
|
||||
<input type=\"password\" id=\"business-password\" class=\"form-control\" placeholder=\"Enter password\" />
|
||||
</div>
|
||||
</div>
|
||||
<button class=\"btn btn-primary\" onclick=\"authenticateBusiness()\">Sign In</button>
|
||||
</div>
|
||||
<div class=\"social-login\">
|
||||
<!-- Social login intentionally hidden for captive portal -->
|
||||
</div>
|
||||
<div class=\"footer\">
|
||||
<div> <span id=\"brandYear\"></span> <span id=\"brandFooter\">Public WiFi</span></div>
|
||||
<div>Fair Use | Privacy</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
const showLoading = () => document.getElementById('loadingOverlay').classList.add('show');
|
||||
const hideLoading = () => document.getElementById('loadingOverlay').classList.remove('show');
|
||||
|
||||
function showError(message) {
|
||||
const el = document.getElementById('error-message');
|
||||
el.textContent = message || 'Authentication failed. Please try again.';
|
||||
el.classList.add('show');
|
||||
hideLoading();
|
||||
}
|
||||
|
||||
async function authenticate() {
|
||||
const email = document.getElementById('email').value.trim();
|
||||
const password = document.getElementById('password').value.trim();
|
||||
document.getElementById('error-message').classList.remove('show');
|
||||
if (!email || !password) { return showError('Please enter email and password.'); }
|
||||
showLoading();
|
||||
// Submit to firmware /login endpoint using a standard form POST (works across captive portals)
|
||||
const form = document.createElement('form');
|
||||
form.method = 'POST';
|
||||
form.action = '/login';
|
||||
const u = document.createElement('input'); u.type = 'hidden'; u.name = 'username'; u.value = email;
|
||||
const p = document.createElement('input'); p.type = 'hidden'; p.name = 'password'; p.value = password;
|
||||
form.appendChild(u); form.appendChild(p);
|
||||
document.body.appendChild(form);
|
||||
form.submit();
|
||||
}
|
||||
|
||||
async function authenticateResident() {
|
||||
const id = document.getElementById('resident-id').value.trim();
|
||||
const pass = document.getElementById('resident-password').value.trim();
|
||||
if (!id || !pass) { return showError('Please enter your Resident ID and password.'); }
|
||||
showLoading();
|
||||
const form = document.createElement('form');
|
||||
form.method = 'POST';
|
||||
form.action = '/login';
|
||||
const u = document.createElement('input'); u.type = 'hidden'; u.name = 'username'; u.value = id;
|
||||
const p = document.createElement('input'); p.type = 'hidden'; p.name = 'password'; p.value = pass;
|
||||
form.appendChild(u); form.appendChild(p);
|
||||
document.body.appendChild(form);
|
||||
form.submit();
|
||||
}
|
||||
|
||||
async function authenticateBusiness() {
|
||||
const id = document.getElementById('business-id').value.trim();
|
||||
const pass = document.getElementById('business-password').value.trim();
|
||||
if (!id || !pass) { return showError('Please enter your Business ID and password.'); }
|
||||
showLoading();
|
||||
const form = document.createElement('form');
|
||||
form.method = 'POST';
|
||||
form.action = '/login';
|
||||
const u = document.createElement('input'); u.type = 'hidden'; u.name = 'username'; u.value = id;
|
||||
const p = document.createElement('input'); p.type = 'hidden'; p.name = 'password'; p.value = pass;
|
||||
form.appendChild(u); form.appendChild(p);
|
||||
document.body.appendChild(form);
|
||||
form.submit();
|
||||
}
|
||||
|
||||
async function populateBranding() {
|
||||
try {
|
||||
const res = await fetch('/api/config');
|
||||
if (res.ok) {
|
||||
const cfg = await res.json();
|
||||
const brand = cfg.branding_name || 'Public WiFi';
|
||||
const org = cfg.organization || 'Wireless Network';
|
||||
document.title = brand + ' Portal';
|
||||
document.getElementById('brandTitle').textContent = brand;
|
||||
document.getElementById('brandSubtitle').textContent = org;
|
||||
document.getElementById('brandFooter').textContent = brand;
|
||||
}
|
||||
} catch (e) { /* ignore */ }
|
||||
document.getElementById('brandYear').textContent = new Date().getFullYear();
|
||||
}
|
||||
|
||||
async function populateNetwork() {
|
||||
try {
|
||||
const res = await fetch('/api/status');
|
||||
if (res.ok) {
|
||||
const s = await res.json();
|
||||
if (s.target_ssid) document.getElementById('ssidName').textContent = s.target_ssid;
|
||||
if (s.ap_ip) document.getElementById('ipAddress').textContent = s.ap_ip; else document.getElementById('ipAddress').textContent = 'Unavailable';
|
||||
} else {
|
||||
document.getElementById('ipAddress').textContent = 'Unavailable';
|
||||
}
|
||||
} catch (e) {
|
||||
document.getElementById('ipAddress').textContent = 'Unavailable';
|
||||
}
|
||||
}
|
||||
|
||||
// Tab switching
|
||||
document.querySelectorAll('.tab').forEach(tab => {
|
||||
tab.addEventListener('click', () => {
|
||||
document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
|
||||
document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active'));
|
||||
tab.classList.add('active');
|
||||
document.getElementById(tab.getAttribute('data-tab')).classList.add('active');
|
||||
});
|
||||
});
|
||||
|
||||
window.onload = () => { populateBranding(); populateNetwork(); };
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
919
WiFiX-Enhanced/data/welcome.html
Normal file
919
WiFiX-Enhanced/data/welcome.html
Normal file
@@ -0,0 +1,919 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>CityNet Public WiFi - Connected</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Roboto', sans-serif;
|
||||
background-color: #f5f5f5;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 20px;
|
||||
background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiPjxkZWZzPjxwYXR0ZXJuIGlkPSJwYXR0ZXJuXzAiIHBhdHRlcm5Vbml0cz0idXNlclNwYWNlT25Vc2UiIHdpZHRoPSIxMCIgaGVpZ2h0PSIxMCIgcGF0dGVyblRyYW5zZm9ybT0icm90YXRlKDQ1KSI+PHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjUiIGhlaWdodD0iNSIgZmlsbD0icmdiYSgyMjAsIDIyMCwgMjIwLCAwLjIpIi8+PC9wYXR0ZXJuPjwvZGVmcz48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI3BhdHRlcm5fMCkiLz48L3N2Zz4=');
|
||||
}
|
||||
|
||||
.container {
|
||||
background: #ffffff;
|
||||
border-radius: 8px;
|
||||
padding: 40px;
|
||||
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.08);
|
||||
max-width: 650px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 30px;
|
||||
border-bottom: 1px solid #eaeaea;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
.logo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.logo-icon {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background: #0078d4;
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.logo-icon svg {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
fill: white;
|
||||
}
|
||||
|
||||
.logo-text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.logo-title {
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.logo-subtitle {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.connection-status {
|
||||
margin-left: auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: #e6f7e6;
|
||||
padding: 6px 12px;
|
||||
border-radius: 16px;
|
||||
font-size: 12px;
|
||||
color: #2e7d32;
|
||||
}
|
||||
|
||||
.status-dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background: #2e7d32;
|
||||
border-radius: 50%;
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
.success-icon {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
background: #e6f7e6;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 0 auto 25px;
|
||||
}
|
||||
|
||||
.success-icon svg {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
fill: #2e7d32;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: #333;
|
||||
margin-bottom: 15px;
|
||||
font-size: 24px;
|
||||
font-weight: 500;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.welcome-text {
|
||||
color: #555;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 25px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.alert {
|
||||
background: #fff8e1;
|
||||
border-left: 4px solid #ffc107;
|
||||
padding: 15px;
|
||||
margin-bottom: 25px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.alert-title {
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
margin-bottom: 5px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.alert-title svg {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
fill: #ffc107;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.alert-content {
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.session-timer {
|
||||
background: #f9f9f9;
|
||||
border-radius: 8px;
|
||||
padding: 15px;
|
||||
margin-bottom: 25px;
|
||||
border: 1px solid #eaeaea;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.timer-label {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.timer-display {
|
||||
font-size: 28px;
|
||||
font-weight: 700;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.dashboard {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 20px;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.dashboard-card {
|
||||
background: #f9f9f9;
|
||||
border-radius: 8px;
|
||||
padding: 15px;
|
||||
border: 1px solid #eaeaea;
|
||||
}
|
||||
|
||||
.dashboard-title {
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
margin-bottom: 15px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.dashboard-title svg {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
fill: #0078d4;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.stat-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
height: 6px;
|
||||
background: #e0e0e0;
|
||||
border-radius: 3px;
|
||||
margin-top: 5px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.progress-fill {
|
||||
height: 100%;
|
||||
background: #0078d4;
|
||||
border-radius: 3px;
|
||||
transition: width 0.5s ease;
|
||||
}
|
||||
|
||||
.progress-fill.high {
|
||||
background: #2e7d32;
|
||||
}
|
||||
|
||||
.progress-fill.medium {
|
||||
background: #ff9800;
|
||||
}
|
||||
|
||||
.progress-fill.low {
|
||||
background: #f44336;
|
||||
}
|
||||
|
||||
.tips-section {
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.tips-title {
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
margin-bottom: 15px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.tips-title svg {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
fill: #0078d4;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.tips-list {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.tip-item {
|
||||
display: flex;
|
||||
margin-bottom: 10px;
|
||||
color: #555;
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.tip-item svg {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
fill: #0078d4;
|
||||
margin-right: 10px;
|
||||
flex-shrink: 0;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
gap: 15px;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
background: white;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
padding: 12px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.action-btn:hover {
|
||||
border-color: #0078d4;
|
||||
background: #f0f7ff;
|
||||
}
|
||||
|
||||
.action-icon {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
fill: #0078d4;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.action-label {
|
||||
font-size: 13px;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.policy-notice {
|
||||
margin-top: 25px;
|
||||
padding-top: 20px;
|
||||
border-top: 1px solid #eaeaea;
|
||||
font-size: 12px;
|
||||
color: #888;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.policy-link {
|
||||
color: #0078d4;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.policy-link:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.footer {
|
||||
margin-top: 30px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 12px;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
.footer-links {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.footer-link {
|
||||
color: #666;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.footer-link:hover {
|
||||
color: #0078d4;
|
||||
}
|
||||
|
||||
.modal {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 1000;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.modal.show {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
padding: 25px;
|
||||
max-width: 500px;
|
||||
width: 100%;
|
||||
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.modal-title {
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.modal-close {
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
font-size: 20px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
display: inline-block;
|
||||
padding: 8px 16px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
text-align: center;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: #0078d4;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background: #006cc1;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: #f5f5f5;
|
||||
color: #333;
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background: #e9e9e9;
|
||||
}
|
||||
|
||||
.speed-test-container {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.speed-gauge {
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
margin: 0 auto 20px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.gauge-value {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.gauge-label {
|
||||
position: absolute;
|
||||
top: 70%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.test-results {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.test-result {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.result-value {
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.result-label {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.container {
|
||||
padding: 25px 20px;
|
||||
}
|
||||
|
||||
.dashboard {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.actions {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="modal" id="speedTestModal">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h3 class="modal-title">Network Speed Test</h3>
|
||||
<button class="modal-close" onclick="closeModal('speedTestModal')">×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="speed-test-container">
|
||||
<div class="speed-gauge">
|
||||
<svg width="150" height="150" viewBox="0 0 150 150">
|
||||
<circle cx="75" cy="75" r="65" fill="none" stroke="#e0e0e0" stroke-width="10" />
|
||||
<circle cx="75" cy="75" r="65" fill="none" stroke="#0078d4" stroke-width="10" stroke-dasharray="408" stroke-dashoffset="408" id="speedGauge" />
|
||||
</svg>
|
||||
<div class="gauge-value" id="speedValue">0</div>
|
||||
<div class="gauge-label">Mbps</div>
|
||||
</div>
|
||||
<div class="test-results">
|
||||
<div class="test-result">
|
||||
<div class="result-value" id="downloadValue">0 Mbps</div>
|
||||
<div class="result-label">Download</div>
|
||||
</div>
|
||||
<div class="test-result">
|
||||
<div class="result-value" id="uploadValue">0 Mbps</div>
|
||||
<div class="result-label">Upload</div>
|
||||
</div>
|
||||
<div class="test-result">
|
||||
<div class="result-value" id="pingValue">0 ms</div>
|
||||
<div class="result-label">Ping</div>
|
||||
</div>
|
||||
</div>
|
||||
<button class="btn btn-primary" id="startTestBtn" onclick="startSpeedTest()">Start Test</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal" id="supportModal">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h3 class="modal-title">Support Information</h3>
|
||||
<button class="modal-close" onclick="closeModal('supportModal')">×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p style="margin-bottom: 15px;">If you're experiencing issues with your connection, please contact our support team:</p>
|
||||
<div style="margin-bottom: 10px;">
|
||||
<strong>Support Hotline:</strong> 1-800-CITYNET (1-800-248-9638)
|
||||
</div>
|
||||
<div style="margin-bottom: 10px;">
|
||||
<strong>Email Support:</strong> support@citynet-wifi.gov
|
||||
</div>
|
||||
<div style="margin-bottom: 20px;">
|
||||
<strong>Hours:</strong> Monday-Friday, 8:00 AM - 8:00 PM
|
||||
</div>
|
||||
<p>Please have your session ID ready when contacting support:</p>
|
||||
<div style="background: #f5f5f5; padding: 10px; border-radius: 4px; font-family: monospace; margin-top: 10px;">
|
||||
Session ID: CN-<span id="sessionId">XXXX-XXXX-XXXX</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-secondary" onclick="closeModal('supportModal')">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<div class="logo">
|
||||
<div class="logo-icon">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<path d="M12,21L15.6,16.2C14.6,15.45 13.35,15 12,15C10.65,15 9.4,15.45 8.4,16.2L12,21M12,3C7.95,3 4.21,4.34 1.2,6.6L3,9C5.5,7.12 8.62,6 12,6C15.38,6 18.5,7.12 21,9L22.8,6.6C19.79,4.34 16.05,3 12,3M12,9C9.3,9 6.81,9.89 4.8,11.4L6.6,13.8C8.1,12.67 9.97,12 12,12C14.03,12 15.9,12.67 17.4,13.8L19.2,11.4C17.19,9.89 14.7,9 12,9Z" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="logo-text">
|
||||
<span class="logo-title">CityNet WiFi</span>
|
||||
<span class="logo-subtitle">Municipal Wireless Network</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="connection-status">
|
||||
<span class="status-dot"></span>
|
||||
<span>Connected</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="success-icon">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<path d="M12 2C6.5 2 2 6.5 2 12S6.5 22 12 22 22 17.5 22 12 17.5 2 12 2M10 17L5 12L6.41 10.59L10 14.17L17.59 6.58L19 8L10 17Z" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<h1>You're Connected!</h1>
|
||||
|
||||
<div class="welcome-text">
|
||||
<p>Welcome to CityNet Public WiFi. You now have full access to our municipal wireless network.</p>
|
||||
</div>
|
||||
|
||||
<div class="alert">
|
||||
<div class="alert-title">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<path d="M13,13H11V7H13M13,17H11V15H13M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2Z" />
|
||||
</svg>
|
||||
Network Maintenance Notice
|
||||
</div>
|
||||
<div class="alert-content">
|
||||
Scheduled maintenance will occur tonight from 2:00 AM to 4:00 AM. You may experience brief connectivity interruptions during this period.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="session-timer">
|
||||
<div class="timer-label">Your session expires in:</div>
|
||||
<div class="timer-display" id="timer">02:00:00</div>
|
||||
</div>
|
||||
|
||||
<div class="dashboard">
|
||||
<div class="dashboard-card">
|
||||
<div class="dashboard-title">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<path d="M12,21L15.6,16.2C14.6,15.45 13.35,15 12,15C10.65,15 9.4,15.45 8.4,16.2L12,21M12,3C7.95,3 4.21,4.34 1.2,6.6L3,9C5.5,7.12 8.62,6 12,6C15.38,6 18.5,7.12 21,9L22.8,6.6C19.79,4.34 16.05,3 12,3M12,9C9.3,9 6.81,9.89 4.8,11.4L6.6,13.8C8.1,12.67 9.97,12 12,12C14.03,12 15.9,12.67 17.4,13.8L19.2,11.4C17.19,9.89 14.7,9 12,9Z" />
|
||||
</svg>
|
||||
Connection Details
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<span class="stat-label">IP Address:</span>
|
||||
<span class="stat-value" id="ipAddress">10.0.0.123</span>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<span class="stat-label">Signal Strength:</span>
|
||||
<span class="stat-value" id="signalStrength">80%</span>
|
||||
<div class="progress-bar">
|
||||
<div class="progress-fill high" id="signalBar" style="width: 80%;"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<span class="stat-label">Connection Type:</span>
|
||||
<span class="stat-value">802.11n (2.4 GHz)</span>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<span class="stat-label">Access Point:</span>
|
||||
<span class="stat-value">CityNet_AP03</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dashboard-card">
|
||||
<div class="dashboard-title">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<path d="M17.9,17.39C17.64,16.59 16.89,16 16,16H15V13A1,1 0 0,0 14,12H8V10H10A1,1 0 0,0 11,9V7H13A2,2 0 0,0 15,5V4.59C17.93,5.77 20,8.64 20,12C20,14.08 19.2,15.97 17.9,17.39M11,19.93C7.05,19.44 4,16.08 4,12C4,11.38 4.08,10.78 4.21,10.21L9,15V16A2,2 0 0,0 11,18M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2Z" />
|
||||
</svg>
|
||||
Network Usage
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<span class="stat-label">Download Speed:</span>
|
||||
<span class="stat-value" id="downloadSpeed">12.5 Mbps</span>
|
||||
<div class="progress-bar">
|
||||
<div class="progress-fill medium" id="downloadBar" style="width: 50%;"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<span class="stat-label">Upload Speed:</span>
|
||||
<span class="stat-value" id="uploadSpeed">5.2 Mbps</span>
|
||||
<div class="progress-bar">
|
||||
<div class="progress-fill medium" id="uploadBar" style="width: 40%;"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<span class="stat-label">Data Used:</span>
|
||||
<span class="stat-value" id="dataUsed">0.0 MB</span>
|
||||
<div class="progress-bar">
|
||||
<div class="progress-fill" id="dataBar" style="width: 5%;"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<span class="stat-label">Latency:</span>
|
||||
<span class="stat-value" id="latency">28 ms</span>
|
||||
<div class="progress-bar">
|
||||
<div class="progress-fill high" id="latencyBar" style="width: 20%;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tips-section">
|
||||
<div class="tips-title">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<path d="M12,2A7,7 0 0,0 5,9C5,11.38 6.19,13.47 8,14.74V17A1,1 0 0,0 9,18H15A1,1 0 0,0 16,17V14.74C17.81,13.47 19,11.38 19,9A7,7 0 0,0 12,2M9,21A1,1 0 0,0 10,22H14A1,1 0 0,0 15,21V20H9V21Z" />
|
||||
</svg>
|
||||
Usage Tips
|
||||
</div>
|
||||
<ul class="tips-list">
|
||||
<li class="tip-item">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<path d="M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M11,16.5L18,9.5L16.59,8.09L11,13.67L7.91,10.59L6.5,12L11,16.5Z" />
|
||||
</svg>
|
||||
For optimal performance, stay within 100 feet of the access point.
|
||||
</li>
|
||||
<li class="tip-item">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<path d="M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M11,16.5L18,9.5L16.59,8.09L11,13.67L7.91,10.59L6.5,12L11,16.5Z" />
|
||||
</svg>
|
||||
Use secure connections (HTTPS) when accessing sensitive websites.
|
||||
</li>
|
||||
<li class="tip-item">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<path d="M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M11,16.5L18,9.5L16.59,8.09L11,13.67L7.91,10.59L6.5,12L11,16.5Z" />
|
||||
</svg>
|
||||
Your session will automatically expire after 2 hours of inactivity.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
<div class="action-btn" onclick="openModal('speedTestModal')">
|
||||
<svg class="action-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<path d="M12,16A3,3 0 0,1 9,13C9,11.88 9.61,10.9 10.5,10.39L20.21,4.77L14.68,14.35C14.18,15.33 13.17,16 12,16M12,3C13.81,3 15.5,3.5 16.97,4.32L14.87,5.53C14,5.19 13,5 12,5A8,8 0 0,0 4,13C4,15.21 4.89,17.21 6.34,18.65H6.35C6.74,19.04 6.74,19.67 6.35,20.06C5.96,20.45 5.32,20.45 4.93,20.07V20.07C3.12,18.26 2,15.76 2,13A10,10 0 0,1 12,3M22,13C22,15.76 20.88,18.26 19.07,20.07V20.07C18.68,20.45 18.05,20.45 17.66,20.06C17.27,19.67 17.27,19.04 17.66,18.65V18.65C19.11,17.2 20,15.21 20,13C20,12 19.81,11 19.46,10.1L20.67,8C21.5,9.5 22,11.18 22,13Z" />
|
||||
</svg>
|
||||
<span class="action-label">Speed Test</span>
|
||||
</div>
|
||||
<div class="action-btn" onclick="openModal('supportModal')">
|
||||
<svg class="action-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<path d="M12,1C7,1 3,5 3,10V17A3,3 0 0,0 6,20H9V12H5V10A7,7 0 0,1 12,3A7,7 0 0,1 19,10V12H15V20H18A3,3 0 0,0 21,17V10C21,5 16.97,1 12,1Z" />
|
||||
</svg>
|
||||
<span class="action-label">Support</span>
|
||||
</div>
|
||||
<div class="action-btn" onclick="disconnect()">
|
||||
<svg class="action-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<path d="M13,3H11V13H13V3M17.83,5.17L16.41,6.59C18.05,7.91 19,9.9 19,12A7,7 0 0,1 12,19C8.14,19 5,15.88 5,12C5,9.91 5.95,7.91 7.58,6.58L6.17,5.17C2.38,8.39 1.92,14.07 5.14,17.86C8.36,21.64 14.04,22.1 17.83,18.88C19.85,17.17 21,14.65 21,12C21,9.37 19.84,6.87 17.83,5.17Z" />
|
||||
</svg>
|
||||
<span class="action-label">Disconnect</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="policy-notice">
|
||||
This is a public network provided by City Municipal Services. By using this service, you agree to our <a href="#" class="policy-link">Terms of Service</a> and <a href="#" class="policy-link">Acceptable Use Policy</a>. Your activities may be monitored for security purposes.
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
<div class="footer-copyright">© 2024 City Municipal Services</div>
|
||||
<div class="footer-links">
|
||||
<a href="#" class="footer-link">Help</a>
|
||||
<a href="#" class="footer-link">Privacy</a>
|
||||
<a href="#" class="footer-link">Contact</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Generate random session ID
|
||||
function generateSessionId() {
|
||||
const chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
||||
let result = '';
|
||||
for (let i = 0; i < 12; i++) {
|
||||
if (i > 0 && i % 4 === 0) result += '-';
|
||||
result += chars[Math.floor(Math.random() * chars.length)];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Generate random IP in the 10.0.0.x range
|
||||
function generateRandomIP() {
|
||||
return "10.0.0." + Math.floor(Math.random() * 254 + 1);
|
||||
}
|
||||
|
||||
// Session timer
|
||||
let totalSeconds = 2 * 60 * 60; // 2 hours
|
||||
function updateTimer() {
|
||||
const hours = Math.floor(totalSeconds / 3600);
|
||||
const minutes = Math.floor((totalSeconds % 3600) / 60);
|
||||
const seconds = totalSeconds % 60;
|
||||
|
||||
document.getElementById('timer').textContent =
|
||||
(hours < 10 ? '0' + hours : hours) + ':' +
|
||||
(minutes < 10 ? '0' + minutes : minutes) + ':' +
|
||||
(seconds < 10 ? '0' + seconds : seconds);
|
||||
|
||||
if (totalSeconds <= 0) {
|
||||
clearInterval(timerInterval);
|
||||
window.location.href = '/';
|
||||
} else {
|
||||
totalSeconds--;
|
||||
}
|
||||
}
|
||||
|
||||
// Update network stats periodically
|
||||
function updateNetworkStats() {
|
||||
// Update signal strength (random fluctuation between 70-95%)
|
||||
const signalStrength = Math.floor(Math.random() * 25) + 70;
|
||||
document.getElementById('signalStrength').textContent = signalStrength + '%';
|
||||
document.getElementById('signalBar').style.width = signalStrength + '%';
|
||||
|
||||
// Update download speed (random fluctuation between 8-15 Mbps)
|
||||
const downloadSpeed = (Math.random() * 7 + 8).toFixed(1);
|
||||
document.getElementById('downloadSpeed').textContent = downloadSpeed + ' Mbps';
|
||||
document.getElementById('downloadBar').style.width = (downloadSpeed / 25 * 100) + '%';
|
||||
|
||||
// Update upload speed (random fluctuation between 3-7 Mbps)
|
||||
const uploadSpeed = (Math.random() * 4 + 3).toFixed(1);
|
||||
document.getElementById('uploadSpeed').textContent = uploadSpeed + ' Mbps';
|
||||
document.getElementById('uploadBar').style.width = (uploadSpeed / 10 * 100) + '%';
|
||||
|
||||
// Update latency (random fluctuation between 20-40 ms)
|
||||
const latency = Math.floor(Math.random() * 20) + 20;
|
||||
document.getElementById('latency').textContent = latency + ' ms';
|
||||
document.getElementById('latencyBar').style.width = (latency / 100 * 100) + '%';
|
||||
|
||||
// Update data used (incrementing)
|
||||
const currentData = parseFloat(document.getElementById('dataUsed').textContent);
|
||||
const newData = (currentData + Math.random() * 0.5).toFixed(1);
|
||||
document.getElementById('dataUsed').textContent = newData + ' MB';
|
||||
document.getElementById('dataBar').style.width = Math.min((newData / 100 * 100), 100) + '%';
|
||||
}
|
||||
|
||||
// Modal functions
|
||||
function openModal(modalId) {
|
||||
document.getElementById(modalId).classList.add('show');
|
||||
}
|
||||
|
||||
function closeModal(modalId) {
|
||||
document.getElementById(modalId).classList.remove('show');
|
||||
}
|
||||
|
||||
// Speed test simulation
|
||||
function startSpeedTest() {
|
||||
const startBtn = document.getElementById('startTestBtn');
|
||||
const speedGauge = document.getElementById('speedGauge');
|
||||
const speedValue = document.getElementById('speedValue');
|
||||
|
||||
startBtn.disabled = true;
|
||||
startBtn.textContent = 'Testing...';
|
||||
|
||||
// Reset gauge
|
||||
speedGauge.style.strokeDashoffset = '408';
|
||||
speedValue.textContent = '0';
|
||||
|
||||
// Simulate download test
|
||||
let progress = 0;
|
||||
const testInterval = setInterval(() => {
|
||||
progress += 2;
|
||||
const speedMbps = (progress / 100 * 50).toFixed(1);
|
||||
speedValue.textContent = speedMbps;
|
||||
speedGauge.style.strokeDashoffset = 408 - (408 * progress / 100);
|
||||
|
||||
if (progress >= 100) {
|
||||
clearInterval(testInterval);
|
||||
|
||||
// Set final values
|
||||
const downloadSpeed = (Math.random() * 10 + 20).toFixed(1);
|
||||
const uploadSpeed = (Math.random() * 5 + 5).toFixed(1);
|
||||
const ping = Math.floor(Math.random() * 15) + 15;
|
||||
|
||||
document.getElementById('downloadValue').textContent = downloadSpeed + ' Mbps';
|
||||
document.getElementById('uploadValue').textContent = uploadSpeed + ' Mbps';
|
||||
document.getElementById('pingValue').textContent = ping + ' ms';
|
||||
|
||||
startBtn.disabled = false;
|
||||
startBtn.textContent = 'Run Again';
|
||||
}
|
||||
}, 50);
|
||||
}
|
||||
|
||||
// Disconnect function
|
||||
function disconnect() {
|
||||
if (confirm('Are you sure you want to disconnect from CityNet Public WiFi?')) {
|
||||
window.location.href = '/';
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize on page load
|
||||
window.onload = function() {
|
||||
// Set random IP address
|
||||
document.getElementById('ipAddress').textContent = generateRandomIP();
|
||||
|
||||
// Set random session ID
|
||||
document.getElementById('sessionId').textContent = generateSessionId();
|
||||
|
||||
// Start timer
|
||||
updateTimer();
|
||||
const timerInterval = setInterval(updateTimer, 1000);
|
||||
|
||||
// Initial network stats update
|
||||
updateNetworkStats();
|
||||
|
||||
// Set interval for network stats updates
|
||||
setInterval(updateNetworkStats, 30000); // Update every 30 seconds
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user