Расчёт оценочный, под крафтовое производство. Прибыль с литра считается как
отпускная цена минус себестоимость. В основе: рост загрузки ЦКТ с 61% до 83%,
снижение брака на ~2,5% объёма, ~8 ч/нед сэкономленного времени команды.
Реальные цифры зависят от ассортимента и процессов — посчитаем точно на демо.
);
}
/* ---------- mini tank for preview ---------- */
function PvTank({ state, fill, color, foam }) {
return ;
}
/* ---------- usage flow: lifecycle stages + per-module mini screens ---------- */
const FLOW = [
{ step: '01', stage: 'Варка', mods: 'Варочный порядок · Рецепты · Задачи', screen: 'brew',
sys: 'Сменам ставятся задачи по плану, варка идёт по рецепту' },
{ step: '02', stage: 'Брожение', mods: 'ЦКТ · Замеры · Алерты', screen: 'ferment',
sys: 'Замеры плотности и температуры по графику, риски подсвечены' },
{ step: '03', stage: 'Дозревание / охлаждение', mods: 'ЦКТ · Маршрут партии', screen: 'cold',
sys: 'Статусы и сроки видны, партия идёт по техмаршруту' },
{ step: '04', stage: 'Розлив', mods: 'План розлива · Склад · Задачи', screen: 'pour',
sys: 'Готовая партия встаёт в план розлива, продукт приходует склад' }];
function TankMini({ num, cap, nm, kv, state, fill, color, foam, badge }) {
return (
№ {num}{cap} л
{nm}
{kv}
{badge.t}
);
}
function renderFlowScreen(s) {
if (s.screen === 'brew') return (
Варочный порядок
Гёзе абрикоссегодня
APA Сосназавтра
IPA Цитрачт
);
if (s.screen === 'ferment') return (
Замер плотности · завтра 10:00
);
if (s.screen === 'cold') return (
до розлива 3 дн
);
// pour
return (
План розлива
готовСтаут · 4500 лсегодня
готовГруша · 500 лпт
Склад+180 кег
);
}
/* ========== DATA for static sections ========== */
const LEAKS = [
{ ic: 'idle', h: 'Танки простаивают', p: 'Пустой или передержанный ЦКТ — это деньги, которые не работают. Без живой картины цеха планируют «на глаз».', cost: 'до 20% оборачиваемости мимо кассы' },
{ ic: 'waste', h: 'Брак и списания', p: 'Пропущенный замер плотности или температуры — и партия уходит в слив. Узнаёте об этом постфактум.', cost: '3–5% объёма в отходы' },
{ ic: 'blind', h: 'Нет прозрачной себестоимости', p: 'Сколько реально стоит партия и с какой наценкой её продавать? В таблицах Excel это считают неделями.', cost: 'продажи ниже маржи' }];
const FEATURES = [
{ ic: 'eye', h: 'Цех как на ладони', p: 'Каждый ЦКТ — живая иконка с уровнем, сортом и состоянием: брожение, дозревание, cold crash, готов. Один экран — весь завод.' },
{ ic: 'bell', h: 'Замеры и алерты', p: 'Система ведёт график замеров плотности и температуры, напоминает о шагах и подсвечивает партии в зоне риска.' },
{ ic: 'cost', h: 'Себестоимость партии', p: 'Стоимость каждой варки считается автоматически — видно маржу и рекомендованную цену ещё до розлива.' },
{ ic: 'plan', h: 'Планирование розлива', p: 'Готовые партии сами встают в план розлива и варочный порядок. Никаких конфликтов за танк.' },
{ ic: 'chart', h: 'Оборачиваемость и аналитика', p: 'Загрузка танков, выход продукта, длительность циклов — метрики, которые напрямую двигают прибыль.' },
{ ic: 'flow', h: 'Маршрут партии', p: 'Весь путь от залива сусла до розлива — на одном таймлайне. Видно, что происходит и что дальше.' }];
const METRICS = [
{ n: '+33%', l: 'партий в год с тех же танков', am: false,
why: 'Загрузка ЦКТ растёт с 61% до 83% — тот же парк танков вмещает на треть больше варок (9→12 с танка).' },
{ n: '−63%', l: 'брака и списаний', am: true,
why: 'Замеры по графику и алерты по партиям в риске: доля списаний падает с 4,0% до 1,5% объёма.' },
{ n: '+22 п.п.', l: 'загрузка ЦКТ (61→83%)', am: false,
why: 'Освобождение танка видно в день готовности — следующая варка встаёт без простоя между партиями.' },
{ n: '<1 мес', l: 'окупаемость системы', am: true,
why: 'Стоимость системы — лишь несколько процентов от возвращаемой прибыли. Даже одна спасённая партия (≈ 120 000 ₽ маржи) перекрывает месяцы подписки.' }];
const METHOD = [
{ k: 'Загрузка ЦКТ', t: 'Крафтовые цеха часто работают на 55–70%: танк ждёт розлива, мойки, лабораторных замеров. Видимость освобождения танков поднимает загрузку на 10–22 п.п. В калькуляторе — средний сценарий, не максимум.' },
{ k: 'Брак', t: 'Бо́льшая часть списаний на крафте — из-за пропущенных или поздних замеров, а не рецептуры. График и алерты убирают именно эту причину — поэтому снижение реальное, а не «магическое».' },
{ k: 'Маржа', t: 'Система не повышает цену за вас. Она показывает реальную себестоимость каждой партии — а где не продавать ниже маржи, решаете вы.' },
{ k: 'Окупаемость', t: 'Стоимость системы — несколько процентов от возвращаемой прибыли. Одна возвращённая в работу партия (≈ 120 000 ₽ маржи) перекрывает месяцы подписки. Точную цену под ваш цех считаем на демо.' }];
const STEPS = [
{ h: 'Заводим ваш цех', p: 'Переносим все ЦКТ, сорта и текущие партии в систему — обычно за один день вместе с вашим технологом.' },
{ h: 'Команда работает в BrewOS', p: 'Операторы ведут замеры, директор видит весь завод в реальном времени. Excel и блокноты больше не нужны.' },
{ h: 'Считаем прибыль', p: 'Через месяц сверяем: выросла загрузка танков, упал брак, видна себестоимость. Прибыль — в отчёте.' }];
/* ---------- preview card ---------- */
function PvCard({ num, cap, nm, kv, state, fill, color, foam, badge, bcls }) {
return (
№ {num}{cap} л
{nm}
{kv}
{badge.led && }{badge.t}
);
}
/* ========== DEMO FORM (152-ФЗ согласие) ========== */
// ВНИМАНИЕ (для дизайнера): endpoint заявок и реквизиты оператора берутся из
// внешнего файла config.js (window.BREWOS_CONFIG). НЕ хардкодить сюда и НЕ
// удалять чтение из BREWOS_CONFIG ниже — иначе форма перестанет слать на бэкенд.
const __CFG = (typeof window !== 'undefined' && window.BREWOS_CONFIG) || {};
// URL входа в веб-приложение (кнопка «Войти»). Берётся из config.js.
const LOGIN_URL = __CFG.appLoginUrl || 'http://localhost:15173/login';
// Открыть бот заявок (для CTA-кнопок вне App: «Получить расчёт» и т.п.).
// start — payload deep-link: 'demo' (просто демо) или 'calc_<ЦКТ>-<объём>-<цикл>-<цена>-<себес>-<выгода>'.
const DEMO_BOT_URL = __CFG.demoBotUrl || 'https://t.me/Scan2Action_requiestbot?start=demo';
const BOT_BASE = DEMO_BOT_URL.split('?')[0];
function openBot(start) {
window.open(BOT_BASE + '?start=' + (start || 'demo'), '_blank', 'noopener');
}
function goDemo(e) {
if (e) e.preventDefault();
openBot('demo');
}
// Реквизиты оператора ПДн (152-ФЗ). Реальные значения — в config.js.
const OPERATOR = __CFG.operator || {
name: 'ООО «БрюОС»',
inn: '0000000000',
address: 'г. Москва, ул. Примерная, д. 1',
email: 'privacy@brewos.ru',
retention: '6 месяцев'
};
// Куда отправлять заявки — из config.js (window.BREWOS_CONFIG).
// endpoint — URL бэкенда (рекомендуется), telegram — прямая отправка (только тест).
const LEADS = {
endpoint: __CFG.leadEndpoint || '',
telegram: __CFG.telegram || { botToken: '', chatId: '' }
};
async function sendLead({ name, phone }){
const text = `🍺 Заявка на демо BrewOS\n👤 ${name}\n📞 ${phone}\n🕒 ${new Date().toLocaleString('ru-RU')}`;
if (LEADS.endpoint){
const res = await fetch(LEADS.endpoint, {
method:'POST', headers:{'Content-Type':'application/json'},
body: JSON.stringify({ name, phone, ts: Date.now() })
});
if(!res.ok) throw new Error('endpoint '+res.status);
return;
}
if (LEADS.telegram.botToken && LEADS.telegram.chatId){
const res = await fetch(`https://api.telegram.org/bot${LEADS.telegram.botToken}/sendMessage`, {
method:'POST', headers:{'Content-Type':'application/json'},
body: JSON.stringify({ chat_id: LEADS.telegram.chatId, text })
});
if(!res.ok) throw new Error('telegram '+res.status);
return;
}
// ничего не настроено — демо-режим (без реальной отправки)
await new Promise(r=>setTimeout(r,500));
}
function fmtPhone(v) {
let d = v.replace(/\D/g, '');
if (d.startsWith('8')) d = '7' + d.slice(1);
if (!d.startsWith('7')) d = '7' + d;
d = d.slice(0, 11);
const p = d.slice(1);
let out = '+7';
if (p.length > 0) out += ' (' + p.slice(0, 3);
if (p.length >= 3) out += ') ' + p.slice(3, 6);
if (p.length >= 6) out += '-' + p.slice(6, 8);
if (p.length >= 8) out += '-' + p.slice(8, 10);
return out;
}
function DemoForm({ open, onClose }) {
const [view, setView] = useState('form'); // form | policy | done
const [name, setName] = useState('');
const [phone, setPhone] = useState('');
const [agree, setAgree] = useState(false);
const [touched, setTouched] = useState(false);
const [sending, setSending] = useState(false);
const [err, setErr] = useState(false);
useEffect(() => {
if (open) {setView('form');setTouched(false);}
}, [open]);
useEffect(() => {
const onKey = (e) => {if (e.key === 'Escape') onClose();};
if (open) window.addEventListener('keydown', onKey);
return () => window.removeEventListener('keydown', onKey);
}, [open, onClose]);
const phoneDigits = phone.replace(/\D/g, '').length;
const nameOk = name.trim().length >= 2;
const phoneOk = phoneDigits >= 11;
const canSubmit = nameOk && phoneOk && agree;
const submit = async (e) => {
e.preventDefault();
setTouched(true);
if (!canSubmit || sending) return;
setErr(false);
setSending(true);
try {
await sendLead({ name: name.trim(), phone });
setView('done');
} catch (_e) {
setErr(true);
} finally {
setSending(false);
}
};
if (!open) return null;
return (
{if (e.target === e.currentTarget) onClose();}}>
{view === 'form' &&
}
{view === 'policy' &&
Согласие на обработку персональных данных
Настоящее согласие даётся в соответствии с Федеральным законом от 27.07.2006 № 152-ФЗ «О персональных данных» свободно, своей волей и в своём интересе, является конкретным, предметным, информированным, сознательным и однозначным.
Оператор
{OPERATOR.name}, ИНН {OPERATOR.inn}, {OPERATOR.address}. Контакт по вопросам ПДн: {OPERATOR.email}.
Цель обработки
Обработка заявки на демонстрацию продукта BrewOS и связь с вами для её исполнения.
Перечень персональных данных
Имя и номер телефона, указанные вами в форме.
Перечень действий
Сбор, запись, систематизация, хранение, уточнение, использование, удаление и уничтожение — смешанным способом (с использованием средств автоматизации и без).
Передача третьим лицам
Данные не распространяются и не передаются третьим лицам, кроме случаев, прямо предусмотренных законом. Распространение (публикация) персональных данных не осуществляется.
Срок и хранение
Данные обрабатываются до достижения цели, но не дольше {OPERATOR.retention} с момента обращения, либо до отзыва согласия. Хранение и обработка — на территории Российской Федерации.
Отзыв согласия
Согласие можно отозвать в любой момент, направив заявление на {OPERATOR.email}. Обращение рассматривается в срок не более 30 дней.
Подробнее — в Политике обработки персональных данных оператора.
}
{view === 'done' &&
Заявка принята
Спасибо, {name.trim().split(' ')[0] || 'будем знакомы'}! Свяжемся с вами по указанному номеру в ближайшее рабочее время.
}
);
}
/* ========== APP ========== */
function App() {
useReveal();
const calcModel = useCalcModel();
// «Записаться на демо» → сразу в Telegram-бот заявок (deep-link), без формы.
const openDemo = (e) => {
if (e) e.preventDefault();
const url = (window.BREWOS_CONFIG && window.BREWOS_CONFIG.demoBotUrl)
|| 'https://t.me/Scan2Action_requiestbot?start=demo';
window.open(url, '_blank', 'noopener');
};
return (
{/* NAV */}
BBrewOS
для заводов напитков
{/* Кнопка «Войти» временно скрыта (вернуть — раскомментировать строку ниже) */}
{/* Войти */}
Записаться на демо
{/* HERO */}
СИСТЕМА УПРАВЛЕНИЯ КРАФТОВЫМ ПРОИЗВОДСТВОМ
Ваша пивоварня зарабатывает БОЛЬШЕ, когда каждый ЦКТ под контролем
BrewOS превращает цех брожения в управляемую систему: меньше простоев ЦКТ,
меньше брака, прозрачная себестоимость партии. Прибыль, которая раньше утекала — остаётся у вас.