import { useState } from "react"; const DEFAULTS = { goal: 5000, priceMain: 1000, priceConsult: 100, days: 30, c1: 5, c2: 50, c3: 40, c4: 50, c5: 30, }; const ceil1 = (n) => Math.ceil(n * 10) / 10; const fmtN = (n) => { const v = ceil1(n); return v >= 1000 ? v.toLocaleString("ru-RU") : String(v); }; function Field({ label, value, onChange, min, max, step = 1, suffix, hint }) { return (
{label}
onChange(Number(e.target.value))} style={{ width: 80, background: "#252525", border: "1px solid #333", borderRadius: 8, color: "#f0ebe0", fontSize: 15, fontWeight: 700, padding: "3px 8px", textAlign: "right", outline: "none" }} /> {suffix && {suffix}}
onChange(Number(e.target.value))} style={{ width: "100%", accentColor: "#c8a96e", cursor: "pointer" }} /> {hint &&

{hint}

}
); } function FunnelStep({ num, label, value, sub, isGoal, isStart }) { return (
шаг {num} — {label}
{value}
{sub &&
{sub}
}
); } function UpArrow({ pct, label }) { return (
{pct}% → {label}
); } function DayItem({ value, label, sub, highlight, green }) { return (
{value}
{label}
{sub &&
{sub}
}
); } export default function App() { const [goal, setGoal] = useState(DEFAULTS.goal); const [priceMain, setPriceMain] = useState(DEFAULTS.priceMain); const [priceConsult, setPriceConsult] = useState(DEFAULTS.priceConsult); const [days, setDays] = useState(DEFAULTS.days); const [c1, setC1] = useState(DEFAULTS.c1); const [c2, setC2] = useState(DEFAULTS.c2); const [c3, setC3] = useState(DEFAULTS.c3); const [c4, setC4] = useState(DEFAULTS.c4); const [c5, setC5] = useState(DEFAULTS.c5); const mainSales = priceMain > 0 ? goal / priceMain : 0; const consults = c5 > 0 ? (mainSales / c5) * 100 : 0; const wantMore = c4 > 0 ? (consults / c4) * 100 : 0; const breakdowns = c3 > 0 ? (wantMore / c3) * 100 : 0; const replies = c2 > 0 ? (breakdowns / c2) * 100 : 0; const touches = c1 > 0 ? (replies / c1) * 100 : 0; const touchesPerDay = days > 0 ? touches / days : 0; const consultRevenue = consults * priceConsult; const weeksTotal = days / 7; const card = { background: "#1a1a1a", border: "1px solid #2a2a2a", borderRadius: 16, padding: 24 }; const cardTitle = { fontSize: 11, letterSpacing: "0.15em", textTransform: "uppercase", color: "#555", marginBottom: 18 }; return (
Обратное планирование

От цели — к касаниям

{/* Цель */}
Цель и продукт
Нужно продаж основного
{fmtN(mainSales)}
{fmtN(mainSales)} × {priceMain}$ = {fmtN(mainSales * priceMain)}$
{priceConsult > 0 && (
+ {fmtN(consults)} консульт. × {priceConsult}$ = {fmtN(consultRevenue)}$ доп.
)}
{/* Конверсии */}
Конверсии воронки
{/* Воронка снизу вверх */}
Воронка — от действий к цели ↑
{/* План */}
Что делать — в день и в неделю
В день
В неделю
Итог за {days} дней
{fmtN(mainSales * priceMain)}$ (основной) + {fmtN(consultRevenue)}$ (консультации) = {fmtN(mainSales * priceMain + consultRevenue)}$
${goal.toLocaleString("ru-RU")} цель
); }