/* Internal pages — Services, Cases, Prices, Contact, About, Platforms */ function PageHead({ crumbs, title, lead }) { return (
{crumbs.map((c, i) => ( {i > 0 && /} {c.href ? {c.label} : c.label} ))}

{title}

{lead &&

{lead}

}
); } /* ========== Services page ========== */ function ServicesPage({ setRoute }) { const backendServices = window.__PUBLIC_DATA__?.services; const detailed = backendServices?.length ? backendServices.map((service) => ({ id: service.slug, cmd: service.slug, title: service.title, price: service.price, body: service.description || service.short, tags: [...(service.includes || []), ...(service.audience || [])].slice(0, 6), href: service.href || `/services/${service.slug}/`, })) : [ { id: 'schema-org-fix', cmd: 'schema-org-fix', title: 'Микроразметка schema.org', price: 'от 2 500 ₽', body: 'Поисковики читают сайт лучше, если на нём есть валидная JSON-LD-разметка. Делаю Article, Product, Organization, Person, BreadcrumbList, WebSite, FAQPage и другие типы — под структуру вашего сайта. Прохожу валидатор Google и Яндекса, остаются комментарии в коде.', tags: ['Article', 'Product', 'Organization', 'Breadcrumb', 'FAQPage', 'WebSite'] }, { id: 'seo-audit', cmd: 'seo-audit', title: 'Технический SEO-аудит', price: 'от 2 000 ₽', body: 'Проверка robots.txt, sitemap.xml, canonical, редиректов, дублей, скорости, серверных ошибок, мета-тегов и микроразметки. На выходе — отчёт с приоритетами: что починить в первую очередь, что во вторую, а что вообще не трогать.', tags: ['robots.txt', 'sitemap', 'canonical', 'redirects', 'duplicates', 'meta'] }, { id: 'php-mysql-fixes', cmd: 'php-mysql-fix', title: 'Доработка PHP/MySQL', price: 'от 3 000 ₽', body: 'Самописные сайты на десять лет, формы, отправки писем, кривые SQL-запросы, админки, импорты, оптимизация запросов. Прихожу в чужой код, разбираюсь, чиню — без переписывания.', tags: ['PHP 7/8', 'MySQL', 'PHPMailer', 'PDO', 'CSRF', 'sessions'] }, { id: 'vps-deploy', cmd: 'vps-deploy', title: 'VPS · Docker · Nginx · SSL', price: 'от 4 000 ₽', body: 'Развёртывание сайта или приложения на VPS: домен, HTTPS через Let’s Encrypt, reverse proxy на Nginx, docker-compose, базовые бэкапы. Передаю инструкцию, как обновлять и что мониторить.', tags: ['Ubuntu', 'Docker', 'Nginx', 'SSL', 'systemd', 'cron'] }, { id: 'api-integrations', cmd: 'api-integrate', title: 'API-интеграции', price: 'от 15 000 ₽', body: 'Связываю сайт с внешними системами: CRM (amoCRM, Bitrix24), мессенджеры (Telegram, Max), почта, выгрузки прайсов поставщиков, импорты в каталог. На FastAPI или PHP — что удобнее под проект.', tags: ['amoCRM', 'Bitrix24', 'Telegram', 'webhooks', 'XML', 'JSON'] }, { id: 'ready-sites', cmd: 'ready-site', title: 'Готовые сайты и шаблоны', price: 'от 19 990 ₽', body: 'Сайты услуг, статейники, каталоги и лендинги с админкой. Установка на VPS, SEO-разметка, формы, аналитика. Дальше — либо передаю под управление, либо поддерживаю точечно.', tags: ['landing', 'каталог', 'статейник', 'admin', 'CMS', 'install'] }, ]; return ( <> { e.preventDefault(); setRoute('home'); window.scrollTo(0,0); } }, { label: 'services' }, ]} title="Услуги" lead="Шесть ключевых направлений. Можно заказать одну точечную задачу или взять услугу пакетом — как удобнее. Финальную стоимость называю до старта." />
{detailed.map((s, i) => (
$ {s.cmd}

{s.title}

{s.price}
))}
); } /* ========== Cases page ========== */ function CasesPage({ setRoute }) { return ( <> { e.preventDefault(); setRoute('home'); window.scrollTo(0,0); } }, { label: 'cases' }, ]} title="Кейсы и примеры задач" lead="Это не «портфолио сайтов». Это шесть типовых ситуаций, в которых ко мне приходят, и итог по каждой — что было, что стало." /> ); } /* ========== Articles ========== */ function articleItems() { return window.__PUBLIC_DATA__?.articles || []; } function ArticleCard({ article, index = 0 }) { return ( {article.cover_image && (
)}
{article.published_label || 'статья'} {article.reading_time}

{article.title}

{article.summary}

{(article.tags || []).slice(0, 4).map((tag) => #{tag})}
); } function ArticlesPreview({ setRoute }) { const items = articleItems().slice(0, 3); if (!items.length) return null; return (
// статьи Практические заметки · без воды Разборы по техническому SEO, schema.org, PHP/Python, серверам и поддержке сайтов.
{ e.preventDefault(); setRoute('articles'); window.scrollTo(0,0); }} className="btn btn-ghost">Все статьи
{items.map((article, i) => )}
); } function ArticlesPage({ setRoute }) { const items = articleItems(); return ( <> { e.preventDefault(); setRoute('home'); window.scrollTo(0,0); } }, { label: 'articles' }, ]} title="Статьи" lead="Практические публикации по техническому SEO, schema.org, PHP, Python, VPS, Docker и поддержке сайтов. Каждая статья формирует структурированные данные schema.org." />
{items.length > 0 ? (
{items.map((article, i) => )}
) : (

Статей пока нет

{/*

Раздел уже готов: публикации появятся после добавления через админку.

*/}
)}
); } function ArticleDetailPage({ setRoute }) { const article = window.__PUBLIC_DATA__?.current_article; if (!article) return ; return ( <> { e.preventDefault(); setRoute('home'); window.scrollTo(0,0); } }, { label: 'articles', href: true, onClick: (e) => { e.preventDefault(); setRoute('articles'); window.scrollTo(0,0); } }, { label: article.slug }, ]} title={article.title} lead={article.summary} />
~/articles/{article.slug}
$schema --type Article
schema.org: Article
reading_time: {article.reading_time}
published: {article.published_label || '—'}
{article.service &&
service: {article.service.title}
}
// JSON-LD вставлен в head страницы
$
{article.cover_image && ( )}
{article.published_label || 'статья'} {article.reading_time} {article.service && {article.service.title}}
{(article.tags || []).map((tag) => #{tag})}
); } /* ========== Prices page ========== */ function PricesPage({ setRoute }) { return ( <> { e.preventDefault(); setRoute('home'); window.scrollTo(0,0); } }, { label: 'prices' }, ]} title="Цены" lead="Здесь — стартовые ориентиры. Цена «от» означает, что задача укладывается в небольшой объём. Если объём больше — называю конкретную сумму до старта." /> ); } /* ========== About page ========== */ function AboutPage({ setRoute }) { const contact = getPublicContact(); return ( <> { e.preventDefault(); setRoute('home'); window.scrollTo(0,0); } }, { label: 'about' }, ]} title="Кто выполняет работу" // lead={`Меня зовут ${contact.specialist_name || ''}. Я — ${contact.specialist_title || ''}. Занимаюсь техническими задачами для сайтов: PHP/MySQL, Python, Docker, VPS, Nginx, SEO-структура, микроразметка, формы и API.`} lead={`Я — ${contact.specialist_title || ''}. Занимаюсь техническими задачами для сайтов: PHP/MySQL, Python, Docker, VPS, Nginx, SEO-структура, микроразметка, формы и API.`} />
whoami
{/*
$whoami
{contact.specialist_name || ''}
*/}
$cat ~/about.txt
role: {contact.specialist_title || ''}
years: 5+
stack: PHP, Python, Docker, Nginx, MySQL
focus: технические задачи для сайтов
format: напрямую · фриланс-площадки
// не маркетолог, не агентство
$

