Add access request modal with Telegram notification

- Modal on login page: Name, Email, Phone with client-side validation
- POST /request-access → sends Telegram message via corporate proxy
- Includes IP + geo lookup (ip-api.com)
- Success screen after submission, Esc/click-outside closes modal

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-15 21:38:14 +03:00
parent 643ec4e0ca
commit a605c92ab3
2 changed files with 198 additions and 1 deletions
+140 -1
View File
@@ -85,10 +85,149 @@
<button type="submit">Войти</button>
</form>
<p class="auth-footer">Нет аккаунта? <a href="{{ url_for('register') }}">Запросить доступ</a></p>
<p class="auth-footer">Нет аккаунта? <a href="#" id="open-request-modal">Запросить доступ</a></p>
</section>
</div>
</div>
<!-- Модал запроса доступа -->
<div class="modal-overlay" id="request-modal">
<div class="modal-card">
<button class="modal-close" id="close-request-modal">&times;</button>
<div id="modal-form-view">
<div class="auth-kicker" style="margin-bottom:16px">
<img src="{{ url_for('static', filename='wb3.png') }}" class="auth-kicker-logo-img" alt="WB">
</div>
<h2 class="login-form-title">Запросить доступ</h2>
<p style="font-size:13px;color:#6b7280;margin-bottom:20px;margin-top:-10px">Оставьте контакты — мы свяжемся и откроем доступ.</p>
<div class="alert alert-error" id="modal-error" style="display:none"></div>
<form class="auth-form" id="request-form" novalidate>
<label>
Имя
<input type="text" name="name" placeholder="Иван Иванов" required autocomplete="name">
<span class="field-error" id="err-name"></span>
</label>
<label>
Email
<input type="email" name="email" placeholder="ivan@company.ru" required autocomplete="email">
<span class="field-error" id="err-email"></span>
</label>
<label>
Телефон
<input type="tel" name="phone" placeholder="+7 999 000-00-00" required autocomplete="tel">
<span class="field-error" id="err-phone"></span>
</label>
<button type="submit" id="request-submit">Отправить запрос</button>
</form>
</div>
<div id="modal-success-view" style="display:none;text-align:center;padding:20px 0">
<div style="font-size:52px;margin-bottom:16px"></div>
<h2 style="margin-bottom:10px">Запрос отправлен!</h2>
<p style="font-size:14px;color:#6b7280">Мы получили ваши данные и скоро свяжемся с вами.</p>
</div>
</div>
</div>
<style>
.modal-overlay {
position: fixed;
inset: 0;
background: rgba(0,0,0,0.5);
backdrop-filter: blur(6px);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
opacity: 0;
pointer-events: none;
transition: opacity 0.2s ease;
}
.modal-overlay.active { opacity: 1; pointer-events: all; }
.modal-card {
background: #fff;
border-radius: 22px;
padding: 40px;
width: 100%;
max-width: 420px;
margin: 20px;
position: relative;
box-shadow: 0 32px 80px rgba(0,0,0,0.22);
transform: translateY(20px) scale(0.98);
transition: transform 0.2s ease;
}
.modal-overlay.active .modal-card { transform: translateY(0) scale(1); }
.modal-close {
position: absolute;
top: 14px; right: 14px;
width: 32px; height: 32px;
border-radius: 50%;
background: #f3f4f6;
border: none;
font-size: 20px;
line-height: 1;
cursor: pointer;
color: #9ca3af;
display: flex; align-items: center; justify-content: center;
box-shadow: none; padding: 0;
}
.modal-close:hover { background: #e5e7eb; color: #374151; transform: none; filter: none; }
.field-error { display: block; font-size: 11px; color: #dc2626; margin-top: 3px; min-height: 15px; }
</style>
<script>
const modal = document.getElementById('request-modal');
const openBtn = document.getElementById('open-request-modal');
const closeBtn = document.getElementById('close-request-modal');
openBtn.addEventListener('click', e => { e.preventDefault(); modal.classList.add('active'); });
closeBtn.addEventListener('click', () => modal.classList.remove('active'));
modal.addEventListener('click', e => { if (e.target === modal) modal.classList.remove('active'); });
document.addEventListener('keydown', e => { if (e.key === 'Escape') modal.classList.remove('active'); });
document.getElementById('request-form').addEventListener('submit', async e => {
e.preventDefault();
const form = e.target;
const name = form.name.value.trim();
const email = form.email.value.trim();
const phone = form.phone.value.trim();
let valid = true;
['name','email','phone'].forEach(f => document.getElementById('err-' + f).textContent = '');
document.getElementById('modal-error').style.display = 'none';
if (!name)
{ document.getElementById('err-name').textContent = 'Введите имя'; valid = false; }
if (!email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email))
{ document.getElementById('err-email').textContent = 'Введите корректный email'; valid = false; }
if (!phone || phone.replace(/\D/g, '').length < 10)
{ document.getElementById('err-phone').textContent = 'Введите корректный телефон'; valid = false; }
if (!valid) return;
const btn = document.getElementById('request-submit');
btn.disabled = true; btn.textContent = 'Отправка…';
try {
const res = await fetch('/request-access', { method: 'POST', body: new FormData(form) });
const data = await res.json();
if (data.ok) {
document.getElementById('modal-form-view').style.display = 'none';
document.getElementById('modal-success-view').style.display = 'block';
} else {
document.getElementById('modal-error').textContent = data.error || 'Ошибка. Попробуйте ещё раз.';
document.getElementById('modal-error').style.display = 'block';
btn.disabled = false; btn.textContent = 'Отправить запрос';
}
} catch {
document.getElementById('modal-error').textContent = 'Ошибка сети. Попробуйте ещё раз.';
document.getElementById('modal-error').style.display = 'block';
btn.disabled = false; btn.textContent = 'Отправить запрос';
}
});
</script>
</body>
</html>