467 lines
25 KiB
HTML
467 lines
25 KiB
HTML
<!doctype html>
|
|
<html lang="ru">
|
|
<head>
|
|
<meta charset="utf-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
<title>Инфраструктурный полигон MONT — демо и пилоты российского ПО</title>
|
|
<meta name="description" content="Инфраструктурный полигон MONT: демонстрация и пилотное тестирование российского ПО для партнёров и заказчиков. Браузерный доступ к рабочим стендам — без установки и настройки." />
|
|
<meta name="keywords" content="инфраструктурный полигон MONT, пилоты MONT, демо MONT, партнёры MONT, демонстрация MONT, российское ПО демо, отечественное ПО тестирование, демостенд ПО" />
|
|
<meta name="robots" content="index, follow" />
|
|
<link rel="canonical" href="https://stend.4mont.ru/" />
|
|
<meta property="og:type" content="website" />
|
|
<meta property="og:url" content="https://stend.4mont.ru/" />
|
|
<meta property="og:title" content="Инфраструктурный полигон MONT — демо и пилоты российского ПО" />
|
|
<meta property="og:description" content="Демонстрация и тестирование российского ПО для партнёров и заказчиков MONT. Доступ к рабочим стендам прямо в браузере." />
|
|
<meta property="og:image" content="https://stend.4mont.ru/static/logo.png?v=2" />
|
|
<meta property="og:locale" content="ru_RU" />
|
|
<meta property="og:site_name" content="Полигон MONT" />
|
|
<meta name="twitter:card" content="summary" />
|
|
<meta name="twitter:title" content="Инфраструктурный полигон MONT" />
|
|
<meta name="twitter:description" content="Демо и пилоты российского ПО для партнёров и заказчиков MONT." />
|
|
<script type="application/ld+json">
|
|
{
|
|
"@context": "https://schema.org",
|
|
"@type": "WebSite",
|
|
"name": "Инфраструктурный полигон MONT",
|
|
"url": "https://stend.4mont.ru/",
|
|
"description": "Платформа для демонстрации и пилотного тестирования российского программного обеспечения. Партнёры и заказчики MONT получают браузерный доступ к рабочим стендам.",
|
|
"publisher": {
|
|
"@type": "Organization",
|
|
"name": "MONT",
|
|
"url": "https://www.mont.com/"
|
|
}
|
|
}
|
|
</script>
|
|
<link rel="stylesheet" href="/static/style.css" />
|
|
<link rel="icon" type="image/svg+xml" href="/static/favicon.svg" />
|
|
<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>
|
|
<a href="https://4mont.ru"><img src="/static/logo.png?v=2" alt="MONT" class="login-corner-logo" /></a>
|
|
<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>полигон MONT</h1>
|
|
<p class="login-left-desc">Платформа для демонстрации и пилотного тестирования российского ПО. Партнёры и заказчики MONT получают браузерный доступ к рабочим стендам с отечественными ОС, платформами виртуализации, СРК и другими решениями — без установки и настройки.</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">
|
|
<button type="button" class="login-footer-link" id="btn-contact-ruslan">Made by Galyaviev</button>
|
|
</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>
|
|
<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>Ваш менеджер в MONT</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 resetAccessForm() {
|
|
if (!document.getElementById('am-name')) {
|
|
document.querySelector('.access-modal-body').innerHTML = `
|
|
<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>Ваш менеджер в MONT</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>`;
|
|
document.querySelector('.access-modal-footer').innerHTML = `<button type="button" class="access-btn-cancel" id="am-cancel">Отмена</button><button type="button" class="access-btn-submit" id="am-submit">Запросить доступ</button>`;
|
|
document.getElementById('am-cancel').addEventListener('click', closeModal);
|
|
document.getElementById('am-submit').addEventListener('click', submitForm);
|
|
document.querySelectorAll('#am-name,#am-company,#am-email,#am-phone').forEach(function(el){ el.addEventListener('input', function(){ el.classList.remove('am-invalid'); }); });
|
|
productsLoaded = false;
|
|
} else {
|
|
['am-name','am-company','am-email','am-phone','am-manager'].forEach(function(id){ var el=document.getElementById(id); if(el){el.value='';el.classList.remove('am-invalid');} });
|
|
document.querySelectorAll('#am-products input[type=checkbox]').forEach(function(cb){ cb.checked=false; });
|
|
var err=document.getElementById('am-error'); if(err) err.style.display='none';
|
|
var btn=document.getElementById('am-submit'); if(btn){btn.disabled=false;btn.textContent='Запросить доступ';}
|
|
}
|
|
}
|
|
|
|
function openModal() {
|
|
resetAccessForm();
|
|
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'));
|
|
}
|
|
window._closeAccessModal = function() { closeModal(); productsLoaded = false; };
|
|
|
|
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 nameEl = document.getElementById('am-name');
|
|
const companyEl = document.getElementById('am-company');
|
|
const emailEl = document.getElementById('am-email');
|
|
const phoneEl = document.getElementById('am-phone');
|
|
const managerEl = document.getElementById('am-manager');
|
|
const submitBtn = document.getElementById('am-submit');
|
|
const errorEl = document.getElementById('am-error');
|
|
|
|
const name = nameEl ? nameEl.value.trim() : '';
|
|
const company = companyEl ? companyEl.value.trim() : '';
|
|
const email = emailEl ? emailEl.value.trim() : '';
|
|
const phone = phoneEl ? phoneEl.value.trim() : '';
|
|
const manager = managerEl ? managerEl.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: nameEl, check: () => !!name, msg: 'Введите имя и фамилию' },
|
|
{ el: companyEl, check: () => !!company, msg: 'Введите название компании' },
|
|
{ el: emailEl, check: () => emailRe.test(email), msg: 'Введите корректный email' },
|
|
{ el: phoneEl, check: () => phoneRe.test(phone), msg: 'Введите корректный номер телефона' },
|
|
];
|
|
|
|
const errors = [];
|
|
fields.forEach(f => {
|
|
if (f.el && !f.check()) { f.el.classList.add('am-invalid'); errors.push(f.msg); }
|
|
else if (f.el) f.el.classList.remove('am-invalid');
|
|
});
|
|
|
|
if (errors.length) {
|
|
if (errorEl) { errorEl.textContent = errors.join(' • '); errorEl.style.display = 'block'; }
|
|
return;
|
|
}
|
|
|
|
if (submitBtn) { submitBtn.disabled = true; submitBtn.textContent = 'Отправка...'; }
|
|
if (errorEl) errorEl.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 || 'Ошибка отправки');
|
|
}
|
|
const body = document.querySelector('#access-modal .access-modal-body');
|
|
const footer = document.querySelector('#access-modal .access-modal-footer');
|
|
body.innerHTML = '<div class="am-success-msg">' +
|
|
'<div class="am-success-icon">✓</div>' +
|
|
'<div class="am-success-title">Запрос отправлен</div>' +
|
|
'<div class="am-success-sub">После утверждения доступы придут на электронную почту <strong>' + email + '</strong></div>' +
|
|
'</div>';
|
|
footer.innerHTML = '<button type="button" class="access-btn-cancel" onclick="window._closeAccessModal()">Закрыть</button>';
|
|
} catch(e) {
|
|
const eb = document.getElementById('am-error');
|
|
if (eb) { eb.textContent = e.message || 'Ошибка отправки, попробуйте позже'; eb.style.display = 'block'; }
|
|
const sb = document.getElementById('am-submit');
|
|
if (sb) { sb.disabled = false; sb.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>
|
|
|
|
<!-- Contact Ruslan Modal -->
|
|
<div id="contact-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>
|
|
<div class="access-modal-body" id="cm-body">
|
|
<div class="access-field">
|
|
<label>Ваше имя <span class="req">*</span></label>
|
|
<input id="cm-name" type="text" placeholder="Иван Иванов" />
|
|
</div>
|
|
<div class="access-field">
|
|
<label>Email <span class="req">*</span></label>
|
|
<input id="cm-email" type="email" placeholder="ivan@company.ru" />
|
|
</div>
|
|
<div class="access-field">
|
|
<label>Телефон <span class="req">*</span></label>
|
|
<input id="cm-phone" type="tel" placeholder="+7 (999) 000-00-00" />
|
|
</div>
|
|
<div class="access-field">
|
|
<label>Сообщение <span class="req">*</span></label>
|
|
<textarea id="cm-text" class="access-textarea" placeholder="Ваш вопрос или предложение..." rows="4"></textarea>
|
|
</div>
|
|
<div id="cm-error" class="access-modal-error" style="display:none"></div>
|
|
</div>
|
|
<div class="access-modal-footer" id="cm-footer">
|
|
<button type="button" class="access-btn-cancel" id="cm-cancel">Отмена</button>
|
|
<button type="button" class="access-btn-submit" id="cm-submit">Отправить</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
(function() {
|
|
const overlay = document.getElementById('contact-modal');
|
|
const btnCancel = document.getElementById('cm-cancel');
|
|
const btnSubmit = document.getElementById('cm-submit');
|
|
const errEl = document.getElementById('cm-error');
|
|
|
|
function resetContactForm() {
|
|
if (!document.getElementById('cm-name')) {
|
|
document.getElementById('cm-body').innerHTML = `
|
|
<div class="access-field"><label>Ваше имя <span class="req">*</span></label><input id="cm-name" type="text" placeholder="Иван Иванов" /></div>
|
|
<div class="access-field"><label>Email <span class="req">*</span></label><input id="cm-email" type="email" placeholder="ivan@company.ru" /></div>
|
|
<div class="access-field"><label>Телефон <span class="req">*</span></label><input id="cm-phone" type="tel" placeholder="+7 (999) 000-00-00" /></div>
|
|
<div class="access-field"><label>Сообщение <span class="req">*</span></label><textarea id="cm-text" class="access-textarea" placeholder="Ваш вопрос или предложение..." rows="4"></textarea></div>
|
|
<div id="cm-error" class="access-modal-error" style="display:none"></div>`;
|
|
document.getElementById('cm-footer').innerHTML = `<button type="button" class="access-btn-cancel" id="cm-cancel">Отмена</button><button type="button" class="access-btn-submit" id="cm-submit">Отправить</button>`;
|
|
document.getElementById('cm-cancel').addEventListener('click', closeContact);
|
|
document.getElementById('cm-submit').addEventListener('click', submitContact);
|
|
document.querySelectorAll('#cm-name,#cm-email,#cm-phone,#cm-text').forEach(function(el){ el.addEventListener('input', function(){ el.classList.remove('am-invalid'); }); });
|
|
} else {
|
|
['cm-name','cm-email','cm-phone','cm-text'].forEach(function(id){ var el=document.getElementById(id); if(el){el.value='';el.classList.remove('am-invalid');} });
|
|
var err=document.getElementById('cm-error'); if(err) err.style.display='none';
|
|
var btn=document.getElementById('cm-submit'); if(btn){btn.disabled=false;btn.textContent='Отправить';}
|
|
}
|
|
}
|
|
|
|
function openContact() {
|
|
resetContactForm();
|
|
overlay.style.display = 'flex';
|
|
document.body.style.overflow = 'hidden';
|
|
}
|
|
|
|
function closeContact() {
|
|
overlay.style.display = 'none';
|
|
document.body.style.overflow = '';
|
|
errEl.style.display = 'none';
|
|
document.querySelectorAll('#contact-modal .am-invalid').forEach(el => el.classList.remove('am-invalid'));
|
|
}
|
|
window._closeContactModal = closeContact;
|
|
|
|
async function submitContact() {
|
|
const nameEl = document.getElementById('cm-name');
|
|
const emailEl = document.getElementById('cm-email');
|
|
const phoneEl = document.getElementById('cm-phone');
|
|
const textEl = document.getElementById('cm-text');
|
|
const submitBtn = document.getElementById('cm-submit');
|
|
const errorEl = document.getElementById('cm-error');
|
|
|
|
const name = nameEl ? nameEl.value.trim() : '';
|
|
const email = emailEl ? emailEl.value.trim() : '';
|
|
const phone = phoneEl ? phoneEl.value.trim() : '';
|
|
const text = textEl ? textEl.value.trim() : '';
|
|
|
|
const emailRe = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
const phoneRe = /^[\+\d][\d\s\-\(\)]{6,18}$/;
|
|
|
|
const fields = [
|
|
{ el: nameEl, check: () => !!name, msg: 'Введите имя' },
|
|
{ el: emailEl, check: () => emailRe.test(email), msg: 'Введите корректный email' },
|
|
{ el: phoneEl, check: () => phoneRe.test(phone), msg: 'Введите корректный номер телефона' },
|
|
{ el: textEl, check: () => !!text, msg: 'Введите сообщение' },
|
|
];
|
|
|
|
const errors = [];
|
|
fields.forEach(f => {
|
|
if (f.el && !f.check()) { f.el.classList.add('am-invalid'); errors.push(f.msg); }
|
|
else if (f.el) f.el.classList.remove('am-invalid');
|
|
});
|
|
|
|
if (errors.length) {
|
|
if (errorEl) { errorEl.textContent = errors.join(' • '); errorEl.style.display = 'block'; }
|
|
return;
|
|
}
|
|
|
|
if (submitBtn) { submitBtn.disabled = true; submitBtn.textContent = 'Отправка...'; }
|
|
if (errorEl) errorEl.style.display = 'none';
|
|
|
|
try {
|
|
const res = await fetch('/api/contact', {
|
|
method: 'POST',
|
|
headers: {'Content-Type': 'application/json'},
|
|
body: JSON.stringify({name, email, phone, text}),
|
|
});
|
|
if (!res.ok) {
|
|
const d = await res.json().catch(() => ({}));
|
|
throw new Error(d.detail || 'Ошибка отправки');
|
|
}
|
|
document.getElementById('cm-body').innerHTML =
|
|
'<div class="am-success-msg">' +
|
|
'<div class="am-success-icon">✓</div>' +
|
|
'<div class="am-success-title">Отправлено</div>' +
|
|
'<div class="am-success-sub">Постараюсь ответить в ближайшее время</div>' +
|
|
'</div>';
|
|
document.getElementById('cm-footer').innerHTML =
|
|
'<button type="button" class="access-btn-cancel" onclick="window._closeContactModal()">Закрыть</button>';
|
|
} catch(e) {
|
|
const eb = document.getElementById('cm-error');
|
|
if (eb) { eb.textContent = e.message || 'Ошибка отправки, попробуйте позже'; eb.style.display = 'block'; }
|
|
const sb = document.getElementById('cm-submit');
|
|
if (sb) { sb.disabled = false; sb.textContent = 'Отправить'; }
|
|
}
|
|
}
|
|
|
|
document.querySelectorAll('#cm-name,#cm-email,#cm-phone,#cm-text').forEach(el => {
|
|
el.addEventListener('input', () => el.classList.remove('am-invalid'));
|
|
});
|
|
|
|
document.getElementById('btn-contact-ruslan').addEventListener('click', function(e) {
|
|
e.preventDefault();
|
|
openContact();
|
|
});
|
|
btnCancel.addEventListener('click', closeContact);
|
|
btnSubmit.addEventListener('click', submitContact);
|
|
overlay.addEventListener('click', function(e) { if (e.target === overlay) closeContact(); });
|
|
document.addEventListener('keydown', function(e) { if (e.key === 'Escape' && overlay.style.display !== 'none') closeContact(); });
|
|
})();
|
|
</script>
|
|
</body>
|
|
</html>
|