254 lines
11 KiB
HTML
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, '"') + '" /><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>
|