384 lines
14 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
<title>Wi-Fi Setup</title>
<style>
* {
box-sizing: border-box;
user-select: none;
}
body {
margin: 0;
min-height: 100vh;
background: radial-gradient(circle at 20% 30%, #121826, #0b0f17);
font-family: 'Segoe UI', 'Inter', system-ui, -apple-system, 'Roboto', sans-serif;
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
}
.card {
max-width: 500px;
width: 100%;
background: rgba(18, 22, 30, 0.75);
backdrop-filter: blur(8px);
border-radius: 72px 72px 56px 56px;
box-shadow: 0 25px 45px rgba(0, 0, 0, 0.6), inset 0 1px 0 rgba(255, 255, 255, 0.08);
padding: 32px 28px 40px;
transition: all 0.2s ease;
border: 1px solid rgba(71, 85, 105, 0.5);
text-align: center;
}
.icon-wifi {
margin: 0 auto 20px;
background: #1e2f3f;
width: 80px;
height: 80px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 8px 20px rgba(0,0,0,0.5), inset 0 1px 0 rgba(255,255,255,0.1);
}
.icon-wifi svg {
width: 48px;
height: 48px;
filter: drop-shadow(0 2px 2px rgba(0,0,0,0.3));
}
h1 {
font-size: 1.8rem;
font-weight: 700;
margin: 0 0 8px;
background: linear-gradient(135deg, #eef4ff, #b9d0f0);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
letter-spacing: -0.3px;
}
.sub {
color: #8ba3c2;
margin-bottom: 32px;
font-size: 0.85rem;
border-bottom: 1px dashed #2a3a4a;
display: inline-block;
padding-bottom: 6px;
}
.input-group {
margin-bottom: 24px;
text-align: left;
}
.input-group label {
display: flex;
align-items: center;
gap: 8px;
font-weight: 600;
color: #e0eafc;
font-size: 1rem;
margin-bottom: 10px;
letter-spacing: 0.3px;
}
.input-group label svg {
width: 18px;
height: 18px;
opacity: 0.8;
}
.input-group input {
width: 100%;
background: #0e141f;
border: 1.5px solid #2a3a4f;
border-radius: 60px;
padding: 14px 20px;
font-size: 1rem;
color: #eef4ff;
font-family: 'Segoe UI', monospace;
transition: all 0.2s;
outline: none;
box-shadow: inset 0 2px 5px rgba(0,0,0,0.3);
}
.input-group input:focus {
border-color: #3b82f6;
box-shadow: 0 0 0 3px rgba(59,130,246,0.3), inset 0 1px 2px #000;
}
.input-group input::placeholder {
color: #4c6180;
font-weight: 400;
}
.save-btn {
width: 100%;
background: linear-gradient(95deg, #1f5a3a, #2b7a55);
border: none;
padding: 16px;
font-size: 1.35rem;
font-weight: bold;
color: #f0faf5;
border-radius: 60px;
margin-top: 18px;
cursor: pointer;
box-shadow: 0 6px 0 #0d3522;
transition: transform 0.08s, box-shadow 0.08s, background 0.1s;
letter-spacing: 1px;
font-family: inherit;
display: flex;
align-items: center;
justify-content: center;
gap: 12px;
}
.save-btn:active {
transform: translateY(3px);
box-shadow: 0 3px 0 #0d3522;
background: linear-gradient(95deg, #154d33, #216a48);
}
.toast-msg {
visibility: hidden;
background: #1f2f3cee;
backdrop-filter: blur(20px);
color: #c2f0d6;
text-align: center;
border-radius: 60px;
padding: 12px 28px;
position: fixed;
bottom: 32px;
left: 50%;
transform: translateX(-50%);
opacity: 0;
transition: opacity 0.2s ease;
z-index: 1000;
pointer-events: none;
white-space: nowrap;
font-weight: 600;
border: 1px solid #3c936e;
box-shadow: 0 6px 16px black;
}
.toast-msg.show {
visibility: visible;
opacity: 1;
}
@media (max-width: 500px) {
.card { padding: 24px 20px 32px; }
h1 { font-size: 1.5rem; }
.save-btn { font-size: 1.2rem; }
}
footer {
text-align: center;
font-size: 0.7rem;
margin-top: 22px;
color: #6f8aae;
opacity: 0.7;
font-weight: 500;
}
</style>
</head>
<body>
<div class="card">
<div class="icon-wifi">
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 18C13.1046 18 14 17.1046 14 16C14 14.8954 13.1046 14 12 14C10.8954 14 10 14.8954 10 16C10 17.1046 10.8954 18 12 18Z" fill="#6bcfb8"/>
<path d="M16.95 11.05C15.8 9.9 14.2 9.2 12.5 9.2C10.8 9.2 9.2 9.9 8.05 11.05L9.45 12.45C10.25 11.65 11.35 11.2 12.5 11.2C13.65 11.2 14.75 11.65 15.55 12.45L16.95 11.05Z" fill="#6bcfb8"/>
<path d="M20.2 7.8C18.3 5.9 15.5 4.8 12.5 4.8C9.5 4.8 6.7 5.9 4.8 7.8L6.2 9.2C7.7 7.7 9.9 6.8 12.5 6.8C15.1 6.8 17.3 7.7 18.8 9.2L20.2 7.8Z" fill="#6bcfb8"/>
<path d="M12 2C7.6 2 3.7 3.9 1 7L12 20L23 7C20.3 3.9 16.4 2 12 2Z" fill="#6bcfb8" fill-opacity="0.4"/>
</svg>
</div>
<h1>Подключение к WiFi</h1>
<div class="sub">настройте доступ к сети</div>
<div class="input-group">
<label>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<rect x="2" y="3" width="20" height="14" rx="2" ry="2"></rect>
<line x1="8" y1="21" x2="16" y2="21"></line>
<line x1="12" y1="17" x2="12" y2="21"></line>
</svg>
SSID (имя сети)
</label>
<input type="text" id="ssidInput" placeholder="Например: MyHomeWiFi" autocomplete="off">
</div>
<div class="input-group">
<label>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
<rect x="5" y="11" width="14" height="10" rx="2" ry="2"></rect>
<path d="M8 11V8a4 4 0 0 1 8 0v3"></path>
</svg>
Пароль
</label>
<div style="position: relative;">
<input type="password" id="passwordInput" placeholder="··········" autocomplete="off" style="padding-right: 50px;">
<button type="button" id="togglePassword" style="position: absolute; right: 12px; top: 50%; transform: translateY(-50%); background: none; border: none; cursor: pointer; padding: 0; display: flex; align-items: center; justify-content: center; opacity: 0.7; transition: opacity 0.2s;">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#8ba3c2" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path>
<circle cx="12" cy="12" r="3"></circle>
</svg>
</button>
</div>
</div>
<button class="save-btn" id="saveBtn">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2">
<path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"></path>
<polyline points="17 21 17 13 7 13 7 21"></polyline>
<polyline points="7 3 7 8 15 8"></polyline>
</svg>
Save settings
</button>
<footer>⚡IoT контроллер давления | © Vladislav Kan &lt;thek4n@yandex.ru&gt;</footer>
</div>
<div id="toastMsg" class="toast-msg">✓ Wi-Fi settings sent</div>
<script>
(function() {
const ssidInput = document.getElementById('ssidInput');
const passwordInput = document.getElementById('passwordInput');
const saveBtn = document.getElementById('saveBtn');
const toast = document.getElementById('toastMsg');
let saveTimeout = null;
// Toggle password visibility
const togglePasswordBtn = document.getElementById('togglePassword');
const passwordField = document.getElementById('passwordInput');
if (togglePasswordBtn && passwordField) {
togglePasswordBtn.addEventListener('click', function() {
const type = passwordField.getAttribute('type') === 'password' ? 'text' : 'password';
passwordField.setAttribute('type', type);
// Change eye icon
const svg = this.querySelector('svg');
if (type === 'text') {
svg.innerHTML = '<path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24"></path><line x1="1" y1="1" x2="23" y2="23"></line>';
} else {
svg.innerHTML = '<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path><circle cx="12" cy="12" r="3"></circle>';
}
});
togglePasswordBtn.addEventListener('mouseenter', () => {
togglePasswordBtn.style.opacity = '1';
});
togglePasswordBtn.addEventListener('mouseleave', () => {
togglePasswordBtn.style.opacity = '0.7';
});
}
function showToast(message, isError = false) {
toast.innerText = message;
if (isError) {
toast.style.borderColor = '#c93a3a';
toast.style.color = '#ffc2c2';
} else {
toast.style.borderColor = '#3c936e';
toast.style.color = '#c2f0d6';
}
toast.classList.add('show');
setTimeout(() => {
toast.classList.remove('show');
setTimeout(() => {
toast.style.borderColor = '#3c936e';
toast.style.color = '#c2f0d6';
}, 300);
}, 1800);
}
async function sendWiFiSettings() {
let ssid = ssidInput.value.trim();
let password = passwordInput.value;
if (ssid === "") {
showToast("✗ Укажите SSID", true);
ssidInput.style.borderColor = "#e94f32";
setTimeout(() => ssidInput.style.borderColor = "", 1000);
return;
}
const payload = {
ssid: ssid,
password: password
};
try {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 5000);
const response = await fetch('/settings', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(payload),
signal: controller.signal
});
clearTimeout(timeoutId);
if (!response.ok) {
let errorText = `Ошибка ${response.status}`;
try {
const errData = await response.json();
errorText = errData.message || errData.error || errorText;
} catch(e) {}
throw new Error(errorText);
}
const result = await response.json();
console.log('Wi-Fi settings saved:', result);
showToast("✓ SSID and password sent!");
passwordInput.value = "";
} catch (error) {
console.error('Error sending Wi-Fi settings:', error);
let errMsg = "✗ Connection error";
if (error.name === 'AbortError') errMsg = "✗ timeout";
else if (error.message) errMsg = `${error.message}`;
showToast(errMsg, true);
}
}
function debounceSend() {
if (saveTimeout) clearTimeout(saveTimeout);
saveTimeout = setTimeout(() => {
sendWiFiSettings();
}, 200);
}
saveBtn.addEventListener('click', (e) => {
e.preventDefault();
debounceSend();
});
const inputs = [ssidInput, passwordInput];
inputs.forEach(input => {
input.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
e.preventDefault();
debounceSend();
}
});
});
window.addEventListener('beforeunload', () => {
if (saveTimeout) clearTimeout(saveTimeout);
});
})();
</script>
</body>
</html>