Files
Stend_mont/app/templates/login.html
T

254 lines
11 KiB
HTML

<!doctype html>
<html lang="ru">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>МОНТ - инфрастуктурный полигон</title>
<link rel="stylesheet" href="/static/style.css" />
<link rel="icon" type="image/png" href="/static/favicon.png" />
<style>
body { background: #070f1c; overflow: hidden; height: 100vh; }
@media (max-width: 820px) { body { overflow: auto; height: auto; } }
</style>
<!-- Yandex.Metrika counter -->
<script type="text/javascript">
(function(m,e,t,r,i,k,a){
m[i]=m[i]||function(){(m[i].a=m[i].a||[]).push(arguments)};
m[i].l=1*new Date();
for (var j = 0; j < document.scripts.length; j++) {if (document.scripts[j].src === r) { return; }}
k=e.createElement(t),a=e.getElementsByTagName(t)[0],k.async=1,k.src=r,a.parentNode.insertBefore(k,a)
})(window, document,"script","https://mc.yandex.ru/metrika/tag.js?id=109119977", "ym");
ym(109119977, "init", {ssr:true, webvisor:true, clickmap:true, ecommerce:"dataLayer", referrer: document.referrer, url: location.href, accurateTrackBounce:true, trackLinks:true});
</script>
<noscript><div><img src="https://mc.yandex.ru/watch/109119977" style="position:absolute; left:-9999px;" alt="" /></div></noscript>
<!-- /Yandex.Metrika counter -->
</head>
<body>
<img src="/static/logo.png" alt="MONT" class="login-corner-logo" />
<div class="login-wrap">
<aside class="login-left">
<div class="login-left-glow login-left-glow-top"></div>
<div class="login-left-glow login-left-glow-bottom"></div>
<div class="login-left-inner">
<h1 class="login-left-title">Инфраструктурный<br>полигон МОНТ</h1>
<p class="login-left-desc">Платформа для демонстрации и практического знакомства с отечественным ПО. Браузерный доступ к рабочим стендам с российскими ОС, платформами виртуализации, СРК и другими решениями — без установки и настройки.</p>
<ul class="login-features">
<li class="login-feature">
<span class="login-feature-icon">🖥</span>
<span>Доступ к рабочим столам ОС</span>
</li>
<li class="login-feature">
<span class="login-feature-icon">🌐</span>
<span>Веб-интерфейсы сервисов</span>
</li>
<li class="login-feature">
<span class="login-feature-icon"></span>
<span>Доступ в один клик</span>
</li>
<li class="login-feature">
<span class="login-feature-icon">🔒</span>
<span>Защищённый контур</span>
</li>
</ul>
<button type="button" class="login-distrib-btn" onclick="window.open('https://maps.4mont.ru','_blank')">Продукты нашей дистрибуции</button>
</div>
</aside>
<main class="login-right">
<div class="login-right-inner">
<div class="login-form-title">Вход в систему</div>
<div class="login-form-subtitle">Инфраструктурный полигон</div>
{% if session_notice %}<div class="session-notice lp-session-notice">{{ session_notice }}</div>{% endif %}
{% if login_error %}<div class="auth-error lp-auth-error">{{ login_error }}</div>{% endif %}
<form method="post" action="/login" class="login-form">
<input type="hidden" name="csrf_token" value="{{ csrf_token }}" />
<div class="login-field">
<label>Логин</label>
<input type="text" name="username" placeholder="Введите логин" required autocomplete="username" />
</div>
<div class="login-field">
<label>Пароль</label>
<input type="password" name="password" placeholder="Введите пароль" required autocomplete="current-password" />
</div>
<button type="submit" class="login-submit">Войти</button>
</form>
<button type="button" class="login-request-btn" id="btn-request-access" data-open-access-modal="1">Запросить доступ</button>
</div>
<footer class="login-footer">
<a href="mailto:rgalyaviev@mont.com" class="login-footer-link">Made by Galyaviev</a>
</footer>
</main>
</div>
<!-- Request Access Modal -->
<div id="access-modal" class="access-modal-overlay" style="display:none" aria-modal="true" role="dialog">
<div class="access-modal">
<div class="access-modal-header">
<div class="access-modal-title">Запрос на доступ</div>
<div class="access-modal-sub">Заполните форму — мы свяжемся с вами в ближайшее время</div>
</div>
<div class="access-modal-body">
<div class="access-field">
<label>Имя и фамилия <span class="req">*</span></label>
<input id="am-name" type="text" placeholder="Иван Иванов" />
</div>
<div class="access-field">
<label>Название компании <span class="req">*</span></label>
<input id="am-company" type="text" placeholder="ООО Компания" />
</div>
<div class="access-field">
<label>Email <span class="req">*</span></label>
<input id="am-email" type="email" placeholder="ivan@company.ru" />
</div>
<div class="access-field">
<label>Телефон <span class="req">*</span></label>
<input id="am-phone" type="tel" placeholder="+7 (999) 000-00-00" />
</div>
<div class="access-field">
<label>Ваш менеджер в МОНТ</label>
<input id="am-manager" type="text" placeholder="Если известно — укажите имя" />
</div>
<div class="access-field">
<label>Интересующие продукты</label>
<div id="am-products" class="access-products-wrap">
<div class="access-products-loading">Загрузка...</div>
</div>
</div>
<div id="am-error" class="access-modal-error" style="display:none"></div>
</div>
<div class="access-modal-footer">
<button type="button" class="access-btn-cancel" id="am-cancel">Отмена</button>
<button type="button" class="access-btn-submit" id="am-submit">Запросить доступ</button>
</div>
</div>
</div>
<script>
(function() {
const overlay = document.getElementById('access-modal');
const btnCancel = document.getElementById('am-cancel');
const btnSubmit = document.getElementById('am-submit');
const errEl = document.getElementById('am-error');
let productsLoaded = false;
function openModal() {
overlay.style.display = 'flex';
document.body.style.overflow = 'hidden';
if (!productsLoaded) loadProducts();
}
function closeModal() {
overlay.style.display = 'none';
document.body.style.overflow = '';
errEl.style.display = 'none';
document.querySelectorAll('.am-invalid').forEach(el => el.classList.remove('am-invalid'));
}
async function loadProducts() {
const wrap = document.getElementById('am-products');
try {
const res = await fetch('/api/public/services-by-category');
const data = await res.json();
wrap.innerHTML = '';
for (const [cat, svcs] of Object.entries(data)) {
const group = document.createElement('div');
group.className = 'access-products-group';
group.innerHTML = '<div class="access-products-cat">' + cat + '</div>';
for (const svc of svcs) {
const lbl = document.createElement('label');
lbl.className = 'access-product-item';
lbl.innerHTML = '<input type="checkbox" value="' + svc.name.replace(/"/g, '&quot;') + '" /><span>' + svc.name + '</span>';
group.appendChild(lbl);
}
wrap.appendChild(group);
}
productsLoaded = true;
} catch(e) {
wrap.innerHTML = '<div class="access-products-loading">Не удалось загрузить список</div>';
}
}
async function submitForm() {
const name = document.getElementById('am-name').value.trim();
const company = document.getElementById('am-company').value.trim();
const email = document.getElementById('am-email').value.trim();
const phone = document.getElementById('am-phone').value.trim();
const manager = document.getElementById('am-manager').value.trim();
const checked = [...document.querySelectorAll('#am-products input[type=checkbox]:checked')];
const products = checked.map(c => c.value);
const emailRe = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
const phoneRe = /^[\+\d][\d\s\-\(\)]{6,18}$/;
const fields = [
{ el: document.getElementById('am-name'), val: name, check: () => !!name, msg: 'Введите имя и фамилию' },
{ el: document.getElementById('am-company'), val: company, check: () => !!company, msg: 'Введите название компании' },
{ el: document.getElementById('am-email'), val: email, check: () => emailRe.test(email), msg: 'Введите корректный email' },
{ el: document.getElementById('am-phone'), val: phone, check: () => phoneRe.test(phone), msg: 'Введите корректный номер телефона' },
];
const errors = [];
fields.forEach(f => {
if (!f.check()) {
f.el.classList.add('am-invalid');
errors.push(f.msg);
} else {
f.el.classList.remove('am-invalid');
}
});
if (errors.length) {
errEl.textContent = errors.join(' • ');
errEl.style.display = 'block';
return;
}
btnSubmit.disabled = true;
btnSubmit.textContent = 'Отправка...';
errEl.style.display = 'none';
try {
const res = await fetch('/api/request-access', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({name, company, email, phone, manager, products}),
});
if (!res.ok) {
const d = await res.json().catch(() => ({}));
throw new Error(d.detail || 'Ошибка отправки');
}
btnSubmit.textContent = 'Отправлено!';
setTimeout(closeModal, 1500);
} catch(e) {
errEl.textContent = e.message || 'Ошибка отправки, попробуйте позже';
errEl.style.display = 'block';
btnSubmit.disabled = false;
btnSubmit.textContent = 'Запросить доступ';
}
}
// Clear invalid highlight on input
document.querySelectorAll('#am-name,#am-company,#am-email,#am-phone').forEach(el => {
el.addEventListener('input', () => el.classList.remove('am-invalid'));
});
// Wire up request-access button
document.querySelectorAll('.login-request-btn, [data-open-access-modal]').forEach(el => {
el.addEventListener('click', function(e) {
e.preventDefault();
openModal();
});
});
btnCancel.addEventListener('click', closeModal);
btnSubmit.addEventListener('click', submitForm);
overlay.addEventListener('click', function(e) {
if (e.target === overlay) closeModal();
});
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape') closeModal();
});
})();
</script>
</body>
</html>