Работаю не как «маркетолог по SEO», а как разработчик: смотрю код, сервер, базу данных, шаблоны, редиректы, индексацию и реальные причины ошибок. Если форма ломается — лезу в PHP, а не пишу «попробуйте перезагрузить страницу».

Часто прихожу к старым самописным сайтам — там, где у владельца «лет десять как никто не лазил». Разбираюсь в чужом коде, аккуратно чиню, оставляю комментарии. Без переписывания всего.

Принцип простой: понятная задача → понятная цена → понятный результат. Если объём растёт по ходу работы — заранее говорю, что это уже отдельная задача.

{[ { idx: '01', t: 'PHP/MySQL и старые сайты', d: 'Формы, админки, отправка писем, оптимизация запросов, миграции версий PHP.' }, { idx: '02', t: 'Python и backend', d: 'FastAPI, скрипты-парсеры, выгрузки прайсов, обработка заявок и Telegram-боты.' }, { idx: '03', t: 'Docker · VPS · Nginx', d: 'Запуск проектов на сервере, HTTPS, обновления и базовые бэкапы.' }, ].map((it) => (
/* {it.idx} */

{it.t}

{it.d}

))}
); } /* ========== Platforms page ========== */ function PlatformsPage({ setRoute }) { const platforms = (window.__PUBLIC_DATA__?.platforms || []).map((platform) => ({ name: platform.name, tag: platform.description, state: platform.state || (platform.is_available ? 'активно' : 'на паузе'), count: platform.count_label || '', url: platform.url, isAvailable: platform.is_available, buttonLabel: platform.button_label, })); return ( <> { e.preventDefault(); setRoute('home'); window.scrollTo(0,0); } }, { label: 'platforms' }, ]} title="Где можно заказать" lead="Если вам комфортнее работать через безопасную сделку — заказ можно оформить на фриланс-площадке. Если проще напрямую — оставляйте заявку через сайт." />
{platforms.map((p, i) => { const isExternal = p.url && /^https?:\/\//.test(p.url); const RowTag = p.isAvailable ? 'a' : 'div'; return (
{p.name}
{p.tag}
● {p.state}
{p.count}
)})}

