wasrusgen1-crm/docs/mockup_manager.html

4263 lines
362 KiB
HTML
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>@wasrusgen1 CRM — Менеджер</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&family=Montserrat:wght@700;800&display=swap" rel="stylesheet">
<style>
*{box-sizing:border-box;margin:0;padding:0}
body{background:#C8CACD;min-height:100vh;display:flex;flex-direction:column;align-items:center;padding:20px;font-family:'Inter',sans-serif}
body{--accent:#003E7E;--accent2:#76BD22;--bg:#F5F6F8;--card:#FFFFFF;--ink:#1A1A2E;--muted:#8A94A6;--danger:#EF4444;--warn:#F59E0B;--success:#10B981;--s-success-bg:#ECFDF5;--s-warning-bg:#FFFBEB;--s-danger-bg:#FEF2F2;--s-info:#3B82F6;--s-info-bg:#EFF6FF}
body[data-theme="radar"]{--accent:#4338CA;--accent2:#6366F1;--bg:#F9FAFB;--card:#FFFFFF;--ink:#111827;--muted:#6B7280}
body[data-theme="dark"]{--accent:#4338CA;--accent2:#6366F1;--bg:#111827;--card:#1F2937;--ink:#F9FAFB;--muted:#9CA3AF}
#controls{display:flex;align-items:center;gap:12px;margin-bottom:16px;flex-wrap:wrap;justify-content:center;width:100%;max-width:700px}
#controls label{color:#fff;font-size:13px;font-weight:600}
#screenSelect{padding:8px 12px;border-radius:10px;border:none;background:#fff;font-size:13px;color:#333;cursor:pointer;min-width:220px;box-shadow:0 2px 8px rgba(0,0,0,.15)}
#themeButtons{display:flex;gap:6px}
.theme-btn{padding:7px 14px;border-radius:9px;border:2px solid transparent;font-size:12px;font-weight:700;cursor:pointer;transition:all .2s}
.theme-btn.active{border-color:#fff}
.theme-btn[data-t="zov"]{background:#003E7E;color:#fff}
.theme-btn[data-t="radar"]{background:linear-gradient(135deg,#1E1B4B,#4338CA);color:#fff}
.theme-btn[data-t="dark"]{background:#111827;color:#6366F1}
#phoneFrame{width:390px;height:844px;background:var(--bg);border-radius:44px;overflow:hidden;box-shadow:0 24px 80px rgba(0,0,0,.4),inset 0 0 0 1px rgba(255,255,255,.15);position:relative;display:flex;flex-direction:column}
#statusBar{height:44px;background:var(--card);display:flex;align-items:center;justify-content:space-between;padding:0 24px;flex-shrink:0;font-size:13px;font-weight:600;color:var(--ink);z-index:10}
#screen{flex:1;overflow-y:auto;overflow-x:hidden;scrollbar-width:none;background:var(--bg)}
#screen::-webkit-scrollbar{display:none}
.bottom-nav{height:60px;background:rgba(255,255,255,.92);backdrop-filter:blur(12px);border-top:1px solid rgba(0,0,0,.06);display:flex;align-items:center;justify-content:space-around;flex-shrink:0;z-index:100}
.nav-item{display:flex;flex-direction:column;align-items:center;gap:2px;cursor:pointer;padding:6px 10px;border-radius:10px;transition:all .15s;flex:1}
.nav-item svg{width:22px;height:22px;color:var(--muted)}
.nav-item span{font-size:10px;color:var(--muted);font-weight:500}
.nav-item.active svg,.nav-item.active span{color:var(--accent)}
.page{padding:0 0 80px;min-height:100%}
.page-header{display:flex;align-items:center;gap:12px;padding:16px 16px 12px;background:var(--card);border-bottom:1px solid rgba(0,0,0,.06);position:sticky;top:0;z-index:50}
.page-header h2{font-size:17px;font-weight:700;color:var(--ink);flex:1}
.back-btn{width:32px;height:32px;border-radius:50%;background:var(--bg);display:flex;align-items:center;justify-content:center;cursor:pointer;border:none;flex-shrink:0}
.back-btn svg{color:var(--accent);width:18px;height:18px}
.card{background:var(--card);border-radius:16px;box-shadow:0 2px 12px rgba(0,0,0,.07);padding:16px;margin-bottom:12px}
.card.warn-b{border-left:4px solid var(--warn)}
.card.ok-b{border-left:4px solid var(--success)}
.card.accent-b{border-left:4px solid var(--accent)}
.card.danger-b{border-left:4px solid var(--danger)}
.section-label{text-transform:uppercase;font-size:11px;letter-spacing:.06em;color:var(--muted);margin:20px 16px 8px;font-weight:600}
@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}
@keyframes btn-shimmer{0%{background-position:250% center}100%{background-position:-250% center}}
@keyframes confirm-pulse{0%,100%{box-shadow:0 6px 20px rgba(0,62,126,.45),0 2px 8px rgba(0,62,126,.2),inset 0 1px 0 rgba(255,255,255,.15)}50%{box-shadow:0 14px 40px rgba(0,62,126,.7),0 5px 18px rgba(0,62,126,.38),inset 0 1px 0 rgba(255,255,255,.22)}}
@keyframes tech-select-pop{0%{transform:scale(.94)}50%{transform:scale(1.04)}100%{transform:scale(1)}}
.btn-primary{width:100%;background:linear-gradient(135deg,#002450 0%,#003E7E 30%,#1560BD 60%,#0A4DA8 90%,#002450 100%);background-size:280% auto;color:#fff;border:none;border-radius:14px;padding:15px 20px;font-size:15px;font-weight:700;cursor:pointer;box-shadow:0 6px 20px rgba(0,62,126,.38),0 2px 6px rgba(0,62,126,.18),inset 0 1px 0 rgba(255,255,255,.13);transition:transform .15s,box-shadow .15s;animation:btn-shimmer 5s ease infinite;letter-spacing:.01em;position:relative;overflow:hidden}
.btn-primary:active{transform:scale(.97);box-shadow:0 3px 10px rgba(0,62,126,.25)!important;animation:none}
.btn-primary:disabled{opacity:.38;cursor:default;animation:none;box-shadow:none;transform:none}
.btn-confirm{background:linear-gradient(135deg,#001E45 0%,#003E7E 20%,#1255A4 45%,#0D6EE8 65%,#1255A4 80%,#003E7E 100%)!important;background-size:300% auto!important;animation:btn-shimmer 3s linear infinite,confirm-pulse 2.5s ease-in-out infinite!important;font-size:16px!important;padding:17px 20px!important;border-radius:16px!important}
.btn-secondary{width:100%;background:transparent;color:var(--accent);border:2px solid var(--accent);border-radius:14px;padding:13px;font-size:15px;font-weight:700;cursor:pointer;margin-top:8px;transition:all .15s}
.btn-secondary:active{background:rgba(0,62,126,.06)}
.btn-sm{padding:8px 15px;font-size:12px;font-weight:700;border-radius:10px;cursor:pointer;border:none;background:linear-gradient(135deg,#003E7E,#1565C0);color:#fff;box-shadow:0 3px 10px rgba(0,62,126,.32);transition:transform .15s}
.btn-sm:active{transform:scale(.93)}
.btn-pill{display:inline-flex;align-items:center;gap:5px;padding:4px 10px;border-radius:20px;font-size:11px;font-weight:700;cursor:pointer;border:none;white-space:nowrap;flex-shrink:0;transition:opacity .15s}
.btn-pill:active{opacity:.7}
.btn-pill.accent{background:rgba(0,62,126,.1);color:var(--accent);border:1px solid rgba(0,62,126,.2)}
.btn-pill.muted{background:var(--bg);color:var(--muted);border:1px solid #E2E8F0}
.btn-pill.danger{background:rgba(239,68,68,.08);color:var(--danger);border:1px solid rgba(239,68,68,.2)}
.btn-pill.success{background:rgba(16,185,129,.08);color:var(--success);border:1px solid rgba(16,185,129,.2)}
.btn-action{flex:1;padding:10px;border-radius:12px;font-size:13px;font-weight:700;cursor:pointer;border:none;transition:opacity .15s;display:flex;align-items:center;justify-content:center;gap:6px}
.btn-action:active{opacity:.8}
.btn-action.ok{background:rgba(16,185,129,.1);color:var(--success);border:1.5px solid rgba(16,185,129,.25)}
.btn-action.warn{background:rgba(245,158,11,.1);color:var(--warn);border:1.5px solid rgba(245,158,11,.25)}
.btn-action.accent{background:rgba(0,62,126,.08);color:var(--accent);border:1.5px solid rgba(0,62,126,.18)}
.tech-pick-card{background:var(--card);border-radius:14px;padding:14px 10px;text-align:center;cursor:pointer;box-shadow:0 2px 8px rgba(0,0,0,.07);transition:transform .18s,box-shadow .18s,background .18s;border:2px solid transparent;position:relative}
.tech-pick-card:active{transform:scale(.95)}
.tech-pick-card.sel{background:linear-gradient(135deg,#002D5C,#003E7E,#1560BD);border-color:transparent;box-shadow:0 6px 18px rgba(0,62,126,.45),0 2px 8px rgba(0,62,126,.22);animation:tech-select-pop .25s ease}
.tech-pick-card.exists{background:#F1F5F9;border-color:#E2E8F0;opacity:.55;cursor:default}
.badge{display:inline-flex;align-items:center;padding:3px 9px;border-radius:20px;font-size:11px;font-weight:700}
.badge.yellow{background:#FEF3C7;color:#D97706}
.badge.green{background:#DCFCE7;color:#15803D}
.badge.blue{background:#DBEAFE;color:#1D4ED8}
.badge.red{background:#FEE2E2;color:#DC2626}
.badge.gray{background:#F1F5F9;color:#64748B}
.badge.teal{background:#CCFBF1;color:#0F766E}
.divider{height:1px;background:rgba(0,0,0,.07);margin:10px 0}
.row{display:flex;align-items:center;gap:10px}
.row.sb{justify-content:space-between}
/* Pipeline */
.pipeline{display:flex;align-items:flex-start;padding:4px 0;overflow-x:auto;scrollbar-width:none}
.pipeline::-webkit-scrollbar{display:none}
.pip-step{display:flex;flex-direction:column;align-items:center;gap:4px;flex-shrink:0;min-width:58px}
.pip-dot{width:26px;height:26px;border-radius:50%;border:2px solid #CBD5E1;background:var(--card);display:flex;align-items:center;justify-content:center;font-size:10px;font-weight:700}
.pip-dot.done{background:var(--success);border-color:var(--success);color:#fff}
.pip-dot.active{background:var(--accent);border-color:var(--accent);color:#fff}
.pip-dot.blocked{background:#FEF3C7;border-color:var(--warn);color:#92400E}
.pip-label{font-size:9px;color:var(--muted);font-weight:600;text-align:center;line-height:1.2;max-width:56px}
.pip-label.active{color:var(--accent)}
.pip-line{flex:1;height:2px;background:#E2E8F0;margin-top:12px;min-width:6px}
.pip-line.done{background:var(--success)}
/* Tech checklist */
.tech-item{display:flex;align-items:center;gap:12px;padding:10px 0;border-bottom:1px solid rgba(0,0,0,.06)}
.tech-item:last-child{border-bottom:none}
.tech-status{width:26px;height:26px;border-radius:8px;display:flex;align-items:center;justify-content:center;font-size:13px;flex-shrink:0;font-weight:700}
.tech-status.done{background:#DCFCE7;color:#15803D}
.tech-status.wait{background:#FEF3C7;color:#92400E}
.tech-status.na{background:#F1F5F9;color:#64748B}
/* Tech icon box */
.tech-icon-box{width:40px;height:40px;border-radius:11px;display:flex;align-items:center;justify-content:center;flex-shrink:0;padding:8px}
.tech-icon-box.wait{background:#FEF9EC;color:#D97706}
.tech-icon-box.done{background:#DCFCE7;color:#15803D}
.tech-icon-box.client{background:#EFF6FF;color:#1D4ED8}
/* Tech action buttons (3-column row) */
.tech-act-row{display:flex;gap:5px;margin-top:7px}
.tech-act-btn{flex:1;padding:7px 4px;border-radius:10px;border:none;font-size:11px;font-weight:700;cursor:pointer;transition:transform .12s;letter-spacing:.01em}
.tech-act-btn:active{transform:scale(.94)}
.tech-act-btn.ai{background:linear-gradient(135deg,#003E7E,#1565C0);color:#fff;box-shadow:0 2px 8px rgba(0,62,126,.3)}
.tech-act-btn.own{background:rgba(0,62,126,.1);color:var(--accent);border:1.5px solid rgba(0,62,126,.2);box-shadow:none}
.tech-act-btn.client{background:rgba(0,62,126,.18);color:var(--accent);border:1.5px solid rgba(0,62,126,.28);box-shadow:none}
/* Wizard */
.wiz-chip{padding:9px 15px;border-radius:24px;font-size:13px;font-weight:500;cursor:pointer;border:2px solid #E2E8F0;background:var(--card);color:var(--ink);transition:all .18s;display:inline-flex;align-items:center;gap:7px}
.wiz-chip.sel{background:var(--accent);color:#fff;border-color:var(--accent);box-shadow:0 2px 8px rgba(0,62,126,.28)}
.wiz-chips{display:flex;flex-wrap:wrap;gap:8px;margin-top:12px}
/* Single-screen filter chips */
.f-chip{padding:6px 12px;border-radius:20px;font-size:12px;font-weight:600;cursor:pointer;border:1.5px solid #E2E8F0;background:var(--card);color:var(--muted);transition:all .15s;display:inline-flex;align-items:center;gap:4px;white-space:nowrap;user-select:none}
.f-chip.sel{background:linear-gradient(135deg,#003E7E,#1560BD);color:#fff;border-color:transparent;box-shadow:0 2px 8px rgba(0,62,126,.32)}
.f-chip:active{transform:scale(.93)}
.f-row{display:flex;flex-wrap:wrap;gap:6px;margin-top:6px}
.f-section{margin-bottom:14px}
.f-label{font-size:11px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.05em}
.bud-pill{flex:1;padding:10px 6px;border-radius:12px;border:2px solid #E2E8F0;background:var(--card);text-align:center;cursor:pointer;transition:all .18s;min-width:0}
.bud-pill.sel{border-color:var(--accent);background:rgba(0,62,126,.05);box-shadow:0 3px 12px rgba(0,62,126,.2)}
.bud-pill:active{transform:scale(.95)}
/* Budget tiers */
.budget-card{background:var(--card);border-radius:14px;padding:14px 16px;cursor:pointer;border:2px solid #E2E8F0;transition:all .18s;display:flex;align-items:center;gap:14px;margin-bottom:10px}
.budget-card.sel{border-color:var(--accent);background:rgba(0,62,126,.04)}
.tier-icon{font-size:22px;width:42px;height:42px;background:var(--bg);border-radius:11px;display:flex;align-items:center;justify-content:center;flex-shrink:0}
.tier-check{margin-left:auto;width:22px;height:22px;border-radius:50%;border:2px solid #CBD5E1;display:flex;align-items:center;justify-content:center;flex-shrink:0}
.budget-card.sel .tier-check{background:var(--accent);border-color:var(--accent)}
/* Result */
.result-model{background:var(--card);border-radius:14px;padding:14px;margin-bottom:10px;border:2px solid transparent;cursor:pointer;transition:all .2s}
.result-model.pick{border-color:var(--accent)}
.search-pill{background:var(--bg);border-radius:10px;padding:10px 12px;font-size:11px;color:var(--muted);font-family:monospace;word-break:break-all;border:1px solid rgba(0,0,0,.08)}
/* Lead stage rail */
.stage-rail{display:flex;align-items:center;padding:4px 0}
.stage-node{display:flex;flex-direction:column;align-items:center;gap:3px;cursor:pointer;flex-shrink:0}
.stage-dot{width:28px;height:28px;border-radius:50%;border:2px solid #CBD5E1;background:var(--card);display:flex;align-items:center;justify-content:center;font-size:10px;font-weight:800;transition:all .15s}
.stage-dot.done{background:var(--success);border-color:var(--success);color:#fff}
.stage-dot.curr{background:var(--accent);border-color:var(--accent);color:#fff;box-shadow:0 0 0 3px rgba(0,62,126,.15)}
.stage-dot.lost{background:var(--danger);border-color:var(--danger);color:#fff}
.stage-lbl{font-size:9px;font-weight:700;color:var(--muted);text-align:center;max-width:44px;line-height:1.2}
.stage-lbl.curr{color:var(--accent)}
.stage-conn{flex:1;height:2px;background:#E2E8F0;margin-top:-14px}
.stage-conn.done{background:var(--success)}
/* Calendar */
.cal-strip{display:flex;gap:6px;padding:12px 16px;overflow-x:auto;scrollbar-width:none}
.cal-strip::-webkit-scrollbar{display:none}
.cal-day{display:flex;flex-direction:column;align-items:center;gap:3px;flex-shrink:0;width:44px;padding:8px 4px;border-radius:12px;cursor:pointer;transition:all .15s}
.cal-day.today{background:var(--bg);border:1.5px solid var(--accent)}
.cal-day.sel{background:var(--accent)}
.cal-day.off{opacity:.45}
.cal-day .cd-dow{font-size:10px;font-weight:700;color:var(--muted);text-transform:uppercase}
.cal-day.sel .cd-dow{color:rgba(255,255,255,.75)}
.cal-day .cd-num{font-size:17px;font-weight:800;color:var(--ink);line-height:1}
.cal-day.sel .cd-num{color:#fff}
.cal-day .cd-dot{width:5px;height:5px;border-radius:50%;background:var(--accent2);margin-top:2px}
.cal-day.sel .cd-dot{background:#fff}
.cal-day.no-dot .cd-dot{opacity:0}
.appt-block{background:var(--card);border-radius:12px;padding:11px 14px;margin-bottom:8px;border-left:4px solid var(--accent);display:flex;align-items:flex-start;gap:10px}
.appt-time{font-size:12px;font-weight:800;color:var(--accent);min-width:36px;padding-top:1px}
.task-item{display:flex;align-items:flex-start;gap:10px;padding:10px 0;border-bottom:1px solid rgba(0,0,0,.06)}
.task-item:last-child{border-bottom:none}
.task-chk{width:20px;height:20px;border-radius:6px;border:2px solid #CBD5E1;flex-shrink:0;margin-top:1px;display:flex;align-items:center;justify-content:center;cursor:pointer}
.task-chk.done{background:var(--success);border-color:var(--success)}
/* Calculations */
.calc-row{display:flex;align-items:center;justify-content:space-between;padding:11px 0;border-bottom:1px solid rgba(0,0,0,.06)}
.calc-row:last-child{border-bottom:none}
.calc-row.total{padding-top:13px;margin-top:4px}
.pay-chip{display:inline-flex;align-items:center;gap:6px;padding:5px 12px;border-radius:20px;font-size:12px;font-weight:700;cursor:pointer;border:none;transition:all .2s}
.pay-chip.act{background:var(--accent);color:#fff}
.pay-chip.inact{background:var(--bg);color:var(--muted);border:1.5px solid #E2E8F0}
</style>
</head>
<body>
<div id="crm-back-nav" style="position:fixed;top:0;left:0;right:0;z-index:9999;background:rgba(255,255,255,0.92);backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);border-bottom:1px solid rgba(0,0,0,.08);padding:8px 16px;display:flex;align-items:center">
<a href="https://wasrusgen.github.io/wasrusgen1-crm/" style="display:inline-flex;align-items:center;gap:6px;font-family:Inter,system-ui,sans-serif;font-size:13px;font-weight:600;color:#003E7E;text-decoration:none;padding:4px 12px;border-radius:8px;background:#F0F4FF;transition:background .15s" onmouseover="this.style.background='#DDE8FF'" onmouseout="this.style.background='#F0F4FF'">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M19 12H5M12 5l-7 7 7 7"/></svg>
Все кабинеты
</a>
<span style="margin-left:12px;font-family:Inter,system-ui,sans-serif;font-size:12px;color:#8A94A6">@wasrusgen1 CRM</span>
</div>
<div style="height:40px"></div>
<div id="controls">
<label>Экран:</label>
<select id="screenSelect" onchange="go(this.value)">
<option value="manager_home">🏠 Мои заказы</option>
<option value="manager_order">📋 Карточка заказа</option>
<option value="manager_tech">🔌 Подбор — тип</option>
<option value="manager_wizard">🧙 Визард — вопросы</option>
<option value="manager_result">✅ Результат подбора</option>
<option value="manager_schedule">📅 График работы</option>
<option value="manager_salary">💵 Зарплата</option>
<option value="manager_calc">💰 Расчёты с клиентом (legacy)</option>
<option value="manager_lead">🟡 Карточка лида</option>
<option value="manager_client">👤 Клиент</option>
<option value="manager_own_tech">📦 Своя техника (A/B/C)</option>
<option value="manager_tech_client">👤 Клиент выбирает</option>
</select>
<div id="themeButtons">
<button class="theme-btn active" data-t="zov" onclick="setTheme('zov',this)">Синяя</button>
<button class="theme-btn" data-t="radar" onclick="setTheme('radar',this)">CRM</button>
<button class="theme-btn" data-t="dark" onclick="setTheme('dark',this)">Dark</button>
</div>
</div>
<div id="phoneFrame">
<a href="./index.html" id="in-frame-back" style="display:flex;align-items:center;gap:6px;padding:5px 14px;background:rgba(0,62,126,.06);border-bottom:1px solid rgba(0,62,126,.09);font-family:Inter,system-ui,sans-serif;font-size:11px;font-weight:700;color:#003E7E;text-decoration:none;flex-shrink:0;z-index:200"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M19 12H5M12 5l-7 7 7 7"/></svg>Мокапы кабинетов</a>
<div id="statusBar">
<span>9:41</span>
<div style="display:flex;align-items:center;gap:6px">
<svg width="15" height="11" viewBox="0 0 15 11" fill="currentColor"><rect x="0" y="3" width="2.5" height="8" rx=".8"/><rect x="4" y="2" width="2.5" height="9" rx=".8"/><rect x="8" y="0" width="2.5" height="11" rx=".8"/><rect x="12" y="0" width="2.5" height="11" rx=".8" opacity=".3"/></svg>
🔋
</div>
</div>
<div id="screen"></div>
<div id="nav"></div>
</div>
<script src="data.js"></script>
<script>
// ── ИДЕНТИЧНОСТЬ КАБИНЕТА (из data.js._HIERARCHY) ────────────────────────────
// Этот кабинет принадлежит менеджеру Анна К. (ak) · Салон Ленина
var _MGR_ID = 'ak';
var _MGR_IDENTITY = (function(){
var mgr = _getMgr(_MGR_ID);
var salon = _getSalon(mgr ? mgr.salon : 'lenina');
return {
mgrId: _MGR_ID,
name: mgr ? mgr.name : 'Анна К.',
short: mgr ? mgr.short : 'АК',
color: mgr ? mgr.color : '#7C3AED',
salon: salon ? salon.salon : 'Салон Ленина',
salonId: salon ? salon.salonId : 'lenina',
admin: salon ? salon.admin.name : 'Анна М.',
};
})();
// ── State ────────────────────────────────────────────────────────────────────
window._managerOrders = window._managerOrders || [
{ id:1,client:'Иванова А.С.',phone:'+7 912 345-67-89',
type:'kitchen',label:'Кухня · Ул. Ленина 34, кв.12',
stage:3,contract:'МБ-2025-041',amount:186000,advance:93000,
assembly:28000,assemblyPaid:false,
advances:[
{label:'Аванс 1 · 50% при подписании',amount:93000,paid:true,date:'03.05.2025'},
{label:'Аванс 2 · при готовности к отгрузке',amount:93000,paid:false,date:null},
],
rooms:['Кухня'],
tech:[
{name:'Духовой шкаф', wkey:'oven', status:'wait', dims:'', brand:'', source:null},
{name:'Варочная панель', wkey:'cooktop', status:'done', dims:'60×52 см', brand:'Bosch', model:'PUE611BB5E', source:'ai'},
{name:'Вытяжка', wkey:'hood', status:'client_chosen', dims:'60 см', brand:'Elica', model:'FLAT GLASS IX/A/60', source:'client'},
{name:'Посудомойка', wkey:'dishwasher', status:'wait', dims:'', brand:'', source:null},
{name:'Холодильник', wkey:'fridge', status:'wait', dims:'', brand:'', source:null},
],
techNote:'Ждём габариты духовки и посудомойки. Передано клиенту 10.05.',
blocker:'Техника: ждём 2 позиции',
zovContract:'ЗОВ-25-041',
zovStatus:{code:'production',label:'В производстве',detail:'Корпуса готовы. Фасады в работе.',date:'27.05.2026 · 14:33',pct:45} },
{ id:2,client:'Петров В.Н.',phone:'+7 903 211-44-55',
type:'wardrobe',label:'Шкаф-купе · Пр. Мира 7, кв.18',
stage:4,contract:'МБ-2025-038',amount:94000,advance:47000,
assembly:14000,assemblyPaid:false,
advances:[
{label:'Аванс 1 · 50% при подписании',amount:47000,paid:true,date:'18.04.2025'},
{label:'Аванс 2 · при готовности к отгрузке',amount:47000,paid:false,date:null},
],
rooms:['Спальня'],tech:[],techNote:'',blocker:'Технолог: есть замечания',
zovContract:'ЗОВ-25-038',
zovStatus:{code:'ready',label:'Готов к отгрузке',detail:'Все позиции готовы. Дата доставки уточняется.',date:'26.05.2026 · 09:15',pct:90} },
{ id:3,client:'Сидорова М.К.',phone:'+7 916 888-22-11',
type:'kitchen',label:'Кухня · Ул. Садовая 5, кв.3',
stage:5,contract:'МБ-2025-029',amount:212000,advance:106000,
assembly:32000,assemblyPaid:true,
advances:[
{label:'Аванс 1 · 50% при подписании',amount:106000,paid:true,date:'10.03.2025'},
{label:'Аванс 2 · при готовности к отгрузке',amount:106000,paid:true,date:'08.05.2025'},
],
rooms:['Кухня','Балкон'],tech:[],techNote:'',blocker:null,
zovContract:'ЗОВ-25-029',
zovStatus:{code:'shipped',label:'Отгружен',detail:'Доставлен 09.05.2026. Договор закрыт.',date:'09.05.2026 · 11:20',pct:100} },
];
window._activeOrder = window._activeOrder || 0;
window._roomsEditing = false;
window._wiz = window._wiz || {type:null,step:0,answers:{},budget:null};
window._schedDay = window._schedDay || 3;
window._schedView = window._schedView || 'month';
window._CHECKIN = window._CHECKIN || {3:{in:'09:52',out:null,gpsIn:{dist:47,ok:true}}};
window._GPS_STATE = window._GPS_STATE || {}; // {d: 'loading'|{ok,dist}|'denied'}
window._GPS_DEMO = window._GPS_DEMO || 'near'; // 'near'|'far' — демо-переключатель
// ── Настройки директора (регулируются в панели руководителя) ──────────────────
window._CONFIG = window._CONFIG || {
gpsRadius: 300, // м — радиус «менеджер в салоне»
dayOffMinHours: 12, // ч — минимум за сколько часов запрашивать выходной
shiftStart: '10:00',
shiftEnd: '19:00',
};
window._homeFilter = window._homeFilter || 'all';
// Запросы на изменение графика: {mday: 'pending-work'|'pending-off'}
window._SCHED_REQ = window._SCHED_REQ || {};
// Лиды (без договора) — добавляем в общий список
window._managerOrders = window._managerOrders.concat(window._managerOrders.length > 3 ? [] : [
{ id:4, isLead:true, client:'Новиков Д.В.', phone:'+7 916 542-11-88',
source:'Звонок', sourceDate:'20.05.2026',
type:'kitchen', label:'Кухня · Пр. Победы 12',
leadStage:'kp', // new | meeting | kp | thinking | lost
leadNote:'КП отправлено 21.05. Ждём ответа.',
tasks:[{text:'Позвонить, уточнить решение по КП', done:false, hi:true}] },
{ id:5, isLead:true, client:'Белова К.И.', phone:'+7 903 774-55-22',
source:'Зал', sourceDate:'23.05.2026',
type:'wardrobe', label:'Шкаф-купе · адрес уточняется',
leadStage:'meeting',
leadNote:'Первичный визит сегодня. Встреча в 15:00.',
tasks:[{text:'Провести консультацию, снять потребности', done:false, hi:false}] },
{ id:6, isLead:true, client:'Воронова Е.С.', phone:'+7 926 318-77-44',
source:'Зал', sourceDate:'22.05.2026',
type:'kitchen', label:'Кухня + гостиная',
leadStage:'thinking',
leadNote:'Думает. Сказала перезвонит на следующей неделе.',
tasks:[{text:'Позвонить 26.05 — напомнить об акции', done:false, hi:false}] },
]);
// ── Schedule fixtures ─────────────────────────────────────────────────────────
var _DOW = ['Пн','Вт','Ср','Чт','Пт','Сб','Вс'];
var _DATES = [19,20,21,22,23,24,25]; // May 2026
var _WORK = [true,true,false,true,true,false,false]; // Пн,Вт,Чт,Пт — рабочие; Ср — выходной; Сб,Вс — всегда выходные
var _APPTS = [
[],
[{time:'10:00',client:'Иванова А.С.',type:'Замер на объекте',order:'МБ-2025-041',color:'#3B82F6'},
{time:'15:30',client:'Новиков Д.В.',type:'Первичная консультация',order:null,color:'#0D9488'}],
[{time:'11:00',client:'Сидорова М.К.',type:'Согласование проекта',order:'МБ-2025-029',color:'#10B981'}],
[{time:'10:00',client:'Петров В.Н.',type:'Встреча с технологом',order:'МБ-2025-038',color:'#F59E0B'},
{time:'15:00',client:'Белова К.И.',type:'Первичная консультация',order:null,color:'#0D9488'},
{time:'17:30',client:'Иванова А.С.',type:'Согласование техники',order:'МБ-2025-041',color:'#3B82F6'}],
[{time:'12:00',client:'Воронова Е.С.',type:'Замер',order:null,color:'#0D9488'}],
[{time:'11:00',client:'Курганов А.В.',type:'Первичная консультация',order:null,color:'#0D9488'},
{time:'14:30',client:'Иванова А.С.',type:'Приёмка кухни',order:'МБ-2025-041',color:'#3B82F6'}],
[{time:'12:00',client:'Ромашова Е.П.',type:'Согласование дизайна',order:'МБ-2025-051',color:'#10B981'}],
];
var _TASKS = {
1:[{order:'МБ-2025-041',text:'Отправить итог замера клиенту',done:true,hi:false}],
2:[{order:'МБ-2025-029',text:'Подготовить финальный проект к встрече',done:false,hi:true}],
3:[{order:'МБ-2025-041',text:'Уточнить габариты духовки и посудомойки',done:false,hi:true},
{order:'МБ-2025-038',text:'Передать замечания технолога клиенту',done:true,hi:false},
{order:'МБ-2025-029',text:'Подтвердить дату запуска производства',done:false,hi:false}],
4:[{order:'МБ-2025-029',text:'Согласовать доставку',done:false,hi:false}],
};
// ── Wizard Config ─────────────────────────────────────────────────────────────
var WIZ = {
oven:{name:'Духовой шкаф',emoji:'🔥',
steps:[
{q:'Как используете духовку?',field:'usage',multi:true,opts:[
{e:'🥐',l:'Выпечка и тесто'},{e:'🥩',l:'Мясо и рыба'},{e:'♻️',l:'Разогрев блюд'},{e:'👨‍🍳',l:'Готовлю всё подряд'}]},
{q:'Высота ниши?',field:'height',multi:false,opts:[
{e:'📦',l:'45 см'},{e:'📦',l:'60 см'},{e:'📦',l:'90 см'}]},
{q:'Очистка духовки?',field:'clean',multi:false,opts:[
{e:'🤖',l:'Пиролиз — сам всё сожжёт'},{e:'🧽',l:'Каталитическая'},{e:'💪',l:'Вручную — не страшно'}]},
{q:'Газ или электричество?',field:'fuel',multi:false,opts:[
{e:'⚡',l:'Только электро'},{e:'🔥',l:'Газ есть'},{e:'🤷',l:'Не знаю'}]},
],
budgets:{
base:{label:'Базовый',range:'до 25 000 ₽',brands:'Hansa · Gorenje · Gefest',icon:'💰'},
mid: {label:'Средний', range:'25 000 55 000 ₽',brands:'Bosch · AEG · Electrolux',icon:'💳'},
prem:{label:'Премиум',range:'55 000 ₽ +',brands:'Neff · Siemens · Miele',icon:'💎'},
},
niche:'595 × 595 мм, глубина 550 мм',
note:'Пиролиз требует зазора 5 см сверху ниши.',
anchor:'«Пиролиз окупается за 2 года — никто потом не пожалел»',
models:{
base:[{name:'Gorenje B6737E0X',price:'22 900 ₽',rating:4.4,reviews:847,pros:'Конвекция, 77 л, гриль, цифровой дисплей.',tags:['Конвекция','Гриль'],
buyerPros:['Большой объём — удобно для большой семьи','Гриль реально работает, корочка отличная','Простое управление, разобрались за 5 минут'],
buyerCons:['Нагрев чуть неравномерный по краям','Дисплей тускловатый при ярком свете']},
{name:'Hansa BOES64111',price:'18 500 ₽',rating:4.2,reviews:623,pros:'Базовая, 65 л, механика — надёжно.',tags:['Надёжный'],
buyerPros:['Ни одной поломки за 3 года','Механика проще электроники — ничего не глючит','Цена полностью оправдана'],
buyerCons:['Нет конвекции — выпечка неравномерная','Долго набирает температуру']},
{name:'Gefest ДА 622-02',price:'15 800 ₽',rating:4.3,reviews:1240,pros:'Газовая, 56 л. Если газ есть.',tags:['Газ'],
buyerPros:['Мгновенный нагрев — газ есть газ','Самая низкая цена в сегменте','Надёжна как танк, стоит 7 лет'],
buyerCons:['Только для газа — не для всех подходит','Решётки тяжело чистить']}],
mid: [{name:'Bosch HBF534ES0R',price:'38 900 ₽',rating:4.7,reviews:2840,pros:'EcoClean, 71 л, тихий вентилятор.',tags:['Рекомендуем','EcoClean'],
buyerPros:['EcoClean реально работает — протёр влажной тряпкой и всё','Работает почти бесшумно','Равномерный прогрев по всему объёму'],
buyerCons:['Цена немного выше конкурентов','Нет встроенного термощупа']},
{name:'AEG BES356010M',price:'42 500 ₽',rating:4.6,reviews:1320,pros:'SteamBoost, 71 л — идеально для выпечки.',tags:['Пар','Выпечка'],
buyerPros:['Пар творит чудеса — хлеб получается профессиональный','Пирог поднялся как в пекарне','Интерфейс интуитивный'],
buyerCons:['Бак для воды нужно заливать вручную','Дольше разогревается с паром']},
{name:'Electrolux OEF5C50V',price:'34 000 ₽',rating:4.5,reviews:1890,pros:'Пиролиз, 72 л, быстрый разогрев.',tags:['Пиролиз'],
buyerPros:['Пиролиз — забыл про чистку духовки навсегда','Быстрее разогревается чем Bosch','Соотношение цена/качество лучшее в сегменте'],
buyerCons:['Во время пиролиза сильно нагревается дверца снаружи','Нет WiFi управления']},
{name:'Gorenje BOP747S13BG',price:'36 500 ₽',rating:4.5,reviews:980,pros:'Паровая функция, 77 л, SteamClean.',tags:['Пар','SteamClean'],
buyerPros:['SteamClean чистит без химии за 20 минут','77 л — самый большой объём в сегменте','Удобные направляющие для противней'],
buyerCons:['Пар слабее чем у AEG','Дисплей немного устаревший']},
{name:'Samsung NV75K3340BS',price:'32 000 ₽',rating:4.4,reviews:1540,pros:'Dual Fan, 75 л, Ceramic Blue эмаль.',tags:['Dual Fan'],
buyerPros:['Dual Fan — два вентилятора, прогрев идеальный','Эмаль не впитывает запахи','Соотношение цена/объём лучшее'],
buyerCons:['Samsung — сервис найти сложнее','Нет пиролиза']},
{name:'Candy FCP615X/E',price:'29 500 ₽',rating:4.3,reviews:1120,pros:'WiFi управление, 70 л, Simply-Fi.',tags:['WiFi'],
buyerPros:['Включаю духовку с телефона до прихода домой','Рецепты прямо в приложении','Цена лучшая среди WiFi-моделей'],
buyerCons:['Приложение иногда подвисает','WiFi только 2.4 ГГц']},
{name:'Zanussi ZOPNA7K1',price:'27 900 ₽',rating:4.3,reviews:870,pros:'AquaClean, 71 л, А-класс.',tags:['AquaClean'],
buyerPros:['AquaClean — 30 минут пара и жир смыт','Тихий вентилятор','Надёжная — 4 года без нареканий'],
buyerCons:['Бренд менее известный','Нет пиролиза']}],
prem:[{name:'Neff B57CR23N0',price:'68 000 ₽',rating:4.8,reviews:945,pros:'CircoTherm, 71 л, без предварительного разогрева.',tags:['Рекомендуем','CircoTherm'],
buyerPros:['CircoTherm — кладёшь холодную еду, результат идеальный','Экономия энергии ощутима на счётах','Дверь открывается одним пальцем — SoftClose'],
buyerCons:['Цена высокая, но оправданная','Нужен мастер для установки']},
{name:'Siemens HB678G4S1',price:'74 500 ₽',rating:4.7,reviews:720,pros:'VarioClima, пар, пиролиз.',tags:['Пар','Пиролиз'],
buyerPros:['Пар + пиролиз в одном — универсал','Управление как у смартфона','Кулинарный помощник встроенный реально помогает'],
buyerCons:['Очень дорогой ремонт если сломается','Меню немного перегружено']},
{name:'Miele H7264B',price:'112 000 ₽',rating:4.9,reviews:380,pros:'M Touch, PerfectClean, немецкая надёжность.',tags:['Премиум'],
buyerPros:['Стоит 8 лет — ни одного обращения в сервис','PerfectClean — покрытие реально не пачкается','Сенсорный экран удобнее чем в телефоне'],
buyerCons:['Цена как у б/у автомобиля','Запчасти только оригинальные, дорогие']}],
}},
cooktop:{name:'Варочная панель',emoji:'🍳',
steps:[
{q:'Тип нагрева?',field:'type',multi:false,opts:[
{e:'⚡',l:'Индукция'},{e:'🔥',l:'Газ'},{e:'🟠',l:'Электро'}]},
{q:'Сколько конфорок?',field:'burners',multi:false,opts:[
{e:'2⃣',l:'2 конфорки'},{e:'4⃣',l:'4 конфорки'},{e:'5⃣',l:'5 конфорок'}]},
{q:'Ширина панели?',field:'width',multi:false,opts:[
{e:'📏',l:'45 см'},{e:'📏',l:'60 см'},{e:'📏',l:'75 см'},{e:'📏',l:'90 см'}]},
{q:'Особые условия?',field:'extra',multi:true,opts:[
{e:'👶',l:'Дети дома'},{e:'🎯',l:'Точная температура важна'},{e:'🤝',l:'В пару с духовкой одного бренда'}]},
],
budgets:{
base:{label:'Базовый',range:'до 15 000 ₽',brands:'Gorenje · Hansa · Zanussi',icon:'💰'},
mid: {label:'Средний', range:'15 000 40 000 ₽',brands:'Bosch · AEG · Electrolux',icon:'💳'},
prem:{label:'Премиум',range:'40 000 ₽ +',brands:'Neff · Siemens · De Dietrich',icon:'💎'},
},
niche:'Вырез точно по размеру панели ±2 мм.',
note:'Индукция: закипает вдвое быстрее, поверхность не нагревается.',
anchor:'«С детьми в доме индукция — не опция, а норма»',
models:{
base:[{name:'Gorenje G642E',price:'12 900 ₽',rating:4.3,reviews:1050,pros:'4 газ. конфорки, чугунные решётки.',tags:['Газ'],
buyerPros:['Чугунные решётки — ничего не скользит','Огонь регулируется плавно','Надёжна, стоит 5 лет без проблем'],
buyerCons:['Решётки тяжёлые, неудобно снимать для мойки','Нет газконтроля']},
{name:'Zanussi ZGO65414XA',price:'14 500 ₽',rating:4.4,reviews:870,pros:'5 конфорок, газконтроль.',tags:['Газконтроль'],
buyerPros:['Газконтроль спасает если задули конфорку','5 конфорок — места хватает всем','Быстрая центральная конфорка'],
buyerCons:['Центральная конфорка иногда плохо поджигается','Щели у решёток трудно чистить']},
{name:'Hansa BХHC610010',price:'11 200 ₽',rating:4.1,reviews:730,pros:'4 конфорки, нерж. сталь.',tags:['Базовый'],
buyerPros:['Нержавейка легко чистится','Самая доступная цена','Поджиг с первого раза'],
buyerCons:['Нет газконтроля — риск при сквозняке','Мощность конфорок средняя']}],
mid: [{name:'Bosch PVS651FC5E',price:'34 900 ₽',rating:4.8,reviews:3120,pros:'4 зоны индукция, PowerBoost.',tags:['Рекомендуем','Индукция'],
buyerPros:['PowerBoost — вода закипает за 90 секунд','Поверхность холодная — дети в безопасности','Чистить элементарно — просто протёр'],
buyerCons:['Нужна индукционная посуда — докупали','Гудит на высокой мощности']},
{name:'Electrolux EIV63440BW',price:'27 500 ₽',rating:4.5,reviews:2050,pros:'4 зоны, Touch-управление.',tags:['Индукция'],
buyerPros:['Touch-управление удобнее ручек','Лучшая цена в категории индукции','Нагрев точный, ничего не пригорает'],
buyerCons:['Touch иногда срабатывает случайно','Нет бустера как у Bosch']},
{name:'AEG IAE64511FB',price:'38 000 ₽',rating:4.6,reviews:1440,pros:'5 зон, 80 см, мощная центральная.',tags:['5 зон'],
buyerPros:['5 зон — всё готовится одновременно','Центральная зона гигантская под вок','Таймер на каждую зону отдельно'],
buyerCons:['80 см — нужно проверить ширину столешницы','Цена выше чем Bosch за схожие функции']},
{name:'Samsung NZ64T3706S1',price:'29 900 ₽',rating:4.5,reviews:1780,pros:'4 зоны индукция, виртуальный огонь.',tags:['Индукция','Samsung'],
buyerPros:['Анимация огня помогает выбрать мощность интуитивно','Защита от детей удобная','Нагрев быстрый как у Bosch'],
buyerCons:['Нет PowerBoost','Сервис Samsung — не везде']},
{name:'Gorenje IS645ASC',price:'25 900 ₽',rating:4.4,reviews:1240,pros:'4 зоны, SlimLine 5 мм, стекло.',tags:['SlimLine'],
buyerPros:['SlimLine — практически вровень со столешницей','Слайдер-управление необычное и удобное','Цена ниже конкурентов'],
buyerCons:['Мощность чуть меньше чем Bosch','Слайдер иногда заедает']},
{name:'Hansa BHC6358D',price:'23 500 ₽',rating:4.3,reviews:980,pros:'4 зоны, детская блокировка, таймер.',tags:['Безопасность'],
buyerPros:['Детская блокировка — надёжная','Самая доступная индукция с таймером','Простой уход'],
buyerCons:['Нет PowerBoost','Дизайн базовый']},
{name:'Lex EVI 640-2 BL',price:'21 900 ₽',rating:4.2,reviews:760,pros:'4 зоны, чёрное стекло, бюджетная индукция.',tags:['Бюджет'],
buyerPros:['Лучшая цена на вход в индукцию','Чёрное стекло выглядит дорого','Работает без нареканий 2 года'],
buyerCons:['Мощность зон слабее топовых','Нет автоматических программ']}],
prem:[{name:'Neff T48FD23X2',price:'62 000 ₽',rating:4.8,reviews:680,pros:'FlexInduction — зоны под любую посуду.',tags:['Рекомендуем','FlexInduction'],
buyerPros:['Кладёшь посуду любого размера — сама определяет зону','Управление жестами удивляет гостей','Полная поверхность без промежутков'],
buyerCons:['Дорогой сервис','При замене стекла — только в авторизованном центре']},
{name:'Siemens EH675MP27E',price:'68 500 ₽',rating:4.7,reviews:520,pros:'iQ700, 5 зон, таймер.',tags:['iQ700'],
buyerPros:['iQ700 — автоматически поддерживает температуру','5 зон хватает для большой семьи','Дизайн делает кухню дороже визуально'],
buyerCons:['Цена выше аналогов','Таймер только через приложение']},
{name:'De Dietrich DPI7690XL',price:'89 000 ₽',rating:4.9,reviews:290,pros:'Full Surface — вся поверхность одна зона.',tags:['Full Surface'],
buyerPros:['Вся панель одна зона — ставишь куда хочешь','Французский дизайн выделяется','Никакого перегрева краёв'],
buyerCons:['Самая дорогая в сегменте','Сервис редкий, не во всех городах']}],
}},
fridge:{name:'Холодильник',emoji:'🧊',
steps:[
{q:'Состав семьи?',field:'family',multi:false,opts:[
{e:'🧍',l:'12 человека'},{e:'👨‍👩‍👦',l:'34 человека'},{e:'👨‍👩‍👧‍👦',l:'5 и больше'}]},
{q:'Тип размещения?',field:'place',multi:false,opts:[
{e:'🚪',l:'Встраиваемый'},{e:'🏠',l:'Отдельностоящий'}]},
{q:'Высота ниши / пространства?',field:'height',multi:false,opts:[
{e:'📐',l:'до 150 см'},{e:'📐',l:'175 см'},{e:'📐',l:'190 см'},{e:'📐',l:'200 см+'}]},
{q:'No Frost важен?',field:'nofrost',multi:false,opts:[
{e:'❄️',l:'Да, обязательно'},{e:'🤷',l:'Не принципиально'}]},
],
budgets:{
base:{label:'Базовый',range:'до 30 000 ₽',brands:'Indesit · Hotpoint · Beko',icon:'💰'},
mid: {label:'Средний', range:'30 000 70 000 ₽',brands:'Bosch · Samsung · LG',icon:'💳'},
prem:{label:'Премиум',range:'70 000 ₽ +',brands:'Liebherr · Miele · Sub-Zero',icon:'💎'},
},
niche:'Встраиваемый: точно по нише + вентзазор 2 см сверху.',
note:'Отдельностоящий: зазор 5 см сверху, 2 см по бокам.',
anchor:'«No Frost = 10 лет без разморозки»',
models:{
base:[{name:'Indesit ITR 4180 W',price:'24 500 ₽',rating:4.3,reviews:2140,pros:'No Frost, 298 л, класс A+.',tags:['No Frost'],
buyerPros:['No Frost — ни разу не размораживали за 4 года','Вместительный, всё влезает','Работает тихо, не слышно ночью'],
buyerCons:['Пластик внутри чуть хрупкий','Полки нельзя переставлять']},
{name:'Beko RCNK356K20W',price:'26 900 ₽',rating:4.4,reviews:1820,pros:'NeoFrost, 300 л.',tags:['NeoFrost'],
buyerPros:['Охлаждение равномерное по всему объёму','Большой морозильник на 100 л','Ящики открываются плавно'],
buyerCons:['Компрессор иногда заметно шумит','Дисплей на двери запотевает']},
{name:'Hotpoint HT 4180 AB',price:'22 000 ₽',rating:4.2,reviews:1560,pros:'278 л, Total No Frost.',tags:['No Frost'],
buyerPros:['Лучшая цена в сегменте No Frost','Надёжный, стоит 5 лет','Просторный морозильник'],
buyerCons:['Нет диспенсера воды','Освещение тускловатое']}],
mid: [{name:'Bosch KGN36VL21R',price:'48 900 ₽',rating:4.8,reviews:4200,pros:'NoFrost, 325 л, VitaFresh.',tags:['Рекомендуем','VitaFresh'],
buyerPros:['VitaFresh — овощи свежие на 2 недели дольше','Работает абсолютно бесшумно','Немецкое качество — ни одного вызова мастера'],
buyerCons:['Цена выше среднего по рынку','Нет льдогенератора']},
{name:'LG GBB59SWJZS',price:'41 000 ₽',rating:4.7,reviews:3150,pros:'Total No Frost, DoorCooling+.',tags:['DoorCooling'],
buyerPros:['DoorCooling+ — дверь закрыл, температура не скачет','Компрессор инверторный — тихий и экономный','Диагностика через смартфон'],
buyerCons:['Приложение LG иногда теряет связь','Дверные полки небольшие']},
{name:'Samsung RB37A5290WW',price:'44 500 ₽',rating:4.6,reviews:3800,pros:'SpaceMax, 367 л, A++.',tags:['A++'],
buyerPros:['SpaceMax — огромный объём при компактном корпусе','Самый большой холодильник в сегменте','Энергопотребление минимальное'],
buyerCons:['Компрессор слышен при старте','Полки стеклянные — осторожно с тяжёлым']},
{name:'Atlant ХМ 4621-101 NL',price:'38 500 ₽',rating:4.5,reviews:2640,pros:'No Frost, 343 л, Full No Frost.',tags:['No Frost','Atlant'],
buyerPros:['Белорусская надёжность — сервис везде','Огромный объём за эти деньги','Тихий компрессор'],
buyerCons:['Дизайн проще чем у Bosch/LG','Нет приложения']},
{name:'Haier CEF537AWD',price:'42 000 ₽',rating:4.5,reviews:1920,pros:'Total No Frost, 330 л, A++, French Door.',tags:['French Door'],
buyerPros:['French Door — две дверцы сверху как в ресторане','Большие ящики морозилки','Тихий как Bosch'],
buyerCons:['Сервис Haier — не все города','Нижняя морозилка неудобна людям с больной спиной']},
{name:'Indesit ITS 5180 W',price:'34 900 ₽',rating:4.4,reviews:2100,pros:'No Frost, 298 л, быстрая заморозка.',tags:['No Frost'],
buyerPros:['Лучшая цена No Frost в сегменте','Быстрая заморозка — мясо замораживает за 4 часа','Тихий'],
buyerCons:['Полок меньше чем у Bosch','Пластик не самый качественный']},
{name:'Candy CCE4T620EB',price:'36 500 ₽',rating:4.3,reviews:1450,pros:'WiFi, No Frost, 331 л, управление с телефона.',tags:['WiFi','Smart'],
buyerPros:['WiFi — смотрю температуру с телефона','Умные алерты если дверь открыта','Стильный дизайн'],
buyerCons:['Приложение Candy базовое','WiFi только 2.4 ГГц']}],
prem:[{name:'Liebherr CUel 2831',price:'78 000 ₽',rating:4.8,reviews:620,pros:'NoFrost, встраиваемый, BioFresh.',tags:['Рекомендуем','BioFresh'],
buyerPros:['BioFresh — рыба и мясо не обветриваются 2 недели','Встройка идеальная — как родная в гарнитуре','Бесшумный как часы'],
buyerCons:['Монтаж только через сертифицированного установщика','Сервис дорогой']},
{name:'Bosch KIS87AF30R',price:'89 500 ₽',rating:4.7,reviews:480,pros:'Встраиваемый, VitaFresh, SoftClose.',tags:['Встраиваемый'],
buyerPros:['SoftClose дверь — открывается и закрывается как в BMW','VitaFresh держит влажность','Немецкое качество без вопросов'],
buyerCons:['Один из дорогих в сегменте встройки','Нет Wi-Fi']},
{name:'Miele KF 2901 Vi',price:'148 000 ₽',rating:4.9,reviews:195,pros:'PerfectFresh Pro, встраиваемый.',tags:['Miele'],
buyerPros:['PerfectFresh Pro — продукты не портятся неделями','Работает 20 лет без обслуживания по отзывам','Премиальный вид и качество сборки'],
buyerCons:['Цена запредельная','Только оригинальные запчасти']}],
}},
hood:{name:'Вытяжка',emoji:'💨',
steps:[
{q:'Куда выводить воздух?',field:'exhaust',multi:false,opts:[
{e:'🌬️',l:'В вентканал (вывод)'},{e:'♻️',l:'Рециркуляция (фильтр)'}]},
{q:'Ширина варочной панели?',field:'width',multi:false,opts:[
{e:'📏',l:'45 см'},{e:'📏',l:'60 см'},{e:'📏',l:'75 см'},{e:'📏',l:'90 см'}]},
{q:'Тип установки?',field:'mount',multi:false,opts:[
{e:'🗄️',l:'Встраиваемая в шкаф'},{e:'📐',l:'Наклонная / настенная'},{e:'🏝️',l:'Островная'}]},
{q:'Что важно?',field:'prio',multi:true,opts:[
{e:'🔇',l:'Тишина (спальня рядом)'},{e:'💪',l:'Высокая мощность'},{e:'🎨',l:'Дизайн виден в интерьере'}]},
],
budgets:{
base:{label:'Базовый',range:'до 10 000 ₽',brands:'Krona · Elikor · Minola',icon:'💰'},
mid: {label:'Средний', range:'10 000 35 000 ₽',brands:'Bosch · Electrolux · Krona',icon:'💳'},
prem:{label:'Премиум',range:'35 000 ₽ +',brands:'Falmec · Elica · Miele',icon:'💎'},
},
niche:'Встраиваемая: ниша = размер вытяжки, глубина шкафа ≥ 300 мм.',
note:'Производительность: площадь кухни × 10 × 1.3 = м³/ч.',
anchor:'«Тихая вытяжка — говоришь во время готовки и слышишь»',
models:{
base:[{name:'Krona KAMILLA 600 Ivory M',price:'8 900 ₽',rating:4.5,reviews:1830,pros:'60 см, 650 м³/ч, 3 скорости.',tags:['Встраиваемая'],
buyerPros:['650 м³/ч — кухня проветривается за минуту','Очень тихая на 1-й скорости','Угольный фильтр легко меняется'],
buyerCons:['Угольные фильтры нужно покупать каждые 6 месяцев','Кнопки чуть дребезжат']},
{name:'Elikor Intro 60П-400-П3Л',price:'7 500 ₽',rating:4.2,reviews:1240,pros:'60 см, 400 м³/ч, базовая модель.',tags:['Базовый'],
buyerPros:['Самая бюджетная и работает нормально','Установка 15 минут — справился сам','Лёгкая — не боится слабых навесов'],
buyerCons:['400 м³/ч маловато для больших кухонь','Шумная на 3-й скорости']},
{name:'Minola HBI 5614 WH 1000 LED',price:'9 200 ₽',rating:4.4,reviews:960,pros:'60 см, LED-подсветка, рециркуляция.',tags:['LED'],
buyerPros:['LED-подсветка ярче чем у других','Работает в рециркуляции — не нужен вентканал','Белый цвет — вписалась в кухню идеально'],
buyerCons:['Рециркуляция хуже вывода в канал','Пульт управления теряется']}],
mid: [{name:'Bosch DWB98JQ50',price:'24 900 ₽',rating:4.8,reviews:2650,pros:'90 см, 740 м³/ч, сенсорное управление.',tags:['Рекомендуем'],
buyerPros:['На 1-й скорости слышно только если рядом стоять','Сенсор не залипает как механика','Жир убирается одной тряпкой'],
buyerCons:['Цена выше чем Electrolux','Нет режима форсаж']},
{name:'Electrolux EFC90560OX',price:'19 500 ₽',rating:4.6,reviews:2100,pros:'90 см, 700 м³/ч, 3 скорости + форсаж.',tags:['Форсаж'],
buyerPros:['Форсаж — запах жарки улетает за 30 сек','Соотношение цена/качество лучшее','Нержавейка не пачкается пальцами'],
buyerCons:['На форсаже шумит заметно','Кнопки немного туговаты']},
{name:'Krona KAMILLA 900 Inox S',price:'16 000 ₽',rating:4.5,reviews:1740,pros:'90 см, слайдер, 1000 м³/ч.',tags:['Слайдер'],
buyerPros:['1000 м³/ч — самая мощная в ценовом сегменте','Слайдер удобнее обычных панелей','Отлично справляется даже с рыбой'],
buyerCons:['Слайдер иногда заедает','Подсветка одна лампочка — тускловато']},
{name:'Samsung NK36M5070BS',price:'18 500 ₽',rating:4.5,reviews:1580,pros:'60 см, 1000 м³/ч, Bluetooth.',tags:['Smart'],
buyerPros:['Управление с телефона — необычно и удобно','Мощная для 60 см','Подключается к плите Samsung — авто-режим'],
buyerCons:['Bluetooth, не WiFi — ограниченный радиус','Работает только с Samsung-плитами для авто-режима']},
{name:'Gorenje WHC953SY2X',price:'22 500 ₽',rating:4.4,reviews:1240,pros:'90 см, 650 м³/ч, SilentBoost.',tags:['Тихая'],
buyerPros:['SilentBoost — тише чем Bosch на 1-й скорости','Хромированные детали выглядят дорого','Фильтры легко снять и помыть'],
buyerCons:['650 м³/ч — не самая мощная','Цена чуть выше Electrolux']},
{name:'Hansa OKC6132IH',price:'15 500 ₽',rating:4.3,reviews:1080,pros:'60 см, 800 м³/ч, LED 3 уровня.',tags:['LED'],
buyerPros:['Яркая LED-подсветка — видно каждый угол кастрюли','Лучшая цена в сегменте','Установил сам за полчаса'],
buyerCons:['Нержавейка оставляет следы от пальцев','60 см — только под 60 см панель']},
{name:'Lex GS 60 IX',price:'14 200 ₽',rating:4.2,reviews:890,pros:'60 см, 650 м³/ч, наклонная.',tags:['Наклонная'],
buyerPros:['Наклонная удобнее прямой — дотянуться легче','Тихая на 1-й скорости','Выглядит современно'],
buyerCons:['Мощность базовая','Нет форсажа']}],
prem:[{name:'Falmec PLANE 90',price:'68 000 ₽',rating:4.8,reviews:340,pros:'90 см, 800 м³/ч, бесшовный дизайн.',tags:['Рекомендуем','Дизайн'],
buyerPros:['Бесшовный стеклянный корпус — выглядит как арт-объект','На 1-й скорости полная тишина','Итальянское качество сборки чувствуется'],
buyerCons:['Монтаж только мастером','Стекло нужно протирать после каждого приготовления']},
{name:'Elica BELT IX/A/120',price:'54 000 ₽',rating:4.7,reviews:280,pros:'120 см, 1200 м³/ч, встраиваемая.',tags:['Мощная'],
buyerPros:['1200 м³/ч — справится с любым объёмом кухни','Встройка идеальная — полностью скрыта в шкафу','Итальянский дизайн'],
buyerCons:['Только для кухонь с мощным вентканалом','120 см — нужна большая ниша']},
{name:'Miele DA 3698',price:'89 000 ₽',rating:4.9,reviews:210,pros:'90 см, ExtraQuiet, 670 м³/ч.',tags:['Тихая'],
buyerPros:['ExtraQuiet — работает тише чем кондиционер','Не нужно кричать на кухне во время готовки','Работает 10 лет без единой замены'],
buyerCons:['Самая дорогая','670 м³/ч скромно за такую цену']}],
}},
dishwasher:{name:'Посудомойка',emoji:'🚿',
steps:[
{q:'Состав семьи?',field:'family',multi:false,opts:[
{e:'🧍',l:'12 человека'},{e:'👨‍👩‍👦',l:'34 человека'},{e:'👨‍👩‍👧‍👦',l:'5+'}]},
{q:'Ширина ниши?',field:'width',multi:false,opts:[
{e:'📏',l:'45 см — компактная'},{e:'📏',l:'60 см — стандарт'}]},
{q:'Тип установки?',field:'mount',multi:false,opts:[
{e:'🚪',l:'Встраиваемая (фасад в ряд)'},{e:'🏠',l:'Отдельностоящая'}]},
{q:'Как сушить посуду?',field:'dry',multi:false,opts:[
{e:'🌬️',l:'Авто-открытие дверцы'},{e:'💧',l:'Конденсационная'},{e:'🤷',l:'Неважно'}]},
],
budgets:{
base:{label:'Базовый',range:'до 25 000 ₽',brands:'Hansa · Gorenje · Indesit',icon:'💰'},
mid: {label:'Средний', range:'25 000 50 000 ₽',brands:'Bosch · AEG · Siemens',icon:'💳'},
prem:{label:'Премиум',range:'50 000 ₽ +',brands:'Miele · V-ZUG · Gaggenau',icon:'💎'},
},
niche:'Стандартная высота 820 мм (регул. ±15 мм).',
note:'Встраиваемая: нужен цоколь и петли для фасада.',
anchor:'«Авто-открытие = посуда всегда сухая без разводов»',
models:{
base:[{name:'Gorenje GS62040W',price:'19 900 ₽',rating:4.3,reviews:1650,pros:'60 см, 12 комплектов, 5 программ.',tags:['Базовый'],
buyerPros:['Моет нормально даже застаревший жир','Тихая — не мешает смотреть ТВ рядом','Соль и ополаскиватель расходуются экономно'],
buyerCons:['Нет авто-открытия — посуда чуть влажная','Пластиковые держатели хрупкие']},
{name:'Hansa ZWM416WH',price:'21 500 ₽',rating:4.4,reviews:1380,pros:'60 см, 12 компл., A++ энергокласс.',tags:['A++'],
buyerPros:['Счёт за электричество заметно ниже','Полностью встраивается — фасад в ряд','Интуитивное управление'],
buyerCons:['Нет половинной загрузки','Корзина для столовых приборов небольшая']},
{name:'Indesit DIF 14B1',price:'18 000 ₽',rating:4.2,reviews:2040,pros:'60 см, 12 компл., половинная загрузка.',tags:['Эконом'],
buyerPros:['Половинная загрузка экономит воду и свет','Самая дешёвая встраиваемая в категории','Ставят посуду быстро, корзины удобные'],
buyerCons:['Сушка слабовата','Нет дисплея — только световые индикаторы']}],
mid: [{name:'Bosch SMV4EVX15E',price:'38 900 ₽',rating:4.9,reviews:5400,pros:'60 см, 14 компл., АвтоОткрытие, A+++.',tags:['Рекомендуем','АвтоОткрытие'],
buyerPros:['АвтоОткрытие — посуда всегда сухая, без разводов','14 комплектов и кастрюли помещаются','Работает ночью — 44 дБ, вообще не слышно'],
buyerCons:['Цена выше конкурентов','Нужен ополаскиватель, иначе разводы']},
{name:'Siemens SN63HX65AK',price:'36 500 ₽',rating:4.6,reviews:3100,pros:'60 см, 14 компл., EfficientDry.',tags:['EfficientDry'],
buyerPros:['EfficientDry лучше конденсационной','Хорошая цена для уровня Siemens','Регулируемая верхняя корзина — высокие бокалы'],
buyerCons:['Нет авто-открытия как у Bosch','Иногда остаётся капля воды на дне тарелки']},
{name:'AEG FSS5360CZM',price:'42 000 ₽',rating:4.7,reviews:2280,pros:'60 см, 15 компл., AirDry, ExtraHygiene.',tags:['AirDry'],
buyerPros:['ExtraHygiene — мощный нагрев убивает бактерии','AirDry сушит хорошо','15 комплектов — большие кастрюли входят'],
buyerCons:['Дороже Bosch','Программы долгие — 2,5 часа стандартная']},
{name:'Samsung DW60A6092BB',price:'34 500 ₽',rating:4.5,reviews:1860,pros:'60 см, 14 компл., Zone Booster, WiFi.',tags:['WiFi','Zone Booster'],
buyerPros:['Zone Booster — верхняя или нижняя корзина отдельно','WiFi — запускаю ПМ удалённо','Стильный чёрный цвет'],
buyerCons:['Нет авто-открытия','Приложение Samsung иногда глючит']},
{name:'Electrolux EEA917110L',price:'32 000 ₽',rating:4.5,reviews:1640,pros:'60 см, 15 компл., AirDry, 43 дБ.',tags:['Тихая','AirDry'],
buyerPros:['43 дБ — абсолютно тихая ночью','AirDry работает хорошо','15 комплектов — влезают высокие кастрюли'],
buyerCons:['Нет WiFi','Цена чуть выше Samsung']},
{name:'Gorenje GS641D60X',price:'28 500 ₽',rating:4.4,reviews:1320,pros:'60 см, 14 компл., инверторный насос.',tags:['Инвертор'],
buyerPros:['Инверторный насос — тихий и надёжный','Лучшая цена в сегменте 14 компл.','Корзины удобные с регулировкой'],
buyerCons:['Нет авто-открытия','Сушка конденсационная — чуть хуже AirDry']},
{name:'Indesit DFC 2B+16 AC X',price:'25 900 ₽',rating:4.3,reviews:1080,pros:'60 см, 16 компл., 9 программ.',tags:['16 комплектов'],
buyerPros:['16 комплектов — больше всех в ценовом диапазоне','9 программ на любой случай','Самая доступная 16-комплектная'],
buyerCons:['Нет авто-открытия','Шумновата — 48 дБ']}],
prem:[{name:'Miele G 7310 SCi',price:'89 000 ₽',rating:4.9,reviews:820,pros:'60 см, 14 компл., AutoDos с AutoOpen.',tags:['Рекомендуем','AutoDos'],
buyerPros:['AutoDos сам дозирует моющее — никогда не думаешь об этом','Стоит 10 лет без единой поломки по отзывам','Тихая как библиотека — 40 дБ'],
buyerCons:['Цена как у техники Apple','Капсулы AutoDos только Miele']},
{name:'V-ZUG AdoraDish V6000',price:'112 000 ₽',rating:4.9,reviews:145,pros:'60 см, 15 компл., швейцарское кач-во.',tags:['Швейцария'],
buyerPros:['Швейцарская точность — всё идеально','Корзины из нержавейки — не ломаются','Бесшумнее Miele'],
buyerCons:['Найти сервис за пределами Москвы сложно','Цена заоблачная']},
{name:'Gaggenau DF270160',price:'145 000 ₽',rating:4.8,reviews:98,pros:'60 см, встраиваемая, zeolith-сушка.',tags:['Zeolith'],
buyerPros:['Zeolith сушит абсолютно сухо с первого раза','Выглядит как произведение искусства','Гарантия 5 лет'],
buyerCons:['Самая дорогая посудомойка на рынке','Сервис только у дилера']}],
}},
micro:{name:'Микроволновка',emoji:'📡',
steps:[
{q:'Как будете использовать?',field:'usage',multi:true,opts:[
{e:'♻️',l:'Только разогрев'},{e:'🍗',l:'Гриль'},{e:'🥖',l:'Конвекция / выпечка'},{e:'🔄',l:'Заменить духовку'}]},
{q:'Нужный объём?',field:'vol',multi:false,opts:[
{e:'📦',l:'до 20 л'},{e:'📦',l:'2030 л'},{e:'📦',l:'30 л+'}]},
{q:'Тип размещения?',field:'mount',multi:false,opts:[
{e:'🗄️',l:'Встраиваемая в шкаф'},{e:'🏠',l:'На столешницу'}]},
],
budgets:{
base:{label:'Базовый',range:'до 10 000 ₽',brands:'Samsung · LG · Горизонт',icon:'💰'},
mid: {label:'Средний', range:'10 000 25 000 ₽',brands:'Bosch · AEG · Electrolux',icon:'💳'},
prem:{label:'Премиум',range:'25 000 ₽ +',brands:'Neff · Miele · Smeg',icon:'💎'},
},
niche:'Встраиваемая: ниша 560 × 340 мм (стандарт под 60 см шкаф).',
note:'Если нет отдельной духовки — выбирайте с конвекцией.',
anchor:'«Встраиваемая не занимает столешницу — кухня выглядит чище»',
models:{
base:[{name:'Samsung MS23K3614AW',price:'7 900 ₽',rating:4.5,reviews:4800,pros:'23 л, 800 Вт, 5 уровней мощности.',tags:['Базовый'],
buyerPros:['Греет равномерно, без холодных зон','Управление проще некуда','Служит 5+ лет без поломок'],
buyerCons:['Нет гриля','Дверь открывается влево — не всем удобно']},
{name:'LG MH6042D',price:'8 500 ₽',rating:4.4,reviews:3200,pros:'20 л, гриль, 700 Вт.',tags:['Гриль'],
buyerPros:['Гриль даёт хрустящую корочку','Дизайн современный','Нагрев быстрый'],
buyerCons:['Гриль требует чистки после каждого раза','Объём 20 л — компактная']},
{name:'Горизонт МВ720-1479',price:'5 800 ₽',rating:4.1,reviews:2100,pros:'20 л, 700 Вт, механика.',tags:['Эконом'],
buyerPros:['Самая дешёвая — работает как надо','Механика не ломается годами','Сделано в Беларуси — сервис везде'],
buyerCons:['Шумновата','Нет таймера с точностью до секунды']}],
mid: [{name:'Bosch BFL554MW0',price:'16 900 ₽',rating:4.7,reviews:1840,pros:'Встраиваемая, 21 л, HotAir.',tags:['Рекомендуем','Встраиваемая'],
buyerPros:['HotAir распределяет тепло равномерно — как конвекция','Встройка идеально вписалась в гарнитур','Работает тихо'],
buyerCons:['Объём 21 л — небольшой','Встройка требует точных размеров ниши']},
{name:'Electrolux EMT25203C',price:'14 000 ₽',rating:4.5,reviews:1560,pros:'Встраиваемая, 25 л, инвертор.',tags:['Инвертор'],
buyerPros:['Инвертор — тихая и экономичная','Лучшая цена среди встраиваемых','25 л — оптимальный объём'],
buyerCons:['Нет гриля','Подсветка слабая']},
{name:'AEG MBE2658SEM',price:'19 500 ₽',rating:4.6,reviews:1320,pros:'Встраиваемая, 26 л, гриль.',tags:['Встраиваемая','Гриль'],
buyerPros:['26 л — большой, влезает целая курица','Гриль работает отлично','Дизайн строгий, вписывается в любой стиль'],
buyerCons:['Дороже Bosch','Чистить гриль неудобно']},
{name:'Samsung MS23DG4504AW',price:'12 500 ₽',rating:4.6,reviews:2100,pros:'23 л, 800 Вт, Anti-Bacterial, настенная.',tags:['Anti-Bacterial'],
buyerPros:['Anti-Bacterial покрытие — гигиенично','Греет равномерно без холодных зон','Вписывается под любой шкаф'],
buyerCons:['Настенная, не встраиваемая','Нет гриля']},
{name:'LG MH6042D',price:'11 000 ₽',rating:4.4,reviews:3200,pros:'20 л, гриль, I-Wave инвертор.',tags:['Гриль','Инвертор'],
buyerPros:['I-Wave — равномерный нагрев без холодных пятен','Гриль с хрустящей корочкой','Тихая — инвертор'],
buyerCons:['20 л — компактная','Нет встройки']},
{name:'Gorenje MO6240SY2X',price:'13 900 ₽',rating:4.4,reviews:970,pros:'20 л, гриль, конвекция, 900 Вт.',tags:['Конвекция','Гриль'],
buyerPros:['Конвекция + гриль — мини-духовка для небольших блюд','900 Вт — самая мощная в классе','Удобное зеркальное стекло'],
buyerCons:['Нет встройки в шкаф','Шумновата на конвекции']},
{name:'Candy CMW20TNDB',price:'10 900 ₽',rating:4.2,reviews:1480,pros:'20 л, инвертор, 5 уровней, сенсор.',tags:['Инвертор','Сенсор'],
buyerPros:['Сенсор удобнее колёсика','Инвертор тихий','Лучшая цена с инвертором'],
buyerCons:['Нет гриля','Объём небольшой']}],
prem:[{name:'Neff HLAWG25S1',price:'34 000 ₽',rating:4.8,reviews:650,pros:'Встраиваемая, 25 л, конвекция, гриль.',tags:['Рекомендуем'],
buyerPros:['Конвекция + гриль — заменяет духовку для мелких блюд','Тихая абсолютно','Немецкое качество — ни одного нарекания'],
buyerCons:['Цена высокая','Ниша должна быть точно по размеру']},
{name:'Miele M 7244 TC',price:'58 000 ₽',rating:4.9,reviews:320,pros:'Встраиваемая, 26 л, M Touch, CleanSteel.',tags:['Miele'],
buyerPros:['M Touch — управление одним касанием','CleanSteel не остаётся следов от пальцев','Работает бесшумно как тень'],
buyerCons:['Самая дорогая в сегменте','Ремонт только у авторизованного сервиса']},
{name:'Smeg FMI325X',price:'42 000 ₽',rating:4.7,reviews:410,pros:'Встраиваемая, 25 л, дизайн 50-х.',tags:['Дизайн'],
buyerPros:['Ретро-дизайн — гости спрашивают где купили','Качество сборки итальянское — ощущается','Греет равномерно'],
buyerCons:['Покупают в первую очередь за дизайн','Нет инвертора']}],
}},
coffee:{name:'Кофемашина',emoji:'☕',
steps:[
{q:'Кто пьёт кофе дома?',field:'who',multi:true,opts:[
{e:'🧍',l:'Только я'},{e:'👫',l:'Двое'},{e:'👨‍👩‍👧',l:'Вся семья'},{e:'🎉',l:'Часто гости'}]},
{q:'Какой кофе пьёте?',field:'type',multi:true,opts:[
{e:'☕',l:'Эспрессо'},{e:'🥛',l:'Капучино / латте'},{e:'🫖',l:'Американо'},{e:'🧋',l:'Всё разное'}]},
{q:'Формат?',field:'format',multi:false,opts:[
{e:'💊',l:'Капсулы — просто и быстро'},{e:'🌾',l:'Зерно — автоматически'},{e:'🎭',l:'Рожковая — ритуал'}]},
{q:'Место установки?',field:'mount',multi:false,opts:[
{e:'🗄️',l:'Встраиваемая в шкаф'},{e:'🏠',l:'На столешницу'}]},
],
budgets:{
base:{label:'Базовый',range:'до 15 000 ₽',brands:'Nespresso · Dolce Gusto · Bosh Tassimo',icon:'💰'},
mid: {label:'Средний', range:'15 000 60 000 ₽',brands:"De'Longhi · Philips · Melitta",icon:'💳'},
prem:{label:'Премиум',range:'60 000 ₽ +',brands:'Jura · Miele · Siemens EQ',icon:'💎'},
},
niche:'Встраиваемая: ниша 45 × 45 см, глубина 550 мм.',
note:'Капсула ≈ 25₽/чашка, зерно ≈ 8₽ — зерновая окупается за год.',
anchor:'«Встраиваемая кофемашина — гости спрашивают: это из кофейни?»',
models:{
base:[{name:'Nespresso Vertuo Pop',price:'7 990 ₽',pros:'Капсульная, 5 размеров напитка.',tags:['Капсульная']},
{name:"De'Longhi EN 80.B",price:'11 500 ₽',pros:'Nespresso-совместимая, компактная.',tags:['Компактная']},
{name:'Bosch TAS3102',price:'6 500 ₽',pros:'Tassimo, многообразие вкусов.',tags:['Tassimo']}],
mid: [{name:"De'Longhi ECAM22.110.B",price:'28 900 ₽',pros:'Зерновая автомат, капучинатор, 1450 Вт.',tags:['Рекомендуем','Автомат']},
{name:'Philips EP2224/10',price:'24 500 ₽',pros:'Зерновая, 1 кофемолка, LatteGo.',tags:['LatteGo']},
{name:'Melitta Barista TS Smart',price:'42 000 ₽',pros:'Wi-Fi управление, 21 напиток.',tags:['Wi-Fi']}],
prem:[{name:'Jura E8 (EB)',price:'78 000 ₽',pros:'Встраиваемая, P.E.P. экстракция, 17 напитков.',tags:['Рекомендуем','Встраиваемая']},
{name:'Miele CVA 7440',price:'142 000 ₽',pros:'Встраиваемая, AromaticSystem, CupSensor.',tags:['Встраиваемая']},
{name:'Siemens TI9553X1RW',price:'89 000 ₽',pros:'iQ700, встраиваемая, autoMilk Clean.',tags:['iQ700']}],
}},
steamer:{name:'Пароварка',emoji:'💧',
steps:[
{q:'Для кого важен пар?',field:'who',multi:true,opts:[
{e:'👶',l:'Детское питание'},{e:'🥗',l:'ЗОЖ и диета'},{e:'🍽️',l:'Разнообразие блюд'},{e:'👩‍⚕️',l:'По медпоказаниям'}]},
{q:'Духовка уже выбрана?',field:'hasoven',multi:false,opts:[
{e:'✅',l:'Да, отдельная духовка'},{e:'🔄',l:'Духовка с функцией пара'},{e:'❌',l:'Нет духовки'}]},
{q:'Высота ниши?',field:'height',multi:false,opts:[
{e:'📦',l:'45 см'},{e:'📦',l:'60 см'}]},
],
budgets:{
base:{label:'Базовый',range:'до 40 000 ₽',brands:'AEG · Electrolux · Gorenje',icon:'💰'},
mid: {label:'Средний', range:'40 000 80 000 ₽',brands:'Bosch · Neff · Siemens',icon:'💳'},
prem:{label:'Премиум',range:'80 000 ₽ +',brands:'Miele · V-ZUG · Gaggenau',icon:'💎'},
},
niche:'Стандартно в колонне: пароварка 45 см + духовка 60 см.',
note:'Если у духовки уже есть пар — уточните нужна ли отдельная.',
anchor:'«Паровой шкаф + духовка = профессиональная кухня дома»',
models:{
base:[{name:'AEG BS7304021M',price:'38 500 ₽',pros:'45 см, SurroundCook, 33 л.',tags:['AEG']},
{name:'Electrolux EOB8956AOX',price:'34 000 ₽',pros:'60 см, SteamBake, электрическая.',tags:['SteamBake']},
{name:'Gorenje BCS798S24X',price:'29 000 ₽',pros:'60 см, пар + конвекция.',tags:['Конвекция']}],
mid: [{name:'Bosch CDG634BS1',price:'62 000 ₽',pros:'45 см, ActiveSteam, 34 л.',tags:['Рекомендуем','ActiveSteam']},
{name:'Neff C17FS22H0',price:'68 000 ₽',pros:'45 см, CircoTherm с паром.',tags:['CircoTherm']},
{name:'Siemens CS536G1S1',price:'58 000 ₽',pros:'45 см, cooKing, 31 л.',tags:['cooKing']}],
prem:[{name:'Miele DGC 7840',price:'112 000 ₽',pros:'45 см, DualSteam, PerfectClean.',tags:['Рекомендуем','DualSteam']},
{name:'V-ZUG SteamCombi V6000',price:'145 000 ₽',pros:'Швейцарское качество, 45 см.',tags:['Швейцария']},
{name:'Gaggenau BS 474 111',price:'168 000 ₽',pros:'45 см, FullSteam, 43 л.',tags:['Gaggenau']}],
}},
freezer:{name:'Морозильник',emoji:'🧊',
steps:[
{q:'Для чего морозильник?',field:'usage',multi:true,opts:[
{e:'🥕',l:'Заготовки / дача'},{e:'🐟',l:'Рыбалка / охота'},{e:'',l:'Доп. объём к холодильнику'},{e:'🛒',l:'Редкие закупки'}]},
{q:'Тип морозильника?',field:'type',multi:false,opts:[
{e:'🗄️',l:'Встраиваемый ящик'},{e:'🏠',l:'Вертикальный отдельный'},{e:'📦',l:'Ларь (горизонтальный)'}]},
{q:'Объём?',field:'vol',multi:false,opts:[
{e:'📦',l:'до 100 л'},{e:'📦',l:'100200 л'},{e:'📦',l:'200 л+'}]},
],
budgets:{
base:{label:'Базовый',range:'до 20 000 ₽',brands:'Indesit · Hotpoint · Beko',icon:'💰'},
mid: {label:'Средний', range:'20 000 45 000 ₽',brands:'Bosch · Samsung · Liebherr',icon:'💳'},
prem:{label:'Премиум',range:'45 000 ₽ +',brands:'Liebherr · Miele · Sub-Zero',icon:'💎'},
},
niche:'Встраиваемый: выдвижной ящик 60 см.',
note:'Ларь — не встраивается в мебель, нужно место вне кухни.',
anchor:'«Морозильник = покупать раз в неделю, а не каждый день»',
models:{
base:[{name:'Indesit EF 16 D1',price:'14 500 ₽',pros:'85 л, A+, 4 режима.',tags:['Базовый']},
{name:'Beko FN 1170',price:'16 000 ₽',pros:'97 л, No Frost, класс A+.',tags:['No Frost']},
{name:'Hotpoint-Ariston HFZ 612 B',price:'12 900 ₽',pros:'119 л, ларь, компрессорный.',tags:['Ларь']}],
mid: [{name:'Bosch GSD36VWEP',price:'32 000 ₽',pros:'242 л, No Frost, VitaFresh, A+++.',tags:['Рекомендуем','No Frost']},
{name:'Samsung RZ32M7120WW',price:'28 500 ₽',pros:'315 л, No Frost, SuperFreezing.',tags:['SuperFreezing']},
{name:'Liebherr FKDv 4523',price:'41 000 ₽',pros:'242 л, SmartFrost, alarm.',tags:['SmartFrost']}],
prem:[{name:'Liebherr GNef 5023',price:'58 000 ₽',pros:'357 л, NoFrost, SuperFrost, A+++.',tags:['Рекомендуем','NoFrost']},
{name:'Miele F 2811 SF',price:'89 000 ₽',pros:'277 л, SoftClose, SuperFrost.',tags:['Miele']},
{name:'Sub-Zero UW24',price:'145 000 ₽',pros:'Встраиваемый, 60 л, американское кач-во.',tags:['Sub-Zero']}],
}},
wine:{name:'Винный шкаф',emoji:'🍷',
steps:[
{q:'Сколько бутылок храните?',field:'bottles',multi:false,opts:[
{e:'🍾',l:'до 20 бутылок'},{e:'🍾',l:'2050 бутылок'},{e:'🏆',l:'50+ (коллекция)'}]},
{q:'Красное и белое вместе?',field:'zones',multi:false,opts:[
{e:'✅',l:'Да, разные температуры'},{e:'❌',l:'Нет, один вид вина'}]},
{q:'Где установить?',field:'place',multi:false,opts:[
{e:'🚪',l:'Под столешницу встраиваемый'},{e:'🏠',l:'Отдельностоящий'}]},
{q:'Что важно?',field:'style',multi:true,opts:[
{e:'🪟',l:'Стеклянная дверь'},{e:'💡',l:'Подсветка коллекции'},{e:'🔇',l:'Тихая работа'}]},
],
budgets:{
base:{label:'Базовый',range:'до 25 000 ₽',brands:'Libhof · Cavanova · Gemlux',icon:'💰'},
mid: {label:'Средний', range:'25 000 60 000 ₽',brands:'Dunavox · Haier · Caso',icon:'💳'},
prem:{label:'Премиум',range:'60 000 ₽ +',brands:'Liebherr · Miele · EuroCave',icon:'💎'},
},
niche:'Встраиваемый под столешницу: 60 × 82 см.',
note:'Две зоны: 812°С (белое / игристое) и 1418°С (красное).',
anchor:'«Гости видят коллекцию — это уже часть интерьера»',
models:{
base:[{name:'Libhof CFD-28',price:'19 900 ₽',pros:'28 бутылок, 2 зоны, LED.',tags:['2 зоны']},
{name:'Gemlux GL-WC28DF',price:'18 500 ₽',pros:'28 бутылок, компрессорный.',tags:['Компрессор']},
{name:'Cavanova OW-008T2',price:'22 000 ₽',pros:'8 бутылок, термоэлектрический.',tags:['Компактный']}],
mid: [{name:'Dunavox DXFH-20.62',price:'38 900 ₽',pros:'20 бутылок, 2 зоны, стекло.',tags:['Рекомендуем','2 зоны']},
{name:'Haier WS50GDA',price:'34 500 ₽',pros:'50 бутылок, стекло, LED, 2 зоны.',tags:['50 бутылок']},
{name:'Caso WineComfort 660 Smart',price:'42 000 ₽',pros:'66 бутылок, Wi-Fi, 3 зоны.',tags:['Wi-Fi','3 зоны']}],
prem:[{name:'Liebherr WKes 553 GrandCru',price:'78 000 ₽',pros:'48 бутылок, встраиваемый, 2 зоны.',tags:['Рекомендуем','Встраиваемый']},
{name:'EuroCave V-REVEL-L',price:'112 000 ₽',pros:'141 бутылка, монтаж в нишу.',tags:['141 бутылка']},
{name:'Miele KWT 6722 iGS',price:'198 000 ₽',pros:'83 бутылки, 3 зоны, интегрируется в кухню.',tags:['Miele']}],
}},
};
var TECH_TYPES = [
{key:'oven', label:'Духовой шкаф'},
{key:'cooktop', label:'Варочная панель'},
{key:'fridge', label:'Холодильник'},
{key:'hood', label:'Вытяжка'},
{key:'dishwasher',label:'Посудомойка'},
{key:'micro', label:'Микроволновка'},
{key:'coffee', label:'Кофемашина'},
{key:'steamer', label:'Пароварка'},
{key:'freezer', label:'Морозильник'},
{key:'wine', label:'Винный шкаф'},
];
var TECH_ICONS = {
oven: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" width="100%" height="100%"><rect x="2" y="4" width="20" height="16" rx="2"/><rect x="6" y="8" width="12" height="9" rx="1"/><circle cx="7.5" cy="6" r=".8" fill="currentColor" stroke="none"/><circle cx="12" cy="6" r=".8" fill="currentColor" stroke="none"/><circle cx="16.5" cy="6" r=".8" fill="currentColor" stroke="none"/></svg>',
cooktop: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" width="100%" height="100%"><rect x="2" y="4" width="20" height="16" rx="2"/><circle cx="8.5" cy="11" r="2.5"/><circle cx="15.5" cy="11" r="2.5"/><circle cx="8.5" cy="17" r="1.3"/><circle cx="15.5" cy="17" r="1.3"/></svg>',
fridge: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" width="100%" height="100%"><rect x="5" y="2" width="14" height="20" rx="2"/><line x1="5" y1="9" x2="19" y2="9"/><line x1="9" y1="5.5" x2="9" y2="8"/><line x1="9" y1="13" x2="9" y2="19"/></svg>',
hood: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" width="100%" height="100%"><path d="M4 4L8 14h8l4-10z"/><rect x="8" y="14" width="8" height="3" rx="1"/><line x1="10" y1="17" x2="10" y2="21"/><line x1="14" y1="17" x2="14" y2="21"/></svg>',
dishwasher:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" width="100%" height="100%"><rect x="3" y="3" width="18" height="18" rx="2"/><line x1="3" y1="8" x2="21" y2="8"/><path d="M8 14Q12 11 16 14"/><path d="M8 18Q12 15 16 18"/><circle cx="12" cy="5.5" r=".8" fill="currentColor" stroke="none"/></svg>',
micro: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" width="100%" height="100%"><rect x="2" y="6" width="20" height="12" rx="2"/><rect x="4" y="8" width="12" height="8" rx="1"/><circle cx="20.2" cy="10" r=".8" fill="currentColor" stroke="none"/><circle cx="20.2" cy="12" r=".8" fill="currentColor" stroke="none"/><circle cx="20.2" cy="14" r=".8" fill="currentColor" stroke="none"/></svg>',
coffee: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" width="100%" height="100%"><rect x="5" y="3" width="14" height="18" rx="2"/><rect x="8" y="7" width="8" height="5" rx="1"/><path d="M19 9Q22 9 22 11Q22 13 19 13"/><line x1="10" y1="17" x2="14" y2="17"/></svg>',
steamer: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" width="100%" height="100%"><rect x="3" y="10" width="18" height="10" rx="2"/><rect x="5" y="5" width="14" height="5" rx="1"/><line x1="8" y1="2" x2="8" y2="5"/><line x1="12" y1="2" x2="12" y2="5"/><line x1="16" y1="2" x2="16" y2="5"/></svg>',
freezer: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" width="100%" height="100%"><rect x="5" y="2" width="14" height="20" rx="2"/><line x1="5" y1="15" x2="19" y2="15"/><line x1="9" y1="17" x2="9" y2="21"/><line x1="12" y1="5" x2="12" y2="13"/><line x1="9.5" y1="7" x2="14.5" y2="11"/><line x1="14.5" y1="7" x2="9.5" y2="11"/></svg>',
wine: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" width="100%" height="100%"><rect x="2" y="2" width="20" height="20" rx="2"/><line x1="2" y1="7" x2="22" y2="7"/><path d="M8 12Q8 10 9 10Q10 10 10 12L10 18Q10 19 9 19Q8 19 8 18Z"/><path d="M14 11Q14 9 15 9Q16 9 16 11L16 18Q16 19 15 19Q14 19 14 18Z"/><line x1="9" y1="4" x2="9" y2="7"/><line x1="15" y1="4" x2="15" y2="7"/></svg>',
};
function _techIcon(key,size){
size=size||24;
return '<div style="width:'+size+'px;height:'+size+'px;flex-shrink:0">'+(TECH_ICONS[key]||TECH_ICONS['oven'])+'</div>';
}
// ── renderScreen ──────────────────────────────────────────────────────────────
// ── CALC EDIT HELPERS — live inline ──────────────────────────────────────────
function _refreshCalc(){ document.getElementById('screen').innerHTML=renderScreen('manager_order'); }
function _liveContract(oi, val) {
var v = parseInt(val, 10);
window._managerOrders[oi].amount = (v > 0) ? v : 0;
}
function _liveAdvAmt(oi, ai, val) {
var v = parseInt(val, 10);
window._managerOrders[oi].advances[ai].amount = (v > 0) ? v : 0;
}
function _liveAdvDate(oi, ai, val) {
window._managerOrders[oi].advances[ai].date = val.trim() || null;
}
function _liveAsm(oi, val) {
var v = parseInt(val, 10);
window._managerOrders[oi].assembly = (v > 0) ? v : 0;
}
function _toggleAdvPaid(oi, ai, paid) {
window._managerOrders[oi].advances[ai].paid = paid;
if (paid && !window._managerOrders[oi].advances[ai].date) {
var d=new Date(); window._managerOrders[oi].advances[ai].date =
String(d.getDate()).padStart(2,'0')+'.'+String(d.getMonth()+1).padStart(2,'0')+'.'+d.getFullYear();
}
_refreshCalc();
}
function _toggleAsm(oi, paid) {
window._managerOrders[oi].assemblyPaid = paid;
_refreshCalc();
}
// ── LEAD CARD ─────────────────────────────────────────────────────────────────
var _LEAD_STAGES = [
{key:'new', label:'Новый', short:'Новый'},
{key:'meeting', label:'Встреча', short:'Встреча'},
{key:'kp', label:'КП', short:'КП'},
{key:'thinking', label:'Думает', short:'Думает'},
{key:'contract', label:'Договор', short:'Договор'},
{key:'lost', label:'Отказ', short:'Отказ'},
];
var _LEAD_TYPE_LABELS = {kitchen:'Кухня',wardrobe:'Шкаф-купе',hallway:'Прихожая',other:'Другое'};
var _APPT_TYPES = ['Консультация','Замер','Показ проекта','Подписание договора'];
function screenLead() {
var o = window._managerOrders[window._activeOrder];
if (!o || !o.isLead) return '<div style="padding:32px">Лид не найден</div>';
var oi = window._activeOrder;
var bk = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M15 18l-6-6 6-6"/></svg>';
// ── Стадия-рейл ──
var si = _LEAD_STAGES.findIndex(function(s){ return s.key===o.leadStage; });
var rail = '<div style="display:flex;align-items:flex-start;padding:16px 16px 4px">';
_LEAD_STAGES.filter(function(s){ return s.key!=='lost'; }).forEach(function(s,idx,arr){
var sIdx = _LEAD_STAGES.findIndex(function(x){ return x.key===s.key; });
var isDone = sIdx < si && o.leadStage!=='lost';
var isCurr = s.key===o.leadStage;
var dotCls = 'stage-dot'+(isDone?' done':isCurr?' curr':'');
var lblCls = 'stage-lbl'+(isCurr?' curr':'');
var inner = isDone?'✓':(idx+1);
rail += '<div class="stage-node" onclick="_setLeadStage('+oi+',\''+s.key+'\')">'
+'<div class="'+dotCls+'">'+inner+'</div>'
+'<div class="'+lblCls+'">'+s.short+'</div></div>';
if (idx < arr.length-1)
rail += '<div class="stage-conn'+(isDone?' done':'')+'"></div>';
});
rail += '</div>';
// ── Задачи ──
var tasks = o.tasks||[];
var taskHtml = tasks.length
? tasks.map(function(t,ti){
var chk = t.done
? '<div style="width:20px;height:20px;border-radius:6px;background:var(--success);border:2px solid var(--success);flex-shrink:0;display:flex;align-items:center;justify-content:center;cursor:pointer" onclick="_leadTaskToggle('+oi+','+ti+')">'
+'<svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="3.5"><polyline points="20 6 9 17 4 12"/></svg></div>'
: '<div style="width:20px;height:20px;border-radius:6px;border:2px solid #CBD5E1;flex-shrink:0;cursor:pointer" onclick="_leadTaskToggle('+oi+','+ti+')"></div>';
return '<div style="display:flex;align-items:flex-start;gap:10px;padding:10px 0;border-bottom:1px solid rgba(0,0,0,.06)">'
+chk
+'<div style="flex:1">'
+(t.hi?'<span style="font-size:10px;font-weight:800;background:#FEF3C7;color:#92400E;border-radius:6px;padding:1px 7px;margin-right:6px">Срочно</span>':'')
+'<span style="font-size:13px;font-weight:600;color:'+(t.done?'var(--muted)':'var(--ink)')+(t.done?';text-decoration:line-through':'')+'">'
+t.text+'</span>'
+'</div>'
+'<div style="font-size:11px;color:var(--muted);cursor:pointer;padding:2px 4px" onclick="_leadTaskDelete('+oi+','+ti+')">✕</div>'
+'</div>';
}).join('')
: '<div style="padding:12px 0;font-size:13px;color:var(--muted)">Задач нет</div>';
// ── Встречи из _APPTS ──
var apptHtml = '';
_APPTS.forEach(function(dayAppts, di){
dayAppts.forEach(function(a){
if (!a.order && a.client===o.client || (a.client===o.client)) {
apptHtml += '<div style="display:flex;align-items:center;gap:10px;padding:9px 0;border-bottom:1px solid rgba(0,0,0,.06)">'
+'<div style="width:42px;height:42px;border-radius:10px;background:#EEF2FF;display:flex;flex-direction:column;align-items:center;justify-content:center;flex-shrink:0">'
+'<div style="font-size:10px;font-weight:700;color:#4338CA">'+_DOW[di]+'</div>'
+'<div style="font-size:14px;font-weight:900;color:#4338CA">'+_DATES[di]+'</div>'
+'</div>'
+'<div style="flex:1">'
+'<div style="font-size:13px;font-weight:700;color:var(--ink)">'+a.type+'</div>'
+'<div style="font-size:11px;color:var(--muted)">'+a.time+'</div>'
+'</div></div>';
}
});
});
if (!apptHtml) apptHtml = '<div style="padding:10px 0;font-size:13px;color:var(--muted)">Встреч не запланировано</div>';
// ── Источник ──
var srcColor = o.source==='Зал'?'#0D9488':'#7C3AED';
var srcBg = o.source==='Зал'?'#CCFBF1':'#EDE9FE';
var ldStage = _LEAD_STAGES.find(function(s){return s.key===o.leadStage;})||{};
var ldBadgeC = {new:'#1D4ED8',meeting:'#0D9488',kp:'#D97706',thinking:'#64748B',lost:'#DC2626'}[o.leadStage]||'#64748B';
var ldBadgeBg= {new:'#DBEAFE',meeting:'#CCFBF1',kp:'#FEF3C7',thinking:'#F1F5F9',lost:'#FEE2E2'}[o.leadStage]||'#F1F5F9';
return '<div class="page">'
// Header
+'<div class="page-header">'
+'<button class="back-btn" onclick="document.getElementById(\'screen\').innerHTML=renderScreen(\'manager_home\');document.getElementById(\'nav\').innerHTML=navBar()">'+bk+'</button>'
+'<div style="flex:1">'
+'<div style="font-size:17px;font-weight:800;color:var(--ink)">'+o.client+'</div>'
+'<div style="font-size:11px;color:var(--muted)">'+o.phone+'</div>'
+'</div>'
+'<a href="tel:'+o.phone+'" style="width:34px;height:34px;border-radius:10px;background:#EEF2FF;display:flex;align-items:center;justify-content:center;text-decoration:none">'
+'<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="#4338CA" stroke-width="2.5"><path d="M22 16.92v3a2 2 0 01-2.18 2 19.79 19.79 0 01-8.63-3.07A19.5 19.5 0 013.07 9.81 19.79 19.79 0 01 0 1.18 2 2 0 012 1h3a2 2 0 012 1.72 12.84 12.84 0 00.7 2.81 2 2 0 01-.45 2.11L6.09 8.91a16 16 0 006 6l1.27-1.27a2 2 0 012.11-.45 12.84 12.84 0 002.81.7A2 2 0 0122 16.92z"/></svg>'
+'</a></div>'
// Мета-строка
+'<div style="padding:10px 16px;display:flex;gap:8px;flex-wrap:wrap;border-bottom:1px solid rgba(0,0,0,.06)">'
+'<span style="font-size:11px;font-weight:700;padding:3px 10px;border-radius:20px;background:'+srcBg+';color:'+srcColor+'">'+o.source+'</span>'
+'<span style="font-size:11px;color:var(--muted);padding:3px 0">'+o.sourceDate+'</span>'
+'<span style="font-size:11px;font-weight:700;padding:3px 10px;border-radius:20px;background:'+ldBadgeBg+';color:'+ldBadgeC+'">'+ldStage.label+'</span>'
+'<span style="font-size:11px;color:var(--muted);padding:3px 0">'+(_LEAD_TYPE_LABELS[o.type]||o.type)+' · '+o.label+'</span>'
+'</div>'
// Стадия рейл
+rail
+'<div style="padding:0 16px">'
// Заметка
+'<div class="section-label" style="margin:12px 0 6px">Заметка</div>'
+'<textarea id="lead_note_'+oi+'" onchange="_saveLeadNote('+oi+',this.value)" '
+'style="width:100%;border:2px solid #E2E8F0;border-radius:12px;padding:11px 14px;font-size:13px;color:var(--ink);font-family:Inter,sans-serif;resize:none;outline:none;background:var(--bg);line-height:1.5" rows="2" placeholder="Добавить заметку...">'+(o.leadNote||'')+'</textarea>'
// Задачи
+'<div class="section-label" style="margin:14px 0 6px">Задачи</div>'
+'<div class="card" style="padding:0 16px">'+taskHtml+'</div>'
+'<button onclick="_addLeadTask('+oi+')" style="display:flex;align-items:center;gap:6px;background:none;border:1.5px dashed #CBD5E1;border-radius:10px;padding:8px 14px;font-size:12px;font-weight:700;color:var(--muted);cursor:pointer;margin-top:8px;width:100%;justify-content:center">+ Добавить задачу</button>'
// Встречи
+'<div class="section-label" style="margin:14px 0 6px">Встречи</div>'
+'<div class="card" style="padding:0 16px">'+apptHtml+'</div>'
+'<button onclick="_addLeadAppt('+oi+')" style="display:flex;align-items:center;gap:6px;background:none;border:1.5px dashed #CBD5E1;border-radius:10px;padding:8px 14px;font-size:12px;font-weight:700;color:var(--muted);cursor:pointer;margin-top:8px;width:100%;justify-content:center">+ Запланировать встречу</button>'
// Конверсионная кнопка
+'<div style="margin:20px 0 8px">'
+'<button onclick="_convertToOrder('+oi+')" '
+'class="btn-primary">'
+'✍️ Договор подписан → создать заказ'
+'</button>'
+'<button onclick="_lostLead('+oi+')" '
+'style="width:100%;background:none;color:var(--muted);border:none;padding:10px;font-size:12px;font-weight:600;cursor:pointer;margin-top:4px">'
+'Отметить как отказ'
+'</button>'
+'</div>'
+'</div></div>';
}
// Хелперы лида
function _setLeadStage(oi, key) {
window._managerOrders[oi].leadStage = key;
// Автозадача при смене этапа
var autoTasks = {
meeting: {text:'Подготовить замерный лист', hi:false},
kp: {text:'Отправить КП клиенту', hi:true},
thinking:{text:'Позвонить через 3 дня — уточнить решение', hi:false},
};
if (autoTasks[key]) {
var o = window._managerOrders[oi];
var already = (o.tasks||[]).some(function(t){ return t.text===autoTasks[key].text; });
if (!already) {
if (!o.tasks) o.tasks=[];
o.tasks.push({text:autoTasks[key].text, done:false, hi:autoTasks[key].hi, auto:true});
}
}
document.getElementById('screen').innerHTML=renderScreen('manager_lead');
_toast('Этап: '+key,'var(--accent)');
}
function _leadTaskToggle(oi, ti) {
window._managerOrders[oi].tasks[ti].done = !window._managerOrders[oi].tasks[ti].done;
document.getElementById('screen').innerHTML=renderScreen('manager_lead');
}
function _leadTaskDelete(oi, ti) {
window._managerOrders[oi].tasks.splice(ti,1);
document.getElementById('screen').innerHTML=renderScreen('manager_lead');
}
function _saveLeadNote(oi, val) {
window._managerOrders[oi].leadNote = val;
}
function _addLeadTask(oi) {
var txt = prompt('Текст задачи:');
if (!txt) return;
if (!window._managerOrders[oi].tasks) window._managerOrders[oi].tasks=[];
window._managerOrders[oi].tasks.push({text:txt.trim(), done:false, hi:false});
document.getElementById('screen').innerHTML=renderScreen('manager_lead');
}
function _addLeadAppt(oi) {
var o = window._managerOrders[oi];
_toast('📅 Встреча добавлена в график','var(--accent)');
}
function _convertToOrder(oi) {
var o = window._managerOrders[oi];
o.isLead = false;
o.leadStage = 'contract';
o.stage = 1;
o.contract = 'МБ-2026-' + String(100 + oi).padStart(3,'0');
o.amount = 0;
o.advance = 0;
o.assembly = 0;
o.assemblyPaid = false;
o.advances = [
{label:'Аванс 1 · 50% при подписании', amount:0, paid:false, date:null},
{label:'Аванс 2 · при готовности к отгрузке', amount:0, paid:false, date:null},
];
o.tech = [];
o.blocker = null;
o.rooms = [];
o.zovContract = '';
o.zovStatus = null;
window._activeOrder = oi;
window._roomsEditing = true;
document.getElementById('screen').innerHTML=renderScreen('manager_order');
document.getElementById('nav').innerHTML=navBar();
_toast('✅ Лид переведён в заказ '+o.contract,'var(--success)');
}
function _lostLead(oi) {
window._managerOrders[oi].leadStage='lost';
document.getElementById('screen').innerHTML=renderScreen('manager_home');
document.getElementById('nav').innerHTML=navBar();
_toast('Отмечен как отказ','var(--muted)');
}
// ── GPS ────────────────────────────────────────────────────────────────────────
var _SALON = {lat:55.7494,lng:37.6172,name:'Салон на Арбате',addr:'ул. Арбат, 34'};
var _GPS_RADIUS = window._CONFIG.gpsRadius; // из настроек директора
function _gpsDist(lat1,lng1,lat2,lng2){
var R=6371000,dL=(lat2-lat1)*Math.PI/180,dG=(lng2-lng1)*Math.PI/180;
var a=Math.sin(dL/2)*Math.sin(dL/2)+Math.cos(lat1*Math.PI/180)*Math.cos(lat2*Math.PI/180)*Math.sin(dG/2)*Math.sin(dG/2);
return Math.round(R*2*Math.atan2(Math.sqrt(a),Math.sqrt(1-a)));
}
function _gpsResult(d, dist, action){
var ok = dist<=_GPS_RADIUS;
window._GPS_STATE[d] = {ok:ok, dist:dist};
if(ok){ action=='checkin'?_schedCheckin(d):_schedCheckout(d); }
else { document.getElementById('screen').innerHTML=renderScreen('manager_schedule'); }
}
function _gpsRequest(d, action){
window._GPS_STATE[d]='loading';
document.getElementById('screen').innerHTML=renderScreen('manager_schedule');
function onGot(lat,lng){ _gpsResult(d,_gpsDist(lat,lng,_SALON.lat,_SALON.lng),action); }
function onFail(){ _gpsResult(d, window._GPS_DEMO==='near'?47:1420, action); }
// 1. Telegram LocationManager (Bot API 8.0+, production path)
var tg = window.Telegram&&Telegram.WebApp;
if(tg&&tg.LocationManager){
tg.LocationManager.init(function(){
if(!tg.LocationManager.isLocationAvailable){ onFail(); return; }
tg.LocationManager.getLocation(function(loc){ loc?onGot(loc.latitude,loc.longitude):onFail(); });
});
}
// 2. Старый TG API (Bot API <8.0)
else if(tg&&tg.requestLocation){
tg.requestLocation(function(loc){ loc?onGot(loc.latitude,loc.longitude):onFail(); });
}
// 3. Браузер (разработка / веб-превью)
else if(navigator.geolocation){
navigator.geolocation.getCurrentPosition(
function(p){ onGot(p.coords.latitude,p.coords.longitude); },
onFail, {timeout:8000,enableHighAccuracy:true}
);
}
// 4. Демо-режим (мокап без GPS)
else{ onFail(); }
}
function _gpsForce(d, action){ // принудительно, несмотря на расстояние
window._GPS_STATE[d]={ok:false,dist:window._GPS_STATE[d]&&window._GPS_STATE[d].dist||0,forced:true};
action=='checkin'?_schedCheckin(d):_schedCheckout(d);
}
function _gpsCancelCheck(d){
window._GPS_STATE[d]=null;
document.getElementById('screen').innerHTML=renderScreen('manager_schedule');
}
function _toggleGpsDemo(){
window._GPS_DEMO = window._GPS_DEMO==='near'?'far':'near';
document.getElementById('screen').innerHTML=renderScreen('manager_schedule');
}
// ── SCHEDULE ──────────────────────────────────────────────────────────────────
function _schedSelectDay(i){
window._schedDay=i;
document.getElementById('screen').innerHTML=renderScreen('manager_schedule');
}
function _schedCheckin(d){
var now=new Date();
var t=String(now.getHours()).padStart(2,'0')+':'+String(now.getMinutes()).padStart(2,'0');
var gps = (window._GPS_STATE[d]&&typeof window._GPS_STATE[d]==='object') ? window._GPS_STATE[d] : null;
window._CHECKIN[d]={in:t,out:null,gpsIn:gps};
window._GPS_STATE[d]=null;
document.getElementById('screen').innerHTML=renderScreen('manager_schedule');
}
function _schedCheckout(d){
if(window._CHECKIN[d]){
var now=new Date();
var t=String(now.getHours()).padStart(2,'0')+':'+String(now.getMinutes()).padStart(2,'0');
var gps = (window._GPS_STATE[d]&&typeof window._GPS_STATE[d]==='object') ? window._GPS_STATE[d] : null;
window._CHECKIN[d].out=t;
window._CHECKIN[d].gpsOut=gps;
window._GPS_STATE[d]=null;
}
document.getElementById('screen').innerHTML=renderScreen('manager_schedule');
}
function _schedCancelCheckin(d){
delete window._CHECKIN[d];
document.getElementById('screen').innerHTML=renderScreen('manager_schedule');
}
function _schedReqShift(mday){
window._SCHED_REQ[mday]='pending-work';
document.getElementById('screen').innerHTML=renderScreen('manager_schedule');
}
function _schedReqOff(mday){
window._SCHED_REQ[mday]='pending-off';
document.getElementById('screen').innerHTML=renderScreen('manager_schedule');
}
function _schedCancelReq(mday){
delete window._SCHED_REQ[mday];
document.getElementById('screen').innerHTML=renderScreen('manager_schedule');
}
function _mFmt(m){ return m>=1000?(m/1000).toFixed(1)+' км':m+' м'; }
// helper: получить смену по дате мая (131); May 1,2026 = Thu = dow-index 3
function _mayDow(date){ return (date+2)%7; } // 0=Пн…6=Вс
// helper: demo-данные есть только для недели 1925 (индексы 06)
function _mayAppts(date){ var wi=date-19; return (wi>=0&&wi<=6)?(_APPTS[wi]||[]):[]; }
function _mayCi(date) { var wi=date-19; return (wi>=0&&wi<=6)?window._CHECKIN[wi]:null; }
function _mayTasks(date){ var wi=date-19; return (wi>=0&&wi<=6)?(_TASKS[wi]||[]):[]; }
// глобальный выбранный день месяца (131, null = не выбран в месяце)
// _schedDay — week-index 06; для месяца используем _schedMday (131)
window._schedMday = window._schedMday || 22; // today = 22 мая
function _schedSelectMday(md){
window._schedMday = md;
// синхронизируем недельный индекс если это demo-неделя
var wi = md-19;
if(wi>=0&&wi<=6) window._schedDay=wi;
document.getElementById('screen').innerHTML=renderScreen('manager_schedule');
}
function _schedToggleView(v){
window._schedView=v;
document.getElementById('screen').innerHTML=renderScreen('manager_schedule');
}
// Общий рендер блока смены + таймлайна по дате мая
function _renderShiftDetail(mday){
var dow = _mayDow(mday);
var isWorkDay = _WORK[dow];
var ci = _mayCi(mday);
var wi = mday-19; // demo-неделя 1925
var req = window._SCHED_REQ[mday]; // 'pending-work' | 'pending-off' | undefined
var dowShort = ['Пн','Вт','Ср','Чт','Пт','Сб','Вс'];
var shiftCard = '';
// ── Определяем эффективный тип дня с учётом запроса ──────────────────────
var effWork = isWorkDay;
if (req==='pending-work') effWork = true; // показываем как рабочий (ожидание)
if (req==='pending-off') effWork = false; // показываем как выходной (ожидание)
// ── Баннер запроса (если есть) ────────────────────────────────────────────
var reqBanner = '';
if (req) {
var isPendWork = req==='pending-work';
reqBanner = '<div style="display:flex;align-items:center;justify-content:space-between;background:#FFFBEB;border:1.5px solid #FDE68A;border-radius:14px;padding:12px 14px;margin-bottom:12px">'
+'<div style="display:flex;align-items:center;gap:10px">'
+'<div style="font-size:20px">'+(isPendWork?'📋':'🌴')+'</div>'
+'<div>'
+'<div style="font-size:13px;font-weight:700;color:#92400E">'+(isPendWork?'Запрос смены — на согласовании':'Запрос выходного — на согласовании')+'</div>'
+'<div style="font-size:11px;color:#B45309;margin-top:1px">Ожидаем ответа Коммерческого директора</div>'
+'</div></div>'
+'<button onclick="_schedCancelReq('+mday+')" style="background:transparent;border:1px solid #FCD34D;border-radius:8px;padding:5px 10px;font-size:11px;font-weight:700;color:#92400E;cursor:pointer">Отменить</button>'
+'</div>';
}
// ── Карточка выходного / рабочего дня ─────────────────────────────────────
if (!effWork) {
shiftCard = '<div style="background:#FEF2F2;border-radius:16px;padding:16px;margin-bottom:14px">'
+'<div style="display:flex;align-items:center;justify-content:space-between">'
+'<div style="display:flex;align-items:center;gap:12px">'
+'<div style="font-size:28px">😴</div>'
+'<div><div style="font-size:15px;font-weight:700;color:#DC2626">Выходной</div>'
+'<div style="font-size:12px;color:#EF9999">'+dowShort[dow]+', '+mday+' мая</div></div>'
+'</div>'
// Кнопка «Запланировать смену» — только если нет активного запроса
+(!req?'<button onclick="_schedReqShift('+mday+')" class="btn-action warn" style="flex:none;padding:7px 12px;font-size:12px">+ Запланировать смену</button>':'')
+'</div></div>';
} else {
var inTime = ci ? (ci.in||'—') : '—';
var outTime = ci ? (ci.out||'—') : '—';
var workedH = (ci&&ci.out) ? (function(){ var ih=parseInt(ci.in),oh=parseInt(ci.out); return (oh-ih)+'ч'; })()
: ci ? 'идёт' : '';
var gst = window._GPS_STATE[wi]; // 'loading' | {ok,dist} | null
// GPS-чипы под статистикой (если есть данные)
var gpsChips = '';
function _gpsChip(g,icon){
if(!g) return '';
var col = g.ok?'var(--success)':(g.forced?'var(--warn)':'var(--danger)');
var bg = g.ok?'rgba(16,185,129,.1)':(g.forced?'rgba(245,158,11,.1)':'rgba(239,68,68,.08)');
var brd = g.ok?'rgba(16,185,129,.3)':(g.forced?'rgba(245,158,11,.3)':'rgba(239,68,68,.25)');
var lbl = g.ok?(g.dist+'м · в салоне ✓'):(g.forced?_mFmt(g.dist)+' · подтверждено вручную':_mFmt(g.dist)+' · вне салона ⚠️');
return '<div style="display:inline-flex;align-items:center;gap:4px;background:'+bg+';border:1px solid '+brd+';border-radius:20px;padding:4px 10px;font-size:11px;font-weight:700;color:'+col+'">'+icon+' '+lbl+'</div> ';
}
if(ci&&ci.gpsIn) gpsChips += _gpsChip(ci.gpsIn,'📍');
if(ci&&ci.gpsOut) gpsChips += _gpsChip(ci.gpsOut,'🏁');
if(gpsChips) gpsChips = '<div style="margin-top:10px;display:flex;flex-wrap:wrap;gap:6px">'+gpsChips+'</div>';
// Кнопки чекина
var checkinBtn = '';
if (wi>=0&&wi<=6&&!req) {
if(gst==='loading'){
checkinBtn = '<div style="margin-top:14px;background:var(--bg);border-radius:12px;padding:14px;text-align:center;font-size:14px;color:var(--muted);font-weight:600">'
+'<span style="display:inline-block;animation:spin 1s linear infinite;margin-right:8px">⏱</span>Определяем местоположение…</div>';
} else if(gst&&!gst.ok){
checkinBtn = '<div style="margin-top:14px;background:rgba(245,158,11,.1);border:1px solid rgba(245,158,11,.3);border-radius:12px;padding:13px">'
+'<div style="font-size:13px;font-weight:700;color:var(--warn);margin-bottom:8px">⚠️ Вы в '+_mFmt(gst.dist)+' от салона</div>'
+'<div style="font-size:12px;color:var(--muted);margin-bottom:10px">'+_SALON.addr+' · Открыть смену вдали от точки?</div>'
+'<div style="display:flex;gap:8px">'
+'<button onclick="_gpsForce('+wi+',\''+(ci?'checkout':'checkin')+'\')" class="btn-action warn" style="flex:1">Открыть всё равно</button>'
+'<button onclick="_gpsCancelCheck('+wi+')" class="btn-pill muted" style="padding:11px 14px;border-radius:12px;font-size:13px">Отмена</button>'
+'</div></div>';
} else if(!ci){
checkinBtn = '<button onclick="_gpsRequest('+wi+',\'checkin\')" style="width:100%;margin-top:14px;background:var(--accent);color:#fff;border:none;border-radius:12px;padding:13px;font-size:15px;font-weight:700;cursor:pointer">📍 Открыть смену</button>';
} else if(!ci.out){
checkinBtn = '<div style="display:flex;gap:8px;margin-top:12px">'
+'<button onclick="_gpsRequest('+wi+',\'checkout\')" class="btn-action ok">📍 Закрыть смену</button>'
+'<button onclick="_schedCancelCheckin('+wi+')" class="btn-pill muted" style="padding:11px 14px;border-radius:12px;font-size:13px">✕</button>'
+'</div>';
}
}
// Демо-переключатель GPS
var gpsDemoToggle = '<div style="margin-top:8px;text-align:right"><span onclick="_toggleGpsDemo()" style="font-size:10px;color:var(--muted);cursor:pointer;text-decoration:underline dotted">GPS демо: '+(window._GPS_DEMO==='near'?'📍 рядом':'🗺 далеко')+'</span></div>';
// Кнопка «Запросить выходной» — минимум за N часов (из _CONFIG), не день-в-день
var _TODAY_MDAY = 22; // в проде: new Date().getDate()
var _NOW_HOUR = 9; // в проде: new Date().getHours()
var _minH = window._CONFIG.dayOffMinHours;
var _hoursAhead = (mday - _TODAY_MDAY) * 24 - _NOW_HOUR;
var _canReqOff = !req && !ci && isWorkDay && _hoursAhead >= _minH;
var _tooSoon = !req && !ci && isWorkDay && _hoursAhead >= 0 && _hoursAhead < _minH;
var reqOffBtn = _canReqOff
? '<button onclick="_schedReqOff('+mday+')" style="width:100%;margin-top:10px;background:var(--bg);color:var(--muted);border:1px solid #E2E8F0;border-radius:10px;padding:9px;font-size:12px;font-weight:600;cursor:pointer">🌴 Запросить выходной</button>'
: _tooSoon
? '<div style="margin-top:10px;text-align:center;font-size:11px;color:var(--muted);padding:6px">🕐 Выходной можно запросить не позднее чем за '+_minH+' ч до смены</div>'
: '';
var statusBadge = req==='pending-off'
? '<div style="background:#FEF3C7;color:#92400E;border-radius:20px;padding:4px 10px;font-size:11px;font-weight:700">⏳ на согл.</div>'
: (ci?'<div style="background:'+(ci.out?'#DCFCE7':'#FEF3C7')+';color:'+(ci.out?'#15803D':'#92400E')+';border-radius:20px;padding:4px 10px;font-size:11px;font-weight:700">'+(ci.out?'✓ отработана':'в процессе')+'</div>':'');
shiftCard = '<div style="background:var(--card);border-radius:16px;padding:16px;margin-bottom:14px">'
+'<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:12px">'
+'<div>'
+'<div style="font-size:15px;font-weight:700;color:var(--ink)">'+dowShort[dow]+', '+mday+' мая</div>'
+'<div style="font-size:12px;color:var(--muted)">Плановая смена · 10:00 19:00 · 9 ч</div>'
+'</div>'
+statusBadge
+'</div>'
+'<div style="display:flex;gap:0;background:var(--bg);border-radius:12px;overflow:hidden">'
+'<div style="flex:1;padding:10px 14px;border-right:1px solid rgba(0,0,0,.07)">'
+'<div style="font-size:10px;color:var(--muted);font-weight:600;text-transform:uppercase;margin-bottom:2px">Начало</div>'
+'<div style="font-size:20px;font-weight:900;color:'+(ci?'var(--success)':'#CBD5E1')+'">'+inTime+'</div>'
+'</div>'
+'<div style="flex:1;padding:10px 14px;border-right:1px solid rgba(0,0,0,.07)">'
+'<div style="font-size:10px;color:var(--muted);font-weight:600;text-transform:uppercase;margin-bottom:2px">Конец</div>'
+'<div style="font-size:20px;font-weight:900;color:'+(ci&&ci.out?'var(--success)':'#CBD5E1')+'">'+outTime+'</div>'
+'</div>'
+'<div style="flex:1;padding:10px 14px">'
+'<div style="font-size:10px;color:var(--muted);font-weight:600;text-transform:uppercase;margin-bottom:2px">Отработано</div>'
+'<div style="font-size:20px;font-weight:900;color:'+(workedH?'var(--accent)':'#CBD5E1')+'">'+(workedH||'—')+'</div>'
+'</div></div>'
+gpsChips+checkinBtn+reqOffBtn+gpsDemoToggle
+'</div>';
}
// ── Таймлайн ──────────────────────────────────────────────────────────────
var appts = _mayAppts(mday).slice().sort(function(a,b){ return a.time.localeCompare(b.time); });
var timelineHtml = '';
if (effWork) {
var events = [{time:'10:00',type:'shift-start',label:'Начало смены',sub:''}]
.concat(appts.map(function(a){ return {time:a.time,type:'appt',label:a.client,sub:a.type+(a.order?' · '+a.order:''),color:a.color}; }))
.concat([{time:'19:00',type:'shift-end',label:'Конец смены',sub:''}]);
timelineHtml = '<div style="position:relative;padding-left:48px">'
+'<div style="position:absolute;left:20px;top:10px;bottom:10px;width:2px;background:linear-gradient(to bottom,var(--accent),#E2E8F0)"></div>';
events.forEach(function(ev){
var isAppt = ev.type==='appt';
var dotBg = isAppt ? (ev.color||'var(--accent)') : ev.type==='shift-start' ? 'var(--accent)' : '#CBD5E1';
var dotSz = isAppt ? '12px' : '10px';
timelineHtml += '<div style="display:flex;align-items:flex-start;margin-bottom:'+(isAppt?'16':'8')+'px;position:relative">'
+'<div style="position:absolute;left:-32px;top:'+(isAppt?'14':'10')+'px;width:'+dotSz+';height:'+dotSz+';border-radius:50%;background:'+dotBg+';border:2px solid var(--card);box-shadow:0 0 0 2px '+dotBg+'33"></div>'
+'<div style="font-size:11px;font-weight:700;color:var(--muted);min-width:36px;padding-top:'+(isAppt?'16':'10')+'px">'+ev.time+'</div>'
+(isAppt
? '<div style="flex:1;background:var(--card);border-radius:12px;padding:10px 14px;border-left:3px solid '+ev.color+';margin-left:8px">'
+'<div style="font-size:13px;font-weight:700;color:var(--ink)">'+ev.label+'</div>'
+'<div style="font-size:11px;color:var(--muted);margin-top:2px">'+ev.sub+'</div>'
+'</div>'
: '<div style="flex:1;padding:8px 0 0 12px;font-size:12px;font-weight:600;color:'+(ev.type==='shift-start'?'var(--accent)':'var(--muted)')+'">'+ev.label+'</div>'
)
+'</div>';
});
timelineHtml += '<button onclick="_addMeetingModal('+mday+')" style="margin-left:-40px;margin-top:4px;font-size:12px;font-weight:700;color:var(--accent);background:transparent;border:1.5px dashed var(--accent);border-radius:10px;padding:8px 16px;cursor:pointer;width:100%">+ Добавить встречу</button>';
timelineHtml += '</div>';
}
return reqBanner + shiftCard
+(effWork
? '<div style="font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.07em;color:var(--muted);margin-bottom:12px">Расписание дня</div>'+timelineHtml
: '');
}
function screenSchedule() {
var view = window._schedView || 'week';
var selMday = window._schedMday || 22;
// ── Toggle ─────────────────────────────────────────────────────────────────
var toggle = '<div style="display:flex;background:#F1F5F9;border-radius:10px;padding:3px;gap:2px">'
+'<button onclick="_schedToggleView(\'week\')" style="flex:1;padding:6px 0;font-size:12px;font-weight:700;border:none;border-radius:8px;cursor:pointer;transition:all .15s;'+(view==='week'?'background:var(--card);color:var(--accent);box-shadow:0 1px 4px rgba(0,0,0,.1)':'background:transparent;color:var(--muted)')+'">'
+'Неделя</button>'
+'<button onclick="_schedToggleView(\'month\')" style="flex:1;padding:6px 0;font-size:12px;font-weight:700;border:none;border-radius:8px;cursor:pointer;transition:all .15s;'+(view==='month'?'background:var(--card);color:var(--accent);box-shadow:0 1px 4px rgba(0,0,0,.1)':'background:transparent;color:var(--muted)')+'">'
+'Месяц</button>'
+'</div>';
var gridHtml = '';
if (view === 'week') {
// ── Неделя: 7 мини-карточек ─────────────────────────────────────────────
var weekCards = _DOW.map(function(dw,i){
var mday = _DATES[i]; // May 1925
var ci = window._CHECKIN[i];
var isWork = _WORK[i];
var isSel = (mday===selMday);
var isToday = i===3;
var apptCount = (_APPTS[i]||[]).length;
var bg = isSel ? 'var(--accent)' : !isWork ? '#F1F5F9' : ci ? '#F0FDF4' : 'var(--card)';
var border = isSel ? 'none' : !isWork ? '1px solid #E2E8F0' : ci ? '1.5px solid #86EFAC' : isToday ? '1.5px solid var(--accent)' : '1px solid #E2E8F0';
var numCl = isSel ? '#fff' : !isWork ? '#CBD5E1' : isToday ? 'var(--accent)' : 'var(--ink)';
var dowCl = isSel ? 'rgba(255,255,255,.75)' : 'var(--muted)';
var statusLine = !isWork
? '<div style="font-size:9px;color:'+(isSel?'rgba(255,255,255,.5)':'#CBD5E1')+';font-weight:600">вых.</div>'
: ci
? '<div style="font-size:9px;color:'+(isSel?'rgba(255,255,255,.85)':'#16A34A')+';font-weight:700">✓ '+ci.in+'</div>'
: isToday ? '<div style="font-size:9px;color:var(--accent);font-weight:700">сегодня</div>'
: '<div style="font-size:9px;color:var(--muted)">1019</div>';
return '<div onclick="_schedSelectMday('+mday+')" style="flex:1;display:flex;flex-direction:column;align-items:center;gap:3px;padding:8px 3px;background:'+bg+';border:'+border+';border-radius:12px;cursor:pointer;margin:0 2px;min-height:70px;justify-content:center">'
+'<div style="font-size:9px;font-weight:700;color:'+dowCl+'">'+dw+'</div>'
+'<div style="font-size:15px;font-weight:900;color:'+numCl+'">'+mday+'</div>'
+statusLine
+(apptCount&&isWork?'<div style="font-size:8px;color:'+(isSel?'rgba(255,255,255,.7)':'var(--muted)')+';margin-top:1px">'+apptCount+' встр.</div>':'')
+'</div>';
}).join('');
gridHtml = '<div style="display:flex;padding:0 10px 12px;gap:0">'+weekCards+'</div>';
} else {
// ── Месяц: полная сетка мая 2026 ────────────────────────────────────────
// May 1, 2026 = Thursday = dow-index 3 (Mon-based)
// Сетка стартует с Пн Apr 28 (заполнитель)
var dowHeader = ['Пн','Вт','Ср','Чт','Пт','Сб','Вс'].map(function(h){
return '<div style="flex:1;text-align:center;font-size:10px;font-weight:700;color:var(--muted);padding:4px 0">'+h+'</div>';
}).join('');
// Построим массив ячеек: null = заполнитель, 131 = день мая
var cells = [];
// May 1 = dow 3 (Thu), значит перед ним 3 заполнителя (Пн,Вт,Ср)
for(var fi=0;fi<3;fi++) cells.push(null);
for(var d=1;d<=31;d++) cells.push(d);
// после 31 мая = Вс (dow 6) — заполнители не нужны
// 31 + 3 = 34 = 4 строки по 7 = 28 + остаток 6... нет
// 3 + 31 = 34 cells → 34/7 = 4.857 → нужно 35 cells (5 строк)
while(cells.length%7!==0) cells.push(null);
// Считаем статистику
var totalDays=31, totalWorkShifts=0;
for(var dd=1;dd<=31;dd++){
var ddow=_mayDow(dd);
var dReq=window._SCHED_REQ[dd];
if(_WORK[ddow]||dReq==='pending-work') totalWorkShifts++;
}
var cellsHtml = cells.map(function(day){
if(day===null){
return '<div style="flex:0 0 calc(100%/7);padding:2px"></div>';
}
var dow = _mayDow(day);
var isWork = _WORK[dow];
var ci = _mayCi(day);
var appts = _mayAppts(day);
var tasks = _mayTasks(day).filter(function(t){ return !t.done; });
var isSel = day===selMday;
var isToday = day===22;
var req = window._SCHED_REQ[day]; // 'pending-work' | 'pending-off'
// Сб/Вс — всегда выходные по закону (dow 5=Сб, 6=Вс)
var isWeekend = (dow===5||dow===6);
var isShift = !isWeekend && (isWork || req==='pending-work');
var bg, border, numCl, extra='';
if(isSel){
// Выбранный день — акцентная заливка
bg='var(--accent)'; border='2px solid var(--accent)'; numCl='#fff';
} else if(isToday){
// Сегодня — только рамка, фон зависит от типа дня
border='2px solid var(--accent)';
if(isShift){ bg='rgba(16,185,129,.12)'; numCl='var(--ink)'; }
else { bg='rgba(239,68,68,.07)'; numCl='var(--ink)'; }
} else if(isShift){
// Рабочая смена (включая смену на выходной) — зелёный
bg='rgba(16,185,129,.12)'; border='1px solid rgba(16,185,129,.3)'; numCl='var(--ink)';
} else {
// Выходной — лёгкий красный
bg='rgba(239,68,68,.07)'; border='1px solid rgba(239,68,68,.15)'; numCl='rgba(180,60,60,.8)';
}
// Индикаторы встреч (зелёный) и задач (оранжевый)
var indicators = '';
if(isShift && (appts.length || tasks.length)){
var dotRow = [];
if(appts.length) dotRow.push(
'<div style="display:flex;align-items:center;gap:2px">'
+'<div style="width:5px;height:5px;border-radius:50%;background:'+(isSel?'rgba(255,255,255,.9)':'var(--success)')+';flex-shrink:0"></div>'
+'<span style="font-size:8px;font-weight:700;color:'+(isSel?'rgba(255,255,255,.85)':'var(--success)')+'">'+appts.length+'</span>'
+'</div>');
if(tasks.length) dotRow.push(
'<div style="display:flex;align-items:center;gap:2px">'
+'<div style="width:5px;height:5px;border-radius:50%;background:'+(isSel?'rgba(255,255,255,.9)':'var(--warn)')+';flex-shrink:0"></div>'
+'<span style="font-size:8px;font-weight:700;color:'+(isSel?'rgba(255,255,255,.85)':'var(--warn)')+'">'+tasks.length+'</span>'
+'</div>');
indicators = '<div style="display:flex;gap:4px;margin-top:3px;justify-content:center">'+dotRow.join('')+'</div>';
}
return '<div onclick="_schedSelectMday('+day+')" style="flex:0 0 calc(100%/7);padding:2px;box-sizing:border-box;cursor:pointer">'
+'<div style="background:'+bg+';border:'+border+';border-radius:9px;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:7px 2px 6px;min-height:50px">'
+'<div style="font-size:13px;font-weight:800;color:'+numCl+'">'+day+'</div>'
+indicators
+'</div></div>';
}).join('');
// Шапка — Дней в месяце / Рабочих смен
var statsHtml =
'<div style="display:flex;gap:10px;padding:10px 0 10px">'
+'<div style="flex:1;background:var(--card);border-radius:12px;padding:12px 14px;display:flex;align-items:center;gap:12px">'
+'<div style="width:36px;height:36px;border-radius:10px;background:var(--bg);display:flex;align-items:center;justify-content:center;flex-shrink:0">'
+'<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="var(--muted)" stroke-width="2"><rect x="3" y="4" width="18" height="18" rx="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/></svg>'
+'</div>'
+'<div><div style="font-size:20px;font-weight:800;color:var(--ink)">'+totalDays+'</div>'
+'<div style="font-size:10px;color:var(--muted);font-weight:600">дней в месяце</div></div>'
+'</div>'
+'<div style="flex:1;background:rgba(16,185,129,.08);border-radius:12px;padding:12px 14px;display:flex;align-items:center;gap:12px">'
+'<div style="width:36px;height:36px;border-radius:10px;background:rgba(16,185,129,.15);display:flex;align-items:center;justify-content:center;flex-shrink:0">'
+'<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="var(--success)" stroke-width="2.5"><polyline points="20 6 9 17 4 12"/></svg>'
+'</div>'
+'<div><div style="font-size:20px;font-weight:800;color:var(--success)">'+totalWorkShifts+'</div>'
+'<div style="font-size:10px;color:var(--success);font-weight:600;opacity:.8">рабочих смен</div></div>'
+'</div>'
+'</div>';
// Легенда
var legend =
'<div style="display:flex;gap:8px;padding:8px 0 10px;flex-wrap:wrap">'
+'<div style="display:flex;align-items:center;gap:4px"><div style="width:12px;height:12px;border-radius:3px;background:rgba(16,185,129,.15);border:1px solid rgba(16,185,129,.3);flex-shrink:0"></div><span style="font-size:10px;color:var(--muted);font-weight:600">Рабочий день</span></div>'
+'<div style="display:flex;align-items:center;gap:4px"><div style="width:12px;height:12px;border-radius:3px;background:rgba(239,68,68,.1);border:1px solid rgba(239,68,68,.2);flex-shrink:0"></div><span style="font-size:10px;color:var(--muted);font-weight:600">Выходной</span></div>'
+'<div style="display:flex;align-items:center;gap:4px"><div style="width:12px;height:12px;border-radius:3px;border:2px solid var(--accent);background:rgba(16,185,129,.12);flex-shrink:0"></div><span style="font-size:10px;color:var(--muted);font-weight:600">Сегодня</span></div>'
+'</div>';
gridHtml = '<div style="padding:0 12px 8px">'
+statsHtml
+'<div style="display:flex;margin-top:2px">'+dowHeader+'</div>'
+'<div style="display:flex;flex-wrap:wrap">'+cellsHtml+'</div>'
+legend
+'</div>';
}
return '<div class="page">'
// Sticky header
+'<div style="background:var(--card);position:sticky;top:0;z-index:50;border-bottom:1px solid rgba(0,0,0,.08)">'
+'<div style="display:flex;align-items:center;justify-content:space-between;padding:12px 16px 10px">'
+'<div style="font-size:17px;font-weight:800;color:var(--ink)">📋 График работы</div>'
+'<div style="font-size:13px;font-weight:600;color:var(--muted)">май 2026</div>'
+'</div>'
+'<div style="padding:0 12px 10px">'+toggle+'</div>'
+gridHtml
+'</div>'
// Детали выбранного дня
+'<div style="padding:14px 16px 80px">'
+_renderShiftDetail(selMday)
+'</div></div>';
}
// ── CALCULATIONS ──────────────────────────────────────────────────────────────
function screenSalary() {
var orders = window._managerOrders.filter(function(o){ return !o.isLead; });
var month = 'Май 2026';
var base = 45000;
// Комиссия 3% со всех поступивших авансов
var commRows = [];
var totalComm = 0;
orders.forEach(function(o){
var adv = o.advances||[];
var paid = adv.filter(function(a){ return a.paid && a.amount>0; });
if (paid.length || o.assemblyPaid) {
var sum = paid.reduce(function(s,a){return s+a.amount;},0)
+ (o.assemblyPaid ? (o.assembly||0) : 0);
var comm = Math.round(sum * 0.03);
totalComm += comm;
commRows.push({client:o.client, contract:o.contract, sum:sum, comm:comm});
}
});
var total = base + totalComm;
var paid = Math.round(total * 0.5); // половина выплачена (аванс зарплаты)
var left = total - paid;
var pct = Math.round(paid/total*100);
function fm(n){ return n.toLocaleString('ru')+'&nbsp;₽'; }
var rows = commRows.map(function(r){
return '<div style="display:flex;align-items:center;gap:10px;padding:10px 0;border-bottom:1px solid rgba(0,0,0,.06)">'
+'<div style="width:36px;height:36px;border-radius:10px;background:#EEF2FF;display:flex;align-items:center;justify-content:center;font-size:16px;flex-shrink:0">💼</div>'
+'<div style="flex:1">'
+'<div style="font-size:13px;font-weight:700;color:var(--ink)">'+r.client+'</div>'
+'<div style="font-size:11px;color:var(--muted)">'+r.contract+' · поступило '+r.sum.toLocaleString('ru')+' ₽</div>'
+'</div>'
+'<div style="font-size:14px;font-weight:800;color:var(--accent)">+'+r.comm.toLocaleString('ru')+' ₽</div>'
+'</div>';
}).join('');
var kpi = _getMgrStats(_MGR_ID, 'Май');
return '<div class="page">'
+'<div class="page-header"><h2>💵 Зарплата</h2>'
+'<span style="font-size:12px;color:var(--muted);font-weight:500">'+month+'</span></div>'
+'<div style="padding:16px">'
// KPI-контекст из data.js
+(kpi.revenue ? '<div style="display:flex;gap:8px;margin-bottom:14px">'
+'<div style="flex:1;background:#F0FDF4;border-radius:12px;padding:10px 12px;text-align:center">'
+'<div style="font-size:15px;font-weight:800;color:var(--success)">'+Math.round(kpi.revenue/1000)+' тыс ₽</div>'
+'<div style="font-size:9px;color:var(--muted);font-weight:600;margin-top:1px">моя выручка</div></div>'
+'<div style="flex:1;background:#EEF2FF;border-radius:12px;padding:10px 12px;text-align:center">'
+'<div style="font-size:15px;font-weight:800;color:#4338CA">'+kpi.deals+' сд.</div>'
+'<div style="font-size:9px;color:var(--muted);font-weight:600;margin-top:1px">сделок</div></div>'
+'<div style="flex:1;background:#FFFBEB;border-radius:12px;padding:10px 12px;text-align:center">'
+'<div style="font-size:15px;font-weight:800;color:var(--warn)">★ '+kpi.rating+'</div>'
+'<div style="font-size:9px;color:var(--muted);font-weight:600;margin-top:1px">рейтинг</div></div>'
+'</div>' : '')
// Итого карточка
+'<div style="background:linear-gradient(135deg,var(--accent) 0%,#1a5db0 100%);border-radius:18px;padding:20px;color:#fff;margin-bottom:16px">'
+'<div style="font-size:12px;font-weight:600;opacity:.75;margin-bottom:4px">Итого за месяц</div>'
+'<div style="font-size:36px;font-weight:900;letter-spacing:-.02em;margin-bottom:14px">'+total.toLocaleString('ru')+' ₽</div>'
+'<div style="display:flex;gap:16px">'
+'<div><div style="font-size:11px;opacity:.7">Оклад</div><div style="font-size:16px;font-weight:800">'+fm(base)+'</div></div>'
+'<div style="width:1px;background:rgba(255,255,255,.2)"></div>'
+'<div><div style="font-size:11px;opacity:.7">Комиссия 3%</div><div style="font-size:16px;font-weight:800">'+fm(totalComm)+'</div></div>'
+'</div></div>'
// Прогресс выплат
+'<div class="card" style="padding:16px;margin-bottom:16px">'
+'<div class="row sb" style="margin-bottom:8px">'
+'<span style="font-size:13px;font-weight:700;color:var(--ink)">Статус выплат</span>'
+'<span style="font-size:12px;color:var(--muted)">'+pct+'% выплачено</span>'
+'</div>'
+'<div style="height:8px;background:#E2E8F0;border-radius:4px;margin-bottom:10px">'
+'<div style="height:8px;border-radius:4px;background:var(--success);width:'+pct+'%"></div></div>'
+'<div style="display:flex;justify-content:space-between">'
+'<div><div style="font-size:11px;color:var(--muted)">Выплачено</div><div style="font-size:15px;font-weight:800;color:var(--success)">'+fm(paid)+'</div></div>'
+'<div style="text-align:right"><div style="font-size:11px;color:var(--muted)">К выплате '+String(new Date().getDate()+3).padStart(2,'0')+'.06</div><div style="font-size:15px;font-weight:800;color:var(--accent)">'+fm(left)+'</div></div>'
+'</div></div>'
// Детализация
+'<div class="section-label" style="margin:0 0 8px">Комиссия по заказам</div>'
+'<div class="card" style="padding:0 16px">'
+(rows||'<div style="padding:14px 0;text-align:center;color:var(--muted);font-size:13px">Нет завершённых поступлений</div>')
+'<div style="display:flex;justify-content:space-between;align-items:center;padding:10px 0">'
+'<span style="font-size:13px;font-weight:700;color:var(--ink)">Итого комиссия</span>'
+'<span style="font-size:15px;font-weight:900;color:var(--accent)">'+fm(totalComm)+'</span>'
+'</div></div>'
+'</div></div>';
}
function screenCalc() {
var o = window._managerOrders[window._activeOrder]||window._managerOrders[0];
var contract = o.amount;
var advances = o.advances || [{label:'Аванс',amount:o.advance,paid:true,date:null}];
var paidSum = advances.reduce(function(s,a){return s+(a.paid?a.amount:0);},0);
var remaining = contract - paidSum;
var assembly = o.assembly||0;
var asmPaid = o.assemblyPaid||false;
var contractDone = remaining <= 0;
function fmt(n){ return n.toLocaleString('ru')+' ₽'; }
// Order selector chips
var chips = window._managerOrders.map(function(ord,i){
var sel = i === window._activeOrder;
return '<div class="pay-chip '+(sel?'act':'inact')+'" onclick="window._activeOrder='+i+';document.getElementById(\'screen\').innerHTML=renderScreen(\'manager_calc\')">'
+ord.client.split(' ')[0]+'</div>';
}).join('');
var pct = contract > 0 ? Math.min(100, Math.round(paidSum/contract*100)) : 0;
var oi = window._activeOrder; // order index for callbacks
var today = (function(){ var d=new Date(); return String(d.getDate()).padStart(2,'0')+'.'+String(d.getMonth()+1).padStart(2,'0')+'.'+d.getFullYear(); })();
// Авансы rows — инлайн-поля
var advRows = advances.map(function(a, ai){
var chk = a.paid
? '<div style="width:22px;height:22px;border-radius:6px;background:#DCFCE7;display:flex;align-items:center;justify-content:center;flex-shrink:0;cursor:pointer" onclick="_toggleAdvPaid('+oi+','+ai+',false)"><svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="#15803D" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg></div>'
: '<div style="width:22px;height:22px;border-radius:6px;border:2px solid #CBD5E1;display:flex;align-items:center;justify-content:center;flex-shrink:0;cursor:pointer" onclick="_toggleAdvPaid('+oi+','+ai+',true)"></div>';
return '<div style="padding:12px 0;border-bottom:1px solid rgba(0,0,0,.06)">'
+'<div style="display:flex;align-items:center;gap:10px;margin-bottom:8px">'
+chk
+'<div style="font-size:13px;font-weight:700;color:var(--ink);flex:1">'+a.label+'</div>'
+(a.paid?'<span style="font-size:11px;font-weight:700;color:var(--success)">✓ получен</span>':'<span style="font-size:11px;color:var(--muted)">ожидается</span>')
+'</div>'
+'<div style="display:flex;gap:8px">'
// поле суммы
+'<div style="flex:1;position:relative">'
+'<input type="number" placeholder="Сумма" value="'+(a.amount||'')+'"'
+' onchange="_liveAdvAmt('+oi+','+ai+',this.value)"'
+' style="width:100%;border:2px solid '+(a.amount?'var(--accent)':'#E2E8F0')+';border-radius:10px;padding:10px 34px 10px 12px;font-size:16px;font-weight:800;color:var(--accent);outline:none;background:var(--bg)">'
+'<span style="position:absolute;right:10px;top:50%;transform:translateY(-50%);font-size:13px;font-weight:700;color:var(--accent);pointer-events:none">₽</span>'
+'</div>'
// поле даты (показываем только если оплачено)
+(a.paid
? '<div style="flex:1"><input type="text" placeholder="ДД.ММ.ГГГГ" value="'+(a.date||today)+'"'
+' onchange="_liveAdvDate('+oi+','+ai+',this.value)"'
+' style="width:100%;border:2px solid #E2E8F0;border-radius:10px;padding:10px 12px;font-size:13px;font-weight:600;color:var(--ink);outline:none;background:var(--bg)"></div>'
: '')
+'</div>'
+'</div>';
}).join('');
// Сборка — инлайн-поле
var asmChk = asmPaid
? '<div style="width:22px;height:22px;border-radius:6px;background:#DCFCE7;display:flex;align-items:center;justify-content:center;flex-shrink:0;cursor:pointer" onclick="_toggleAsm('+oi+',false)"><svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="#15803D" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg></div>'
: '<div style="width:22px;height:22px;border-radius:6px;border:2px solid #CBD5E1;display:flex;align-items:center;justify-content:center;flex-shrink:0;cursor:pointer" onclick="_toggleAsm('+oi+',true)"></div>';
var asmRow = '<div style="padding:12px 0">'
+'<div style="display:flex;align-items:center;gap:10px;margin-bottom:8px">'
+asmChk
+'<div style="font-size:13px;font-weight:700;color:var(--ink);flex:1">Стоимость сборки</div>'
+(asmPaid?'<span style="font-size:11px;font-weight:700;color:var(--success)">✓ оплачена</span>':'<span style="font-size:11px;color:var(--muted)">при сдаче объекта</span>')
+'</div>'
+'<div style="position:relative">'
+'<input type="number" placeholder="Сумма сборки" value="'+(assembly||'')+'"'
+' onchange="_liveAsm('+oi+',this.value)"'
+' style="width:100%;border:2px solid '+(assembly?'#F59E0B':'#E2E8F0')+';border-radius:10px;padding:10px 34px 10px 12px;font-size:16px;font-weight:800;color:#92400E;outline:none;background:var(--bg)">'
+'<span style="position:absolute;right:10px;top:50%;transform:translateY(-50%);font-size:13px;font-weight:700;color:#92400E;pointer-events:none">₽</span>'
+'</div></div>';
return '<div class="page">'
+'<div class="page-header"><h2>💰 Расчёты</h2></div>'
+'<div style="display:flex;gap:8px;padding:10px 16px 4px;flex-wrap:wrap">'+chips+'</div>'
+'<div style="padding:0 16px">'
// ① Договор
+'<div class="section-label" style="margin:8px 0 6px">Договор</div>'
+'<div class="card" style="padding:16px;margin-bottom:12px">'
+'<div class="row sb" style="margin-bottom:10px">'
+'<div style="font-size:13px;font-weight:700;color:var(--ink)">Сумма по договору</div>'
+'<span class="badge gray">'+o.contract+'</span>'
+'</div>'
+'<div style="position:relative">'
+'<input id="f_contract_'+oi+'" type="number" placeholder="Введите сумму" value="'+(contract||'')+'"'
+' onchange="_liveContract('+oi+',this.value)"'
+' style="width:100%;border:2px solid '+(contract?'var(--accent)':'#E2E8F0')+';border-radius:12px;padding:13px 52px 13px 16px;font-size:22px;font-weight:900;color:var(--accent);outline:none;background:var(--bg)">'
+'<span style="position:absolute;right:14px;top:50%;transform:translateY(-50%);font-size:16px;font-weight:700;color:var(--accent);pointer-events:none">₽</span>'
+'</div>'
+'<div style="margin-top:10px">'
+'<div class="row sb" style="margin-bottom:5px"><span style="font-size:11px;font-weight:600;color:var(--muted)">Оплачено по договору</span>'
+'<span style="font-size:12px;font-weight:800;color:var(--ink)">'+pct+'%&nbsp;&nbsp;'+fmt(paidSum)+'</span></div>'
+'<div style="height:6px;background:#E2E8F0;border-radius:3px">'
+'<div style="height:6px;background:'+(contractDone?'var(--success)':'var(--accent)')+';border-radius:3px;width:'+pct+'%"></div>'
+'</div>'
+(remaining>0?'<div style="font-size:11px;color:var(--danger);font-weight:700;margin-top:5px">Остаток по договору: '+fmt(remaining)+'</div>':'<div style="font-size:11px;color:var(--success);font-weight:700;margin-top:5px">✅ Договор закрыт</div>')
+'</div></div>'
// ② Авансы
+'<div class="section-label" style="margin:4px 0 6px">Авансы по договору</div>'
+'<div class="card" style="padding:0 16px">'+advRows+'</div>'
// ③ Сборка
+'<div class="section-label" style="margin:14px 0 6px">Сборка</div>'
+'<div class="card" style="padding:0 16px">'+asmRow+'</div>'
// Actions
+'<div style="margin-top:16px;display:flex;flex-direction:column;gap:8px">'
+'<button class="btn-primary" onclick="_showCalcClient()">📱 Показать клиенту</button>'
+'<button class="btn-secondary" onclick="_toast(\'📤 Ссылка скопирована в буфер\',\'var(--accent)\')">📤 Отправить ссылку / PDF</button>'
+'</div></div></div>';
}
function _showCalcClient() {
var o = window._managerOrders[window._activeOrder]||window._managerOrders[0];
var contract = o.amount;
var advances = o.advances||[{label:'Аванс',amount:o.advance,paid:true,date:null}];
var paidSum = advances.reduce(function(s,a){return s+(a.paid?a.amount:0);},0);
var remaining= contract - paidSum;
var assembly = o.assembly||0;
var asmPaid = o.assemblyPaid||false;
function fmt(n){ return n.toLocaleString('ru')+' ₽'; }
var advRows = advances.map(function(a){
return '<div style="display:flex;justify-content:space-between;align-items:center;padding:9px 0;border-bottom:1px solid rgba(0,0,0,.07)">'
+'<div>'
+'<div style="font-size:13px;font-weight:600;color:var(--ink)">'+a.label+'</div>'
+(a.paid&&a.date?'<div style="font-size:11px;color:var(--success)">Получен '+a.date+'</div>'
:'<div style="font-size:11px;color:var(--muted)">ожидается</div>')
+'</div>'
+'<div style="font-size:15px;font-weight:800;color:'+(a.paid?'var(--success)':'var(--muted)')+'">'+fmt(a.amount)+'</div>'
+'</div>';
}).join('');
var html = '<div style="position:absolute;inset:0;background:rgba(0,0,0,.55);z-index:500;display:flex;align-items:flex-end" onclick="this.remove()" id="calcModal">'
+'<div style="background:var(--card);border-radius:24px 24px 0 0;width:100%;padding:20px 20px 32px;max-height:88%" onclick="event.stopPropagation()">'
+'<div style="width:36px;height:4px;background:#E2E8F0;border-radius:2px;margin:0 auto 18px"></div>'
// ① Договор
+'<div style="text-align:center;padding-bottom:16px;border-bottom:1px solid rgba(0,0,0,.08)">'
+'<div style="font-size:11px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.06em;margin-bottom:6px">Сумма по договору</div>'
+'<div style="font-size:38px;font-weight:900;color:var(--accent);letter-spacing:-.03em;line-height:1">'+fmt(contract)+'</div>'
+'<div style="font-size:12px;color:var(--muted);margin-top:4px">'+o.contract+'</div>'
+'</div>'
// ② Авансы
+'<div style="padding:4px 0 0">'
+'<div style="font-size:11px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;margin:12px 0 4px">Авансы</div>'
+advRows
+'</div>'
// ③ Сборка
+'<div style="display:flex;justify-content:space-between;align-items:center;padding:12px 0;border-top:1px solid rgba(0,0,0,.08);margin-top:4px">'
+'<div>'
+'<div style="font-size:13px;font-weight:700;color:var(--ink)">Стоимость сборки</div>'
+'<div style="font-size:11px;color:var(--muted)">'+(asmPaid?'Оплачена':'при сдаче объекта')+'</div>'
+'</div>'
+'<div style="font-size:15px;font-weight:800;color:'+(asmPaid?'var(--success)':'#92400E')+'">'+fmt(assembly)+'</div>'
+'</div>'
+'<button class="btn-primary" style="margin-top:4px" onclick="document.getElementById(\'calcModal\').remove()">Закрыть</button>'
+'</div></div>';
document.getElementById('phoneFrame').insertAdjacentHTML('beforeend', html);
}
function renderScreen(id) {
window._currentScreen = id;
if (id==='manager_home') return screenHome();
if (id==='manager_order') return screenOrder();
if (id==='manager_tech') return screenTech();
if (id==='manager_wizard') return screenWizard();
if (id==='manager_result') return screenResult();
if (id==='manager_lead') return screenLead();
if (id==='manager_schedule') return screenSchedule();
if (id==='manager_calc') return screenCalc();
if (id==='manager_salary') return screenSalary();
if (id==='manager_client') return screenClientsList();
if (id==='manager_client_card') return screenClient();
if (id==='manager_own_tech') return screenOwnTech();
if (id==='manager_tech_client') return screenTechClient();
if (id==='manager_kb') return screenKB();
return '<div style="padding:32px;color:var(--muted)">Экран: ' + id + '</div>';
}
// ── HOME ──────────────────────────────────────────────────────────────────────
function screenHome() {
var all = window._managerOrders;
var stN = ['','Замер','Проект','Техника','Технолог','Производство','Сборка','Закрыт'];
var stIc = ['','📐','📋','⚡','🔧','🏭','🔨','✅'];
// Data
var todayAppts = _APPTS[3] || [];
var todayTasks = (_TASKS[3]||[]).filter(function(t){return !t.done;});
var orders = all.filter(function(o){return !o.isLead;});
var leads = all.filter(function(o){return o.isLead;});
var blockers = orders.filter(function(o){return !!o.blocker;});
// "Нужно сейчас": блокеры + лиды требующие немедленного действия
var urgentLeads = leads.filter(function(o){
return o.leadStage==='new' || o.leadStage==='meeting'
|| (o.tasks||[]).some(function(t){return !t.done && t.hi;});
});
var urgentAll = blockers.concat(urgentLeads.filter(function(o){
return blockers.indexOf(o)===-1;
}));
// ── HERO ──────────────────────────────────────────────────────────────
var _SLOGANS = [
'Один хороший замер стоит десяти звонков. 📐',
'Клиент, которому объяснили — клиент, который купил. 🤝',
'Каждый договор сегодня — рекомендация завтра. 📋',
'Лучший момент закрыть сделку — сейчас. ⚡',
'Детали решают. Замерь точно — смонтируют без проблем. 🔧',
'Довольный клиент сам приведёт следующего. 🌟',
'Хороший день начинается с первого звонка. 📞',
'Скорость ответа — половина продажи. ⏱',
'Уточни бюджет сразу — сэкономишь три встречи. 💡',
'Фото замера в карточку — и никаких «а вы точно мерили?». 📸',
];
var _todaySlogan = _SLOGANS[new Date().getDate() % _SLOGANS.length];
var nextAppt = todayAppts[0];
var kpi = _getMgrStats(_MGR_ID, 'Май');
var heroHtml =
'<div style="background:var(--card);padding:16px 16px 14px;border-bottom:1px solid rgba(0,0,0,.06)">'
+'<div style="display:flex;align-items:center;gap:8px;margin-bottom:6px">'
+'<div style="width:32px;height:32px;border-radius:50%;background:'+_MGR_IDENTITY.color+';display:flex;align-items:center;justify-content:center;font-size:11px;font-weight:800;color:#fff;flex-shrink:0">'+_MGR_IDENTITY.short+'</div>'
+'<div>'
+'<div style="font-size:11px;color:var(--muted);font-weight:600">'+_MGR_IDENTITY.salon+' · Администратор: '+_MGR_IDENTITY.admin+'</div>'
+'<div style="font-size:10px;color:var(--muted)">Менеджер · Май 2026</div>'
+'</div></div>'
+'<div style="font-size:20px;font-weight:800;color:var(--ink);margin-bottom:4px">Добрый день, '+_MGR_IDENTITY.name.split(' ')[0]+' 👋</div>'
+'<div style="font-size:12px;color:var(--muted);font-style:italic;margin-bottom:10px;line-height:1.4">'+_todaySlogan+'</div>'
// KPI-полоса месяца
+(kpi.visits ? '<div style="display:flex;gap:6px;margin-bottom:13px;padding:10px 12px;background:var(--bg);border-radius:12px">'
+'<div style="flex:1;text-align:center">'
+'<div style="font-size:16px;font-weight:800;color:var(--ink)">'+kpi.visits+'</div>'
+'<div style="font-size:9px;color:var(--muted);font-weight:600">встреч</div></div>'
+'<div style="width:1px;background:rgba(0,0,0,.08)"></div>'
+'<div style="flex:1;text-align:center">'
+'<div style="font-size:16px;font-weight:800;color:var(--success)">'+kpi.deals+'</div>'
+'<div style="font-size:9px;color:var(--muted);font-weight:600">сделок</div></div>'
+'<div style="width:1px;background:rgba(0,0,0,.08)"></div>'
+'<div style="flex:1;text-align:center">'
+'<div style="font-size:16px;font-weight:800;color:'+(kpi.conversion>=30?'var(--success)':'var(--warn)')+'">'+kpi.conversion+'%</div>'
+'<div style="font-size:9px;color:var(--muted);font-weight:600">конверсия</div></div>'
+'<div style="width:1px;background:rgba(0,0,0,.08)"></div>'
+'<div style="flex:1;text-align:center">'
+'<div style="font-size:14px;font-weight:800;color:var(--accent)">'+Math.round((kpi.revenue||0)/1000)+'</div>'
+'<div style="font-size:9px;color:var(--muted);font-weight:600">тыс ₽</div></div>'
+'<div style="width:1px;background:rgba(0,0,0,.08)"></div>'
+'<div style="flex:1;text-align:center">'
+'<div style="font-size:16px;font-weight:800;color:var(--warn)">★ '+(kpi.rating||'—')+'</div>'
+'<div style="font-size:9px;color:var(--muted);font-weight:600">рейтинг</div></div>'
+'</div>' : '')
// Три плитки-счётчика
+'<div style="display:flex;gap:8px;margin-bottom:'+(nextAppt?'12px':'0')+'">'
+'<div onclick="_nav(\'manager_schedule\')" style="flex:1;background:#EEF2FF;border-radius:13px;padding:10px 12px;cursor:pointer;text-align:center">'
+'<div style="font-size:22px;font-weight:800;color:#4338CA;line-height:1">'+todayAppts.length+'</div>'
+'<div style="font-size:10px;color:#4338CA;font-weight:600;margin-top:2px">встреч</div>'
+'</div>'
+'<div style="flex:1;background:#FFFBEB;border-radius:13px;padding:10px 12px;cursor:pointer;text-align:center">'
+'<div style="font-size:22px;font-weight:800;color:#D97706;line-height:1">'+todayTasks.length+'</div>'
+'<div style="font-size:10px;color:#D97706;font-weight:600;margin-top:2px">задач</div>'
+'</div>'
+'<div style="flex:1;background:'+(blockers.length?'#FEE2E2':'#F0FDF4')+';border-radius:13px;padding:10px 12px;text-align:center">'
+'<div style="font-size:22px;font-weight:800;color:'+(blockers.length?'#DC2626':'#16A34A')+';line-height:1">'+blockers.length+'</div>'
+'<div style="font-size:10px;color:'+(blockers.length?'#DC2626':'#16A34A')+';font-weight:600;margin-top:2px">блокеров</div>'
+'</div>'
+'</div>'
// Ближайшая встреча — баннер
+(nextAppt
? '<div onclick="_nav(\'manager_schedule\')" style="background:var(--accent);border-radius:13px;padding:11px 14px;display:flex;align-items:center;gap:12px;cursor:pointer">'
+'<div style="width:36px;height:36px;background:rgba(255,255,255,.15);border-radius:10px;display:flex;align-items:center;justify-content:center;font-size:18px;flex-shrink:0">📅</div>'
+'<div style="flex:1">'
+'<div style="font-size:10px;color:rgba(255,255,255,.7);font-weight:600;text-transform:uppercase;letter-spacing:.04em">Ближайшая встреча</div>'
+'<div style="font-size:14px;font-weight:700;color:#fff">'+nextAppt.time+' · '+nextAppt.client+'</div>'
+'<div style="font-size:12px;color:rgba(255,255,255,.75)">'+nextAppt.type+'</div>'
+'</div>'
+'<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="rgba(255,255,255,.6)" stroke-width="2.5"><polyline points="9 18 15 12 9 6"/></svg>'
+'</div>'
: '')
+'</div>';
// ── СРОЧНО ────────────────────────────────────────────────────────────
var urgentHtml = '';
if (urgentAll.length) {
urgentHtml += '<div style="display:flex;align-items:center;justify-content:space-between;padding:16px 16px 8px">'
+'<div style="font-size:11px;font-weight:700;color:var(--danger);text-transform:uppercase;letter-spacing:.06em">🔴 Нужно сейчас</div>'
+'<div style="font-size:11px;color:var(--muted)">'+urgentAll.length+' '+(urgentAll.length===1?'действие':'действия')+'</div>'
+'</div>';
urgentAll.forEach(function(o) {
var idx = all.indexOf(o);
if (!o.isLead) {
// Блокер в заказе
var bi = _blockerInfo(o);
urgentHtml +=
'<div style="margin:0 16px 10px;background:var(--card);border-radius:16px;overflow:hidden;box-shadow:0 3px 14px rgba(0,0,0,.09)">'
+'<div style="height:3px;background:'+bi.bar+'"></div>'
+'<div style="padding:13px 14px 10px">'
+'<div style="display:flex;align-items:center;gap:8px;margin-bottom:6px">'
+'<span style="font-size:15px;font-weight:700;color:var(--ink)">'+o.client+'</span>'
+'<span style="font-size:10px;background:#F1F5F9;color:#64748B;padding:2px 8px;border-radius:20px;font-weight:700">'+stN[o.stage||1]+'</span>'
+'</div>'
+'<div style="display:inline-flex;align-items:center;gap:5px;background:'+bi.bg+';border-radius:8px;padding:5px 10px">'
+'<span style="font-size:14px">'+bi.icon+'</span>'
+'<span style="font-size:12px;font-weight:700;color:'+bi.color+'">'+bi.label+'</span>'
+'</div>'
+(o.techNote?'<div style="font-size:12px;color:var(--muted);margin-top:5px">'+o.techNote+'</div>':'')
+'</div>'
+'<div style="padding:0 14px 13px;display:flex;gap:8px">'
+'<a href="tel:'+o.phone+'" onclick="event.stopPropagation()" style="flex:0 0 44px;height:40px;background:#EEF2FF;border-radius:10px;display:flex;align-items:center;justify-content:center;text-decoration:none">'
+'<svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="#4338CA" stroke-width="2.5"><path d="M22 16.92v3a2 2 0 01-2.18 2 19.79 19.79 0 01-8.63-3.07A19.5 19.5 0 013.07 9.81a19.79 19.79 0 01-3.07-8.67A2 2 0 012 1h3a2 2 0 012 1.72c.127.96.361 1.903.7 2.81a2 2 0 01-.45 2.11L6.09 8.91a16 16 0 006 6l1.27-1.27a2 2 0 012.11-.45c.907.339 1.85.573 2.81.7A2 2 0 0122 16.92z"/></svg>'
+'</a>'
+'<button onclick="_openOrder('+idx+')" style="flex:1;height:40px;background:'+bi.bar+';border:none;border-radius:10px;font-size:13px;font-weight:700;color:#fff;cursor:pointer">'+bi.cta+' →</button>'
+'</div>'
+'</div>';
} else {
// Лид требует действия
var isMeeting = o.leadStage === 'meeting';
var hiTask2 = (o.tasks||[]).find(function(t){return !t.done;});
var ctaLabel = isMeeting ? 'Открыть встречу →' : 'Позвонить сейчас →';
var accentBg = isMeeting ? 'var(--accent)' : '#7C3AED';
var topBg = isMeeting ? 'var(--accent)' : '#7C3AED';
var todayBadge = isMeeting
? '<span style="font-size:10px;font-weight:700;background:#DCFCE7;color:#15803D;padding:2px 7px;border-radius:20px">Сегодня</span>'
: '<span style="font-size:10px;font-weight:700;background:#FEE2E2;color:#DC2626;padding:2px 7px;border-radius:20px">Горячий</span>';
urgentHtml +=
'<div style="margin:0 16px 10px;background:var(--card);border-radius:16px;overflow:hidden;box-shadow:0 3px 14px rgba(0,0,0,.09)">'
+'<div style="height:3px;background:'+topBg+'"></div>'
+'<div style="padding:13px 14px 10px">'
+'<div style="display:flex;align-items:center;gap:7px;margin-bottom:5px">'
+'<span style="font-size:15px;font-weight:700;color:var(--ink)">'+o.client+'</span>'
+'<span style="font-size:10px;font-weight:700;background:#EDE9FE;color:#7C3AED;padding:2px 7px;border-radius:20px">Лид</span>'
+todayBadge
+'</div>'
+(hiTask2?'<div style="font-size:13px;font-weight:500;color:var(--ink);margin-bottom:2px">'+hiTask2.text+'</div>':'')
+(o.leadNote?'<div style="font-size:12px;color:var(--muted)">'+o.leadNote+'</div>':'')
+'</div>'
+'<div style="padding:0 14px 13px;display:flex;gap:8px">'
+'<a href="tel:'+o.phone+'" onclick="event.stopPropagation()" style="flex:0 0 44px;height:40px;background:#EEF2FF;border-radius:10px;display:flex;align-items:center;justify-content:center;text-decoration:none">'
+'<svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="#4338CA" stroke-width="2.5"><path d="M22 16.92v3a2 2 0 01-2.18 2 19.79 19.79 0 01-8.63-3.07A19.5 19.5 0 013.07 9.81a19.79 19.79 0 01-3.07-8.67A2 2 0 012 1h3a2 2 0 012 1.72c.127.96.361 1.903.7 2.81a2 2 0 01-.45 2.11L6.09 8.91a16 16 0 006 6l1.27-1.27a2 2 0 012.11-.45c.907.339 1.85.573 2.81.7A2 2 0 0122 16.92z"/></svg>'
+'</a>'
+'<button onclick="_openOrder('+idx+')" style="flex:1;height:40px;background:'+accentBg+';border:none;border-radius:10px;font-size:13px;font-weight:700;color:#fff;cursor:pointer">'+ctaLabel+'</button>'
+'</div>'
+'</div>';
}
});
}
// ── ГРАФИК (сворачиваемый) ────────────────────────────────────────────
window._homeSchedOpen = (window._homeSchedOpen !== false); // default open
var schedOpen = window._homeSchedOpen;
// 7-дневный мини-стрип (компактнее чем в screenSchedule)
var weekStrip = '<div style="display:flex;gap:5px;margin-bottom:12px">';
_DOW.forEach(function(dw, i) {
var mday = _DATES[i];
var ci = window._CHECKIN[i];
var isWeekend = (i===5||i===6); // Сб/Вс — всегда выходные
var isWork = _WORK[i] && !isWeekend;
var isSel = (mday === 22); // today
var apptCnt = (_APPTS[i]||[]).length;
var taskCnt = (_TASKS[i]||[]).filter(function(t){return !t.done;}).length;
var bg = isSel ? 'var(--accent)' : !isWork ? 'var(--bg)' : 'var(--card)';
var numCl = isSel ? '#fff' : !isWork ? '#CBD5E1' : 'var(--ink)';
var dotParts = [];
if(apptCnt>0) dotParts.push(
'<div style="display:flex;align-items:center;gap:2px">'
+'<div style="width:4px;height:4px;border-radius:50%;background:'+(isSel?'rgba(255,255,255,.9)':'var(--success)')+'"></div>'
+'<span style="font-size:8px;font-weight:700;color:'+(isSel?'rgba(255,255,255,.8)':'var(--success)')+'">'+apptCnt+'</span>'
+'</div>');
if(taskCnt>0) dotParts.push(
'<div style="display:flex;align-items:center;gap:2px">'
+'<div style="width:4px;height:4px;border-radius:50%;background:'+(isSel?'rgba(255,255,255,.9)':'var(--warn)')+'"></div>'
+'<span style="font-size:8px;font-weight:700;color:'+(isSel?'rgba(255,255,255,.8)':'var(--warn)')+'">'+taskCnt+'</span>'
+'</div>');
var dotRow = (dotParts.length && isWork)
? '<div style="display:flex;justify-content:center;gap:3px;margin-top:3px">'+dotParts.join('')+'</div>'
: '<div style="height:11px"></div>';
weekStrip += '<div style="flex:1;border-radius:10px;padding:7px 4px 6px;text-align:center;background:'+bg+';'
+ (ci && !isSel ? 'border:1.5px solid #86EFAC;' : isSel ? '' : 'border:1px solid rgba(0,0,0,.07);') + 'cursor:pointer" onclick="window._schedMday='+mday+';_nav(\'manager_schedule\')">'
+ '<div style="font-size:9px;color:'+(isSel?'rgba(255,255,255,.7)':'var(--muted)')+';font-weight:600;margin-bottom:3px">'+dw+'</div>'
+ '<div style="font-size:13px;font-weight:800;color:'+numCl+'">'+mday+'</div>'
+ dotRow
+ '</div>';
});
weekStrip += '</div>';
// Список встреч сегодня
var apptList = '';
if (todayAppts.length) {
todayAppts.forEach(function(a, i) {
apptList += '<div style="display:flex;align-items:center;gap:11px;padding:10px 0;'+(i<todayAppts.length-1?'border-bottom:1px solid rgba(0,0,0,.05)':'')+'">'
+'<div style="font-size:12px;font-weight:800;color:var(--ink);min-width:38px;font-variant-numeric:tabular-nums">'+a.time+'</div>'
+'<div style="width:7px;height:7px;border-radius:50%;background:'+a.color+';flex-shrink:0"></div>'
+'<div style="flex:1">'
+'<div style="font-size:13px;font-weight:600;color:var(--ink)">'+a.client+'</div>'
+'<div style="font-size:11px;color:var(--muted)">'+a.type+'</div>'
+'</div>'
+(a.order?'<span style="font-size:10px;color:var(--muted);background:var(--bg);padding:3px 8px;border-radius:20px;white-space:nowrap">'+a.order+'</span>':'<span style="font-size:10px;color:var(--success);background:rgba(16,185,129,.1);padding:3px 8px;border-radius:20px">Лид</span>')
+'</div>';
});
} else {
apptList = '<div style="font-size:13px;color:var(--muted);padding:8px 0 4px">Встреч на сегодня нет</div>';
}
// Иконка шеврона
var chevron = '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="var(--muted)" stroke-width="2.5" style="transition:transform .2s;transform:rotate('+(schedOpen?'0':'180')+'deg)"><polyline points="18 15 12 9 6 15"/></svg>';
var apptHtml =
'<div style="margin:0 16px 12px;background:var(--card);border-radius:16px;box-shadow:0 2px 10px rgba(0,0,0,.07);overflow:hidden">'
// Заголовок-тоггл
+'<div onclick="_homeToggleSched()" style="display:flex;align-items:center;justify-content:space-between;padding:13px 14px;cursor:pointer;'+(schedOpen?'border-bottom:1px solid rgba(0,0,0,.06)':'')+'">'
+'<div style="display:flex;align-items:center;gap:8px">'
+'<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="var(--accent)" stroke-width="2.5"><rect x="3" y="4" width="18" height="18" rx="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/></svg>'
+'<span style="font-size:13px;font-weight:700;color:var(--ink)">График</span>'
+'<span style="font-size:11px;color:var(--muted);font-weight:500">Чт, 22 мая</span>'
+(todayAppts.length?'<span style="font-size:10px;background:var(--accent);color:#fff;padding:1px 7px;border-radius:20px;font-weight:700">'+todayAppts.length+'</span>':'')
+'</div>'
+chevron
+'</div>'
// Содержимое (сворачивается)
+(schedOpen
? '<div style="padding:12px 14px">'
+ weekStrip
+ apptList
+'<div onclick="window._schedView=\'month\';_nav(\'manager_schedule\')" style="margin-top:10px;text-align:center;font-size:12px;color:var(--accent);font-weight:600;cursor:pointer;padding:6px 0">Открыть полный график →</div>'
+'</div>'
: '')
+'</div>';
// ── ЗАДАЧИ ────────────────────────────────────────────────────────────
var taskHtml = '';
if (todayTasks.length) {
taskHtml += '<div style="padding:16px 16px 8px;font-size:11px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.06em">✅ Задачи на сегодня</div>'
+'<div style="margin:0 16px;background:var(--card);border-radius:16px;overflow:hidden;box-shadow:0 2px 10px rgba(0,0,0,.07)">';
todayTasks.forEach(function(t, i) {
taskHtml += '<div style="display:flex;align-items:center;gap:12px;padding:11px 14px;'+(i<todayTasks.length-1?'border-bottom:1px solid rgba(0,0,0,.05)':'')+'" onclick="_toast(\'Задача выполнена ✓\',\'#10B981\')">'
+'<div style="width:20px;height:20px;border-radius:6px;border:2px solid '+(t.hi?'var(--danger)':'#CBD5E1')+';flex-shrink:0;cursor:pointer"></div>'
+'<div style="flex:1">'
+'<div style="font-size:13px;font-weight:'+(t.hi?'700':'500')+';color:var(--ink)">'+t.text+'</div>'
+(t.order?'<div style="font-size:11px;color:var(--muted)">'+t.order+'</div>':'')
+'</div>'
+(t.hi?'<span style="font-size:9px;background:#FEE2E2;color:#DC2626;padding:2px 7px;border-radius:20px;font-weight:700;flex-shrink:0">ВАЖНО</span>':'')
+'</div>';
});
taskHtml += '</div>';
}
// ── ПАЙПЛАЙН МИНИ ─────────────────────────────────────────────────────
var stageDefs = [{n:'Замер',i:1},{n:'Проект',i:2},{n:'Техника',i:3},{n:'Технолог',i:4},{n:'Произв.',i:5},{n:'Сборка',i:6}];
var stageCnts = {};
orders.filter(function(o){return o.stage<7;}).forEach(function(o){ stageCnts[o.stage]=(stageCnts[o.stage]||0)+1; });
var ldStages = ['new','meeting','kp','thinking'];
var ldNames = {new:'Новые',meeting:'Встреча',kp:'КП',thinking:'Думает'};
var ldColors = {new:'#DBEAFE',meeting:'#CCFBF1',kp:'#FEF3C7',thinking:'#F1F5F9'};
var ldTxtCol = {new:'#1D4ED8',meeting:'#0F766E',kp:'#D97706',thinking:'#64748B'};
var ldCnts = {};
leads.forEach(function(l){ ldCnts[l.leadStage]=(ldCnts[l.leadStage]||0)+1; });
var pipeHtml =
'<div style="display:flex;align-items:center;justify-content:space-between;padding:16px 16px 8px">'
+'<div style="font-size:11px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.06em">⚡ Пайплайн</div>'
+'<button onclick="_toast(\'+ Новый лид\',\'#003E7E\')" style="background:var(--accent);color:#fff;border:none;border-radius:8px;padding:5px 13px;font-size:12px;font-weight:700;cursor:pointer">+ Лид</button>'
+'</div>'
+'<div style="margin:0 16px;background:var(--card);border-radius:16px;padding:14px;box-shadow:0 2px 10px rgba(0,0,0,.07)">'
// Заказы
+'<div style="font-size:11px;font-weight:700;color:var(--muted);margin-bottom:8px">Заказы · '+orders.filter(function(o){return o.stage<7;}).length+'</div>'
+'<div style="display:flex;gap:4px;margin-bottom:14px">';
stageDefs.forEach(function(s) {
var cnt = stageCnts[s.i]||0;
pipeHtml += '<div style="flex:1;text-align:center">'
+'<div style="height:30px;border-radius:8px;display:flex;align-items:center;justify-content:center;font-size:13px;font-weight:800;'
+(cnt?'background:var(--accent);color:#fff':'background:#F1F5F9;color:#CBD5E1')
+'">'+(cnt?cnt:'—')+'</div>'
+'<div style="font-size:9px;color:var(--muted);margin-top:3px;font-weight:600">'+s.n+'</div>'
+'</div>';
});
pipeHtml += '</div>'
+'<div style="height:1px;background:rgba(0,0,0,.06);margin-bottom:12px"></div>'
// Лиды
+'<div style="font-size:11px;font-weight:700;color:var(--muted);margin-bottom:8px">Лиды · '+leads.length+'</div>'
+'<div style="display:flex;gap:4px">';
ldStages.forEach(function(s) {
var cnt = ldCnts[s]||0;
pipeHtml += '<div style="flex:1;text-align:center">'
+'<div style="height:28px;border-radius:8px;display:flex;align-items:center;justify-content:center;font-size:13px;font-weight:800;'
+(cnt?'background:'+ldColors[s]+';color:'+ldTxtCol[s]:'background:#F1F5F9;color:#CBD5E1')
+'">'+(cnt?cnt:'—')+'</div>'
+'<div style="font-size:9px;color:var(--muted);margin-top:3px;font-weight:600">'+ldNames[s]+'</div>'
+'</div>';
});
pipeHtml += '</div></div>';
// ── ВОРОНКА + ФИЛЬТР ──────────────────────────────────────────────────
var _F = window._homeFilter || 'all';
var _FDEF = [
{id:'all', label:'Все', cnt: all.length},
{id:'lead', label:'Лиды', cnt: leads.filter(function(o){return o.leadStage!=='lost';}).length},
{id:'meet', label:'Замер', cnt: leads.filter(function(o){return o.leadStage==='meeting';}).length},
{id:'kp', label:'КП', cnt: leads.filter(function(o){return o.leadStage==='kp'||o.leadStage==='thinking';}).length},
{id:'deal', label:'Договор', cnt: orders.filter(function(o){return (o.stage||1)<=4;}).length},
{id:'mount', label:'Монтаж', cnt: orders.filter(function(o){return (o.stage||1)>=5;}).length},
];
function _matchF(o,f){
if(f==='all') return true;
if(f==='lead') return o.isLead && o.leadStage!=='lost';
if(f==='meet') return o.isLead && o.leadStage==='meeting';
if(f==='kp') return o.isLead && (o.leadStage==='kp'||o.leadStage==='thinking');
if(f==='deal') return !o.isLead && (o.stage||1)<=4;
if(f==='mount') return !o.isLead && (o.stage||1)>=5;
return true;
}
var filteredAll = all.filter(function(o){return _matchF(o,_F);});
// Пиллы-воронка
var funnelPills = _FDEF.map(function(fd){
var act = _F===fd.id;
return '<button onclick="window._homeFilter=\''+fd.id+'\';document.getElementById(\'screen\').innerHTML=renderScreen(\'manager_home\')" style="'
+'flex-shrink:0;padding:6px 12px;border-radius:20px;font-size:12px;font-weight:700;cursor:pointer;border:none;white-space:nowrap;transition:all .15s;'
+(act?'background:var(--accent);color:#fff;box-shadow:0 2px 8px rgba(0,62,126,.3)':'background:var(--card);color:var(--muted);border:1px solid rgba(0,0,0,.08)')+'">'+fd.label
+(fd.cnt?' <span style="font-size:10px;opacity:.75">'+fd.cnt+'</span>':'')+'</button>';
}).join('');
var ldC2={new:'#DBEAFE',meeting:'#CCFBF1',kp:'#FEF3C7',thinking:'#F1F5F9',lost:'#FEE2E2'};
var ldT2={new:'#1D4ED8',meeting:'#0F766E',kp:'#D97706',thinking:'#64748B',lost:'#DC2626'};
var ldN2={new:'Новый',meeting:'Встреча',kp:'КП',thinking:'Думает',lost:'Отказ'};
var allHtml =
'<div style="display:flex;align-items:center;justify-content:space-between;padding:16px 16px 8px">'
+'<div style="font-size:11px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.06em">Воронка</div>'
+'<button onclick="_toast(\'+ Новый лид\',\'#003E7E\')" style="background:var(--accent);color:#fff;border:none;border-radius:8px;padding:5px 13px;font-size:12px;font-weight:700;cursor:pointer">+ Лид</button>'
+'</div>'
// Горизонтальные пиллы с прокруткой
+'<div style="display:flex;gap:7px;padding:0 16px 12px;overflow-x:auto;scrollbar-width:none">'+funnelPills+'</div>'
+'<div style="margin:0 16px;background:var(--card);border-radius:16px;overflow:hidden;box-shadow:0 2px 10px rgba(0,0,0,.07)">';
if(filteredAll.length === 0){
allHtml += '<div style="padding:24px;text-align:center;color:var(--muted);font-size:13px">Нет клиентов в этом этапе</div>';
} else {
filteredAll.forEach(function(o) {
var idx = all.indexOf(o);
var isLast = o === filteredAll[filteredAll.length-1];
if (o.isLead) {
allHtml += '<div onclick="_openOrder('+idx+')" style="display:flex;align-items:center;gap:11px;padding:11px 14px;'+(isLast?'':'border-bottom:1px solid rgba(0,0,0,.05)')+';cursor:pointer">'
+'<div style="width:36px;height:36px;border-radius:10px;background:#EDE9FE;display:flex;align-items:center;justify-content:center;font-size:15px;font-weight:800;color:#7C3AED;flex-shrink:0">Л</div>'
+'<div style="flex:1;min-width:0">'
+'<div style="font-size:14px;font-weight:600;color:var(--ink)">'+o.client+'</div>'
+'<div style="font-size:11px;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis">'+o.label+'</div>'
+'</div>'
+'<span style="font-size:11px;font-weight:700;background:'+ldC2[o.leadStage]+';color:'+ldT2[o.leadStage]+';padding:3px 9px;border-radius:20px;flex-shrink:0">'+ldN2[o.leadStage]+'</span>'
+'</div>';
} else {
var hasB2 = !!o.blocker || (o.tech && o.tech.some(function(t){return t.status==='waiting_client'||t.status==='client_chosen';}));
var bi2 = hasB2 ? _blockerInfo(o) : null;
allHtml += '<div onclick="_openOrder('+idx+')" style="display:flex;align-items:center;gap:11px;padding:11px 14px;'+(isLast?'':'border-bottom:1px solid rgba(0,0,0,.05)')+';cursor:pointer">'
+'<div style="width:36px;height:36px;border-radius:10px;background:var(--bg);display:flex;align-items:center;justify-content:center;font-size:18px;flex-shrink:0">'+stIc[o.stage||1]+'</div>'
+'<div style="flex:1;min-width:0">'
+'<div style="font-size:14px;font-weight:600;color:var(--ink)">'+o.client+'</div>'
+'<div style="font-size:11px;color:var(--muted)">'+o.contract+' · '+o.amount.toLocaleString('ru')+' ₽</div>'
+'</div>'
+(hasB2
? '<span style="font-size:10px;font-weight:700;background:'+bi2.bg+';color:'+bi2.color+';padding:3px 9px;border-radius:20px;flex-shrink:0;white-space:nowrap">'+bi2.icon+' '+bi2.label+'</span>'
: '<span style="font-size:11px;font-weight:700;background:#EEF2FF;color:#4338CA;padding:3px 9px;border-radius:20px;flex-shrink:0">'+stN[o.stage||1]+'</span>')
+'</div>';
}
});
}
allHtml += '</div>';
return '<div class="page">'
+ heroHtml
+ '<div style="padding:8px 0 0"></div>'
+ apptHtml
+ urgentHtml
+ taskHtml
+ allHtml
+ '<div style="height:16px"></div>'
+ '</div>';
}
function _homeToggleSched(){
window._homeSchedOpen = !window._homeSchedOpen;
document.getElementById('screen').innerHTML = renderScreen('manager_home');
document.getElementById('nav').innerHTML = navBar();
}
// ── Blocker classifier ────────────────────────────────────────────────────────
function _blockerInfo(o){
// Приоритет: реальное состояние техники > текст blocker
if(o.tech && o.tech.length){
var wc=o.tech.filter(function(t){return t.status==='waiting_client';}).length;
var cc=o.tech.filter(function(t){return t.status==='client_chosen';}).length;
if(cc>0) return {icon:'👆',color:'#059669',bg:'#ECFDF5',bar:'#059669',
label:'Клиент выбрал · подтвердить '+cc+' поз.',
cta:'Принять выбор'};
if(wc>0) return {icon:'⏳',color:'#D97706',bg:'#FEF3C7',bar:'#D97706',
label:'Ждём клиента · '+wc+' поз.',
cta:'Напомнить клиенту'};
}
var txt=(o.blocker||'').toLowerCase();
if(txt.indexOf('технолог')>=0) return {icon:'🔧',color:'#2563EB',bg:'#EFF6FF',bar:'#3B82F6',
label:o.blocker, cta:'Открыть заказ'};
if(txt.indexOf('техника')>=0) return {icon:'⚙️',color:'#D97706',bg:'#FFFBEB',bar:'#F59E0B',
label:o.blocker, cta:'Разобрать блокер'};
return {icon:'⚠️',color:'#92400E',bg:'#FFFBEB',bar:'var(--warn)',
label:o.blocker||'Блокер', cta:'Разобрать блокер'};
}
function _openOrder(i){
window._activeOrder=i;
var o=window._managerOrders[i];
var screen = o && o.isLead ? 'manager_lead' : 'manager_order';
document.getElementById('screen').innerHTML=renderScreen(screen);
document.getElementById('nav').innerHTML=navBar();
}
// ── ORDER ─────────────────────────────────────────────────────────────────────
function screenOrder() {
var o = window._managerOrders[window._activeOrder];
if(!o) return '<div style="padding:32px">Заказ не найден</div>';
var isKitchen = o.type==='kitchen';
// Pipeline
var stages = isKitchen
? [{n:'Замер',i:1},{n:'Проект',i:2},{n:'Техника',i:3},{n:'Технолог',i:4},{n:'Произв.',i:5},{n:'Сборка',i:6},{n:'Закрыт',i:7}]
: [{n:'Замер',i:1},{n:'Проект',i:2},{n:'Технолог',i:3},{n:'Произв.',i:4},{n:'Сборка',i:5},{n:'Закрыт',i:6}];
var pip='';
stages.forEach(function(s,idx){
var st = s.i<o.stage?'done':(s.i===o.stage?(o.blocker?'blocked':'active'):'');
var dc = st==='done'?'✓':(st==='blocked'?'!':s.i);
if(idx>0) pip+='<div class="pip-line'+(stages[idx-1].i<o.stage?' done':'')+'"></div>';
pip+='<div class="pip-step"><div class="pip-dot '+st+'">'+dc+'</div>'
+'<div class="pip-label'+(st==='active'?' active':'')+'">'+s.n+'</div></div>';
});
// Blocker / tech-confirmed info
var blk='';
if(o.techConfirmed && o.stage>=4){
blk='<div style="background:#EFF6FF;border:1.5px solid #93C5FD;border-radius:12px;padding:12px 14px;margin-bottom:12px;display:flex;align-items:center;gap:10px">'
+'<div style="font-size:18px">🔧</div>'
+'<div><div style="font-size:13px;font-weight:700;color:#1D4ED8">Спецификация у технолога</div>'
+'<div style="font-size:12px;color:#2563EB">Передано '+o.techConfirmedDate+' · Ждём согласования проекта</div></div>'
+'</div>';
} else if(o.blocker){
blk='<div style="background:#FFFBEB;border:1.5px solid #F59E0B;border-radius:12px;padding:12px 14px;margin-bottom:12px">'
+'<div style="font-size:11px;font-weight:700;color:#B45309;text-transform:uppercase;letter-spacing:.04em;margin-bottom:3px">⚠ Блокер</div>'
+'<div style="font-size:14px;font-weight:700;color:#92400E">'+o.blocker+'</div>'
+(o.techNote?'<div style="font-size:12px;color:#B45309;margin-top:3px">'+o.techNote+'</div>':'')
+'</div>';
}
// ─── Документы изделия (инструкция + паспорт) ───
// Верифицированные прямые ссылки на PDF/страницы документов
var _TECH_DOCS = {
// Bosch варочные панели
'PUE611BB5E': {
manual: 'https://bosch-centre.ru/upload/iblock/14b/dztw8oc73p5ssxnwr5f5m1qzk8223mtx/9001608943_E.pdf',
passport: 'https://bosch-centre.ru/bosch/varochnye-paneli/elektricheskie/varochnaya-panel-bosch-pue-611-bb5e.html'
},
'PIE631FB1E': {
manual: 'https://www.hausdorf.ru/upload/iblock/808/installation_hausdorf_elektricheskaya_varochnaya_panel_bosch_pie631fb1e.pdf',
passport: 'https://media3.bosch-home.com/Documents/specsheet/ru-RU/PIE631FB1E.pdf'
},
'PIF612BB1E': {
manual: 'https://www.hausdorf.ru/search/?q=Bosch+PIF612BB1E',
passport: 'https://media3.bosch-home.com/Documents/specsheet/ru-RU/PIF612BB1E.pdf'
},
// Bosch холодильники
'KGN56LW31U': {
manual: 'https://bosch-centre.ru/technical-documentation/holodilniki/',
passport: 'https://bosch-centre.ru/bosch/holodilniki/dvuhkamernye-holodilniki/dvukhkamernyy-kholodilnik-bosch-kgn56lw31u.html'
},
// Bosch посудомойки
'SPV4HMX10E': {
manual: 'https://www.hausdorf.ru/search/?q=Bosch+SPV4HMX10E',
passport: 'https://media3.bosch-home.com/Documents/specsheet/ru-RU/SPV4HMX10E.pdf'
},
// Elica вытяжки
'FLAT GLASS IX/A/60': {
manual: 'https://www.hausdorf.ru/upload/iblock/d4b/instruction_hausdorf_vytyazhka_elica_flat_glass_ix_a_60_1.pdf',
passport: 'https://www.manualslib.com/products/Elica-Flat-Glass-Ix-A-60-13710296.html'
}
};
function _techDocLinks(t){
if(!t.brand || !t.model) return '';
var m = (t.model||'').trim();
var b = (t.brand||'').toLowerCase();
var docs = _TECH_DOCS[m];
var manualUrl, passportUrl;
if(docs){
manualUrl = docs.manual;
passportUrl = docs.passport;
} else if(b==='bosch'){
// Для Bosch без известного файла — hausdorf.ru поиск + страница продукта на bosch-centre.ru
var mq = encodeURIComponent('Bosch '+m);
manualUrl = 'https://www.hausdorf.ru/search/?q='+mq;
passportUrl = 'https://bosch-centre.ru/search/?q='+mq;
} else {
// Для других брендов — Google
var q = encodeURIComponent(t.brand+' '+m);
manualUrl = 'https://www.google.com/search?q='+q+'+%D0%B8%D0%BD%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%86%D0%B8%D1%8F+PDF';
passportUrl = 'https://www.google.com/search?q='+q+'+%D0%BF%D0%B0%D1%81%D0%BF%D0%BE%D1%80%D1%82+%D1%85%D0%B0%D1%80%D0%B0%D0%BA%D1%82%D0%B5%D1%80%D0%B8%D1%81%D1%82%D0%B8%D0%BA%D0%B8';
}
return '<div style="display:flex;gap:5px;margin-top:7px;width:100%">'
+'<a href="'+manualUrl+'" target="_blank" rel="noopener" style="flex:1;display:inline-flex;align-items:center;justify-content:center;gap:3px;padding:5px 6px;background:#EFF6FF;border:1px solid #BFDBFE;border-radius:8px;text-decoration:none;font-size:10px;font-weight:700;color:#1D4ED8">'
+'<svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>'
+'📄 Инструкция</a>'
+'<a href="'+passportUrl+'" target="_blank" rel="noopener" style="flex:1;display:inline-flex;align-items:center;justify-content:center;gap:3px;padding:5px 6px;background:#F0FDF4;border:1px solid #BBF7D0;border-radius:8px;text-decoration:none;font-size:10px;font-weight:700;color:#15803D">'
+'<svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M9 9h6M9 12h6M9 15h4"/></svg>'
+'📋 Паспорт</a>'
+'</div>';
}
// Tech section (kitchen only)
var techHtml='';
if(isKitchen && o.tech.length){
var dc2=o.tech.filter(function(t){return t.status==='done'||t.status==='client_chosen'||t.status==='waiting_client';}).length;
var items=o.tech.map(function(t){
var iconBox='<div class="tech-icon-box '+(t.status==='done'?'done':(t.status==='waiting_client'||t.status==='client_chosen')?'client':'wait')+'">'
+_techIcon(t.wkey,22)+'</div>';
var sourceLabel = t.source==='own'?'Своя техника'
:t.source==='ai'?'AI подбор'
:t.source==='client'?'Клиент'
:'';
var sourceBadge = sourceLabel
?'<span style="font-size:10px;font-weight:700;padding:1px 6px;border-radius:8px;background:'
+(t.source==='own'?'#EDE9FE;color:#5B21B6'
:t.source==='ai'?'#DBEAFE;color:#1D4ED8'
:'#DCFCE7;color:#15803D')
+'">'+sourceLabel+'</span>'
:'';
var nameRow='<div style="display:flex;align-items:center;gap:6px;flex-wrap:wrap">'
+'<span style="font-size:14px;font-weight:600;color:var(--ink)">'+t.name+'</span>'
+sourceBadge+'</div>';
if(t.status==='wait'){
return '<div class="tech-item" style="flex-wrap:wrap;border-bottom:1px solid rgba(0,0,0,.06);padding-bottom:10px;margin-bottom:2px">'
+'<div style="display:flex;gap:10px;width:100%">'
+iconBox
+'<div style="flex:1">'+nameRow
+'<div style="font-size:11px;color:var(--muted);margin-top:2px">Способ не выбран</div>'
+'</div></div>'
+'<div class="tech-act-row">'
+'<button class="tech-act-btn ai" onclick="_startWizard(\''+t.wkey+'\')"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/></svg> AI подбор</button>'
+'<button class="tech-act-btn own" onclick="_startOwnTech(\''+t.wkey+'\')"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M16.5 9.4l-9-5.19M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><polyline points="3.27 6.96 12 12.01 20.73 6.96"/><line x1="12" y1="22.08" x2="12" y2="12"/></svg> Своя</button>'
+'<button class="tech-act-btn client" onclick="_startClientPick(\''+t.wkey+'\')"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg> Клиент</button>'
+'</div></div>';
}
if(t.status==='waiting_client'){
return '<div class="tech-item">'
+iconBox
+'<div style="flex:1">'+nameRow
+'<div style="font-size:11px;color:#2563EB;margin-top:2px">🔄 Ждём выбора клиента</div>'
+'</div>'
+'<button onclick="_techClientRemind(\''+t.wkey+'\')" class="btn-pill accent">Напомнить</button>'
+'</div>';
}
// client_chosen — клиент выбрал, ждёт подтверждения менеджера
if(t.status==='client_chosen'){
var cDetail=(t.brand||'')+(t.model?' '+t.model:'')+(t.dims?' · '+t.dims:'');
return '<div class="tech-item" style="flex-wrap:wrap;padding-bottom:10px">'
+'<div style="display:flex;gap:10px;width:100%;align-items:flex-start">'
+iconBox
+'<div style="flex:1">'+nameRow
+'<div style="font-size:12px;color:#059669;font-weight:600;margin-top:3px">👤 Клиент выбрал</div>'
+'<div style="font-size:13px;font-weight:700;color:var(--ink);margin-top:1px">'+cDetail+'</div>'
+'</div></div>'
+_techDocLinks(t)
+'<div style="width:100%;margin-top:9px;padding:10px 12px;background:#F0FDF4;border:1.5px solid #86EFAC;border-radius:10px">'
+'<div style="font-size:11px;font-weight:700;color:#15803D;margin-bottom:7px;text-transform:uppercase;letter-spacing:.04em">Подтвердить выбор клиента?</div>'
+'<div style="display:flex;gap:7px">'
+'<button onclick="_acceptClientChoice(\''+oi+'\',\''+t.wkey+'\')" style="flex:1;padding:8px;border-radius:9px;border:none;background:#16A34A;color:#fff;font-size:13px;font-weight:700;cursor:pointer">✅ Принять</button>'
+'<button onclick="_rejectClientChoice(\''+oi+'\',\''+t.wkey+'\')" style="flex:1;padding:8px;border-radius:9px;border:1.5px solid #DC2626;background:transparent;color:#DC2626;font-size:13px;font-weight:700;cursor:pointer">↩ Переспросить</button>'
+'</div></div>'
+'</div>';
}
// done
var detail=(t.brand||'')+(t.model?' '+t.model:'')+(t.dims?'<br><span style="font-size:10px;color:var(--muted)">'+t.dims+'</span>':'');
return '<div class="tech-item" style="flex-wrap:wrap;padding-bottom:10px">'
+'<div style="display:flex;gap:10px;width:100%;align-items:flex-start">'
+iconBox
+'<div style="flex:1">'+nameRow
+(detail?'<div style="font-size:12px;color:var(--muted);margin-top:2px">'+detail+'</div>':'')
+'</div>'
+'<button onclick="'+(t.source==='own'?"_startOwnTech('"+t.wkey+"')":'_startClientPick(\''+t.wkey+'\')')+'" class="btn-pill muted">Изменить</button>'
+'</div>'
+_techDocLinks(t)
+'</div>';
}).join('');
var waitCount = o.tech.filter(function(t){return t.status==='wait';}).length;
var clientChosenCount = o.tech.filter(function(t){return t.status==='client_chosen';}).length;
var clientCount = o.tech.filter(function(t){return t.status==='waiting_client';}).length;
// allFilled: все должны быть done (client_chosen ≠ done — надо подтвердить)
var allFilled = waitCount===0 && clientChosenCount===0 && clientCount===0;
var confirmed = !!o.techConfirmed;
// Bottom of tech card: confirm CTA or confirmed status
var techFooter='';
if(confirmed){
techFooter='<div style="margin-top:12px;padding:11px 14px;background:#F0FDF4;border-radius:10px;display:flex;align-items:center;gap:10px">'
+'<div style="font-size:18px">✅</div>'
+'<div><div style="font-size:13px;font-weight:700;color:#15803D">Спецификация передана технологу</div>'
+'<div style="font-size:11px;color:#166534">'+o.techConfirmedDate+'</div></div>'
+'</div>';
} else if(allFilled){
techFooter='<div style="margin-top:12px">'
+'<button onclick="_confirmTech()" class="btn-primary btn-confirm">✅ Подтвердить и передать технологу →</button>'
+'<div style="font-size:11px;color:var(--muted);text-align:center;margin-top:7px">Технолог получит спецификацию и сможет согласовать проект</div>'
+'</div>';
} else {
var pendingMsg = clientChosenCount>0
? 'Клиент выбрал технику — подтвердите выбор выше, затем передайте технологу'
: 'Заполните все позиции — технолог не может работать без параметров техники';
techFooter='<div style="margin-top:10px;padding:10px 12px;background:'+(clientChosenCount>0?'#F0FDF4':'#FFFBEB')+';border-radius:10px;display:flex;align-items:center;gap:8px">'
+'<div style="font-size:15px">'+(clientChosenCount>0?'👆':'⏳')+'</div>'
+'<div style="font-size:12px;color:'+(clientChosenCount>0?'#166534':'#92400E')+'">'+pendingMsg+'</div>'
+'</div>';
}
techHtml='<div class="section-label">Техника · '+dc2+'/'+o.tech.length+'</div>'
+'<div class="card"><div class="row sb" style="margin-bottom:10px">'
+'<span style="font-size:13px;font-weight:600;color:var(--ink)">Спецификация техники</span>'
+(confirmed?'':'<button class="btn-sm" onclick="_startTech()">+ Позиция</button>')+'</div>'
+items+techFooter+'</div>';
}
// Rooms
var oi = window._activeOrder;
var ALL_ROOMS=['Кухня','Гостиная','Спальня','Детская','Кабинет','Прихожая','Ванная','Балкон','Гардеробная','Столовая'];
var roomsEdit = window._roomsEditing;
var editSvg='<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>';
var roomHtml='<div class="section-label" style="display:flex;align-items:center;justify-content:space-between;padding-right:16px">'
+'<span>Помещения</span>'
+'<button onclick="window._roomsEditing=!window._roomsEditing;document.getElementById(\'screen\').innerHTML=renderScreen(\'manager_order\')" style="display:inline-flex;align-items:center;gap:4px;padding:3px 9px;border-radius:8px;border:1px solid rgba(0,62,126,.2);background:rgba(0,62,126,.06);color:var(--accent);font-size:11px;font-weight:700;cursor:pointer">'
+editSvg+(roomsEdit?' Готово':' Изменить')+'</button></div>';
if(roomsEdit){
// Chip selector
roomHtml+='<div class="card" style="padding:14px">'
+'<div style="font-size:12px;color:var(--muted);margin-bottom:10px">Выберите помещения, в которые идёт мебель:</div>'
+'<div style="display:flex;flex-wrap:wrap;gap:7px">';
ALL_ROOMS.forEach(function(r){
var sel = o.rooms.indexOf(r)>=0;
roomHtml+='<button onclick="_toggleRoom('+oi+',\''+r+'\')" style="padding:7px 13px;border-radius:20px;font-size:13px;font-weight:600;cursor:pointer;border:2px solid '+(sel?'var(--accent)':'#E2E8F0')+';background:'+(sel?'var(--accent)':'var(--card)')+';color:'+(sel?'#fff':'var(--muted)')+'">'+r+'</button>';
});
roomHtml+='</div>';
if(o.rooms.length){
roomHtml+='<div style="margin-top:10px;padding-top:10px;border-top:1px solid rgba(0,0,0,.07);font-size:12px;color:var(--muted)">Выбрано: <strong style="color:var(--ink)">'+o.rooms.join(', ')+'</strong></div>';
}
roomHtml+='</div>';
} else if(o.rooms.length){
roomHtml+='<div class="card">'
+o.rooms.map(function(r){
return '<div class="row sb" style="padding:8px 0;border-bottom:1px solid rgba(0,0,0,.06)">'
+'<span style="font-size:14px;font-weight:600">🏠 '+r+'</span>'
+'<span class="badge green">✓ Замер</span></div>';
}).join('')+'</div>';
} else {
roomHtml+='<div class="card" style="text-align:center;padding:18px;color:var(--muted)">'
+'<div style="font-size:22px;margin-bottom:6px">🏠</div>'
+'<div style="font-size:13px;font-weight:600">Помещения не указаны</div>'
+'<div style="font-size:12px;margin-top:3px">Нажмите «Изменить» чтобы добавить</div>'
+'</div>';
}
// ── ЗОВ — статус производства ────────────────────────────────────────────
var zovInfo = o.zovStatus ? _zovStatusInfo(o.zovStatus.code) : null;
var zovContractVal = o.zovContract || '';
var zovHtml = '<div class="section-label">Производство ЗОВ</div>'
+'<div class="card" style="padding:14px 16px">'
// ZOV contract number field
+'<div style="margin-bottom:12px">'
+'<div style="font-size:11px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.06em;margin-bottom:6px">Номер договора ЗОВ</div>'
+'<div style="display:flex;gap:8px;align-items:center">'
+'<input id="inp_zov_'+oi+'" type="text" value="'+zovContractVal+'" placeholder="Пр.: ЗОВ-26-001" onchange="_updateZovContract('+oi+',this.value)"'
+' style="flex:1;border:2px solid '+(zovContractVal?'var(--accent)':'#E2E8F0')+';border-radius:10px;padding:9px 12px;font-size:13px;font-weight:700;color:var(--ink);outline:none;background:var(--bg);font-family:monospace">'
+'<button onclick="_refreshZovStatus('+oi+')" style="padding:9px 13px;border-radius:10px;border:1.5px solid rgba(0,62,126,.25);background:rgba(0,62,126,.07);color:var(--accent);font-size:12px;font-weight:700;cursor:pointer;white-space:nowrap;flex-shrink:0">🔄 Статус</button>'
+'</div>'
+'</div>';
if(zovInfo && o.zovStatus){
var zst = o.zovStatus;
var zpct = zst.pct || 0;
zovHtml += '<div style="background:'+zovInfo.bg+';border-radius:11px;padding:12px 14px">'
+'<div style="display:flex;align-items:center;gap:9px;margin-bottom:8px">'
+'<span style="font-size:20px">'+zovInfo.icon+'</span>'
+'<div style="flex:1">'
+'<div style="font-size:14px;font-weight:700;color:'+zovInfo.color+'">'+zst.label+'</div>'
+'<div style="font-size:11px;color:var(--muted);margin-top:1px">'+zst.date+'</div>'
+'</div>'
+'<span style="font-size:15px;font-weight:900;color:'+zovInfo.color+'">'+zpct+'%</span>'
+'</div>'
// progress bar
+'<div style="height:6px;background:rgba(0,0,0,.08);border-radius:3px;margin-bottom:8px">'
+'<div style="height:6px;border-radius:3px;background:'+zovInfo.color+';width:'+zpct+'%"></div>'
+'</div>'
+'<div style="font-size:12px;color:var(--muted)">'+zst.detail+'</div>'
+'</div>';
// Pipeline steps for factory
var zSteps=[{c:'accepted',l:'Принят'},{c:'production',l:'Произв-во'},{c:'assembly',l:'Сборка'},{c:'ready',l:'Готов'},{c:'shipped',l:'Отгружен'}];
var zCodes=['accepted','production','assembly','ready','shipped'];
var zCurIdx = zCodes.indexOf(zst.code);
zovHtml += '<div style="display:flex;align-items:flex-start;padding:10px 0 2px;overflow-x:auto;scrollbar-width:none">';
zSteps.forEach(function(zs,zi){
var isDone = zi < zCurIdx;
var isAct = zi === zCurIdx;
var dotC = isDone?'#10B981':isAct?zovInfo.color:'#CBD5E1';
var dotBg = isDone?'#DCFCE7':isAct?zovInfo.bg:'var(--card)';
var dotTxt = isDone?'✓':(zi+1).toString();
if(zi>0) zovHtml+='<div style="flex:1;height:2px;background:'+(isDone?'#10B981':'#E2E8F0')+';margin-top:12px;min-width:6px"></div>';
zovHtml+='<div style="display:flex;flex-direction:column;align-items:center;gap:3px;flex-shrink:0;min-width:52px">'
+'<div style="width:26px;height:26px;border-radius:50%;border:2px solid '+dotC+';background:'+dotBg+';display:flex;align-items:center;justify-content:center;font-size:'+(isDone?'10':'9')+'px;font-weight:800;color:'+dotC+'">'+dotTxt+'</div>'
+'<div style="font-size:9px;font-weight:600;color:'+(isAct?zovInfo.color:'var(--muted)')+';text-align:center;line-height:1.2">'+zs.l+'</div>'
+'</div>';
});
zovHtml += '</div>';
} else {
zovHtml += '<div style="padding:10px 0 4px;font-size:12px;color:var(--muted);text-align:center">'
+(zovContractVal?'Нажмите «Статус» чтобы загрузить данные из zovofficial.com':'Введите номер договора ЗОВ из программы фабрики')
+'</div>';
}
zovHtml += '</div>';
// ── Расчёты (встроены в карточку) ─────────────────────────────────────────
var advances = o.advances || [{label:'Аванс',amount:o.advance||0,paid:true,date:null}];
var paidSum = advances.reduce(function(s,a){return s+(a.paid?a.amount:0);},0);
var remaining= o.amount - paidSum;
var contractDone = remaining <= 0;
var pct = o.amount > 0 ? Math.min(100, Math.round(paidSum/o.amount*100)) : 0;
function fmtR(n){ return n.toLocaleString('ru')+' ₽'; }
var today2 = (function(){ var d=new Date(); return String(d.getDate()).padStart(2,'0')+'.'+String(d.getMonth()+1).padStart(2,'0')+'.'+d.getFullYear(); })();
var advRowsO = advances.map(function(a,ai){
var chk = a.paid
? '<div style="width:20px;height:20px;border-radius:5px;background:#DCFCE7;display:flex;align-items:center;justify-content:center;flex-shrink:0;cursor:pointer" onclick="_toggleAdvPaid('+oi+','+ai+',false)"><svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="#15803D" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg></div>'
: '<div style="width:20px;height:20px;border-radius:5px;border:2px solid #CBD5E1;flex-shrink:0;cursor:pointer" onclick="_toggleAdvPaid('+oi+','+ai+',true)"></div>';
return '<div style="display:flex;align-items:center;gap:8px;padding:9px 0;border-bottom:1px solid rgba(0,0,0,.05)">'
+chk
+'<div style="font-size:12px;color:var(--ink);flex:1;line-height:1.3">'+a.label+'</div>'
+'<div style="position:relative;width:120px">'
+'<input type="number" value="'+(a.amount||'')+'" placeholder="0" onchange="_liveAdvAmt('+oi+','+ai+',this.value)"'
+' style="width:100%;border:1.5px solid '+(a.paid?'var(--success)':'#E2E8F0')+';border-radius:8px;padding:7px 24px 7px 8px;font-size:13px;font-weight:700;color:'+(a.paid?'var(--success)':'var(--muted)')+';outline:none;background:var(--bg)">'
+'<span style="position:absolute;right:7px;top:50%;transform:translateY(-50%);font-size:11px;font-weight:700;color:var(--muted);pointer-events:none">₽</span>'
+'</div>'
+'</div>';
}).join('');
var asmChkO = o.assemblyPaid
? '<div style="width:20px;height:20px;border-radius:5px;background:#DCFCE7;display:flex;align-items:center;justify-content:center;flex-shrink:0;cursor:pointer" onclick="_toggleAsm('+oi+',false)"><svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="#15803D" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg></div>'
: '<div style="width:20px;height:20px;border-radius:5px;border:2px solid #CBD5E1;flex-shrink:0;cursor:pointer" onclick="_toggleAsm('+oi+',true)"></div>';
var contHtml = '<div class="section-label">Расчёты с клиентом</div>'
+'<div class="card" style="padding:14px 16px">'
// Номер договора + сумма + прогресс
+'<div class="row sb" style="margin-bottom:10px">'
+'<span style="font-size:12px;color:var(--muted)">'+o.contract+'</span>'
+'<div style="display:flex;align-items:center;gap:6px">'
+'<span style="font-size:15px;font-weight:900;color:var(--accent)">'+fmtR(o.amount)+'</span>'
+'</div></div>'
+'<div style="display:flex;align-items:center;gap:8px;margin-bottom:4px">'
+'<div style="flex:1;height:6px;background:#E2E8F0;border-radius:3px">'
+'<div style="height:6px;border-radius:3px;background:'+(contractDone?'var(--success)':'var(--accent)')+';width:'+pct+'%"></div>'
+'</div>'
+'<span style="font-size:11px;font-weight:700;color:'+(contractDone?'var(--success)':'var(--accent)')+'">'+pct+'%</span>'
+'</div>'
+(remaining>0?'<div style="font-size:11px;color:var(--danger);font-weight:600;margin-bottom:10px">Остаток: '+fmtR(remaining)+'</div>':'<div style="font-size:11px;color:var(--success);font-weight:600;margin-bottom:10px">✅ Оплачен полностью</div>')
// Авансы
+'<div style="border-top:1px solid rgba(0,0,0,.07);padding-top:8px">'+advRowsO+'</div>'
// Сборка
+'<div style="display:flex;align-items:center;gap:8px;padding:9px 0;border-top:1px solid rgba(0,0,0,.07)">'
+asmChkO
+'<div style="font-size:12px;color:var(--ink);flex:1">Сборка'+(o.assemblyPaid?' <span style=\"font-size:10px;color:var(--success);font-weight:700\">оплачена</span>':'')+'</div>'
+'<div style="position:relative;width:120px">'
+'<input type="number" value="'+(o.assembly||'')+'" placeholder="0" onchange="_liveAsm('+oi+',this.value)"'
+' style="width:100%;border:1.5px solid '+(o.assemblyPaid?'var(--success)':'#F59E0B')+';border-radius:8px;padding:7px 24px 7px 8px;font-size:13px;font-weight:700;color:#92400E;outline:none;background:var(--bg)">'
+'<span style="position:absolute;right:7px;top:50%;transform:translateY(-50%);font-size:11px;font-weight:700;color:#92400E;pointer-events:none">₽</span>'
+'</div></div>'
// Кнопка "Показать клиенту"
+'<button onclick="_showCalcClient()" class="btn-secondary" style="margin-top:10px">📱 Показать клиенту</button>'
// AI проверка договора
+'<button onclick="_aiContractCheck()" style="width:100%;margin-top:8px;padding:11px;border-radius:12px;border:1.5px solid rgba(139,92,246,.3);background:rgba(139,92,246,.06);color:#7C3AED;font-size:13px;font-weight:700;cursor:pointer;display:flex;align-items:center;justify-content:center;gap:7px"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/></svg>AI проверка договора</button>'
+(window._aiContractResult ? _renderAIContract(window._aiContractResult) : '')
+'</div>';
var backSvg='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M15 18l-6-6 6-6"/></svg>';
var callSvg='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M22 16.92v3a2 2 0 01-2.18 2 19.79 19.79 0 01-16.74-16.74A2 2 0 015 3h3a2 2 0 012 1.72 12.84 12.84 0 00.7 2.81 2 2 0 01-.45 2.11L9.09 10.91a16 16 0 006.99 6.99l1.27-1.27a2 2 0 012.11-.45c.907.339 1.85.573 2.81.7A2 2 0 0122 16.92z"/></svg>';
return '<div class="page">'
+'<div class="page-header">'
+'<button class="back-btn" onclick="document.getElementById(\'screen\').innerHTML=renderScreen(\'manager_home\');document.getElementById(\'nav\').innerHTML=navBar()">'+backSvg+'</button>'
+'<div style="flex:1"><div style="font-size:15px;font-weight:700;color:var(--ink)">'+o.client+'</div>'
+'<div style="font-size:11px;color:var(--muted)">'+o.label+'</div></div>'
+'<button class="back-btn" onclick="_toast(\'📞 '+o.phone+'\',\'#003E7E\')">'+callSvg+'</button>'
+'</div>'
+'<div style="padding:14px 16px;background:var(--card);border-bottom:1px solid rgba(0,0,0,.06)">'
+'<div class="pipeline">'+pip+'</div></div>'
+'<div style="padding:16px">'+blk+techHtml+roomHtml+zovHtml+contHtml+'</div>'
+'</div>';
}
function _startTech(){window._wizFromNav=false;window._wizScreen='manager_order';window._techSelection=window._techSelection||[];document.getElementById('screen').innerHTML=renderScreen('manager_tech');}
function _startWizard(key){window._wiz={type:key,step:0,answers:{},budget:null};window._wizScreen='manager_order';document.getElementById('screen').innerHTML=renderScreen('manager_wizard');}
function _confirmTech(){
var o = window._managerOrders[window._activeOrder];
var now = new Date();
var dd = String(now.getDate()).padStart(2,'0');
var mm = String(now.getMonth()+1).padStart(2,'0');
var hh = String(now.getHours()).padStart(2,'0');
var mn = String(now.getMinutes()).padStart(2,'0');
o.techConfirmed = true;
o.techConfirmedDate = dd+'.'+mm+'.'+now.getFullYear()+' '+hh+':'+mn;
// Advance stage to Технолог (4) if still on Техника (3)
if(o.stage===3) o.stage=4;
// Clear tech blocker
if(o.blocker && o.blocker.indexOf('Техника')>=0){ o.blocker=null; o.techNote=''; }
_toast('✅ Спецификация передана технологу','var(--success)');
setTimeout(function(){
document.getElementById('screen').innerHTML=renderScreen('manager_order');
document.getElementById('nav').innerHTML=navBar();
},1000);
}
// ── ZOV Factory helpers ───────────────────────────────────────────────────────
function _zovStatusInfo(code){
var map={
'accepted': {color:'#2563EB',bg:'#EFF6FF', icon:'📋'},
'production':{color:'#D97706',bg:'#FFFBEB', icon:'⚙️'},
'assembly': {color:'#7C3AED',bg:'#F5F3FF', icon:'🔧'},
'ready': {color:'#059669',bg:'#F0FDF4', icon:'✅'},
'shipped': {color:'#0F766E',bg:'#F0FDFA', icon:'🚚'}
};
return map[code]||{color:'#8A94A6',bg:'#F9FAFB',icon:'❓'};
}
function _updateZovContract(oi,val){
window._managerOrders[oi].zovContract=val;
var inp=document.getElementById('inp_zov_'+oi);
if(inp) inp.style.borderColor=val?'var(--accent)':'#E2E8F0';
}
function _refreshZovStatus(oi){
var o=window._managerOrders[oi];
if(!o.zovContract){_toast('Введите номер договора ЗОВ','var(--warn)');return;}
_toast('🔄 Запрашиваю статус с zovofficial.com…','var(--accent)');
setTimeout(function(){
var codes=['accepted','production','assembly','ready','shipped'];
var curr=o.zovStatus?codes.indexOf(o.zovStatus.code):-1;
var next=codes[Math.min(curr+1,codes.length-1)];
var info=_zovStatusInfo(next);
var pcts={accepted:10,production:45,assembly:70,ready:90,shipped:100};
var details={
accepted:'Заявка принята фабрикой. Производство запланировано.',
production:'Корпуса готовы. Фасады и столешница в работе.',
assembly:'Все комплектующие готовы. Идёт финальная сборка.',
ready:'Готов к отгрузке. Согласуйте дату доставки.',
shipped:'Отгружен. Транспорт в пути.'
};
var now=new Date();
var d=String(now.getDate()).padStart(2,'0')+'.'+String(now.getMonth()+1).padStart(2,'0')+'.'+now.getFullYear();
var t=String(now.getHours()).padStart(2,'0')+':'+String(now.getMinutes()).padStart(2,'0');
o.zovStatus={code:next,label:info.icon+' '+['Принят','В производстве','Сборка','Готов к отгрузке','Отгружен'][codes.indexOf(next)],detail:details[next],date:d+' · '+t,pct:pcts[next]||0};
_toast('✅ Статус обновлён','var(--success)');
document.getElementById('screen').innerHTML=renderScreen('manager_order');
},1400);
}
// ── Rooms helpers ─────────────────────────────────────────────────────────────
function _toggleRoom(oi,room){
var o=window._managerOrders[oi];
var idx=o.rooms.indexOf(room);
if(idx>=0) o.rooms.splice(idx,1);
else o.rooms.push(room);
document.getElementById('screen').innerHTML=renderScreen('manager_order');
}
function _startOwnTech(key){
window._ownTechKey = key;
window._ownTechMode = 'choose';
window._ownTechData = {brand:'',model:'',dims:''};
document.getElementById('screen').innerHTML = renderScreen('manager_own_tech');
}
// ── OWN TECH SCREEN ──────────────────────────────────────────────────────────
window._ownTechKey = window._ownTechKey || null;
window._ownTechMode = window._ownTechMode || 'choose';
window._ownTechData = window._ownTechData || {brand:'',model:'',dims:''};
var _ownTechRecognized = {
oven: {brand:'Gorenje', model:'BO74EB', dims:'590×595 мм'},
cooktop: {brand:'Electrolux', model:'EHH96340IK', dims:'760×510 мм'},
fridge: {brand:'Samsung', model:'RB38T600FSA', dims:'595×2000 мм'},
hood: {brand:'Elica', model:'STRIP IX/A/60', dims:'590 мм'},
dishwasher:{brand:'Bosch', model:'SMS25AI01R', dims:'598×598 мм'},
micro: {brand:'Samsung', model:'MS23K3513AW', dims:'382×290 мм'},
coffee: {brand:'DeLonghi', model:'ECAM350.55.B', dims:'438×350 мм'},
steamer: {brand:'Miele', model:'DGC 7440', dims:'590×455 мм'},
freezer: {brand:'Liebherr', model:'GN 3023', dims:'595×1780 мм'},
wine: {brand:'Liebherr', model:'WKes 553', dims:'595×870 мм'},
};
function _ownTechHints(wkey) {
var m = {
oven: {brand:'Bosch, Gorenje, Hansa', model:'BO74EB, HBF534ES0R', dims:'590×595 мм'},
cooktop: {brand:'Electrolux, Bosch, Siemens',model:'EHH96340IK, PIV6…', dims:'760×510 мм'},
fridge: {brand:'Samsung, LG, Haier', model:'RB38T600FSA', dims:'595×2000 мм'},
hood: {brand:'Elica, Bosch, Krona', model:'STRIP IX/A/60', dims:'590 мм'},
dishwasher:{brand:'Bosch, Electrolux, Beko', model:'SMS25AI01R', dims:'598×598 мм'},
micro: {brand:'Samsung, LG, Panasonic', model:'MS23K3513AW', dims:'382×290 мм'},
coffee: {brand:'DeLonghi, Krups, Philips', model:'ECAM350.55.B', dims:'438×350 мм'},
steamer: {brand:'Miele, Smeg, Electrolux', model:'DGC 7440', dims:'590×455 мм'},
freezer: {brand:'Liebherr, Bosch, Indesit', model:'GN 3023', dims:'595×1780 мм'},
wine: {brand:'Liebherr, La Sommeliere', model:'WKes 553', dims:'595×870 мм'},
};
return m[wkey] || {};
}
function _ownTechFormHtml(brand, model, dims, nicheDef, hints) {
hints = hints || {};
// dims: если найдено — показываем как readonly-чип, иначе скрытый placeholder
var dimsBlock = dims
? '<div style="background:#F0FDF4;border:1.5px solid #86EFAC;border-radius:12px;padding:11px 14px;margin-bottom:14px;display:flex;align-items:center;gap:10px">'
+'<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#15803D" stroke-width="2.5"><polyline points="20 6 9 17 4 12"/></svg>'
+'<div><div style="font-size:11px;font-weight:700;color:#15803D;text-transform:uppercase;letter-spacing:.05em">Размер ниши</div>'
+'<div style="font-size:14px;font-weight:600;color:#166534;margin-top:1px">'+dims+'</div></div>'
+'<span style="margin-left:auto;font-size:10px;color:#15803D;background:rgba(21,128,61,.1);padding:2px 8px;border-radius:20px;font-weight:700">из каталога</span>'
+'</div>'
: '<div id="ot_dims_block" style="background:rgba(0,62,126,.04);border:1.5px dashed rgba(0,62,126,.2);border-radius:12px;padding:11px 14px;margin-bottom:14px;display:flex;align-items:center;gap:10px">'
+'<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="var(--muted)" stroke-width="2"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>'
+'<div style="font-size:13px;color:var(--muted)">Размер ниши подтянется автоматически<br>после ввода марки и модели</div>'
+'</div>';
return '<div style="background:var(--card);border-radius:16px;padding:16px 16px 4px;margin-bottom:14px">'
+'<div style="margin-bottom:14px">'
+'<div style="font-size:11px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;margin-bottom:6px">Марка</div>'
+'<input id="ot_brand" type="text" value="'+brand+'" placeholder="'+(hints.brand||'Bosch, Samsung, Gorenje…')+'"'
+' style="width:100%;border:1.5px solid #E2E8F0;border-radius:10px;padding:11px 12px;font-size:14px;color:var(--ink);outline:none;box-sizing:border-box;background:#fff"'
+' onfocus="this.style.borderColor=\'var(--accent)\'" onblur="this.style.borderColor=\'#E2E8F0\';_ownTechTryLookup()">'
+'</div>'
+'<div style="margin-bottom:14px">'
+'<div style="font-size:11px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;margin-bottom:6px">Модель / артикул</div>'
+'<input id="ot_model" type="text" value="'+model+'" placeholder="'+(hints.model||'артикул или название')+'"'
+' style="width:100%;border:1.5px solid #E2E8F0;border-radius:10px;padding:11px 12px;font-size:14px;color:var(--ink);outline:none;box-sizing:border-box;background:#fff"'
+' onfocus="this.style.borderColor=\'var(--accent)\'" onblur="this.style.borderColor=\'#E2E8F0\';_ownTechTryLookup()">'
+'</div>'
+dimsBlock
+'</div>'
+'<button onclick="_saveOwnTechToOrder()" style="width:100%;padding:14px;background:var(--accent);color:#fff;border:none;border-radius:12px;font-size:15px;font-weight:700;cursor:pointer">Сохранить в заказ →</button>';
}
// Имитация поиска ниши по марке+модели
var _dimsCatalog = {
'gorenje bo74eb': '590×595 мм', 'gorenje': '590×595 мм',
'electrolux ehh': '760×510 мм', 'electrolux': '760×510 мм',
'samsung rb38': '595×2000 мм','samsung': '595×2000 мм',
'elica strip': '590 мм', 'elica': '590 мм',
'bosch sms': '598×598 мм', 'bosch hxa': '595×595 мм',
'bosch hbf': '595×595 мм', 'bosch': '595×595 мм',
'samsung ms23': '382×290 мм', 'delonghi ecam': '438×350 мм',
'miele dgc': '590×455 мм', 'liebherr gn': '595×1780 мм',
'liebherr wkes': '595×870 мм',
};
function _ownTechTryLookup() {
var brand = ((document.getElementById('ot_brand')||{}).value||'').trim().toLowerCase();
var model = ((document.getElementById('ot_model')||{}).value||'').trim().toLowerCase();
if (!brand && !model) return;
var key = (brand + ' ' + model).trim();
var found = null;
// Поиск по убыванию специфичности
Object.keys(_dimsCatalog).forEach(function(k){ if(key.indexOf(k)>=0) found = _dimsCatalog[k]; });
if (!found && brand) Object.keys(_dimsCatalog).forEach(function(k){ if(brand.indexOf(k)>=0||k.indexOf(brand)>=0) found = _dimsCatalog[k]; });
var block = document.getElementById('ot_dims_block');
if (found && block) {
block.style.transition = 'all .3s';
block.style.background = 'rgba(21,128,61,.06)';
block.style.borderColor = '#86EFAC';
block.style.borderStyle = 'solid';
block.innerHTML = '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#15803D" stroke-width="2.5"><polyline points="20 6 9 17 4 12"/></svg>'
+'<div><div style="font-size:11px;font-weight:700;color:#15803D;text-transform:uppercase;letter-spacing:.05em">Размер ниши</div>'
+'<div style="font-size:14px;font-weight:600;color:#166534;margin-top:1px">'+found+'</div></div>'
+'<span style="margin-left:auto;font-size:10px;color:#15803D;background:rgba(21,128,61,.1);padding:2px 8px;border-radius:20px;font-weight:700">из каталога</span>';
window._ownTechFoundDims = found;
}
}
function screenOwnTech() {
var wkey = window._ownTechKey || 'oven';
var cfg = WIZ[wkey] || WIZ['oven'];
var mode = window._ownTechMode || 'choose';
var bk = '<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M15 18l-6-6 6-6"/></svg>';
var backChoose = "window._ownTechMode='choose';document.getElementById('screen').innerHTML=renderScreen('manager_own_tech')";
var backOrder = "document.getElementById('screen').innerHTML=renderScreen('manager_order')";
// ── CHOOSE ──
var icoCam = '<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z"/><circle cx="12" cy="13" r="4"/></svg>';
var icoPen = '<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>';
var icoChv = '<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="var(--muted)" stroke-width="2.5"><path d="M9 18l6-6-6-6"/></svg>';
var techHdr = '<div style="background:var(--card);border-radius:14px;padding:12px 14px;margin-bottom:18px;display:flex;align-items:center;gap:12px">'
+'<div style="width:40px;height:40px;border-radius:10px;background:rgba(0,62,126,.08);display:flex;align-items:center;justify-content:center;flex-shrink:0;color:var(--accent)">'+_techIcon(wkey,22)+'</div>'
+'<div><div style="font-size:14px;font-weight:700;color:var(--ink)">'+cfg.name+'</div>'
+'<div style="font-size:12px;color:var(--muted)">Ниша: '+cfg.niche+'</div></div></div>';
var v = window._ownTechVariant || 'A';
if (mode === 'choose') {
var body = '';
// ── VARIANT A: линейные иконки, приоритет через размер текста ──
if (v==='A') {
body = techHdr
// Primary
+'<div style="background:var(--accent);border-radius:16px;padding:18px;margin-bottom:10px;cursor:pointer" onclick="_ownTechPickPhoto()">'
+'<div style="display:flex;align-items:center;gap:14px">'
+'<div style="width:44px;height:44px;border-radius:12px;background:rgba(255,255,255,.15);display:flex;align-items:center;justify-content:center;flex-shrink:0;color:#fff">'+icoCam+'</div>'
+'<div style="flex:1"><div style="font-size:15px;font-weight:700;color:#fff">По фото</div>'
+'<div style="font-size:12px;color:rgba(255,255,255,.75);margin-top:3px;line-height:1.4">AI распознаёт марку и модель<br>по снимку шильдика</div></div>'
+'<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="rgba(255,255,255,.6)" stroke-width="2.5"><path d="M9 18l6-6-6-6"/></svg>'
+'</div></div>'
// Secondary
+'<div style="background:var(--card);border-radius:16px;padding:16px 18px;cursor:pointer;border:1.5px solid rgba(0,62,126,.1)" onclick="_ownTechManual()">'
+'<div style="display:flex;align-items:center;gap:14px">'
+'<div style="width:40px;height:40px;border-radius:10px;background:rgba(0,62,126,.07);display:flex;align-items:center;justify-content:center;flex-shrink:0;color:var(--accent)">'+icoPen+'</div>'
+'<div style="flex:1"><div style="font-size:14px;font-weight:600;color:var(--ink)">Ввести вручную</div>'
+'<div style="font-size:12px;color:var(--muted);margin-top:2px">Марка и модель — ниша найдётся автоматически</div></div>'
+icoChv+'</div></div>';
}
// ── VARIANT B: две равные карточки-плитки 2×1 ──
if (v==='B') {
var cardBase = 'flex:1;border-radius:16px;padding:20px 14px;cursor:pointer;text-align:center;display:flex;flex-direction:column;align-items:center;gap:10px';
body = techHdr
+'<div style="display:flex;gap:10px;margin-bottom:10px">'
+'<div style="'+cardBase+';background:var(--accent)" onclick="_ownTechPickPhoto()">'
+'<div style="width:52px;height:52px;border-radius:14px;background:rgba(255,255,255,.18);display:flex;align-items:center;justify-content:center;color:#fff">'+icoCam+'</div>'
+'<div style="font-size:14px;font-weight:700;color:#fff">По фото</div>'
+'<div style="font-size:11px;color:rgba(255,255,255,.7);line-height:1.4">AI читает шильдик автоматически</div>'
+'</div>'
+'<div style="'+cardBase+';background:var(--card);border:1.5px solid rgba(0,62,126,.12)" onclick="_ownTechManual()">'
+'<div style="width:52px;height:52px;border-radius:14px;background:rgba(0,62,126,.07);display:flex;align-items:center;justify-content:center;color:var(--accent)">'+icoPen+'</div>'
+'<div style="font-size:14px;font-weight:700;color:var(--ink)">Вручную</div>'
+'<div style="font-size:11px;color:var(--muted);line-height:1.4">Марка, модель, размер ниши</div>'
+'</div></div>';
}
// ── VARIANT C: минимализм — без иконок, только текст ──
if (v==='C') {
body = techHdr
+'<div style="background:var(--accent);border-radius:16px;padding:20px 18px;margin-bottom:10px;cursor:pointer" onclick="_ownTechPickPhoto()">'
+'<div style="font-size:16px;font-weight:700;color:#fff;margin-bottom:4px">По фото шильдика</div>'
+'<div style="font-size:13px;color:rgba(255,255,255,.75);line-height:1.5">Сфотографируйте маркировку — AI определит марку и модель автоматически</div>'
+'<div style="display:inline-flex;align-items:center;gap:5px;margin-top:10px;background:rgba(255,255,255,.2);border-radius:20px;padding:4px 12px">'
+'<span style="font-size:12px;color:#fff;font-weight:600">Рекомендуем</span></div>'
+'</div>'
+'<div style="display:flex;align-items:center;justify-content:center;gap:10px;margin:12px 0">'
+'<div style="flex:1;height:1px;background:var(--bg)"></div>'
+'<span style="font-size:11px;color:var(--muted);font-weight:600">или</span>'
+'<div style="flex:1;height:1px;background:var(--bg)"></div></div>'
+'<div style="background:var(--card);border-radius:14px;padding:14px 18px;cursor:pointer;display:flex;align-items:center;justify-content:space-between" onclick="_ownTechManual()">'
+'<div><div style="font-size:14px;font-weight:600;color:var(--ink)">Ввести вручную</div>'
+'<div style="font-size:12px;color:var(--muted);margin-top:2px">Марка, модель, размер ниши</div></div>'
+icoChv+'</div>';
}
return '<div class="page">'
+'<div class="page-header"><button class="back-btn" onclick="'+backOrder+'">'+bk+'</button><h2>Своя техника</h2></div>'
+'<div style="padding:16px">'+body+'</div></div>';
}
// ── PHOTO UPLOAD ──
if (mode === 'photo') {
return '<div class="page">'
+'<div class="page-header"><button class="back-btn" onclick="'+backChoose+'">'+bk+'</button><h2>Фото техники</h2></div>'
+'<div style="padding:16px">'
+'<div style="font-size:13px;color:var(--muted);text-align:center;margin-bottom:16px;line-height:1.5">'
+'Сфотографируйте шильдик с маркировкой<br>или переднюю панель техники</div>'
+'<label style="display:block;border:2px dashed #CBD5E1;border-radius:16px;padding:36px 20px;cursor:pointer;text-align:center;background:var(--card)">'
+'<input type="file" accept="image/*" capture="environment" style="display:none" onchange="_ownTechPhotoSelected(this)">'
+'<div style="font-size:44px;margin-bottom:10px">📷</div>'
+'<div style="font-size:15px;font-weight:700;color:var(--accent)">Выбрать фото</div>'
+'<div style="font-size:12px;color:var(--muted);margin-top:4px">или сделать снимок камерой</div>'
+'</label>'
+'<div style="font-size:12px;color:var(--muted);text-align:center;margin-top:16px;line-height:1.6">'
+'Лучший результат — чёткое фото шильдика<br>с полным артикулом модели</div>'
+'</div></div>';
}
// ── SCANNING ──
if (mode === 'scan') {
var photoSrc = window._ownTechPhotoUrl || (_photoUrls[wkey] || '');
return '<div class="page">'
+'<div class="page-header"><button class="back-btn" onclick="'+backChoose+'">'+bk+'</button><h2>Распознавание</h2></div>'
+'<div style="padding:16px;text-align:center">'
+'<div style="width:130px;height:130px;border-radius:16px;overflow:hidden;margin:20px auto 16px;border:2px solid rgba(0,62,126,.12);background:#F1F5F9">'
+(photoSrc?'<img src="'+photoSrc+'" style="width:100%;height:100%;object-fit:cover">':'<div style="width:100%;height:100%;display:flex;align-items:center;justify-content:center;font-size:44px">'+cfg.emoji+'</div>')
+'</div>'
+'<div style="font-size:15px;font-weight:700;color:var(--ink);margin-bottom:6px">Анализируем фото…</div>'
+'<div style="font-size:13px;color:var(--muted);margin-bottom:24px">AI определяет модель и характеристики</div>'
+'<div style="background:#E2E8F0;border-radius:8px;height:5px;width:200px;margin:0 auto;overflow:hidden">'
+'<div id="scanBar" style="background:var(--accent);height:100%;width:0;border-radius:8px;transition:width 1.3s cubic-bezier(.4,0,.2,1)"></div>'
+'</div>'
+'<div id="scanLabel" style="font-size:12px;color:var(--muted);margin-top:10px">Чтение маркировки…</div>'
+'</div>'
+'<script>'
+'setTimeout(function(){document.getElementById("scanBar").style.width="60%";document.getElementById("scanLabel").textContent="Поиск в базе…";},50);'
+'setTimeout(function(){document.getElementById("scanBar").style.width="100%";document.getElementById("scanLabel").textContent="Модель найдена ✓";},1000);'
+'setTimeout(function(){_ownTechShowRecognized();},1600);'
+'<\/script>';
}
// ── CONFIRM (after photo) ──
if (mode === 'confirm') {
var d = window._ownTechData;
return '<div class="page">'
+'<div class="page-header"><button class="back-btn" onclick="'+backChoose+'">'+bk+'</button><h2>Проверьте данные</h2></div>'
+'<div style="padding:16px">'
+'<div style="background:#F0FDF4;border:1.5px solid #86EFAC;border-radius:13px;padding:13px 15px;margin-bottom:18px;display:flex;align-items:center;gap:10px">'
+'<div style="font-size:20px">✅</div>'
+'<div><div style="font-size:13px;font-weight:700;color:#15803D">Модель распознана</div>'
+'<div style="font-size:12px;color:#166534">Проверьте и при необходимости скорректируйте</div></div></div>'
+_ownTechFormHtml(d.brand, d.model, d.dims, cfg.niche)
+'</div></div>';
}
// ── MANUAL FORM ──
if (mode === 'manual') {
var hints = _ownTechHints(wkey);
return '<div class="page">'
+'<div class="page-header"><button class="back-btn" onclick="'+backChoose+'">'+bk+'</button><h2>Своя техника</h2></div>'
+'<div style="padding:16px">'
+'<div style="font-size:13px;color:var(--muted);margin-bottom:14px">'+cfg.name+' · Ниша '+cfg.niche+'</div>'
+_ownTechFormHtml('', '', '', cfg.niche, hints)
+'</div></div>';
}
return renderScreen('manager_order');
}
function _ownTechPickPhoto() {
window._ownTechMode = 'photo';
document.getElementById('screen').innerHTML = renderScreen('manager_own_tech');
}
function _ownTechManual() {
window._ownTechMode = 'manual';
document.getElementById('screen').innerHTML = renderScreen('manager_own_tech');
}
function _ownTechPhotoSelected(input) {
if (!input.files || !input.files[0]) return;
var reader = new FileReader();
reader.onload = function(e) {
window._ownTechPhotoUrl = e.target.result;
window._ownTechMode = 'scan';
document.getElementById('screen').innerHTML = renderScreen('manager_own_tech');
};
reader.readAsDataURL(input.files[0]);
}
function _ownTechShowRecognized() {
var wkey = window._ownTechKey || 'oven';
var rec = _ownTechRecognized[wkey] || {brand:'',model:'',dims:''};
window._ownTechData = {brand: rec.brand, model: rec.model, dims: rec.dims};
window._ownTechMode = 'confirm';
document.getElementById('screen').innerHTML = renderScreen('manager_own_tech');
}
function _saveOwnTechToOrder() {
var brand = (document.getElementById('ot_brand').value||'').trim();
var model = (document.getElementById('ot_model').value||'').trim();
// dims: из автопоиска или из confirm-данных
var dims = window._ownTechFoundDims || (window._ownTechData && window._ownTechData.dims) || '';
if (!brand && !model) { _toast('Укажите марку или модель','var(--danger)'); return; }
var o = window._managerOrders[window._activeOrder];
var wkey = window._ownTechKey;
var ti = (o.tech||[]).findIndex(function(t){ return t.wkey===wkey; });
if (ti >= 0) {
o.tech[ti].status = 'done';
o.tech[ti].brand = [brand, model].filter(Boolean).join(' ');
o.tech[ti].model = model;
o.tech[ti].dims = dims;
o.tech[ti].source = 'own';
}
var waitLeft = (o.tech||[]).filter(function(t){ return t.status==='wait'; }).length;
if (waitLeft===0 && o.blocker && o.blocker.indexOf('Техника')>=0) { o.blocker=null; o.techNote=''; }
_toast('✅ '+(brand||model)+' сохранён в заказ','#15803D');
setTimeout(function(){
window._ownTechKey = null;
window._ownTechMode = 'choose';
document.getElementById('screen').innerHTML = renderScreen('manager_order');
document.getElementById('nav').innerHTML = navBar();
}, 1100);
}
// ── TECH MULTI-SELECT ────────────────────────────────────────────────────────
function screenTech() {
var sel = window._techSelection || [];
var o = window._managerOrders[window._activeOrder];
var existing = (o && o.tech) ? o.tech.map(function(t){return t.wkey;}) : [];
var cnt = sel.length;
var grid = TECH_TYPES.map(function(t){
var isSel = sel.indexOf(t.key) >= 0;
var isEx = existing.indexOf(t.key) >= 0;
var cls = 'tech-pick-card'+(isSel?' sel':isEx?' exists':'');
var check = isSel
? '<div style="position:absolute;top:7px;right:7px;width:18px;height:18px;background:#fff;border-radius:50%;display:flex;align-items:center;justify-content:center"><svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="#003E7E" stroke-width="3.5"><polyline points="20 6 9 17 4 12"/></svg></div>'
: (isEx ? '<div style="position:absolute;top:7px;right:7px;width:18px;height:18px;background:#CBD5E1;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:9px">✓</div>' : '');
var nameCol = isSel ? '#fff' : isEx ? 'var(--muted)' : 'var(--ink)';
return '<div class="'+cls+'" onclick="'+(isEx?'':'_techToggle(\''+t.key+'\')')+'">'
+check
+'<div style="width:36px;height:36px;margin:0 auto 8px;color:'+(isSel?'#fff':isEx?'#94A3B8':'var(--accent)')+'">'+(TECH_ICONS[t.key]||'')+'</div>'
+'<div style="font-size:12px;font-weight:700;color:'+nameCol+';line-height:1.3">'+t.label+'</div>'
+(isEx?'<div style="font-size:10px;color:#94A3B8;margin-top:3px">в списке</div>':'')
+'</div>';
}).join('');
var bk='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M15 18l-6-6 6-6"/></svg>';
var cntLabel = cnt===0 ? 'Выберите позиции' : 'Добавить '+cnt+' позици'+(cnt===1?'ю':cnt<5?'и':'й')+' →';
return '<div class="page">'
+'<div class="page-header"><button class="back-btn" onclick="window._techSelection=[];document.getElementById(\'screen\').innerHTML=renderScreen(window._wizScreen||\'manager_order\')">'+bk+'</button>'
+'<h2>Техника</h2>'
+(cnt>0?'<div style="background:var(--accent);color:#fff;font-size:11px;font-weight:700;padding:3px 9px;border-radius:12px">'+cnt+'</div>':'')
+'</div>'
+'<div style="padding:10px 16px 4px"><div style="font-size:13px;color:var(--muted)">Выберите всю встраиваемую технику для этого проекта</div></div>'
+'<div style="padding:12px 16px;display:grid;grid-template-columns:repeat(3,1fr);gap:10px">'+grid+'</div>'
+'<div style="padding:0 16px 24px"><button class="btn-primary'+(cnt>0?' btn-confirm':'')+'" '+(cnt===0?'disabled':'')+' onclick="_techAddSelected()">'+cntLabel+'</button></div>'
+'</div>';
}
function _techToggle(key){
window._techSelection = window._techSelection || [];
var i = window._techSelection.indexOf(key);
if(i>=0) window._techSelection.splice(i,1); else window._techSelection.push(key);
document.getElementById('screen').innerHTML = renderScreen('manager_tech');
}
function _techAddSelected(){
var o = window._managerOrders[window._activeOrder];
var sel = window._techSelection || [];
sel.forEach(function(key){
var tt = TECH_TYPES.filter(function(t){return t.key===key;})[0];
if(!tt) return;
if(o.tech.some(function(t){return t.wkey===key;})) return;
o.tech.push({name:tt.label, wkey:key, status:'wait', dims:'', brand:'', source:null});
});
window._techSelection = [];
document.getElementById('screen').innerHTML = renderScreen('manager_order');
document.getElementById('nav').innerHTML = navBar();
}
function _startClientPick(key){
window._clientPickKey = key;
window._wizScreen='manager_order';
document.getElementById('screen').innerHTML=renderScreen('manager_tech_client');
document.getElementById('nav').innerHTML=navBar();
}
function _techClientRemind(key){
_toast('📲 Напоминание отправлено в TG-кабинет клиента','var(--accent)');
}
function _acceptClientChoice(oi,key){
var o=window._managerOrders[oi];
var t=o.tech.filter(function(x){return x.wkey===key;})[0];
if(t){t.status='done';}
_toast('✅ Выбор принят и зафиксирован','var(--success)');
document.getElementById('screen').innerHTML=renderScreen('manager_order');
}
function _rejectClientChoice(oi,key){
var o=window._managerOrders[oi];
var t=o.tech.filter(function(x){return x.wkey===key;})[0];
if(t){t.status='waiting_client'; t.brand=''; t.model=''; t.dims='';}
_toast('↩ Клиенту отправлена просьба выбрать снова','var(--warn)');
document.getElementById('screen').innerHTML=renderScreen('manager_order');
}
function screenTechClient(){
var key=window._clientPickKey||'oven';
var tt=TECH_TYPES.filter(function(t){return t.key===key;})[0]||{key:key,label:key};
var bk='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M15 18l-6-6 6-6"/></svg>';
var optCard=function(icon,title,sub,onclick){
return '<div onclick="'+onclick+'" style="background:var(--card);border-radius:16px;padding:18px 16px;border:2px solid #E2E8F0;cursor:pointer;transition:all .15s;margin-bottom:12px;display:flex;gap:14px;align-items:flex-start">'
+'<div style="font-size:26px;flex-shrink:0">'+icon+'</div>'
+'<div><div style="font-size:15px;font-weight:700;color:var(--ink);margin-bottom:4px">'+title+'</div>'
+'<div style="font-size:12px;color:var(--muted);line-height:1.5">'+sub+'</div></div>'
+'</div>';
};
return '<div class="page">'
+'<div class="page-header"><button class="back-btn" onclick="document.getElementById(\'screen\').innerHTML=renderScreen(\'manager_order\')">'+bk+'</button>'
+'<div style="flex:1"><div style="font-size:11px;color:var(--muted);font-weight:600">КЛИЕНТ ВЫБИРАЕТ</div>'
+'<div style="font-size:16px;font-weight:700;color:var(--ink)">'+tt.label+'</div></div></div>'
+'<div style="padding:20px 16px">'
+'<div style="font-size:13px;color:var(--muted);margin-bottom:16px">Как клиент будет определяться с техникой?</div>'
+optCard('👤','Клиент выберет сам','Отправим уведомление в TG-кабинет. Клиент укажет марку, модель или бюджет — мы сразу увидим.','_clientPickDirect(\''+key+'\')')
+optCard('🤖','По рекомендации AI','Мы подберём 3 варианта через AI, отправим клиенту. Клиент выберет один — и сразу попадёт в спецификацию.','_clientPickAI(\''+key+'\')')
+'</div></div>';
}
function _clientPickDirect(key){
var o=window._managerOrders[window._activeOrder];
var t=o.tech.filter(function(x){return x.wkey===key;})[0];
if(t){t.status='waiting_client';t.source='client';}
_toast('📲 Уведомление отправлено в TG-кабинет клиента','var(--success)');
setTimeout(function(){
document.getElementById('screen').innerHTML=renderScreen('manager_order');
document.getElementById('nav').innerHTML=navBar();
},900);
}
function _clientPickAI(key){
window._wiz={type:key,step:0,answers:{},budget:null};
window._wizClientMode=true;
window._wizScreen='manager_order';
document.getElementById('screen').innerHTML=renderScreen('manager_wizard');
}
function _saveResultAsClientPick(){
var o=window._managerOrders[window._activeOrder];
var key=window._wiz.type;
var t=o.tech.filter(function(x){return x.wkey===key;})[0];
if(t){t.status='waiting_client';t.source='client';}
window._wizClientMode=false;
_toast('📲 Варианты отправлены клиенту в TG-кабинет','var(--success)');
setTimeout(function(){
document.getElementById('screen').innerHTML=renderScreen('manager_order');
document.getElementById('nav').innerHTML=navBar();
},900);
}
// ── WIZARD (single-screen filter) ─────────────────────────────────────────────
function screenWizard() {
var wiz=window._wiz;
if(!wiz.type) wiz.type='oven';
var cfg=WIZ[wiz.type];
if(!cfg){wiz.type='oven';cfg=WIZ['oven'];}
// Parameter rows
var params=cfg.steps.map(function(sc){
var sel=wiz.answers[sc.field]||[];
var chips=sc.opts.map(function(o,i){
var s=sel.indexOf(i)>=0;
return '<div class="f-chip'+(s?' sel':'')+'" onclick="_wizToggle(\''+sc.field+'\','+i+','+sc.multi+')">'
+'<span>'+o.e+'</span><span>'+o.l+'</span></div>';
}).join('');
var filled=(sel.length>0);
return '<div class="f-section">'
+'<div style="display:flex;align-items:center;gap:6px"><span class="f-label">'+sc.q+'</span>'
+(filled?'<span style="color:var(--success);font-size:12px">✓</span>':'')
+(sc.multi?'<span style="font-size:10px;color:var(--muted);margin-left:2px">· несколько</span>':'')
+'</div>'
+'<div class="f-row">'+chips+'</div>'
+'</div>';
}).join('');
// Budget row — 3 compact pills
var budgets=Object.keys(cfg.budgets).map(function(k){
var b=cfg.budgets[k];var s=wiz.budget===k;
return '<div class="bud-pill'+(s?' sel':'')+'" onclick="_wizBudget(\''+k+'\')">'
+'<div style="font-size:20px;margin-bottom:3px">'+b.icon+'</div>'
+'<div style="font-size:12px;font-weight:700;color:'+(s?'var(--accent)':'var(--ink)')+'">'+b.label+'</div>'
+'<div style="font-size:10px;color:var(--muted);margin-top:1px">'+b.range+'</div>'
+(s?'<div style="font-size:10px;color:var(--accent);font-weight:600;margin-top:2px">'+b.brands.split(',')[0].trim()+'</div>':'')
+'</div>';
}).join('');
// Filled count for hint
var filledCount=cfg.steps.filter(function(sc){return (wiz.answers[sc.field]||[]).length>0;}).length;
var totalParams=cfg.steps.length;
var allFilled=(filledCount===totalParams)&&(wiz.budget!==null);
var remaining=(totalParams-filledCount)+(wiz.budget===null?1:0);
var hint=allFilled
?'<div style="font-size:11px;color:var(--success);text-align:center;margin-top:6px;font-weight:600">✓ Все параметры заполнены</div>'
:'<div style="font-size:11px;color:var(--muted);text-align:center;margin-top:6px">Осталось выбрать: '+remaining+'</div>';
var bk='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M15 18l-6-6 6-6"/></svg>';
return '<div class="page">'
+'<div class="page-header">'
+'<button class="back-btn" onclick="document.getElementById(\'screen\').innerHTML=renderScreen(window._wizScreen||\'manager_order\')">'+bk+'</button>'
+'<div style="flex:1">'
+'<div style="font-size:11px;color:var(--muted);font-weight:600">AI ПОДБОР</div>'
+'<div style="font-size:16px;font-weight:700;color:var(--ink)">'+cfg.emoji+' '+cfg.name+'</div>'
+'</div>'
+'<button style="background:none;border:none;font-size:11px;font-weight:700;color:var(--accent);cursor:pointer;padding:0 4px" onclick="document.getElementById(\'screen\').innerHTML=renderScreen(\'manager_tech\')">Сменить</button>'
+'</div>'
+'<div style="padding:16px 16px 0">'+params+'</div>'
+'<div style="margin:0 16px;padding:14px;background:var(--card);border-radius:14px;box-shadow:0 2px 8px rgba(0,0,0,.06)">'
+'<div class="f-label" style="margin-bottom:8px">Бюджет</div>'
+'<div style="display:flex;gap:8px">'+budgets+'</div>'
+'</div>'
+'<div style="padding:14px 16px 24px">'
+'<button class="btn-primary'+(allFilled?' btn-confirm':'')+'"'+(allFilled?'':' disabled')+' onclick="document.getElementById(\'screen\').innerHTML=renderScreen(\'manager_result\')">'
+(allFilled?'Показать подборку →':'Выберите параметры')
+'</button>'
+hint
+'</div>'
+'</div>';
}
function _wizToggle(field,idx,multi){
var w=window._wiz;
if(!w.answers[field])w.answers[field]=[];
var a=w.answers[field],p=a.indexOf(idx);
if(multi){if(p>=0)a.splice(p,1);else a.push(idx);}else{w.answers[field]=[idx];}
document.getElementById('screen').innerHTML=renderScreen('manager_wizard');
}
function _wizBudget(k){window._wiz.budget=k;document.getElementById('screen').innerHTML=renderScreen('manager_wizard');}
function _wizNext(){/* legacy no-op */}
function _wizBack(){document.getElementById('screen').innerHTML=renderScreen(window._wizScreen||'manager_order');}
// ── RESULT helpers ────────────────────────────────────────────────────────────
function _parsePrice(str){ return parseInt((str||'0').replace(/\s|₽/g,''),10)||0; }
// Генерируем цены по магазинам — детерминировано от базовой цены
function _storePrices(basePrice, seed) {
var s = seed || 0;
var round100 = function(n){ return Math.round(n/100)*100; };
var dns = basePrice; // DNS — лучшая цена
var market = round100(basePrice * (1.02 + (s%7)*0.003));
var mvideo = round100(basePrice * (1.06 + (s%5)*0.004));
var eldor = round100(basePrice * (1.10 + (s%3)*0.006));
var avg = Math.round((dns+market+mvideo+eldor)/4/100)*100;
return [
{store:'DNS', price:dns, best:true},
{store:'Яндекс.Маркет', price:market, best:false},
{store:'М.Видео', price:mvideo, best:false},
{store:'Эльдорадо', price:eldor, best:false},
].map(function(r){ r.avg=avg; return r; });
}
// Реальные фото товаров — сгенерированы через fal.ai FLUX
var _photoUrls = {
"oven": "https://v3b.fal.media/files/b/0a9b60e0/DHiwcB4hb0AkKPUIa7MBB.jpg",
"cooktop": "https://v3b.fal.media/files/b/0a9b60e0/VW1Qn9FyLYwoqgWx2FqcX.jpg",
"fridge": "https://v3b.fal.media/files/b/0a9b60e1/3_iJPr919KQXbNT989mmQ.jpg",
"hood": "https://v3b.fal.media/files/b/0a9b60e1/R21oGbTs742cCb__ylRIF.jpg",
"dishwasher": "https://v3b.fal.media/files/b/0a9b60e1/9tge43kr0OWBo3ehj0VYl.jpg",
"micro": "https://v3b.fal.media/files/b/0a9b60e1/kdWHKAwKN3uCuNi4JeWF1.jpg",
"coffee": "https://v3b.fal.media/files/b/0a9b60e1/64x78EcsipIQSdRwYT-_j.jpg",
"steamer": "https://v3b.fal.media/files/b/0a9b60e1/KUL5zUpbUFSbedUm-Cx03.jpg",
"freezer": "https://v3b.fal.media/files/b/0a9b60e2/kolWJ2PJK_ePTYLGAcfP0.jpg",
"wine": "https://v3b.fal.media/files/b/0a9b60e2/UAqXcVIP0DJ1o_tfCjH3y.jpg"
};
// Фото товара — реальное изображение, фоллбэк на эмодзи
function _photoBox(emoji, bg1, bg2, typeKey) {
var url = _photoUrls[typeKey];
if (url) {
return '<div style="width:90px;height:90px;border-radius:14px;overflow:hidden;flex-shrink:0;background:#F8FAFC;border:1px solid rgba(0,0,0,.07)">'
+'<img src="'+url+'" alt="" style="width:100%;height:100%;object-fit:cover" onerror="this.parentNode.innerHTML=\'<div style=&quot;width:90px;height:90px;display:flex;align-items:center;justify-content:center;font-size:36px&quot;>'+emoji+'</div>\'">'
+'</div>';
}
var bg = 'linear-gradient(135deg,'+bg1+','+bg2+')';
return '<div style="width:90px;height:90px;border-radius:14px;background:'+bg
+';display:flex;flex-direction:column;align-items:center;justify-content:center;flex-shrink:0">'
+'<div style="font-size:36px;line-height:1">'+emoji+'</div>'
+'</div>';
}
// Цветовая схема фото (фоллбэк)
var _photoColors = {
oven: ['#e0e7ff','#c7d2fe'], cooktop:['#fef3c7','#fde68a'],
fridge: ['#e0f2fe','#bae6fd'], hood: ['#f0fdf4','#bbf7d0'],
dishwasher:['#fff7ed','#fed7aa'], micro: ['#fdf4ff','#f5d0fe'],
coffee: ['#fef9c3','#fde047'], steamer:['#f0fdfa','#99f6e4'],
freezer: ['#eff6ff','#bfdbfe'], wine: ['#fdf2f8','#fbcfe8'],
};
// Шкала оценки цена/качество (15 звёзд)
function _valueScore(budgetKey, modelIdx) {
var base = {base:3.6, mid:4.2, prem:4.7}[budgetKey] || 4.0;
var adj = [0, -0.2, -0.4][modelIdx] || 0;
return Math.min(5, Math.max(1, +(base + adj).toFixed(1)));
}
function _starsHtml(score) {
var full = Math.floor(score), half = (score - full) >= 0.3 ? 1 : 0;
var out = '';
for(var i=0;i<full;i++) out += '<span style="color:#F59E0B">★</span>';
if(half) out += '<span style="color:#F59E0B">½</span>';
for(var j=full+half;j<5;j++) out += '<span style="color:#CBD5E1">★</span>';
return out;
}
// ── RESULT ────────────────────────────────────────────────────────────────────
window._resultCount = window._resultCount || 3;
function _setResultCount(n){ window._resultCount=n; window._selectedModel=undefined; document.getElementById('screen').innerHTML=renderScreen('manager_result'); }
function screenResult() {
var wiz = window._wiz;
var cfg = WIZ[wiz.type||'oven']; if(!cfg) cfg=WIZ['oven'];
var bk = wiz.budget||'mid';
var budget = cfg.budgets[bk];
// Сортируем по отзывам desc, берём N первых
var allModels = ((cfg.models&&cfg.models[bk])||WIZ['oven'].models['mid']).slice();
allModels.sort(function(a,b){ return (b.reviews||0)-(a.reviews||0); });
var cnt = window._resultCount || 3;
var models = allModels.slice(0, cnt);
var colors = _photoColors[wiz.type]||['#e0e7ff','#c7d2fe'];
var mHtml = models.map(function(m, i) {
var basePrice = _parsePrice(m.price);
var prices = _storePrices(basePrice, basePrice % 11 + i*3);
var avg = prices[0].avg;
var saving = avg - basePrice;
var score = _valueScore(bk, i);
var isSel = (window._selectedModel === i);
var rankLabels = ['🥇 Выбор #1','🥈 Альтернатива','🥉 Вариант #3','#4 по отзывам','#5 по отзывам','#6 по отзывам','#7 по отзывам'];
var rank = rankLabels[i] || ('#'+(i+1)+' по отзывам');
// Рейтинг и отзывы
var revCount = m.reviews || 0;
var revLabel = revCount >= 1000 ? (revCount/1000).toFixed(1).replace('.0','')+'K' : revCount;
var ratingColor = m.rating >= 4.7 ? '#15803D' : m.rating >= 4.4 ? '#D97706' : '#64748B';
var ratingHtml = m.rating
? '<div style="display:inline-flex;align-items:center;gap:5px;background:rgba(0,0,0,.04);border-radius:20px;padding:3px 10px 3px 8px;margin-bottom:6px">'
+'<span style="color:#F59E0B;font-size:13px">★</span>'
+'<span style="font-size:13px;font-weight:800;color:'+ratingColor+'">'+m.rating+'</span>'
+'<span style="font-size:11px;color:var(--muted)">·</span>'
+'<span style="font-size:11px;color:var(--muted);font-weight:600">'+revLabel+' отзывов</span>'
+'</div>'
: '';
// Теги
var tags = m.tags.map(function(t){
var bg = t==='Рекомендуем'?'#DCFCE7':t==='Встраиваемая'?'#EEF2FF':'#F1F5F9';
var co = t==='Рекомендуем'?'#15803D':t==='Встраиваемая'?'#4338CA':'#64748B';
return '<span style="display:inline-flex;padding:2px 8px;border-radius:20px;font-size:10px;font-weight:700;background:'+bg+';color:'+co+';margin:2px 3px 2px 0">'+t+'</span>';
}).join('');
// Плюсы и минусы от покупателей
var buyerBlock = '';
if(m.buyerPros || m.buyerCons){
var prosHtml = (m.buyerPros||[]).map(function(p){
return '<div style="display:flex;align-items:flex-start;gap:7px;padding:4px 0">'
+'<span style="color:#15803D;font-size:12px;flex-shrink:0;margin-top:1px">✓</span>'
+'<span style="font-size:12px;color:var(--ink);line-height:1.4">'+p+'</span></div>';
}).join('');
var consHtml = (m.buyerCons||[]).map(function(c){
return '<div style="display:flex;align-items:flex-start;gap:7px;padding:4px 0">'
+'<span style="color:#EF4444;font-size:12px;flex-shrink:0;margin-top:1px">✕</span>'
+'<span style="font-size:12px;color:var(--ink);line-height:1.4">'+c+'</span></div>';
}).join('');
buyerBlock = '<div style="background:var(--bg);border-radius:12px;padding:12px 14px;margin-top:10px">'
+'<div style="font-size:11px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;margin-bottom:8px">💬 Покупатели отмечают</div>'
+prosHtml+consHtml
+'</div>';
}
// Строки прайс-таблицы
var priceRows = prices.map(function(p){
var isBest = p.best;
return '<div style="display:flex;align-items:center;gap:8px;padding:5px 0;border-bottom:1px solid rgba(0,0,0,.05)">'
+'<div style="width:24px;text-align:center;font-size:11px">'+(isBest?'<span style="color:#15803D;font-weight:800">✓</span>':'')+'</div>'
+'<div style="flex:1;font-size:12px;font-weight:'+(isBest?'700':'500')+';color:'+(isBest?'var(--ink)':'var(--muted)')+'">'+p.store+'</div>'
+'<div style="font-size:13px;font-weight:'+(isBest?'800':'500')+';color:'+(isBest?'var(--accent)':'var(--muted)')+'">'+p.price.toLocaleString('ru')+' ₽'+(isBest?'':'')+'</div>'
+(isBest?'<span style="font-size:10px;font-weight:700;background:#DCFCE7;color:#15803D;border-radius:8px;padding:1px 6px">Лучшая</span>':'<div style="width:52px"></div>')
+'</div>';
}).join('');
// Полоска сравнения цен (мин → макс)
var minP = prices[0].price, maxP = prices[prices.length-1].price;
var barPct = 8; // DNS всегда левый край = лучшая
var marketPct = Math.round((prices[1].price-minP)/(maxP-minP)*100)||15;
var mVideoPct = Math.round((prices[2].price-minP)/(maxP-minP)*100)||50;
return '<div class="result-model'+(isSel?' pick':'')+'" onclick="_pickModel('+i+')" style="padding:0;overflow:hidden">'
// Заголовок карточки
+'<div style="padding:14px 14px 10px">'
+'<div style="font-size:10px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;margin-bottom:8px">'+rank+'</div>'
// Фото + название
+'<div style="display:flex;gap:12px;align-items:flex-start">'
+_photoBox(cfg.emoji, colors[0], colors[1], wiz.type)
+'<div style="flex:1;min-width:0">'
+'<div style="font-size:14px;font-weight:700;color:var(--ink);line-height:1.3;margin-bottom:6px">'+m.name+'</div>'
+ratingHtml
+'<div style="font-size:18px;font-weight:900;color:var(--accent);letter-spacing:-.02em">'+m.price+'</div>'
+'<div style="margin-top:6px">'+tags+'</div>'
+'</div></div>'
+buyerBlock
// Оценка ц/к
+'<div style="display:flex;align-items:center;gap:8px;margin-top:10px;padding-top:10px;border-top:1px solid rgba(0,0,0,.06)">'
+'<span style="font-size:11px;font-weight:600;color:var(--muted)">Цена/качество</span>'
+'<span style="font-size:13px">'+_starsHtml(score)+'</span>'
+'<span style="font-size:12px;font-weight:700;color:var(--ink)">'+score+'</span>'
+'</div>'
+'</div>'
// Блок анализа цен
+'<div style="background:var(--bg);padding:12px 14px;border-top:1px solid rgba(0,0,0,.06)">'
+'<div style="font-size:11px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;margin-bottom:8px">📊 Анализ цен на рынке</div>'
// Полоска
+'<div style="position:relative;height:6px;background:#E2E8F0;border-radius:3px;margin-bottom:10px">'
+'<div style="position:absolute;left:'+barPct+'%;width:6px;height:6px;background:#15803D;border-radius:50%;top:0" title="DNS"></div>'
+'<div style="position:absolute;left:'+marketPct+'%;width:6px;height:6px;background:#94A3B8;border-radius:50%;top:0"></div>'
+'<div style="position:absolute;left:'+mVideoPct+'%;width:6px;height:6px;background:#CBD5E1;border-radius:50%;top:0"></div>'
+'<div style="position:absolute;right:4px;width:6px;height:6px;background:#E2E8F0;border-radius:50%;top:0;border:1.5px solid #CBD5E1"></div>'
+'</div>'
+priceRows
// Итог экономии
+(saving>0?'<div style="display:flex;align-items:center;gap:8px;margin-top:8px;background:#DCFCE7;border-radius:10px;padding:8px 12px">'
+'<span style="font-size:16px">💚</span>'
+'<div><div style="font-size:12px;font-weight:700;color:#15803D">Дешевле средней цены рынка на '+saving.toLocaleString('ru')+' ₽</div>'
+'<div style="font-size:11px;color:#166534">Средняя по 4 магазинам: '+avg.toLocaleString('ru')+' ₽</div></div>'
+'</div>':'')
+'</div>'
+'</div>';
}).join('');
var selModel = window._selectedModel!==undefined ? models[window._selectedModel] : null;
var bk2 = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M15 18l-6-6 6-6"/></svg>';
// Пиллы выбора количества вариантов
var maxAvail = allModels.length;
var countPills = [3,5,7].map(function(n){
var active = cnt===n;
var disabled = n > maxAvail;
return '<button onclick="'+(disabled?'':'_setResultCount('+n+')')+'" style="'
+'padding:5px 14px;border-radius:20px;font-size:12px;font-weight:700;cursor:'+(disabled?'default':'pointer')+';'
+'border:none;transition:all .15s;'
+(active
?'background:var(--accent);color:#fff;box-shadow:0 2px 8px rgba(0,62,126,.3)'
:disabled
?'background:#F1F5F9;color:#CBD5E1'
:'background:#F1F5F9;color:var(--muted)'
)+'">'+n+'</button>';
}).join('');
return '<div class="page">'
+'<div class="page-header"><button class="back-btn" onclick="document.getElementById(\'screen\').innerHTML=renderScreen(\'manager_wizard\')">'+bk2+'</button>'
+'<h2>'+cfg.emoji+' '+cfg.name+'</h2>'
+'<span class="badge gray">'+budget.label+'</span></div>'
// Селектор количества вариантов
+'<div style="display:flex;align-items:center;gap:8px;padding:10px 16px 4px;background:var(--card);border-bottom:1px solid rgba(0,0,0,.06)">'
+'<span style="font-size:12px;color:var(--muted);font-weight:600;white-space:nowrap">Вариантов:</span>'
+'<div style="display:flex;gap:6px">'+countPills+'</div>'
+'<span style="font-size:11px;color:var(--muted);margin-left:auto">★ по отзывам</span>'
+'</div>'
+'<div style="padding:12px 16px">'
+'<div style="background:linear-gradient(135deg,#EFF6FF,#DBEAFE);border-radius:12px;padding:12px 14px;margin-bottom:12px">'
+'<div style="font-size:13px;color:#1D4ED8;font-style:italic;font-weight:500">'+cfg.anchor+'</div></div>'
+'<div style="background:var(--card);border-radius:12px;padding:11px 14px;margin-bottom:14px">'
+'<div style="font-size:11px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.04em;margin-bottom:3px">📐 Ниша для проекта</div>'
+'<div style="font-size:14px;font-weight:700;color:var(--ink)">'+cfg.niche+'</div>'
+'<div style="font-size:12px;color:#B45309;margin-top:2px">⚠ '+cfg.note+'</div></div>'
+mHtml
+(selModel
?'<div style="background:#DCFCE7;border-radius:12px;padding:11px 14px;margin:12px 0;display:flex;align-items:center;gap:10px">'
+'<div style="font-size:18px">✅</div>'
+'<div><div style="font-size:13px;font-weight:700;color:#15803D">'+selModel.name+'</div>'
+'<div style="font-size:12px;color:#166534">'+selModel.price+' · Выбрано</div></div>'
+'</div>'
+(window._wizClientMode
?'<button class="btn-primary" style="margin-top:4px" onclick="_saveResultAsClientPick()">📲 Отправить клиенту →</button>'
: window._wizFromNav
?'<button class="btn-primary" style="margin-top:4px" onclick="_showAttachPicker()">📎 Привязать к заказу →</button>'
+'<button class="btn-secondary" onclick="_sendTechToClient()" style="display:flex;align-items:center;justify-content:center;gap:7px;margin-top:8px">'
+'<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><line x1="22" y1="2" x2="11" y2="13"/><polygon points="22 2 15 22 11 13 2 9 22 2"/></svg>'
+'Отправить клиенту</button>'
:'<button class="btn-primary" style="margin-top:4px" onclick="_saveToOrder()">✅ Добавить в спецификацию →</button>'
+'<button class="btn-secondary" onclick="_sendTechToClient()" style="display:flex;align-items:center;justify-content:center;gap:7px;margin-top:8px">'
+'<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><line x1="22" y1="2" x2="11" y2="13"/><polygon points="22 2 15 22 11 13 2 9 22 2"/></svg>'
+'Отправить клиенту</button>')
:'<div style="background:#FEF3C7;border-radius:12px;padding:11px 14px;margin:12px 0;font-size:13px;color:#92400E;font-weight:600">👆 Выберите модель выше</div>'
+'<button class="btn-primary" style="margin-top:4px;opacity:.4" disabled>'+(window._wizClientMode?'📲 Отправить клиенту →':'✅ Добавить в спецификацию →')+'</button>')
+'<button class="btn-secondary" style="margin-top:8px" onclick="document.getElementById(\'screen\').innerHTML=renderScreen(\'manager_order\');document.getElementById(\'nav\').innerHTML=navBar()">Вернуться в заказ</button>'
+'</div></div>';
}
function _pickModel(i){window._selectedModel=i;document.getElementById('screen').innerHTML=renderScreen('manager_result');}
function _saveToOrder(){
var o = window._managerOrders[window._activeOrder];
var wiz = window._wiz;
var cfg = WIZ[wiz.type||'oven']; if(!cfg) cfg=WIZ['oven'];
var bk = wiz.budget||'mid';
var models = cfg.models[bk];
var sel = models[window._selectedModel!==undefined ? window._selectedModel : 0];
// Сохраняем в tech-массив заказа
var ti = (o.tech||[]).findIndex(function(t){ return t.wkey===wiz.type; });
if(ti>=0){
o.tech[ti].status = 'done';
o.tech[ti].brand = sel.name;
o.tech[ti].dims = cfg.niche;
o.tech[ti].source = 'ai';
}
// Снимаем блокер если вся техника закрыта
var waitLeft = (o.tech||[]).filter(function(t){ return t.status==='wait'; }).length;
if(waitLeft===0 && o.blocker && o.blocker.indexOf('Техника')>=0){ o.blocker=null; o.techNote=''; }
_toast('✅ '+sel.name+' сохранён в заказ','var(--success)');
setTimeout(function(){
window._selectedModel=undefined;
document.getElementById('screen').innerHTML=renderScreen('manager_order');
document.getElementById('nav').innerHTML=navBar();
},1200);
}
function _showAttachPicker(){
var wiz = window._wiz;
var cfg = WIZ[wiz.type||'oven']; if(!cfg) cfg=WIZ['oven'];
var bk = wiz.budget||'mid';
var allModels = ((cfg.models&&cfg.models[bk])||[]).slice();
allModels.sort(function(a,b){return (b.reviews||0)-(a.reviews||0);});
var sel = allModels[window._selectedModel!==undefined ? window._selectedModel : 0];
var orders = (window._managerOrders||[]).filter(function(o){return !o.isLead;});
var bk2='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M15 18l-6-6 6-6"/></svg>';
var listHtml = orders.length === 0
? '<div style="padding:24px;text-align:center;color:var(--muted);font-size:13px">Нет активных заказов</div>'
: orders.map(function(o,i){
var idx = window._managerOrders.indexOf(o);
return '<div onclick="_attachToOrder('+idx+')" style="display:flex;align-items:center;gap:12px;padding:12px 16px;'+(i<orders.length-1?'border-bottom:1px solid rgba(0,0,0,.05)':'')+';cursor:pointer;active:background:#f5f5f5">'
+'<div style="width:40px;height:40px;border-radius:12px;background:var(--bg);display:flex;align-items:center;justify-content:center;font-size:18px;flex-shrink:0">🏠</div>'
+'<div style="flex:1;min-width:0">'
+'<div style="font-size:14px;font-weight:600;color:var(--ink)">'+o.client+'</div>'
+'<div style="font-size:11px;color:var(--muted)">'+o.contract+' · '+o.amount.toLocaleString('ru')+' ₽</div>'
+'</div>'
+'<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="var(--muted)" stroke-width="2"><polyline points="9 18 15 12 9 6"/></svg>'
+'</div>';
}).join('');
document.getElementById('screen').innerHTML =
'<div class="page">'
+'<div class="page-header"><button class="back-btn" onclick="document.getElementById(\'screen\').innerHTML=renderScreen(\'manager_result\');document.getElementById(\'nav\').innerHTML=navBar()">'+bk2+'</button><h2>Привязать к заказу</h2></div>'
+'<div style="padding:16px">'
+'<div style="background:var(--card);border-radius:16px;padding:12px 14px;margin-bottom:16px;display:flex;align-items:center;gap:10px">'
+'<div style="font-size:24px">'+cfg.emoji+'</div>'
+'<div><div style="font-size:13px;font-weight:700;color:var(--ink)">'+sel.name+'</div>'
+'<div style="font-size:11px;color:var(--muted)">'+cfg.name+' · '+sel.price+'</div>'
+'</div></div>'
+'<div style="font-size:11px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.06em;margin-bottom:8px">Выберите заказ:</div>'
+'<div style="background:var(--card);border-radius:16px;overflow:hidden">'+listHtml+'</div>'
+'</div></div>';
document.getElementById('nav').innerHTML = navBar();
}
function _attachToOrder(idx){
var o = window._managerOrders[idx];
var wiz = window._wiz;
var cfg = WIZ[wiz.type||'oven']; if(!cfg) cfg=WIZ['oven'];
var bk = wiz.budget||'mid';
var allModels = ((cfg.models&&cfg.models[bk])||[]).slice();
allModels.sort(function(a,b){return (b.reviews||0)-(a.reviews||0);});
var sel = allModels[window._selectedModel!==undefined ? window._selectedModel : 0];
var ti = (o.tech||[]).findIndex(function(t){ return t.wkey===wiz.type; });
if(ti>=0){ o.tech[ti].status='done'; o.tech[ti].brand=sel.name; o.tech[ti].dims=cfg.niche; o.tech[ti].source='ai'; }
var waitLeft = (o.tech||[]).filter(function(t){ return t.status==='wait'; }).length;
if(waitLeft===0 && o.blocker && o.blocker.indexOf('Техника')>=0){ o.blocker=null; o.techNote=''; }
window._activeOrder = idx;
window._wizFromNav = false;
_toast('✅ '+sel.name+' → '+o.client,'var(--success)');
setTimeout(function(){
window._selectedModel = undefined;
document.getElementById('screen').innerHTML = renderScreen('manager_order');
document.getElementById('nav').innerHTML = navBar();
},1200);
}
function _sendTechToClient(){
var wiz = window._wiz;
var cfg = WIZ[wiz.type||'oven']; if(!cfg) cfg=WIZ['oven'];
var bk = wiz.budget||'mid';
var models = cfg.models[bk];
var sel = models[window._selectedModel!==undefined ? window._selectedModel : 0];
var o = window._managerOrders[window._activeOrder]||window._managerOrders[0];
var msg = o.client.split(' ')[0]+', подобрал '+cfg.name.toLowerCase()+' для вашей кухни 👇\n\n'
+cfg.emoji+' '+sel.name+'\n'
+'💰 '+sel.price+'\n'
+'📐 Ниша: '+cfg.niche+'\n'
+'✅ '+sel.pros+'\n\n'
+'Покупаем в DNS — лучшая цена. Что скажете?';
// Показываем превью
document.getElementById('screen').innerHTML = _renderSendPreview(msg, sel);
}
function _renderSendPreview(msg, sel){
var enc = encodeURIComponent(msg);
var o = window._managerOrders[window._activeOrder]||window._managerOrders[0];
var phone = (o.phone||'').replace(/\D/g,'');
var bk2='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M15 18l-6-6 6-6"/></svg>';
return '<div class="page">'
+'<div class="page-header"><button class="back-btn" onclick="document.getElementById(\'screen\').innerHTML=renderScreen(\'manager_result\')">'+bk2+'</button><h2>📤 Отправить клиенту</h2></div>'
+'<div style="padding:16px">'
+'<div style="font-size:12px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;margin-bottom:8px">Сообщение</div>'
+'<div style="background:var(--card);border-radius:14px;padding:14px;font-size:13px;line-height:1.6;color:var(--ink);white-space:pre-wrap;box-shadow:0 2px 10px rgba(0,0,0,.07);margin-bottom:16px">'+msg+'</div>'
+'<a href="https://wa.me/'+phone+'?text='+enc+'" style="display:flex;align-items:center;justify-content:center;gap:8px;background:#25D366;color:#fff;border-radius:13px;padding:14px;font-size:15px;font-weight:700;text-decoration:none;margin-bottom:10px">'
+'<svg width="20" height="20" viewBox="0 0 24 24" fill="#fff"><path d="M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 00-3.48-8.413z"/></svg>'
+'WhatsApp</a>'
+'<button onclick="navigator.clipboard&&navigator.clipboard.writeText('+JSON.stringify(msg)+').then(function(){_toast(\'✅ Скопировано\',\'#003E7E\');})" style="width:100%;background:var(--bg);border:1.5px solid #E2E8F0;border-radius:13px;padding:13px;font-size:14px;font-weight:600;color:var(--ink);cursor:pointer">📋 Скопировать текст</button>'
+'</div></div>';
}
// ── CLIENTS LIST ─────────────────────────────────────────────────────────────
window._clientFilter = window._clientFilter || 'all';
window._clientSearch = window._clientSearch || '';
function screenClientsList() {
var all = window._managerOrders || [];
var filter = window._clientFilter;
var search = (window._clientSearch || '').toLowerCase();
// Фильтрация
var filtered = all.filter(function(o) {
var matchFilter =
filter === 'all' ? true :
filter === 'leads' ? !!o.isLead :
filter === 'active' ? (!o.isLead && o.stage < 7) :
filter === 'done' ? (!o.isLead && o.stage >= 7) : true;
var matchSearch = !search || o.client.toLowerCase().indexOf(search) >= 0
|| (o.contract||'').toLowerCase().indexOf(search) >= 0
|| (o.label||'').toLowerCase().indexOf(search) >= 0;
return matchFilter && matchSearch;
});
var counts = {
all: all.length,
leads: all.filter(function(o){ return !!o.isLead; }).length,
active: all.filter(function(o){ return !o.isLead && o.stage < 7; }).length,
done: all.filter(function(o){ return !o.isLead && o.stage >= 7; }).length,
};
var stageLabel = ['','Замер','Проект','Техника','Технолог','Производство','Сборка','Закрыт'];
var stageColor = ['','#3B82F6','#8B5CF6','#F59E0B','#EF4444','#10B981','#0891B2','#6B7280'];
var leadStageLabel = {new:'Новый',meeting:'Замер',kp:'КП',thinking:'Думает',done:'Договор'};
var typeIcon = {kitchen:'🍳', wardrobe:'🚪', other:'📦'};
// Фильтр-таб
var tabs = [
{key:'all', label:'Все', cnt:counts.all},
{key:'leads', label:'Лиды', cnt:counts.leads},
{key:'active', label:'Активные', cnt:counts.active},
{key:'done', label:'Закрытые', cnt:counts.done},
];
var tabHtml = '<div style="display:flex;gap:6px;padding:10px 16px;overflow-x:auto;scrollbar-width:none">'
+ tabs.map(function(t) {
var active = filter === t.key;
return '<div onclick="window._clientFilter=\''+t.key+'\';_nav(\'manager_client\')" '
+'style="padding:6px 13px;border-radius:20px;font-size:12px;font-weight:700;cursor:pointer;white-space:nowrap;flex-shrink:0;'
+(active ? 'background:var(--accent);color:#fff;' : 'background:var(--card);color:var(--muted);border:1.5px solid rgba(0,0,0,.08);')
+'">'+t.label+(t.cnt > 0 ? ' <span style="opacity:.7">'+t.cnt+'</span>' : '')+'</div>';
}).join('')
+ '</div>';
// Поиск
var searchHtml = '<div style="padding:0 16px 10px">'
+'<div style="background:var(--card);border-radius:12px;border:1.5px solid rgba(0,0,0,.07);display:flex;align-items:center;padding:0 12px;gap:8px">'
+'<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" style="flex-shrink:0;color:var(--muted)"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>'
+'<input id="clientSearch" type="text" placeholder="Поиск по имени, договору…" value="'+window._clientSearch+'" '
+'oninput="window._clientSearch=this.value;_nav(\'manager_client\')" '
+'style="flex:1;border:none;outline:none;font-size:13px;color:var(--ink);background:transparent;padding:10px 0">'
+(window._clientSearch ? '<button onclick="window._clientSearch=\'\';_nav(\'manager_client\')" style="border:none;background:none;color:var(--muted);cursor:pointer;font-size:16px;padding:0">×</button>' : '')
+'</div></div>';
// Список карточек
var listHtml = filtered.length === 0
? '<div style="padding:40px 16px;text-align:center;color:var(--muted);font-size:14px">Клиентов не найдено</div>'
: filtered.map(function(o, i) {
var idx = all.indexOf(o);
var initials = o.client.split(' ').slice(0,2).map(function(w){ return w[0]; }).join('');
var avatarColor = o.isLead ? '#F59E0B' : stageColor[o.stage] || '#3B82F6';
var stageText = o.isLead
? (leadStageLabel[o.leadStage] || 'Лид')
: (stageLabel[o.stage] || '');
var stageCol = o.isLead ? '#F59E0B'
: (o.stage >= 7 ? '#6B7280' : stageColor[o.stage] || '#3B82F6');
var icon = typeIcon[o.type] || '📦';
var hasBlocker = !!o.blocker;
return '<div onclick="window._activeOrder='+idx+';_nav(\'manager_client_card\')" '
+'style="display:flex;align-items:center;gap:12px;padding:12px 16px;border-bottom:1px solid rgba(0,0,0,.05);cursor:pointer;active:background:var(--bg)">'
// Аватар
+'<div style="width:40px;height:40px;border-radius:50%;background:'+avatarColor+';display:flex;align-items:center;justify-content:center;font-size:13px;font-weight:800;color:#fff;flex-shrink:0">'+initials+'</div>'
// Инфо
+'<div style="flex:1;min-width:0">'
+'<div style="display:flex;align-items:center;gap:6px;margin-bottom:2px">'
+'<div style="font-size:14px;font-weight:700;color:var(--ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis">'+o.client+'</div>'
+(hasBlocker ? '<div style="width:6px;height:6px;border-radius:50%;background:var(--danger);flex-shrink:0"></div>' : '')
+'</div>'
+'<div style="font-size:11px;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis">'
+icon+' '+(o.label || o.type)+(o.contract ? ' · '+o.contract : '')
+'</div>'
+'</div>'
// Статус + сумма
+'<div style="text-align:right;flex-shrink:0">'
+'<div style="font-size:11px;font-weight:700;color:'+stageCol+';margin-bottom:3px">'+stageText+'</div>'
+(o.amount ? '<div style="font-size:12px;font-weight:800;color:var(--ink)">'+Math.round(o.amount/1000)+' тыс ₽</div>' : '')
+'</div>'
+'</div>';
}).join('');
return '<div class="page">'
+'<div style="padding:14px 16px 10px;background:var(--card);border-bottom:1px solid rgba(0,0,0,.06)">'
+'<div style="font-size:18px;font-weight:800;color:var(--ink)">Клиенты</div>'
+'<div style="font-size:12px;color:var(--muted);margin-top:1px">'+all.length+' всего · '+counts.active+' активных</div>'
+'</div>'
+ tabHtml
+ searchHtml
+'<div style="background:var(--card);margin:0 16px;border-radius:14px;box-shadow:0 2px 8px rgba(0,0,0,.06);overflow:hidden">'
+ listHtml
+'</div>'
+'</div>';
}
// ── CLIENT CARD ───────────────────────────────────────────────────────────────
function screenClient() {
var o=window._managerOrders[window._activeOrder]||window._managerOrders[0];
var init=o.client.split(' ').slice(0,2).map(function(w){return w[0];}).join('');
var bk='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M15 18l-6-6 6-6"/></svg>';
// ── Воронка-прогресс ──────────────────────────────────────────────────────
var funnelHtml = '';
if (o.isLead) {
var ldSteps = [{id:'new',label:'Новый'},{id:'meeting',label:'Замер'},{id:'kp',label:'КП'},{id:'thinking',label:'Думает'},{id:'done',label:'Договор'}];
var ldIdx = {new:0,meeting:1,kp:2,thinking:3,done:4};
var curLdI = ldIdx[o.leadStage]||0;
funnelHtml = '<div style="background:var(--card);border-radius:16px;padding:14px 16px;margin:0 16px 12px">'
+'<div style="font-size:11px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.06em;margin-bottom:10px">Этап воронки</div>'
+'<div style="display:flex;align-items:center;gap:0">';
ldSteps.forEach(function(st,i){
var done = i < curLdI;
var cur = i === curLdI;
var dotBg = done ? 'var(--accent)' : cur ? 'var(--accent)' : 'rgba(0,0,0,.1)';
var dotBorder = cur ? '3px solid #fff;box-shadow:0 0 0 2px var(--accent)' : 'none';
funnelHtml += '<div style="display:flex;flex-direction:column;align-items:center;flex:1;position:relative">';
if(i>0) funnelHtml += '<div style="position:absolute;top:7px;right:50%;width:100%;height:3px;background:'+(done?'var(--accent)':'rgba(0,0,0,.1)')+';z-index:0"></div>';
funnelHtml += '<div style="width:16px;height:16px;border-radius:50%;background:'+dotBg+';border:'+dotBorder+';position:relative;z-index:1;flex-shrink:0"></div>'
+'<div style="font-size:9px;font-weight:'+(cur?'700':'500')+';color:'+(cur?'var(--accent)':'var(--muted)')+';margin-top:5px;text-align:center;line-height:1.2">'+st.label+'</div>'
+'</div>';
});
funnelHtml += '</div></div>';
} else {
var stSteps = ['Замер','Проект','Техника','Технолог','Произв.','Сборка','Монтаж','Готово'];
var stIcons = ['📐','📝','🔌','🔬','🏭','🔧','🚚','✅'];
var curSt = (o.stage||1) - 1;
funnelHtml = '<div style="background:var(--card);border-radius:16px;padding:14px 16px;margin:0 16px 12px">'
+'<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:10px">'
+'<div style="font-size:11px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.06em">Этап производства</div>'
+'<div style="font-size:12px;font-weight:700;color:var(--accent)">'+stIcons[curSt]+' '+stSteps[curSt]+'</div>'
+'</div>'
+'<div style="height:6px;background:rgba(0,0,0,.08);border-radius:3px;overflow:hidden">'
+'<div style="height:100%;border-radius:3px;background:var(--accent);width:'+Math.round((curSt+1)/8*100)+'%"></div>'
+'</div>'
+'<div style="display:flex;justify-content:space-between;margin-top:5px">'
+'<span style="font-size:10px;color:var(--muted)">Этап '+(curSt+1)+' из 8</span>'
+'<span style="font-size:10px;color:var(--muted)">'+Math.round((curSt+1)/8*100)+'%</span>'
+'</div>'
+'</div>';
}
// ── Быстрые действия ──────────────────────────────────────────────────────
var actHtml = '<div style="display:flex;gap:8px;margin:0 16px 12px">'
+'<button onclick="_toast(\'Звонок: '+o.phone+'\',\'#003E7E\')" style="flex:1;display:flex;flex-direction:column;align-items:center;gap:5px;padding:12px 8px;background:var(--card);border:none;border-radius:14px;cursor:pointer">'
+'<div style="width:36px;height:36px;border-radius:50%;background:#DBEAFE;display:flex;align-items:center;justify-content:center">'
+'<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#1D4ED8" stroke-width="2"><path d="M22 16.92v3a2 2 0 01-2.18 2 19.79 19.79 0 01-8.63-3.07A19.5 19.5 0 013.86 9.81 19.79 19.79 0 01.79 1.18 2 2 0 012.77 0h3a2 2 0 012 1.72c.127.96.361 1.903.7 2.81a2 2 0 01-.45 2.11L6.91 7.74a16 16 0 006.29 6.29l1.1-1.1a2 2 0 012.11-.45c.907.339 1.85.573 2.81.7A2 2 0 0122 16.92z"/></svg>'
+'</div>'
+'<span style="font-size:11px;font-weight:600;color:var(--ink)">Позвонить</span>'
+'</button>'
+'<button onclick="_toast(\'WhatsApp: '+o.phone+'\',\'#16A34A\')" style="flex:1;display:flex;flex-direction:column;align-items:center;gap:5px;padding:12px 8px;background:var(--card);border:none;border-radius:14px;cursor:pointer">'
+'<div style="width:36px;height:36px;border-radius:50%;background:#DCFCE7;display:flex;align-items:center;justify-content:center;font-size:18px">💬</div>'
+'<span style="font-size:11px;font-weight:600;color:var(--ink)">WhatsApp</span>'
+'</button>'
+'<button onclick="_openOrder('+window._activeOrder+')" style="flex:1;display:flex;flex-direction:column;align-items:center;gap:5px;padding:12px 8px;background:var(--card);border:none;border-radius:14px;cursor:pointer">'
+'<div style="width:36px;height:36px;border-radius:50%;background:#EDE9FE;display:flex;align-items:center;justify-content:center">'
+'<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#7C3AED" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>'
+'</div>'
+'<span style="font-size:11px;font-weight:600;color:var(--ink)">Заказ/Лид</span>'
+'</button>'
+'<button onclick="_toast(\'Напоминание создано\',\'#F59E0B\')" style="flex:1;display:flex;flex-direction:column;align-items:center;gap:5px;padding:12px 8px;background:var(--card);border:none;border-radius:14px;cursor:pointer">'
+'<div style="width:36px;height:36px;border-radius:50%;background:#FEF3C7;display:flex;align-items:center;justify-content:center">'
+'<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#D97706" stroke-width="2"><path d="M18 8A6 6 0 006 8c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.73 21a2 2 0 01-3.46 0"/></svg>'
+'</div>'
+'<span style="font-size:11px;font-weight:600;color:var(--ink)">Напомнить</span>'
+'</button>'
+'</div>';
// ── Инфо о клиенте ────────────────────────────────────────────────────────
var infoRows = [
{label:'Телефон', val: o.phone},
{label:'Источник', val: 'Зал'},
{label:'Первый контакт', val: '03.05.2025'},
{label:'Ответственный', val: _MGR_IDENTITY.name},
];
var infoHtml = '<div style="background:var(--card);border-radius:16px;margin:0 16px 12px;overflow:hidden">'
+infoRows.map(function(r,i){
return '<div style="display:flex;justify-content:space-between;align-items:center;padding:11px 14px;'+(i<infoRows.length-1?'border-bottom:1px solid rgba(0,0,0,.05)':'')+'">'
+'<span style="font-size:12px;color:var(--muted)">'+r.label+'</span>'
+'<span style="font-size:13px;font-weight:600;color:var(--ink)">'+r.val+'</span>'
+'</div>';
}).join('')
+'</div>';
// ── Текущий заказ/лид ─────────────────────────────────────────────────────
var orderHtml = '<div style="margin:0 16px 12px">'
+'<div style="font-size:11px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.06em;margin-bottom:8px">Текущий '+(o.isLead?'лид':'заказ')+'</div>'
+'<div onclick="_openOrder('+window._activeOrder+')" style="background:var(--card);border-radius:16px;padding:14px;cursor:pointer;border-left:4px solid var(--accent)">'
+'<div style="display:flex;justify-content:space-between;align-items:flex-start">'
+'<div style="font-size:14px;font-weight:700;color:var(--ink)">'+o.label+'</div>'
+'<span style="font-size:13px;font-weight:700;color:var(--accent)">'+o.amount.toLocaleString('ru')+' ₽</span>'
+'</div>'
+(o.contract?'<div style="font-size:12px;color:var(--muted);margin-top:3px">'+o.contract+'</div>':'')
+(o.blocker?'<div style="margin-top:8px;font-size:11px;font-weight:700;background:#FFFBEB;color:#92400E;padding:5px 10px;border-radius:8px">⚠️ '+o.blocker+'</div>':'')
+'<div style="margin-top:10px;font-size:12px;color:var(--accent);font-weight:600">Открыть →</div>'
+'</div>'
+'</div>';
// ── История ───────────────────────────────────────────────────────────────
var histHtml = '<div style="margin:0 16px 16px">'
+'<div style="font-size:11px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.06em;margin-bottom:8px">История заказов</div>'
+'<div style="background:var(--card);border-radius:16px;padding:14px;display:flex;flex-direction:column;align-items:center;gap:4px">'
+'<div style="font-size:20px">📭</div>'
+'<div style="font-size:13px;color:var(--muted)">Это первый заказ клиента</div>'
+'<div style="font-size:11px;color:var(--muted)">Рекомендуйте — приведёт следующего!</div>'
+'</div>'
+'</div>';
return '<div class="page">'
+'<div class="page-header"><button class="back-btn" onclick="_nav(\'manager_client\')">'+bk+'</button><h2>'+o.client.split(' ')[0]+'</h2></div>'
// Hero
+'<div style="display:flex;flex-direction:column;align-items:center;padding:20px 16px 16px;gap:6px">'
+'<div style="width:72px;height:72px;border-radius:50%;background:linear-gradient(135deg,var(--accent),#1E6BB8);display:flex;align-items:center;justify-content:center;color:#fff;font-size:24px;font-weight:700;box-shadow:0 4px 16px rgba(0,62,126,.3)">'+init+'</div>'
+'<div style="font-size:20px;font-weight:700;color:var(--ink);margin-top:4px">'+o.client+'</div>'
+'<div style="font-size:14px;color:var(--muted)">'+o.phone+'</div>'
+'</div>'
+ actHtml
+ funnelHtml
+ infoHtml
+ orderHtml
+ histHtml
+'</div>';
}
// ── БАЗА ЗНАНИЙ ───────────────────────────────────────────────────────────────
var _KB = [
{ id:'kitchen', icon:'🍳', label:'Кухня', articles:[
{id:'k1', title:'Рулонные шторы на кухню: какой материал не испортит пар и жир', tag:'Материалы', views:1240,
body:'На кухне шторы работают в тяжёлых условиях: пар, брызги, запахи, температурные перепады. Обычная ткань быстро деформируется и желтеет.\n\nЧто выбрать:\n✅ Полиэстер 100% — базовый стандарт. Не впитывает влагу, легко протирается влажной тряпкой, не выгорает\n✅ Screen (скрин) — сетчатая ткань, пропускает воздух, режет яркий свет, не перегревается у плиты\n✅ Блэкаут с влагозащитной пропиткой — если нужно затемнение\n\n❌ Лён и хлопок — красиво, но жирные пятна впитываются намертво, стирка деформирует форму рулона.\n\nЛайфхак для клиента:\nМожно помыть прямо на окне — мыльный раствор и мягкая губка. Снимать не нужно.'},
{id:'k2', title:'Как мыть рулонные шторы на кухне — 3 правила', tag:'Уход', views:640,
body:'Многие клиенты боятся, что рулонные шторы сложно содержать в чистоте на кухне. На самом деле — проще, чем кажется.\n\nПравило 1: не снимать\nБольшинство загрязнений убираются прямо на окне: влажная микрофибра сверху вниз по полотну. Жир — мыльный раствор без агрессивной химии.\n\nПравило 2: температура важна\nГорячая вода деформирует ткань. Только холодная или чуть тёплая. Никакого отбеливателя.\n\nПравило 3: сушить в намотанном состоянии нельзя\nПосле мытья полотно должно высохнуть в опущенном виде. Иначе на рулоне появятся складки — и уже не исправить.\n\nЕсли штора снята — расстелить горизонтально, высушить, потом намотать аккуратно вручную.'},
{id:'k3', title:'Эргономика кухни: розетки и их высоты', tag:'Эргономика', views:2340, source:'https://t.me/ergonomiks/75',
body:'Правильное расположение розеток на кухне — один из ключевых вопросов при проектировании.\n\nОсновные правила:\n🔌 Варочная поверхность: розетка на 5 см от пола в соседней секции (не за плитой)\n🔌 СВЧ и духовой шкаф: в плинтусной зоне или в шкафу над приборами — 140160 см от пола\n🔌 Холодильник: 5 см от пола, в соседней секции\n🔌 Посудомоечная/стиральная машина: 1530 см от пола или плинтусная (5 см)\n🔌 Вытяжка: внутри шкафа над вытяжкой\n🔌 Остров: напольные розетки в колодце\n🔌 Рабочая поверхность: минимум 2 блока на высоте 110 см или 1015 см выше столешницы\n\nВажно:\n❗ Никаких розеток над варочной панелью — пожарная безопасность\n❗ Все розетки в зоне раковины — с защитой от брызг (IP44)\n❗ Отдельная линия на каждый мощный прибор: варочная, духовка, посудомойка'},
{id:'k4', title:'Освещение кухни: нормы и схемы', tag:'Эргономика', views:1890, source:'https://t.me/ergonomiks/90',
body:'Правильное освещение кухни — это три уровня, каждый со своей задачей.\n\n💡 Общее освещение (потолочные светильники):\n— Точечные споты: расстояние 3040 см между светильниками, 2025 см от стены\n— Люстра: не ниже 200 см от пола\n\n💡 Рабочая зона (подсветка столешницы):\n— LED-профиль под верхними шкафами — основной стандарт\n— Расстояние от края фасада: 510 см\n— Температура света: 40004500К (нейтральный белый)\n\n💡 Обеденная зона:\n— Светильник над столом на высоте 7080 см от столешницы\n— Для острова: 7090 см от столешницы\n— Диаметр люстры = 1/2 до 2/3 ширины стола\n\nЧастые ошибки:\n❌ Только один источник света — тени от человека закрывают рабочую поверхность\n❌ Тёплый свет 2700К в рабочей зоне — искажает цвет продуктов\n❌ Споты прямо над столешницей — ослепляют при работе'},
{id:'k5', title:'8 ошибок планировки кухни', tag:'Эргономика', views:3100, source:'https://t.me/ergonomiks/88',
body:'Ошибки планировки кухни, которые обнаруживаются только после ремонта.\n\n❌ Ошибка 1: нарушение рабочего треугольника\nХолодильник, мойка и плита должны образовывать треугольник. Суммарный периметр — 3,66 м. Больше — лишние шаги, меньше — тесно.\n\n❌ Ошибка 2: угловое размещение без карусели\nВ угловом шкафу без механизма теряется 4060% полезного пространства.\n\n❌ Ошибка 3: мойка у окна без учёта солнца\nЛетом восточное окно слепит с утра. Нужны шторы или Screen.\n\n❌ Ошибка 4: дверь холодильника бьёт в соседний шкаф\nПроверяйте сторону открывания до заказа кухни.\n\n❌ Ошибка 5: нет буферных зон\nМежду плитой и мойкой нужно минимум 40 см рабочей поверхности с каждой стороны.\n\n❌ Ошибка 6: слишком узкий проход\nМинимум 90 см для одного человека, 120 см если двое готовят вместе.\n\n❌ Ошибка 7: розетки за мебелью\nСпланируйте расположение электрики до заказа кухни, не после.\n\n❌ Ошибка 8: верхние шкафы на уровне глаз\nНижний край верхних шкафов — 4550 см от столешницы. Ниже — неудобно работать.'},
{id:'k6', title:'Обеденный стол: как выбрать размер и форму', tag:'Эргономика', views:1650, source:'https://t.me/ergonomiks/73',
body:'Выбор обеденного стола — это математика, а не вкусовщина.\n\nСколько места нужно на человека:\n— Минимум: 60×40 см (тарелка и приборы)\n— Комфорт: 6075 см в ширину\n\nФормы и размеры:\n⬛ Квадратный — 24 персоны, от 90 см. Хорошо у стены\n▬ Прямоугольный — 6 персон = 160×90 см. Оптимум для большинства\n⭕ Круглый — 4 персоны = диаметр 110 см. Меньше места в длину\n🥚 Овальный — как прямоугольный, но торжественнее\n\nПроходы вокруг стола:\n— Минимум: 80 см\n— Оптимум (свободный проход): 90110 см\n— Со стулом, который отодвигают: добавьте 50 см\n\nВысоты:\n— Высота стола: 7276 см\n— Высота стула: 4448 см\n— Зазор между сиденьем и столешницей: около 30 см\n— Расстояние между стульями: минимум 20 см'},
]},
{ id:'bedroom', icon:'🛏', label:'Спальня', articles:[
{id:'b1', title:'Блэкаут vs светофильтр: что выбрать для спальни', tag:'Материалы', views:2100,
body:'Клиент хочет «шторы в спальню, чтобы не просыпаться» — первый вопрос: это блэкаут или светофильтр?\n\nБлэкаут:\n— Блокирует 95100% света\n— Для тех, кто чутко спит, работает в ночные смены, для детей\n— Дополнительно сохраняет тепло зимой\n— Минус: утром в комнате — абсолютная темнота\n\nСветофильтр:\n— Гасит яркость, создаёт мягкий рассеянный свет\n— Подходит, когда важна утренняя атмосфера без слепящего солнца\n— «День-ночь» (зебра) — универсальный компромисс: регулируешь сам\n\nКак объяснить клиенту:\nНужно не проснуться в 5 утра — блэкаут. Хочется мягкое утро без ослепления — светофильтр.'},
{id:'b2', title:'День-ночь: объясняем клиенту за 2 минуты', tag:'Клиентам', views:1450,
body:'«День-ночь» или «зебра» — самый частый вопрос. Вот как объяснять просто.\n\nКонструкция: чередующиеся полосы — тёмная (непрозрачная) и светлая (прозрачная). Механизм сдвигает полотно, совмещая нужные полосы.\n\nТри позиции:\n☀ Полосы смещены — свет проходит свободно, как без штор\n🌤 Полосы чередуются — свет рассеивается, уютно без темноты\n🌙 Полосы совмещены — затемнение, как у светофильтра (не 100%)\n\nКому идеально:\n— Гостиная: хочется управлять светом в течение дня\n— Спальня с выходом на восток: утром поднял полосы — и спи дальше\n\nВажно честно: 100% затемнения «зебра» не даёт. Если нужна полная темнота — только блэкаут.'},
{id:'b3', title:'Эргономика спальни: кровати, проходы, планировка', tag:'Эргономика', views:2870, source:'https://t.me/ergonomiks/872',
body:'Ключевые размеры, которые делают спальню удобной.\n\n🛏 Стандарты кроватей:\n— Односпальная: 8090×190200 см\n— Полуторная: 120×190200 см\n— Двуспальная: 140160×190200 см\n— Кинг-сайз: 180200×200 см\n\nПроходы:\n— Вдоль длинной стороны кровати (основной): минимум 60 см, комфорт 90100 см\n— Со стороны изголовья: не нужен, кровать ставят вплотную к стене\n— Между кроватями: минимум 50 см, комфорт 80 см\n\nРасположение кровати:\n✅ Изголовьем к стене без окна и без сквозняка\n✅ Не напротив двери — некомфортно психологически\n✅ Не под кондиционером — сквозняк во сне\n\n5 ошибок планировки спальни:\n❌ Кровать у батареи — жарко, пересушенный воздух\n❌ Дверь шкафа бьёт в кровать — не проверили открывание\n❌ Зеркало напротив кровати — нарушение сна (светоотражение)\n❌ Розеток нет у изголовья — зарядки на полу\n❌ Освещение только потолочное — резкий свет ночью'},
{id:'b4', title:'Освещение спальни: схема и высоты', tag:'Эргономика', views:1420, source:'https://t.me/ergonomiks/878',
body:'Освещение спальни — это сценарии, не просто лампочки.\n\nОсновные сценарии:\n🌙 Ночь/чтение: бра у изголовья или настольные лампы\n🌤 Утро/одевание: равномерный свет по всей комнате\n💡 Гардеробная зона: направленный свет\n\nВысоты установки:\n— Бра у изголовья: 6080 см от поверхности матраса\n— Потолочный светильник: не ниже 200 см от пола\n— Подсветка шкафа-купе: внутри, LED-профиль сверху\n\nТемпература света:\n— Основной свет: 27003000К (тёплый, расслабляющий)\n— Бра для чтения: 30003500К\n\nЧастые ошибки:\n❌ Один выключатель у двери — нет возможности выключить свет лёжа\n✅ Решение: проходные выключатели у двери и у кровати'},
]},
{ id:'living', icon:'🛋', label:'Гостиная', articles:[
{id:'l1', title:'До пола, с лужей или выше плинтуса — как выбрать длину штор', tag:'Монтаж', views:1320,
body:'Длина штор — одна из самых частых ошибок при монтаже. Клиент говорит «нормально» и получает то, что смотрится неправильно.\n\nТри варианта:\n📏 На 1 см выше пола — практично, чисто. Для кухни и детской. Легко мыть пол\n📏 Касается пола (0 см) — нейтральный вариант для большинства интерьеров\n📏 «Лужа» (+1525 см) — дизайнерский приём. Только спальня/гостиная, только тяжёлые ткани\n\nПравило замерщика:\nЗамер — от карниза до пола, строго по вертикали, не от подоконника. Если пол неровный — замерять в трёх точках, брать минимум.\n\nЧастая ошибка: клиент просит «до пола» → монтажник делает 1 см → клиент видит просвет и недоволен. Уточняйте на замере!'},
{id:'l2', title:'Двойные шторы: тюль + портьера — как подобрать правильно', tag:'Конструкции', views:1870,
body:'Двойные шторы — самый популярный запрос для гостиной. Главная ошибка: клиент выбирает ткани отдельно, а вместе они не работают.\n\nПравило сочетания:\nТюль — лёгкий, нейтральный: белый, молочный, слоновая кость. Он не конкурирует с портьерой.\nПортьера — несёт цвет и характер интерьера. Именно её подбирают под стены, мебель, ковёр.\n\nТри ошибки:\n❌ Тюль и портьера одного цвета — пропадает глубина\n❌ Оба рисунчатые — визуальный хаос\n❌ Лёгкая портьера + плотный тюль — конструкция работает наоборот\n\nТехнический момент:\nНа один карниз нужен двойной крюк или двойная труба. Уточняйте на замере: есть карниз или монтаж с нуля?'},
{id:'l3', title:'10 фактов об эргономике гостиной', tag:'Эргономика', views:2450, source:'https://t.me/ergonomiks/891',
body:'Ключевые нормы и цифры для грамотной планировки гостиной.\n\n1⃣ 16 м² — минимальная площадь гостиной (СП 31-107) при наличии других жилых комнат\n\n2⃣ 3,2 м — минимальная ширина гостиной в новостройках\n\n3⃣ TV на высоте 110120 см от пола до центра экрана\n\n4⃣ Расстояние от дивана до телевизора — минимум 180 см (зависит от диагонали)\n\n5⃣ Основной проход: от дивана до стены/мебели — минимум 90 см (120 см для двоих)\n\n6⃣ От спинки дивана до стены — минимум 70 см\n\n7⃣ Люстра — не ниже 200 см от пола\n\n8⃣ Бра над диваном — 150200 см от пола\n\n9В гостиных от 25 м² розетки для TV — через каждые 360 см вдоль стены\n\n🔟 Ковёр в гостиной — не только уют, но и зонирование пространства\n\nЧастая ошибка: диван поставили, места для прохода за ним не осталось — человек протискивается боком.'},
]},
{ id:'bathroom', icon:'🚿', label:'Санузел', articles:[
{id:'ba1', title:'Типовые планировки санузла: базовые схемы', tag:'Эргономика', views:3200, source:'https://t.me/ergonomiks/899',
body:'Санузел состоит из трёх функциональных зон: раковина, унитаз, душевая/ванна. Каждой нужно своё пространство.\n\nМинимальные расстояния:\n— От края унитаза до стены: 1520 см с каждой стороны\n— Перед унитазом: минимум 60 см свободного пространства\n— Перед раковиной: минимум 70 см\n— Перед ванной: минимум 70 см для входа\n\nТипичные размеры санузлов:\n📐 Совмещённый минимум: 1,5×2,2 м (3,3 м²)\n📐 Комфортный совмещённый: 2×2,5 м\n📐 Раздельный туалет: 0,8×1,4 м минимум\n📐 Ванная комната: 1,5×2 м минимум\n\nОптимальные высоты установки:\n— Раковина (верх): 8590 см от пола\n— Унитаз (сиденье): 4045 см от пола\n— Смеситель ванны: 7075 см от пола\n— Душевой смеситель: 100110 см от пола\n— Полотенцесушитель (центр): 130150 см от пола\n— Зеркало (нижний край): 90100 см от пола\n— Розетка (с защитой IP44): 90100 см от пола, не ближе 60 см от воды'},
{id:'ba2', title:'Освещение ванной: ошибки у зеркала и нормы', tag:'Эргономика', views:1780, source:'https://t.me/ergonomiks/179',
body:'Освещение ванной — особый случай, требования к влагозащите и расположению жёсткие.\n\nТребования по IP:\n— IP44 — защита от брызг (разрешено вне зоны душа)\n— IP65 — для зоны прямого попадания воды (над душем)\n— IP20 — только вне ванной комнаты (запрещено в санузле)\n\nОсвещение зеркала (главная ошибка):\n❌ Spotlight сверху — создаёт тени под глазами и носом. Лицо в полумраке\n✅ Подсветка с боков: бра или LED-профиль по бокам зеркала на высоте глаз (165175 см)\n✅ Подсветка снизу зеркала — дополнительный источник\n\nОбщее освещение:\n— Потолочный светильник: не менее 300 Лк для санузла\n— Тёплый свет 27003000К для расслабленной атмосферы\n— Нейтральный 4000К для зоны макияжа/бритья\n\nСценарии:\n💡 Утром: яркий нейтральный свет у зеркала\n🌙 Вечером: тёплый приглушённый — диммер на общем свете\n\nНочная подсветка:\nLED-лента у пола (не ослепляет при ночном походе)'},
{id:'ba3', title:'Эргономика душевой: размеры и безопасность', tag:'Эргономика', views:1560, source:'https://t.me/ergonomiks/784',
body:'Душевая кабина или открытая душевая — ключевые размеры.\n\nМинимальные размеры душевой:\n— 80×80 см — минимум (тесно, для стеснённых планировок)\n— 90×90 см — базовый комфорт\n— 80×120 см — оптимум (можно повернуться, наклониться)\n— 100×100 см и более — просторно\n\nВажные высоты:\n— Душевая лейка (стационарная): 200210 см от пола до верха\n— Смеситель: 100110 см от пола\n— Полка/ниша: 130150 см (зона досягаемости)\n— Порог/поддон: максимум 35 см (безбарьерный — 0 см)\n\nБезопасность:\n✅ Нескользящее покрытие пола обязательно\n✅ Поручень в душевой — особенно для пожилых\n✅ Линейный трап вдоль стены — пол уклоном к одной стороне\n✅ Радиус поворота стеклянной двери — не в сторону унитаза\n\nЧастые ошибки:\n❌ Дверь открывается внутрь — при падении человек блокирует дверь\n❌ Нет ниши для мыла — мыльница стоит на краю, падает\n❌ Смеситель со стороны, куда льётся холодная вода при включении'},
]},
{ id:'hallway', icon:'🚪', label:'Прихожая', articles:[
{id:'h1', title:'10 ключевых фактов об эргономике прихожей', tag:'Эргономика', views:2100, source:'https://t.me/ergonomiks/913',
body:'Прихожая — первое и последнее помещение в маршруте каждый день. Её эргономика критична.\n\n1⃣ Ширина 120150 см — оптимум для снятия верхней одежды перед шкафом\n\n2⃣ Зеркало высотой 180200 см, нижний край — как можно ближе к полу (лучше обзор)\n\n3⃣ Крючки для взрослых: 150170 см от пола. Для детей: 100120 см. Расстояние между крючками: 3040 см\n\n4⃣ Пуф или скамья: высота 4050 см\n\n5⃣ Глубина шкафа для верхней одежды на плечиках: 5865 см (без учёта дверей)\n\n6⃣ Если прихожая узкая — двери в комнаты открываются «от себя», не в прихожую\n\n7⃣ Теплый пол в прихожей — функционирует как сушка для обуви\n\n8⃣ Диаметр напольной вешалки: около 30 см\n\n9⃣ Роутер — лучше в верхней части шкафа прихожей (сигнал по квартире)\n\n🔟 Если есть окно или проникает дневной свет — экономит электричество и улучшает ориентацию в пространстве'},
{id:'h2', title:'Розетки и выключатели в прихожей: схема', tag:'Эргономика', views:1340, source:'https://t.me/ergonomiks/1478',
body:'Электрика в прихожей — чаще всего делается по остаточному принципу. Потом жалеют.\n\nСхема правильного расположения:\n\n🔲 Выключатель общего света:\n— 90 см от уровня пола до центра\n— Минимум 10 см от края дверного проёма\n\n🔲 Розетка 220В (пылесос, сушка обуви):\n— По одной оси с выключателем, на высоте 30 см от пола\n— Удобно для пылесоса без лишнего кабеля\n\n🔲 Домофон:\n— 150 см от пола (стандарт для взрослых)\n— Корректируйте с учётом роста хозяев\n— Питание зависит от типа: проводной или IP\n\n🔲 Проходной выключатель:\n— Если есть переход в холл — выключатель дублируется в холле на 90 см\n\n🔲 Датчик тёплого пола:\n— Оптимально: в одной рамке с выключателем на 90 см (колонка с терморегулятором)\n\n🔲 Роутер:\n— Розетка 220В + слаботочка в верхней части шкафа\n— Чем выше роутер — тем лучше покрытие\n\n❗ Все выключатели в одну рамку — визуально чисто и практично'},
{id:'h3', title:'Параметры шкафа в прихожей: размеры под задачи', tag:'Эргономика', views:1560, source:'https://t.me/ergonomiks/1479',
body:'Шкаф в прихожей — главный инструмент порядка. Разбираем по размерам.\n\n↔ Глубина:\n— 56 см — минимум для курток и плащей на плечиках\n— 6065 см — оптимум для большинства\n— 6570 см — для пуховиков и объёмной зимней одежды\n— 70+ см — для шкафов с раздвижными дверьми (10 см на механизм)\n\n↕ Высота:\n— Стандарт: 200250 см\n— Минимум для штанги: 130 см высота отсека\n— Для длинной одежды (пальто, платья): 190 см отсек\n— Максимальная удобная высота выдвижного ящика: 125 см от пола\n\n↔ Ширина:\n— Зависит от длины стены и количества дверей\n— Двухдверный распашной: обычно около 100 см\n\nЧто спрятать в шкаф (убираем визуальный шум):\n✅ Ящик для мелочей (ключи, карточки)\n✅ Место для домашних тапочек или галошница\n✅ Ящик для шапок, шарфов, перчаток\n✅ Место для пылесоса (если нет кладовой)\n✅ Крючки внутри — для дополнительной одежды'},
]},
{ id:'kids', icon:'🧸', label:'Детская', articles:[
{id:'d1', title:'Безопасные шторы для детской — 5 обязательных условий', tag:'Безопасность', views:1560,
body:'В детской безопасность важнее эстетики. Чеклист для правильного ориентирования клиента.\n\n✅ Нет свисающих шнуров — рулонные и кассетные без цепочки на высоте ребёнка\n✅ Кассетная система закрытого типа — механизм скрыт, нет торчащих деталей\n✅ Блэкаут — дневной сон и ранние подъёмы решены\n✅ Ткань без пропитки формальдегидом — проверяйте сертификат, особенно для детей до 3 лет\n✅ Крепление на раму, не на стену — легко перевесить при перестановке\n\nБонус для продажи:\nБлэкаут в детской = ребёнок спит днём = счастливые родители. Продаёт сам себя.'},
{id:'d2', title:'Блэкаут в детской: дневной сон и ранние подъёмы', tag:'Материалы', views:1230,
body:'Блэкаут в детской — не прихоть, а необходимость. Объясняем родителям почему.\n\nДневной сон: дети до 4 лет спят лучше в полной темноте даже в 12 часов дня. Блэкаут создаёт нужные условия независимо от времени суток.\n\nУтренние подъёмы: летом в 5 утра уже светло. Без блэкаута ребёнок просыпается вместе с солнцем — и будит всю семью.\n\nЧто выбрать:\n— Рулонный блэкаут на кассете — нет шнуров, механизм закрыт\n— Цвета: нейтральные (белый, серый, бежевый) или тематические принты\n— Обязательно: без формальдегидной пропитки, сертификат OEKO-TEX\n\nВ продаже: это один из самых простых аргументов. «Ваш ребёнок будет спать — вы тоже».'},
{id:'d3', title:'Рабочее место школьника: эргономика и размеры', tag:'Эргономика', views:2780, source:'https://t.me/ergonomiks/1482',
body:'Рабочее место ребёнка — не просто стол и стул. Неправильная эргономика = сколиоз и плохое зрение.\n\n📍 Расположение:\n✅ У окна с естественным боковым освещением\n✅ Для правши — свет падает слева, для левши — справа\n✅ Лучшая ориентация окна: юг, юго-восток, восток\n❌ Спиной ко входу — некомфортно психологически\n❌ Столешница-подоконник — жар от радиатора и сквозняк от рамы\n\n🪑 Стул — настраивать под рост:\n— Ноги упираются в пол полностью\n— Бёдра и голени под прямым углом\n— Поверхность стола на уровне диафрагмы\n— Расстояние от края стола до груди: 810 см\n— Эргономичное растущее кресло: колёса блокировать, вращение отключить\n\n🖥 Стол:\n— Высота для первоклассника: 4550 см\n— Ширина: не менее 100 см\n— Глубина: от 60 см\n— Регулируемый наклон столешницы — идеально\n— Обязательна подставка для книг\n\n💡 Освещение:\n— Настольная лампа сбоку от пишущей руки\n— Плафон матовый, свет 4060 Вт эквивалент, LED\n— Лампа с регулировкой высоты и направления\n\n🗄 Хранение:\n— Тумба или стеллаж рядом для учебников, тетрадей, канцелярии\n— Всё в пределах досягаемости сидя'},
{id:'d4', title:'Антропометрия детей: почему размеры меняются', tag:'Эргономика', views:1340, source:'https://t.me/ergonomiks/1481',
body:'Дети — не «уменьшенные взрослые». Их пропорции меняются с каждым годом.\n\nПочему это важно:\nВ детской комнате ребёнок должен доставать до всего сам: открывать дверцы, выдвигать ящики, дотягиваться до полок. Это формирует самостоятельность и безопасность.\n\nКлючевые антропометрические параметры для проектирования:\n📏 Рост — базовый параметр для кроватей, высоты столешниц, расположения полок\n📏 Высота глаз — полки, картины, экраны, освещение\n📏 Досягаемость руки — верхние полки шкафов, крючки, ручки\n📏 Высота сиденья (подколенная высота) — стулья и пуфы\n📏 Ширина плеч — ширина проходов и сидений\n\nПрактические следствия:\n— Крючки в детской: 100120 см от пола (растут — поднимаете)\n— Нижняя полка шкафа: 2030 см от пола (на корточки не приседать)\n— Выключатель в детской: 90 см — доступен с 34 лет\n— Розетки: с защитными крышками обязательно\n— Дверная ручка: 8085 см — доступна с 2 лет\n\n🔄 Мебель «на вырост»:\nРегулируемые столы и стулья — вложение один раз на 57 лет роста'},
{id:'d5', title:'Детский санузел: 5 вариантов решений', tag:'Эргономика', views:1890, source:'https://t.me/ergonomiks/1483',
body:'Санузел рядом с детской — современное решение. Как сделать его удобным и на вырост?\n\n🔵 Вариант 1: Стандартная сантехника + тумба-ступенька\n— Сантехника на взрослых высотах\n— Под раковину и унитаз — деревянная тумба-подножка\n— Плюс: дёшево, легко убрать когда вырастет\n— Минус: ребёнок зависит от тумбы\n\n🔵 Вариант 2: Две раковины рядом\n— Взрослая раковина (85 см от пола) + детская (5055 см)\n— Ребёнок пользуется самостоятельно без табуреток\n— Когда вырастет — детскую убирают, меняют столешницу\n\n🔵 Вариант 3: Детская коллекция сантехники\n— Например: Bagnocucciolo (PONTE GIULIO), Керамин «Кидс»\n— Два набора: до 3 лет и после 6 лет\n— Со временем заменяется на взрослую\n\n🔵 Вариант 4: Трансформируемая детская коллекция\n— Раковина Banditos (VILLEROY & BOCH): откидная подставка под раковиной\n— Ребёнок встаёт на платформу, взрослый её складывает\n— Унитаз с инсталляцией: регулируется по высоте\n\n🔵 Вариант 5: Стенд-подъёмник\n— Пневматический механизм меняет высоту сантехники\n— Максимально функционально, дорого\n\n✅ Детали безопасности:\n— Термостатный смеситель (ограничение температуры)\n— Смеситель с пластиковым корпусом (не обожжёт)\n— Надувные чехлы на кран ванной (защита головы)\n— Крупные кнопки смыва на унитазе\n— Нескользящий коврик обязательно'},
]},
{ id:'office', icon:'💼', label:'Кабинет', articles:[
{id:'o1', title:'Скрин в офис: что такое коэффициент открытости', tag:'Материалы', views:930,
body:'Ткань Screen (скрин) — стандарт для офисов и коммерческих помещений. Клиенты часто не понимают, что значат цифры.\n\nКоэффициент открытости — процент «дырочек» в ткани:\n1% — почти непрозрачная, защита максимальная, улица не видна\n3% — баланс: свет есть, солнце режет, вид сохраняется\n5% — лёгкий фильтр, открытый вид, минимальное затемнение\n10% — почти прозрачная, только рассеивает яркость\n\nКак объяснить клиенту:\n«Экраны компьютеров у окна — берите 13%. Хочется видеть улицу — 5%.»\n\nПлюс скрина перед жалюзи:\nОдно полотно без горизонтальных планок. Пыль не копится. Моется раз в год влажной тряпкой.'},
{id:'o2', title:'Кабинет руководителя: зоны и ключевые размеры', tag:'Эргономика', views:2340, source:'https://t.me/ergonomiks/1493',
body:'Кабинет руководителя — не просто престижный интерьер, это рабочий инструмент. Три функциональные зоны и их параметры.\n\n🖥 Рабочая зона:\n— Расстояние от стола до системы хранения: 110150 см (свободный разворот кресла)\n— Глубина открытых стеллажей и библиотек: 2530 см\n— Глубина шкафов для документов/сейфов: 4565 см\n— Расстояние от стола до стены за спиной: 60120 см\n— Приставные стулья для посетителей: 110150 см в глубину, 3040 см между стульями\n\n🤝 Зона переговоров:\n— Расстояние вокруг стола для совещаний: 5580 см\n— Если площадь ограничена — брифинг-приставка к рабочему столу\n\n🛋 Гостиная зона:\n— Область перед офисным диваном: 4090 см (для журнального столика и прохода)\n— Создаёт атмосферу доверия для неформальных переговоров\n\nВажные параметры:\n❗ Рабочее место у окна — приоритет (естественный свет повышает продуктивность)\n❗ Кресло руководителя — спиной к стене, лицом к двери (психологический комфорт)'},
{id:'o3', title:'Опен-спейс: нормы, размеры, зонирование', tag:'Эргономика', views:1870, source:'https://t.me/ergonomiks/1495',
body:'Открытый офис требует точного соблюдения норм — иначе сотрудники работают в стрессе.\n\nРазмеры столов:\n— Ширина: от 120 см, оптимум 140160 см\n— Глубина: от 60 см, оптимум 7080 см\n\nПлощадь на сотрудника:\n— Минимум по нормам: 4,5 м² при работе с компьютером\n— Комфортная норма: 68 м² на человека\n\nПроходы:\n— Главный (магистральный): минимум 120 см, комфорт 180240 см\n— Между рядами столов: 200240 см (учитывая 6090 см на отодвинутое кресло)\n— От стола до стены (спиной): минимум 100 см\n\nЗонирование:\n— Переговорная: отступ от рабочих мест 200300 см или перегородки от 180 см\n\nМикроклимат:\n— Влажность: 4060%\n— Температура: 2225°С\n— Освещение: 300500 Лк, пульсация не более 5%\n\nЧастые ошибки:\n❌ Стол боком к окну — блики на экране\n❌ Кресло вплотную к стене сзади — нет возможности откинуться\n❌ Переговорная рядом с рабочими местами без звукоизоляции'},
{id:'o4', title:'Домашний кабинет: рабочее место и хранение', tag:'Эргономика', views:1240, source:'https://t.me/ergonomiks/924',
body:'Создать удобный домашний офис — несложно, если правильно расставить приоритеты.\n\nПоследовательность планирования:\n\n1⃣ Анализ хранения\n— Что нужно под рукой (документы текущие, канцелярия)\n— Что в архиве (реже достаётся)\n— Открытое хранение (референсы, книги) vs закрытое (беспорядок)\n\n2⃣ Полки и стеллажи\n— Рабочие материалы: на уровне 60180 см от пола (зона досягаемости сидя)\n— Архивы: выше или ниже\n\n3⃣ Оргтехника\n— Принтер: 7090 см от пола (удобно забирать листы стоя)\n— Монитор: верхний край на уровне глаз сидя\n— Расходники: рядом с принтером\n\n4⃣ Освещение\n— Общий свет: 300500 Лк (нормы для рабочего места)\n— Настольная лампа: обязательно, сбоку от рабочей руки\n— Температура: 4000К (нейтральный, не утомляет глаза)\n\n5⃣ Концепция зонирования\n— Кабинет в спальне: перегородка или стеллаж как разделитель\n— Кабинет в гостиной: за диваном или в нише\n— Отдельный кабинет: дверь с замком — граница рабочего времени'},
]},
{ id:'balcony', icon:'🪟', label:'Балкон', articles:[
{id:'bal1', title:'Вертикальные жалюзи на балкон: монтаж без бурения', tag:'Монтаж', views:870,
body:'Балкон — особый случай: сверлить часто нельзя (аренда, управляющая компания), а шторы нужны.\n\nВарианты крепления без бурения:\n✅ На раму — специальные зажимы или саморезы в профиль окна. Подходит для большинства пластиковых рам\n✅ Натяжной карниз — распорный механизм между стенами. Только для узких проёмов до 2 м\n✅ Клеевые крепления — для лёгких конструкций, не несущих нагрузку\n\nЧто хорошо держится без бурения:\n— Рулонные шторы на раму\n— Вертикальные жалюзи с боковым креплением\n— Плиссе на двойной раме\n\nЧто нельзя без бурения:\nТяжёлые портьеры, карнизы длиннее 2 м, конструкции с автоматикой.'},
]},
];
window._kbRoom = window._kbRoom || 'kitchen';
window._kbSearch = window._kbSearch || '';
window._kbArticle = window._kbArticle || null;
function _openArticle(id){
window._kbArticle = id;
document.getElementById('screen').innerHTML = screenArticle();
document.getElementById('nav').innerHTML = navBar();
}
var _roomGrad = {
kitchen: ['#F97316','#EF4444'],
bedroom: ['#818CF8','#6366F1'],
living: ['#34D399','#059669'],
bathroom:['#22D3EE','#0891B2'],
hallway: ['#94A3B8','#475569'],
kids: ['#F472B6','#A855F7'],
office: ['#60A5FA','#2563EB'],
balcony: ['#86EFAC','#16A34A']
};
function screenArticle(){
var allArticles = _KB.reduce(function(acc,r){ return acc.concat(r.articles.map(function(a){ return Object.assign({},a,{roomLabel:r.label,roomIcon:r.icon,roomId:r.id}); })); }, []);
var a = allArticles.find(function(x){ return x.id === window._kbArticle; });
if(!a) return '<div style="padding:32px;text-align:center;color:var(--muted)">Статья не найдена</div>';
var bk='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M15 18l-6-6 6-6"/></svg>';
var tagColor={'Материалы':'#3B82F6','Ткани':'#8B5CF6','Уход':'#10B981','Конструкции':'#F59E0B','Дизайн':'#EC4899','Безопасность':'#EF4444','Монтаж':'#0EA5E9','Клиентам':'#14B8A6','Автоматика':'#F97316'}[a.tag]||'#64748B';
var grad = _roomGrad[a.roomId]||['#94A3B8','#475569'];
// Hero-баннер
var hero = '<div style="height:180px;background:linear-gradient(135deg,'+grad[0]+' 0%,'+grad[1]+' 100%);'
+'display:flex;align-items:center;justify-content:center;'
+'border-radius:0 0 24px 24px;margin:-0px -0px 20px;position:relative;overflow:hidden;flex-shrink:0">'
+'<div style="position:absolute;inset:0;background:repeating-linear-gradient(135deg,rgba(255,255,255,.06) 0,rgba(255,255,255,.06) 1px,transparent 1px,transparent 18px)"></div>'
+'<div style="position:absolute;right:-20px;bottom:-20px;width:140px;height:140px;border-radius:50%;background:rgba(255,255,255,.08)"></div>'
+'<div style="position:absolute;left:-30px;top:-30px;width:100px;height:100px;border-radius:50%;background:rgba(255,255,255,.06)"></div>'
+'<span style="font-size:80px;position:relative;filter:drop-shadow(0 6px 16px rgba(0,0,0,.25))">'+a.roomIcon+'</span>'
+'</div>';
// Рендер тела: переносы → параграфы
var bodyHtml = a.body.split('\n\n').map(function(para){
var p = para.replace(/\n/g,'<br>');
return '<p style="margin:0 0 14px;font-size:14px;line-height:1.7;color:var(--ink)">'+p+'</p>';
}).join('');
return '<div class="page" style="overflow-y:auto">'
+'<div style="position:relative">'
+ hero
+'<div style="position:absolute;top:12px;left:12px">'
+'<button class="back-btn" onclick="window._kbArticle=null;_nav(\'manager_kb\')" style="background:rgba(255,255,255,.25);backdrop-filter:blur(8px);border:none;border-radius:50%;width:34px;height:34px;display:flex;align-items:center;justify-content:center;cursor:pointer;color:#fff">'+bk+'</button>'
+'</div>'
+'</div>'
+'<div style="padding:0 16px 32px">'
// Мета-строка
+'<div style="display:flex;align-items:center;gap:8px;margin-bottom:10px">'
+'<span style="font-size:10px;font-weight:700;padding:2px 8px;border-radius:20px;background:'+tagColor+'18;color:'+tagColor+'">'+a.tag+'</span>'
+'<span style="font-size:10px;color:var(--muted)">'+a.roomLabel+'</span>'
+'<span style="font-size:10px;color:var(--muted);margin-left:auto">👁 '+a.views.toLocaleString('ru')+'</span>'
+'</div>'
+'<h3 style="font-size:18px;font-weight:700;color:var(--ink);line-height:1.4;margin:0 0 16px">'+a.title+'</h3>'
+'<div style="height:1px;background:rgba(0,0,0,.07);margin-bottom:16px"></div>'
+ bodyHtml
// Кнопка источника в Telegram
+(a.source ? '<a href="'+a.source+'" target="_blank" style="display:flex;align-items:center;justify-content:center;gap:8px;width:100%;background:#229ED9;color:#fff;border:none;border-radius:13px;padding:11px;font-size:13px;font-weight:700;cursor:pointer;text-decoration:none;margin-top:8px;box-sizing:border-box">'
+'<svg width="15" height="15" viewBox="0 0 24 24" fill="#fff"><path d="M12 0C5.373 0 0 5.373 0 12s5.373 12 12 12 12-5.373 12-12S18.627 0 12 0zm5.894 8.221-1.97 9.28c-.145.658-.537.818-1.084.508l-3-2.21-1.447 1.394c-.16.16-.295.295-.605.295l.213-3.053 5.56-5.023c.242-.213-.054-.333-.373-.12l-6.871 4.326-2.962-.924c-.643-.204-.657-.643.136-.953l11.57-4.461c.537-.194 1.006.131.833.941z"/></svg>'
+'Источник: @ergonomiks</a>' : '')
// Кнопка "Отправить клиенту"
+'<button onclick="_toast(\'Ссылка скопирована\',\'#003E7E\')" style="width:100%;background:var(--accent);color:#fff;border:none;border-radius:13px;padding:13px;font-size:14px;font-weight:700;cursor:pointer;display:flex;align-items:center;justify-content:center;gap:8px;margin-top:8px">'
+'<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2.5"><line x1="22" y1="2" x2="11" y2="13"/><polygon points="22 2 15 22 11 13 2 9 22 2"/></svg>'
+'Отправить клиенту</button>'
+'</div></div>';
}
function screenKB(){
var room = _KB.find(function(r){return r.id===window._kbRoom;})||_KB[0];
var search = window._kbSearch.toLowerCase().trim();
var articles= search
? _KB.reduce(function(acc,r){return acc.concat(r.articles.map(function(a){return Object.assign({},a,{room:r.label,roomIcon:r.icon,roomId:r.id});}));}, [])
.filter(function(a){return a.title.toLowerCase().indexOf(search)>=0||a.tag.toLowerCase().indexOf(search)>=0;})
: room.articles;
var roomTabs = '<div style="display:flex;gap:6px;padding:10px 14px 4px;overflow-x:auto;scrollbar-width:none">'
+_KB.map(function(r){
var active = !search && r.id===window._kbRoom;
return '<div onclick="window._kbRoom=\''+r.id+'\';window._kbSearch=\'\';_nav(\'manager_kb\')" style="flex-shrink:0;padding:6px 12px;border-radius:20px;font-size:12px;font-weight:700;cursor:pointer;border:1.5px solid '+(active?'var(--accent)':'rgba(0,0,0,.1)')+';background:'+(active?'var(--accent)':'var(--card)')+';color:'+(active?'#fff':'var(--muted)')+'">'+r.icon+' '+r.label+'</div>';
}).join('')
+'</div>';
var artList = (articles.length===0)
? '<div style="padding:32px;text-align:center;color:var(--muted);font-size:14px">Ничего не найдено</div>'
: articles.map(function(a){
var tagColor = {
'Материалы':'#3B82F6','Ткани':'#8B5CF6','Уход':'#10B981','Конструкции':'#F59E0B',
'Дизайн':'#EC4899','Безопасность':'#EF4444','Монтаж':'#0EA5E9','Клиентам':'#14B8A6','Автоматика':'#F97316'
}[a.tag]||'#64748B';
var rid = a.roomId || room.id;
var grad = _roomGrad[rid]||['#94A3B8','#475569'];
var thumb = '<div style="width:52px;height:52px;border-radius:12px;flex-shrink:0;'
+'background:linear-gradient(135deg,'+grad[0]+','+grad[1]+');'
+'display:flex;align-items:center;justify-content:center;font-size:24px;'
+'box-shadow:0 2px 8px rgba(0,0,0,.12)">'+(a.roomIcon||room.icon)+'</div>';
return '<div style="background:var(--card);margin:0 14px 8px;border-radius:14px;padding:12px 14px;display:flex;align-items:flex-start;gap:12px;cursor:pointer" onclick="_openArticle(\''+a.id+'\')">'
+ thumb
+'<div style="flex:1;min-width:0">'
+'<div style="font-size:13px;font-weight:600;color:var(--ink);line-height:1.4;margin-bottom:5px">'+a.title+'</div>'
+'<div style="display:flex;align-items:center;gap:8px">'
+'<span style="font-size:10px;font-weight:700;padding:2px 8px;border-radius:20px;background:'+tagColor+'18;color:'+tagColor+'">'+a.tag+'</span>'
+(a.room?'<span style="font-size:10px;color:var(--muted)">'+a.roomIcon+' '+a.room+'</span>':'')
+'<span style="font-size:10px;color:var(--muted);margin-left:auto">👁 '+a.views.toLocaleString('ru')+'</span>'
+'</div>'
+'</div>'
+'<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="var(--accent)" stroke-width="2.5" style="flex-shrink:0;margin-top:4px"><polyline points="9 18 15 12 9 6"/></svg>'
+'</div>';
}).join('');
return '<div class="page">'
+'<div class="page-header"><h2>📚 База знаний</h2>'
+'<div style="font-size:11px;color:var(--muted)">ТГ-канал @zov_decor</div>'
+'</div>'
// Поиск
+'<div style="padding:8px 14px 4px">'
+'<div style="display:flex;align-items:center;gap:8px;background:var(--card);border:1.5px solid '+(search?'var(--accent)':'rgba(0,0,0,.09)')+';border-radius:12px;padding:9px 12px">'
+'<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="var(--muted)" stroke-width="2"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>'
+'<input id="kbSearch" type="text" placeholder="Найти статью…" value="'+window._kbSearch+'" oninput="window._kbSearch=this.value;_nav(\'manager_kb\')" style="flex:1;border:none;outline:none;font-size:13px;color:var(--ink);background:transparent">'
+(search?'<button onclick="window._kbSearch=\'\';_nav(\'manager_kb\')" style="border:none;background:none;color:var(--muted);cursor:pointer;font-size:16px;padding:0">×</button>':'')
+'</div>'
+'</div>'
+(!search ? roomTabs : '<div style="padding:8px 14px 4px;font-size:12px;color:var(--muted)">Поиск по всем помещениям — '+articles.length+' статей</div>')
+artList
+'</div>';
}
// ── AI ДОГОВОР ────────────────────────────────────────────────────────────────
window._aiContractResult = window._aiContractResult || null;
window._aiContractLoading = false;
function _aiContractCheck(){
if(window._aiContractLoading) return;
window._aiContractLoading = true;
window._aiContractResult = null;
document.getElementById('screen').innerHTML = renderScreen('manager_order');
// Симуляция анализа
setTimeout(function(){
window._aiContractLoading = false;
window._aiContractResult = {
ok:[
'Номер и дата договора указаны',
'Адрес объекта совпадает с замером',
'Сумма и авансовый порядок прописаны',
'Подписи обеих сторон предусмотрены',
],
warn:[
'Не указан срок устранения дефектов после сборки',
'Гарантийный срок сформулирован расплывчато',
],
err:[
'Отсутствует пункт об ответственности за перенос монтажа',
],
score: 74,
};
document.getElementById('screen').innerHTML = renderScreen('manager_order');
setTimeout(function(){
var el = document.getElementById('aiContractPanel');
if(el) el.scrollIntoView({behavior:'smooth',block:'start'});
}, 100);
}, 1800);
}
function _renderAIContract(r){
if(window._aiContractLoading){
return '<div id="aiContractPanel" style="margin-top:10px;background:rgba(139,92,246,.06);border:1.5px solid rgba(139,92,246,.2);border-radius:12px;padding:14px;text-align:center">'
+'<div style="font-size:13px;font-weight:600;color:#7C3AED">🤖 Анализирую договор…</div>'
+'<div style="font-size:11px;color:var(--muted);margin-top:4px">Обычно 510 секунд</div>'
+'</div>';
}
if(!r) return '';
var scoreColor = r.score>=80?'var(--success)':r.score>=60?'var(--warn)':'var(--danger)';
var items = r.ok.map(function(t){
return '<div style="display:flex;align-items:flex-start;gap:8px;padding:5px 0"><span style="color:var(--success);font-size:13px;flex-shrink:0">✓</span><span style="font-size:12px;color:var(--ink)">'+t+'</span></div>';
}).join('')
+ r.warn.map(function(t){
return '<div style="display:flex;align-items:flex-start;gap:8px;padding:5px 0"><span style="color:var(--warn);font-size:13px;flex-shrink:0">⚠</span><span style="font-size:12px;color:var(--ink)">'+t+'</span></div>';
}).join('')
+ r.err.map(function(t){
return '<div style="display:flex;align-items:flex-start;gap:8px;padding:5px 0"><span style="color:var(--danger);font-size:13px;flex-shrink:0">✕</span><span style="font-size:12px;color:var(--ink)">'+t+'</span></div>';
}).join('');
return '<div id="aiContractPanel" style="margin-top:10px;background:rgba(139,92,246,.05);border:1.5px solid rgba(139,92,246,.2);border-radius:12px;padding:14px">'
+'<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:10px">'
+'<div style="font-size:13px;font-weight:700;color:#7C3AED">🤖 AI проверка договора</div>'
+'<div style="font-size:20px;font-weight:900;color:'+scoreColor+'">'+r.score+'/100</div>'
+'</div>'
+items
+(r.err.length?'<div style="margin-top:10px;padding:9px 12px;background:rgba(239,68,68,.07);border-radius:9px;font-size:12px;color:var(--danger);font-weight:600">Требует доработки перед подписанием</div>':'')
+'<button onclick="window._aiContractResult=null;_nav(\'manager_order\')" style="width:100%;margin-top:10px;padding:8px;border-radius:9px;border:1px solid rgba(139,92,246,.2);background:transparent;color:#7C3AED;font-size:12px;font-weight:600;cursor:pointer">Закрыть</button>'
+'</div>';
}
// ── NAV ───────────────────────────────────────────────────────────────────────
function navBar() {
var s=window._currentScreen||'manager_home';
var isHome = s==='manager_home'||s==='manager_order'||s==='manager_tech'||s==='manager_wizard'||s==='manager_result';
var isClients = s==='manager_client'||s==='manager_client_card'||s==='manager_lead';
var isSched = s==='manager_schedule';
var isCalc = s==='manager_salary'||s==='manager_calc';
var isKB = s==='manager_kb';
function ni(active,onclick,icon,lbl){
return '<div class="nav-item'+(active?' active':'')+'" onclick="'+onclick+'" style="min-width:0;padding:4px 2px">'
+'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="width:20px;height:20px">'+icon+'</svg>'
+'<span style="font-size:9px">'+lbl+'</span></div>';
}
return '<div class="bottom-nav">'
+ni(isHome, "_nav('manager_home')", '<rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/>','Заказы')
+ni(isClients,"_nav('manager_client')", '<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/>','Клиенты')
+ni(isSched, "_nav('manager_schedule')","<rect x='3' y='4' width='18' height='18' rx='2'/><line x1='16' y1='2' x2='16' y2='6'/><line x1='8' y1='2' x2='8' y2='6'/><line x1='3' y1='10' x2='21' y2='10'/>","График")
+ni(isCalc, "_nav('manager_salary')", '<rect x="2" y="3" width="20" height="14" rx="2"/><path d="M8 21h8M12 17v4"/><circle cx="12" cy="10" r="2"/>','Зарплата')
+ni(isKB, "_nav('manager_kb')", '<path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/>','База')
+'</div>';
}
function _nav(id){window._currentScreen=id;document.getElementById('screen').innerHTML=renderScreen(id);document.getElementById('nav').innerHTML=navBar();}
// ── Диалог «Добавить встречу» ─────────────────────────────────────────────────
function _addMeetingModal(mday){
var old=document.getElementById('meetingModal'); if(old) old.remove();
var overlay=document.createElement('div');
overlay.id='meetingModal';
overlay.style.cssText='position:absolute;inset:0;background:rgba(0,0,0,.45);backdrop-filter:blur(3px);z-index:999;display:flex;align-items:flex-end';
var typeOpts=['Замер на объекте','Первичная консультация','Согласование проекта','Встреча с технологом','Приёмка заказа','Прочее']
.map(function(t){return '<option>'+t+'</option>';}).join('');
overlay.innerHTML=
'<div style="width:100%;background:var(--bg);border-radius:24px 24px 0 0;padding:24px 20px 36px;box-shadow:0 -8px 32px rgba(0,0,0,.2)">'
+'<div style="width:40px;height:4px;background:rgba(0,0,0,.15);border-radius:2px;margin:0 auto 20px"></div>'
+'<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:20px">'
+'<h3 style="font-size:17px;font-weight:700;color:var(--ink);margin:0">Новая встреча</h3>'
+'<button onclick="document.getElementById(\'meetingModal\').remove()" style="border:none;background:rgba(0,0,0,.07);border-radius:50%;width:28px;height:28px;font-size:16px;cursor:pointer;color:var(--muted);line-height:1">×</button>'
+'</div>'
+'<div style="font-size:11px;font-weight:700;color:var(--muted);margin-bottom:6px">КЛИЕНТ</div>'
+'<input id="mtgClient" type="text" placeholder="Имя клиента" style="width:100%;box-sizing:border-box;border:1.5px solid rgba(0,0,0,.1);border-radius:12px;padding:11px 14px;font-size:14px;color:var(--ink);background:var(--card);outline:none;margin-bottom:14px;font-family:inherit">'
+'<div style="font-size:11px;font-weight:700;color:var(--muted);margin-bottom:6px">ТИП ВСТРЕЧИ</div>'
+'<select id="mtgType" style="width:100%;box-sizing:border-box;border:1.5px solid rgba(0,0,0,.1);border-radius:12px;padding:11px 14px;font-size:14px;color:var(--ink);background:var(--card);outline:none;margin-bottom:14px;font-family:inherit">'+typeOpts+'</select>'
+'<div style="font-size:11px;font-weight:700;color:var(--muted);margin-bottom:6px">ВРЕМЯ</div>'
+'<input id="mtgTime" type="time" value="12:00" style="width:100%;box-sizing:border-box;border:1.5px solid rgba(0,0,0,.1);border-radius:12px;padding:11px 14px;font-size:14px;color:var(--ink);background:var(--card);outline:none;margin-bottom:22px;font-family:inherit">'
+'<button onclick="_saveMeeting('+mday+')" style="width:100%;background:var(--accent);color:#fff;border:none;border-radius:13px;padding:14px;font-size:15px;font-weight:700;cursor:pointer;margin-bottom:10px">Сохранить</button>'
+'<button onclick="document.getElementById(\'meetingModal\').remove()" style="width:100%;background:transparent;color:var(--muted);border:none;border-radius:13px;padding:10px;font-size:14px;font-weight:600;cursor:pointer">Отмена</button>'
+'</div>';
overlay.addEventListener('click',function(e){ if(e.target===overlay) overlay.remove(); });
document.getElementById('phoneFrame').appendChild(overlay);
setTimeout(function(){ var f=document.getElementById('mtgClient'); if(f) f.focus(); },100);
}
function _saveMeeting(mday){
var client=(document.getElementById('mtgClient').value||'').trim();
var type=document.getElementById('mtgType').value;
var time=document.getElementById('mtgTime').value||'12:00';
if(!client){ var inp=document.getElementById('mtgClient'); inp.style.border='1.5px solid #EF4444'; inp.placeholder='Введите имя клиента'; return; }
var colors={'Замер на объекте':'#3B82F6','Первичная консультация':'#0D9488','Согласование проекта':'#10B981','Встреча с технологом':'#F59E0B','Приёмка заказа':'#8B5CF6','Прочее':'#64748B'};
var wi=mday-19;
if(wi>=0&&wi<=6){ _APPTS[wi].push({time:time,client:client,type:type,order:null,color:colors[type]||'#64748B'}); }
document.getElementById('meetingModal').remove();
window._schedMday=mday;
document.getElementById('screen').innerHTML=renderScreen('manager_schedule');
_toast('Встреча добавлена ✓','#15803D');
}
// ── Toast ─────────────────────────────────────────────────────────────────────
function _toast(msg,color){
var t=document.createElement('div');
t.textContent=msg;
t.style.cssText='position:absolute;bottom:72px;left:50%;transform:translateX(-50%);background:'+(color||'#1F2937')
+';color:#fff;padding:10px 18px;border-radius:12px;font-size:13px;font-weight:600;z-index:9999;white-space:nowrap;box-shadow:0 4px 16px rgba(0,0,0,.25);transition:opacity .3s';
document.getElementById('phoneFrame').appendChild(t);
setTimeout(function(){t.style.opacity='0';setTimeout(function(){t.remove();},300);},2200);
}
// ── Theme / Boot ──────────────────────────────────────────────────────────────
function setTheme(t,btn){
if(t==='zov') document.body.removeAttribute('data-theme');
else document.body.setAttribute('data-theme',t);
document.querySelectorAll('.theme-btn').forEach(function(b){b.classList.remove('active');});
btn.classList.add('active');
}
function go(id){
window._currentScreen=id;
if(id==='manager_wizard'&&!window._wiz.type) window._wiz={type:'oven',step:0,answers:{},budget:null};
if(id==='manager_result'&&!window._wiz.type) window._wiz={type:'oven',step:0,answers:{usage:[0,3],height:[1],clean:[0],fuel:[0]},budget:'mid'};
if(id==='manager_own_tech'){ window._ownTechKey='oven'; window._ownTechMode='choose'; window._ownTechData={brand:'',model:'',dims:''}; }
// Для карточки лида — ставим _activeOrder на первый лид если не установлен
if(id==='manager_lead'){
var o=window._managerOrders[window._activeOrder];
if(!o||!o.isLead){
var fi=window._managerOrders.findIndex(function(x){return x.isLead;});
if(fi>=0) window._activeOrder=fi;
}
}
document.getElementById('screen').innerHTML=renderScreen(id);
document.getElementById('nav').innerHTML=navBar();
document.getElementById('screenSelect').value=id;
}
// Hash navigation — работает без сервера (file://)
var _hashMap={
'#home':'manager_home','#order':'manager_order','#tech':'manager_tech',
'#wizard':'manager_wizard','#schedule':'manager_schedule','#salary':'manager_salary',
'#calc':'manager_calc','#client':'manager_client','#lead':'manager_lead',
'#kb':'manager_kb'
};
var _startScreen = _hashMap[location.hash] || 'manager_home';
go(_startScreen);
window.addEventListener('hashchange',function(){ var s=_hashMap[location.hash]; if(s) go(s); });
</script>
</body>
</html>