/* Contact + Footer */
function getPublicContact() {
return window.__PUBLIC_DATA__?.contact || {};
}
function externalTarget(url) {
return /^https?:\/\//.test(url || '') ? { target: '_blank', rel: 'noopener noreferrer' } : {};
}
function Contact({ setRoute }) {
const [tags, setTags] = React.useState(['SEO-аудит']);
const allTags = ['SEO-аудит', 'Микроразметка', 'PHP/MySQL', 'VPS/Docker', 'API-интеграция', 'Готовый сайт', 'Другое'];
const toggle = (t) => setTags((cur) => cur.includes(t) ? cur.filter(x => x !== t) : [...cur, t]);
const contact = getPublicContact();
const channels = [
contact.email && { ico: '@', name: contact.email, sub: 'почта · приоритет для аудитов', href: contact.email_href },
contact.telegram_url && { ico: 'T', name: contact.telegram_label || 'Telegram', sub: 'Telegram · быстрые задачи', href: contact.telegram_url },
contact.phone && { ico: '+', name: contact.phone, sub: 'телефон · если удобнее голосом', href: contact.phone_href },
contact.primary_platform_url && { ico: '$', name: 'Безопасная сделка', sub: 'через фриланс-площадку', href: '#', route: 'platforms' },
].filter(Boolean);
const [submitted, setSubmitted] = React.useState(false);
const [submitError, setSubmitError] = React.useState('');
const onSubmit = async (e) => {
e.preventDefault();
const form = e.currentTarget;
const consent = form.querySelector('input[name="pd_consent"]');
if (!consent?.checked) {
setSubmitted(false);
setSubmitError('Нужно подтвердить согласие на обработку персональных данных.');
return;
}
setSubmitError('');
const payload = new FormData(form);
payload.set('task_type', tags.join(', ').slice(0, 80) || 'Другое');
payload.set('pd_consent', 'yes');
payload.set('website', '');
try {
const res = await fetch(window.__CONTACT_ENDPOINT__ || '/contact/', {
method: 'POST',
body: payload,
credentials: 'same-origin',
});
if (!res.ok) throw new Error('submit failed');
setSubmitted(true);
form.reset();
setTimeout(() => setSubmitted(false), 4000);
} catch (err) {
setSubmitted(false);
window.location.href = '/contact/';
}
};
return (
);
}
function FooterCta({ setRoute }) {
return (
Технические задачи для сайтов, серверов, SEO и автоматизации
Если задача нестандартная, можно просто коротко описать проблему. Я помогу понять, с чего лучше начать и какой формат работы подойдёт.
);
}
function Footer({ setRoute }) {
const go = (id) => routeClick(setRoute, id);
const contact = getPublicContact();
const brand = getBrandData();
const siteLabel = brand.domain;
const siteName = brand.name;
const specialistName = contact.specialist_name || '';
const legalMeta = contact.legal_status || contact.legal_name
? `${contact.legal_status || 'Исполнитель'}${contact.legal_name ? ` · ${contact.legal_name}` : ''}`
: 'работаю напрямую и через фриланс-площадки';
const contactLinks = [
contact.email && { label: contact.email, href: contact.email_href },
contact.telegram_url && { label: `Telegram · ${contact.telegram_label || 'написать'}`, href: contact.telegram_url },
contact.phone && { label: contact.phone, href: contact.phone_href },
contact.site_url && { label: siteLabel, href: contact.site_url },
].filter(Boolean);
return (
Технические задачи для сайтов, серверов, SEO и автоматизации.
Работаю как независимый технический исполнитель — напрямую и через фриланс-площадки.
© 2026 {specialistName ? `${specialistName} / ${siteName}` : siteName} · {legalMeta}
{`// ~/winetlab $ goodnight`}
);
}
function CookieConsent() {
const consentKey = 'winetlab_cookie_consent_v2';
const [visible, setVisible] = React.useState(false);
const loadAnalytics = React.useCallback(() => {
const id = String(window.__YANDEX_METRIKA_ID__ || '').trim();
if (!id || window.__WINETLAB_METRIKA_LOADED__) return;
window.__WINETLAB_METRIKA_LOADED__ = true;
(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 (let j = 0; j < document.scripts.length; j += 1) {
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', 'ym');
window.ym(Number(id), 'init', { clickmap:true, trackLinks:true, accurateTrackBounce:true, webvisor:false });
}, []);
React.useEffect(() => {
try {
const stored = window.localStorage.getItem(consentKey);
if (!stored) {
setVisible(true);
return;
}
const parsed = JSON.parse(stored);
if (parsed?.level === 'all') loadAnalytics();
} catch (err) {
setVisible(true);
}
}, [loadAnalytics]);
const save = (level) => {
const payload = { level, accepted_at: new Date().toISOString(), version: 2 };
window.localStorage.setItem(consentKey, JSON.stringify(payload));
window.dispatchEvent(new CustomEvent('winetlab:cookie-consent', { detail: payload }));
if (level === 'all') loadAnalytics();
setVisible(false);
};
if (!visible) return null;
return (
privacy.confirm
Cookie и технические данные
Сайт использует необходимые cookie/localStorage для работы интерфейса. Аналитика подключается
только после согласия. Подробности — в Cookie Policy и политике обработки данных .
save('necessary')}>Только необходимые
save('all')}>Принять все →
);
}
window.Contact = Contact;
window.Footer = Footer;
window.CookieConsent = CookieConsent;