Что значит «безопасная сделка»?

Деньги вы кладёте на счёт площадки, а не мне напрямую. Я делаю работу, вы её принимаете — и тогда площадка переводит оплату. Если что-то не так — у вас всегда есть возможность открыть спор. Особенно удобно, если работаем впервые.

); } /* ========== Contact page ========== */ function ContactPage({ setRoute }) { return ( <> { e.preventDefault(); setRoute('home'); window.scrollTo(0,0); } }, { label: 'contact' }, ]} title="Опишите задачу" lead="Я отвечу обычно в течение пары часов. Чем подробнее опишете симптом — тем точнее назову оценку." /> ); } function ServiceDetailPage({ setRoute }) { const [openFaq, setOpenFaq] = React.useState(0); const data = window.__PUBLIC_DATA__ || {}; const service = data.current_service; if (!service) return ; const related = service.related_cases || []; return ( <> { e.preventDefault(); setRoute('home'); window.scrollTo(0,0); } }, { label: 'services', href: true, onClick: (e) => { e.preventDefault(); setRoute('services'); window.scrollTo(0,0); } }, { label: service.slug }, ]} title={service.title} lead={service.short} />
~/services/{service.slug}
service
$winetlab service --show {service.slug}
name: {service.title}
price: {service.price}
timeline: {service.timeline || 'по оценке'}
related_cases: {related.length}
{/*
// данные берутся из текущей админки
*/}
$
// услуга

Что входит в работу

Входит

    {(service.includes || []).map((item, i) =>
  • +{item}
  • )}

Подходит для

    {(service.audience || []).map((item, i) =>
  • {item}
  • )}
{related.length > 0 && (
// связанные кейсы

Где это уже применялось

{related.map((item, i) => (
{item.category} {item.timeline}

{item.title}

FROM

{plainText(item.problem_html, item.problem)}

TO

{plainText(item.result_html, item.result)}

))}
)} {typeof ReviewsPanel !== 'undefined' && } {service.faq?.length > 0 && (
// faq

Вопросы по услуге

{service.faq.map((item, i) => (

{item.answer}

))}
)} ); } function CaseDetailDynamicPage({ setRoute }) { const data = window.__PUBLIC_DATA__ || {}; const item = data.current_case; if (!item) return ; const service = item.service; const related = (data.cases || []).filter((c) => c.slug !== item.slug && c.service_slug && c.service_slug === item.service_slug).slice(0, 3); return ( <> { e.preventDefault(); setRoute('home'); window.scrollTo(0,0); } }, { label: 'cases', href: true, onClick: (e) => { e.preventDefault(); setRoute('cases'); window.scrollTo(0,0); } }, { label: item.slug }, ]} title={item.title} lead={plainText(item.result_html, item.result) || plainText(item.problem_html, item.problem)} />
{item.category} {item.timeline} {service && {service.title}}

{item.title}

{item.price_from || 'по оценке'}
стоимость
проекта
{item.timeline || '—'}
срок
работы
{(item.stack || []).length}
технологий
в стеке
~/cases/{item.slug}
$cat case.json
category: {item.category}
service: {service?.title || 'без привязки'}
stack: {(item.stack || []).join(' · ') || '—'}
{/*
status: опубликован из админки
*/}
$
// from

С чем пришли

// work

Что сделано

// result

Что изменилось

{(item.stack || []).map((tag) => {tag})}
{related.length > 0 && (
// ещё по этой услуге

Похожие кейсы

)} ); } Object.assign(window, { ServicesPage, ServiceDetailPage, CasesPage, CaseDetailDynamicPage, ArticlesPreview, ArticlesPage, ArticleDetailPage, PricesPage, AboutPage, PlatformsPage, ContactPage });