publish: все 9 мокапов + обновлённый index со статусами ролей

This commit is contained in:
wasrusgen 2026-05-28 11:25:05 +03:00
parent 2b8a93c016
commit bdbc058801
8 changed files with 13886 additions and 44 deletions

View File

@ -3,62 +3,112 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>@wasrusgen1 CRM — Мокапы</title> <title>@wasrusgen1 CRM — Мокапы кабинетов</title>
<style> <style>
*{box-sizing:border-box;margin:0;padding:0} *{box-sizing:border-box;margin:0;padding:0}
body{font-family:'Inter',sans-serif;background:#F5F6F8;min-height:100vh;padding:40px 20px} body{font-family:'Inter',system-ui,sans-serif;background:#F5F6F8;min-height:100vh;padding:40px 20px}
h1{font-size:22px;font-weight:800;color:#1A1A2E;margin-bottom:4px} .header{max-width:960px;margin:0 auto 32px}
.sub{font-size:13px;color:#8A94A6;margin-bottom:32px} .logo{font-size:13px;font-weight:700;color:#003E7E;margin-bottom:14px;letter-spacing:.02em}
.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));gap:16px;max-width:900px;margin:0 auto}
.card{background:#fff;border-radius:16px;padding:24px;box-shadow:0 1px 4px rgba(0,0,0,.08);transition:box-shadow .15s,transform .15s;text-decoration:none;display:block}
.card:hover{box-shadow:0 4px 16px rgba(0,0,0,.12);transform:translateY(-2px)}
.role{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.06em;color:#8A94A6;margin-bottom:8px}
.title{font-size:17px;font-weight:800;color:#1A1A2E;margin-bottom:6px}
.desc{font-size:12px;color:#8A94A6;line-height:1.5}
.badge{display:inline-block;padding:3px 10px;border-radius:20px;font-size:11px;font-weight:600;margin-top:14px}
.done{background:#F0FDF4;color:#16A34A}
.wip{background:#FFFBEB;color:#D97706}
.soon{background:#F1F5F9;color:#8A94A6}
.header{max-width:900px;margin:0 auto 28px}
.logo{font-size:13px;font-weight:700;color:#003E7E;margin-bottom:16px;display:flex;align-items:center;gap:8px}
.logo span{color:#76BD22} .logo span{color:#76BD22}
h1{font-size:24px;font-weight:800;color:#1A1A2E;margin-bottom:4px}
.sub{font-size:13px;color:#8A94A6}
.section{max-width:960px;margin:0 auto 32px}
.section-title{font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.08em;color:#8A94A6;margin-bottom:12px}
.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(270px,1fr));gap:14px}
a.card{background:#fff;border-radius:16px;padding:22px 20px;box-shadow:0 1px 3px rgba(0,0,0,.07);text-decoration:none;display:block;transition:box-shadow .15s,transform .15s;border-left:4px solid transparent}
a.card:hover{box-shadow:0 6px 20px rgba(0,0,0,.11);transform:translateY(-2px)}
.card.ok {border-left-color:#76BD22}
.card.wip {border-left-color:#F59E0B}
.card.ready{border-left-color:#3B82F6}
.card.dim {border-left-color:#CBD5E1;opacity:.6;pointer-events:none}
.role{font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.07em;color:#8A94A6;margin-bottom:7px}
.title{font-size:16px;font-weight:800;color:#1A1A2E;margin-bottom:5px}
.desc{font-size:12px;color:#8A94A6;line-height:1.55;margin-bottom:14px}
.badge{display:inline-flex;align-items:center;gap:4px;padding:3px 10px;border-radius:20px;font-size:11px;font-weight:600}
.badge.ok {background:#F0FDF4;color:#16A34A}
.badge.wip {background:#FFFBEB;color:#D97706}
.badge.ready{background:#EFF6FF;color:#2563EB}
.badge.dim {background:#F1F5F9;color:#94A3B8}
hr{max-width:960px;margin:0 auto 28px;border:none;border-top:1px solid #E2E8F0}
</style> </style>
</head> </head>
<body> <body>
<div class="header"> <div class="header">
<div class="logo">@wasrusgen1 <span>CRM</span></div> <div class="logo">@wasrusgen1 <span>CRM</span></div>
<h1>Мокапы кабинетов</h1> <h1>Мокапы кабинетов</h1>
<div class="sub">Telegram MiniApp · Мебельный дилер · ИП Васильев Р.Г.</div> <div class="sub">Telegram MiniApp · Мебельный дилер · ИП Васильев Р.Г. · 2 салона</div>
</div> </div>
<div class="grid"> <div class="section">
<a class="card" href="mockup_owner.html"> <div class="section-title">Руководство</div>
<div class="role">Роль 1</div> <div class="grid">
<div class="title">Генеральный директор</div> <a class="card ok" href="mockup_owner.html">
<div class="desc">Выручка по салонам, финансы, команда агрегированно, склад (статус поставок)</div> <div class="role">Роль 1</div>
<div class="badge done">✓ Принято как база</div> <div class="title">Генеральный директор</div>
</a> <div class="desc">Выручка по салонам, финансы план/факт, команда агрегированно, статус поставок с фабрик</div>
<span class="badge ok">✓ Принят как база</span>
<a class="card" href="mockup_commercial.html"> </a>
<div class="role">Роль 2</div> <a class="card wip" href="mockup_commercial.html">
<div class="title">Коммерческий директор</div> <div class="role">Роль 2</div>
<div class="desc">Воронка продаж, заказы, персонал (администраторы), закупки, финансы план/факт</div> <div class="title">Коммерческий директор</div>
<div class="badge wip">В разработке</div> <div class="desc">Воронка продаж, заказы, персонал (администраторы), согласование закупок, финансы</div>
</a> <span class="badge wip">В разработке</span>
</a>
<div class="card" style="opacity:.55;cursor:default"> <a class="card ready" href="mockup_director.html">
<div class="role">Роль 3</div> <div class="role">Роль 3</div>
<div class="title">Директор по сервису</div> <div class="title">Директор по сервису</div>
<div class="desc">Доставки, монтажники, замерщики, рекламации, NPS</div> <div class="desc">Рекламации, сборщики онлайн, экспедиция, замеры, нарушения регламента</div>
<div class="badge soon">Скоро</div> <span class="badge ready">↻ Требует обновления</span>
</div> </a>
<div class="card" style="opacity:.55;cursor:default">
<div class="role">Роль 4</div>
<div class="title">Администратор салона</div>
<div class="desc">Оперативное управление салоном: заказы, менеджеры, закупки, смена</div>
<div class="badge soon">Скоро</div>
</div> </div>
</div> </div>
<hr>
<div class="section">
<div class="section-title">Операционный уровень</div>
<div class="grid">
<a class="card ready" href="mockup_admin.html">
<div class="role">Роль 4</div>
<div class="title">Администратор салона</div>
<div class="desc">Касса смены, приём оплат, инкассация, операции дня, заказы салона</div>
<span class="badge ready">↻ Требует обновления</span>
</a>
<a class="card ready" href="mockup_manager.html">
<div class="role">Роль 5</div>
<div class="title">Менеджер</div>
<div class="desc">График встреч, мои заказы, блокеры, воронка личная, KPI</div>
<span class="badge ready">↻ Требует обновления</span>
</a>
</div>
</div>
<hr>
<div class="section">
<div class="section-title">Полевые роли</div>
<div class="grid">
<a class="card ready" href="mockup_assembler.html">
<div class="role">Роль 6</div>
<div class="title">Сборщик</div>
<div class="desc">Текущая сборка с прогрессом, расписание, загрузка месяца, акт доп. работ</div>
<span class="badge ready">↻ Требует обновления</span>
</a>
<a class="card ready" href="mockup_measurer.html">
<div class="role">Роль 7</div>
<div class="title">Замерщик</div>
<div class="desc">Текущий замер пошагово, расписание на день, схема помещения, статистика</div>
<span class="badge ready">↻ Требует обновления</span>
</a>
<a class="card dim" href="mockup_client.html">
<div class="role">Клиент</div>
<div class="title">Клиентский портал</div>
<div class="desc">Статус заказа, этапы, документы, связь с менеджером</div>
<span class="badge dim">Черновик</span>
</a>
</div>
</div>
</body> </body>
</html> </html>

1289
docs/mockup_admin.html Normal file

File diff suppressed because it is too large Load Diff

1962
docs/mockup_assembler.html Normal file

File diff suppressed because it is too large Load Diff

903
docs/mockup_client.html Normal file
View File

@ -0,0 +1,903 @@
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>@wasrusgen1 CRM — Клиент</title>
<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:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif}
body[data-theme="zov"] {--accent:#003E7E;--accent2:#76BD22;--bg:#F5F6F8;--card:#FFFFFF;--ink:#1A1A2E;--muted:#8A94A6;--danger:#EF4444;--warn:#F59E0B;--success:#10B981}
body[data-theme="radar"] {--accent:#4338CA;--accent2:#6366F1;--bg:#F9FAFB;--card:#FFFFFF;--ink:#111827;--muted:#6B7280;--danger:#EF4444;--warn:#F59E0B;--success:#10B981}
body[data-theme="dark"] {--accent:#4338CA;--accent2:#6366F1;--bg:#111827;--card:#1F2937;--ink:#F9FAFB;--muted:#9CA3AF;--danger:#EF4444;--warn:#F59E0B;--success:#10B981}
#controls{display:flex;align-items:center;gap:12px;margin-bottom:16px;flex-wrap:wrap;justify-content:center;width:100%;max-width:600px}
#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;transform:scale(1.05)}
.theme-btn[data-t="zov"] {background:#003E7E;color:#fff}
.theme-btn[data-t="radar"]{background:#4338CA;color:#fff}
.theme-btn[data-t="dark"] {background:#111827;color:#6366F1}
#phoneWrap{position:relative;width:390px;flex-shrink:0}
#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}
.sb-right{display:flex;align-items:center;gap:6px}
#screen{flex:1;overflow-y:auto;overflow-x:hidden;position:relative;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;position:relative;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{color:var(--accent)}
.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}
.header-action{width:36px;height:36px;border-radius:50%;background:var(--bg);display:flex;align-items:center;justify-content:center;cursor:pointer;border:none}
.header-action svg{color:var(--accent);width:20px;height:20px}
.card{background:var(--card);border-radius:16px;box-shadow:0 2px 12px rgba(0,0,0,.07);padding:16px;margin-bottom:12px}
.card.warn-border{border-left:3px solid var(--warn)}
.card.success-border{border-left:3px solid var(--success)}
.card.accent-border{border-left:3px solid var(--accent)}
.card.danger-border{border-left:3px solid var(--danger)}
.section-label{text-transform:uppercase;font-size:11px;letter-spacing:.06em;color:var(--muted);margin:20px 16px 8px;font-weight:600}
.btn-primary{width:100%;background:var(--accent);color:#fff;border:none;border-radius:12px;padding:14px;font-size:15px;font-weight:600;cursor:pointer;transition:opacity .2s}
.btn-primary:hover{opacity:.9}
.btn-secondary{width:100%;background:transparent;color:var(--accent);border:1.5px solid var(--accent);border-radius:12px;padding:13px;font-size:15px;font-weight:600;cursor:pointer;transition:all .2s;margin-top:8px}
.btn-sm{padding:8px 14px;font-size:13px;font-weight:600;border-radius:9px;cursor:pointer;border:none;background:var(--accent);color:#fff}
.btn-sm.outline{background:transparent;border:1.5px solid var(--accent);color:var(--accent)}
.btn-sm.success{background:var(--success);color:#fff}
.btn-sm.danger{background:var(--danger);color:#fff}
.badge{display:inline-flex;align-items:center;padding:3px 10px;border-radius:20px;font-size:11px;font-weight:700;letter-spacing:.02em}
.badge.blue{background:#DBEAFE;color:#1D4ED8}
.badge.yellow{background:#FEF3C7;color:#D97706}
.badge.green{background:#DCFCE7;color:#15803D}
.badge.red{background:#FEE2E2;color:#DC2626}
.badge.gray{background:#F1F5F9;color:#64748B}
.form-group{margin-bottom:14px}
.form-group label{display:block;font-size:12px;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;margin-bottom:6px}
.form-input{width:100%;padding:12px 14px;border:1.5px solid #E2E8F0;border-radius:12px;font-size:15px;color:var(--ink);background:var(--card);outline:none}
.form-textarea{width:100%;padding:12px 14px;border:1.5px solid #E2E8F0;border-radius:12px;font-size:14px;color:var(--ink);background:var(--card);outline:none;resize:none;min-height:80px}
.row{display:flex;align-items:center;gap:10px}
.row.sb{justify-content:space-between}
.col{display:flex;flex-direction:column;gap:4px}
.avatar{width:40px;height:40px;border-radius:50%;background:var(--accent);display:flex;align-items:center;justify-content:center;color:#fff;font-size:15px;font-weight:700;flex-shrink:0}
.avatar.lg{width:64px;height:64px;font-size:22px}
.avatar.green{background:var(--success)}
.avatar.xl{width:80px;height:80px;font-size:28px}
.progress-bar{height:6px;background:#E2E8F0;border-radius:3px;overflow:hidden;flex:1}
.progress-fill{height:100%;background:var(--accent);border-radius:3px}
.progress-fill.green{background:var(--success)}
.divider{height:1px;background:rgba(0,0,0,.07);margin:8px 0}
.signature-canvas{width:100%;height:180px;background:#F8FAFC;border-radius:12px;border:2px dashed #CBD5E1;display:flex;align-items:center;justify-content:center;cursor:crosshair;position:relative;overflow:hidden}
.signature-canvas span{color:var(--muted);font-size:14px}
.price-item{display:flex;align-items:center;justify-content:space-between;padding:10px 0;border-bottom:1px solid #F1F5F9}
.price-item:last-child{border-bottom:none}
.info-row{display:flex;gap:8px;padding:10px 0;border-bottom:1px solid rgba(0,0,0,.06)}
.info-row:last-child{border-bottom:none}
.info-label{font-size:12px;color:var(--muted);font-weight:500;width:100px;flex-shrink:0}
.info-val{font-size:13px;font-weight:600;color:var(--ink);flex:1}
/* Timeline */
.timeline{padding:0 16px}
.tl-item{display:flex;gap:14px;padding-bottom:0}
.tl-left{display:flex;flex-direction:column;align-items:center;width:36px;flex-shrink:0}
.tl-icon{width:36px;height:36px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:16px;flex-shrink:0;border:2px solid transparent}
.tl-icon.done{background:#DCFCE7;border-color:var(--success)}
.tl-icon.active{background:var(--accent);border-color:var(--accent);box-shadow:0 0 0 4px rgba(0,62,126,.15)}
.tl-icon.pending{background:#F1F5F9;border-color:#E2E8F0}
.tl-line{width:2px;flex:1;min-height:24px;background:#E2E8F0;margin:4px 0}
.tl-line.done{background:var(--success)}
.tl-line.active{background:linear-gradient(to bottom,var(--accent),#E2E8F0)}
.tl-body{flex:1;padding-bottom:24px}
.tl-title{font-size:15px;font-weight:700;color:var(--ink);margin-bottom:3px}
.tl-title.muted{color:var(--muted)}
.tl-desc{font-size:12px;color:var(--muted);line-height:1.5}
.tl-date{font-size:11px;color:var(--muted);margin-top:4px;font-weight:600}
.tl-badge{display:inline-flex;margin-top:6px}
/* Chat bubbles */
.chat-area{padding:12px 16px;display:flex;flex-direction:column;gap:8px}
.bubble-wrap{display:flex;flex-direction:column;max-width:78%}
.bubble-wrap.me{align-self:flex-end;align-items:flex-end}
.bubble-wrap.other{align-self:flex-start;align-items:flex-start}
.bubble{padding:10px 14px;border-radius:18px;font-size:14px;line-height:1.45}
.bubble.me{background:var(--accent);color:#fff;border-bottom-right-radius:4px}
.bubble.other{background:var(--card);color:var(--ink);border-bottom-left-radius:4px;box-shadow:0 1px 4px rgba(0,0,0,.08)}
.bubble-time{font-size:10px;color:var(--muted);margin-top:2px;padding:0 4px}
.chat-sender{font-size:11px;font-weight:700;color:var(--accent);margin-bottom:3px;padding:0 4px}
.chat-input-bar{position:sticky;bottom:60px;background:var(--card);border-top:1px solid rgba(0,0,0,.07);padding:10px 12px;display:flex;align-items:center;gap:8px;z-index:20}
.chat-input{flex:1;padding:10px 14px;border:1.5px solid #E2E8F0;border-radius:22px;font-size:14px;color:var(--ink);background:var(--bg);outline:none}
.chat-send{width:38px;height:38px;border-radius:50%;background:var(--accent);border:none;display:flex;align-items:center;justify-content:center;cursor:pointer;flex-shrink:0}
.chat-send svg{color:#fff;width:16px;height:16px}
/* Stars */
.star-row{display:flex;gap:8px;justify-content:center;margin:12px 0}
.star{font-size:36px;cursor:pointer;transition:transform .15s;filter:grayscale(1) opacity(.4)}
.star.active{filter:none;transform:scale(1.1)}
/* dark fixes */
body[data-theme="dark"] .form-input,body[data-theme="dark"] .form-textarea{background:#374151;border-color:#4B5563;color:#F9FAFB}
body[data-theme="dark"] .page-header{background:#1F2937;border-color:#374151}
body[data-theme="dark"] .bottom-nav{background:rgba(31,41,55,.95);border-color:#374151}
body[data-theme="dark"] #statusBar{background:#1F2937}
body[data-theme="dark"] .back-btn,body[data-theme="dark"] .header-action{background:#374151}
body[data-theme="dark"] .price-item{border-color:#374151}
body[data-theme="dark"] .info-row{border-color:#374151}
body[data-theme="dark"] .progress-bar{background:#374151}
body[data-theme="dark"] .tl-line{background:#374151}
body[data-theme="dark"] .tl-icon.pending{background:#374151;border-color:#4B5563}
body[data-theme="dark"] .bubble.other{background:#374151}
body[data-theme="dark"] .chat-input{background:#374151;border-color:#4B5563;color:#F9FAFB}
body[data-theme="dark"] .chat-input-bar{background:#1F2937;border-color:#374151}
body[data-theme="dark"] .signature-canvas{background:#374151;border-color:#4B5563}
@keyframes slideFade{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}
.page{animation:slideFade .18s ease-out}
</style>
</head>
<body data-theme="zov">
<div id="controls">
<label>Экран:</label>
<select id="screenSelect"></select>
<div id="themeButtons">
<button class="theme-btn active" data-t="zov" onclick="setTheme('zov')">CRM</button>
<button class="theme-btn" data-t="radar" onclick="setTheme('radar')">CRM</button>
<button class="theme-btn" data-t="dark" onclick="setTheme('dark')">Dark</button>
</div>
</div>
<div id="phoneWrap">
<div id="phoneFrame">
<div id="statusBar">
<span>9:41</span>
<div class="sb-right">
<svg width="16" height="12" viewBox="0 0 16 12" fill="currentColor" style="color:var(--ink)"><rect x="0" y="4" width="3" height="8" rx="1"/><rect x="4.5" y="2.5" width="3" height="9.5" rx="1"/><rect x="9" y="0.5" width="3" height="11.5" rx="1"/><rect x="13.5" y="0" width="2.5" height="12" rx="1" opacity=".3"/></svg>
<svg width="15" height="12" viewBox="0 0 15 12" fill="none" stroke="currentColor" stroke-width="1.5" style="color:var(--ink)"><path d="M7.5 3C9.5 3 11.3 3.8 12.7 5.1M2.3 5.1C3.7 3.8 5.5 3 7.5 3"/><path d="M7.5 6.5C8.7 6.5 9.8 7 10.6 7.8M4.4 7.8C5.2 7 6.3 6.5 7.5 6.5"/><circle cx="7.5" cy="10" r="1" fill="currentColor"/></svg>
<svg width="25" height="12" viewBox="0 0 25 12" fill="none" style="color:var(--ink)"><rect x="0.5" y="0.5" width="21" height="11" rx="3" stroke="currentColor" stroke-opacity=".35"/><rect x="1.5" y="1.5" width="17" height="9" rx="2" fill="currentColor"/><path d="M23 4v4a2 2 0 000-4z" fill="currentColor" fill-opacity=".4"/></svg>
</div>
</div>
<div id="screen"></div>
</div>
</div>
<script>
const SCREENS = {
home: 'Главная',
order_status: 'Мой заказ',
assembly_today: 'Сборка сегодня',
extra_act: 'Акт доп. работ',
extra_sign: 'Подпись акта',
extra_signed: 'Акт подписан',
chat: 'Чат с менеджером',
feedback: 'Оценка работы',
feedback_done: 'Спасибо за оценку',
profile: 'Профиль',
history: 'История заказов',
};
let cur = 'home';
const sel = document.getElementById('screenSelect');
Object.entries(SCREENS).forEach(([k,v]) => {
const o = document.createElement('option');
o.value = k; o.textContent = v; sel.appendChild(o);
});
sel.addEventListener('change', e => go(e.target.value));
function go(id) {
cur = id; sel.value = id;
const s = document.getElementById('screen');
s.innerHTML = render(id);
s.scrollTop = 0;
initCanvas();
}
function setTheme(t) {
document.body.dataset.theme = t;
document.querySelectorAll('.theme-btn').forEach(b => b.classList.toggle('active', b.dataset.t === t));
}
function nav(active) {
return `<div class="bottom-nav">
<div class="nav-item ${active==='home'?'active':''}" onclick="go('home')">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 9l9-7 9 7v11a2 2 0 01-2 2H5a2 2 0 01-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>
<span>Главная</span>
</div>
<div class="nav-item ${active==='order_status'?'active':''}" onclick="go('order_status')">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="2" y="7" width="20" height="14" rx="2"/><path d="M16 7V5a2 2 0 00-2-2h-4a2 2 0 00-2 2v2"/><polyline points="12 12 12 16"/><line x1="10" y1="14" x2="14" y2="14"/></svg>
<span>Заказ</span>
</div>
<div class="nav-item ${active==='chat'?'active':''}" onclick="go('chat')">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15a2 2 0 01-2 2H7l-4 4V5a2 2 0 012-2h14a2 2 0 012 2z"/></svg>
<span>Чат</span>
</div>
<div class="nav-item ${active==='feedback'?'active':''}" onclick="go('feedback')">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg>
<span>Оценить</span>
</div>
<div class="nav-item ${active==='profile'?'active':''}" onclick="go('profile')">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M20 21v-2a4 4 0 00-4-4H8a4 4 0 00-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>
<span>Профиль</span>
</div>
</div>`;
}
function render(id) {
switch(id) {
case 'home': return screenHome();
case 'order_status': return screenOrderStatus();
case 'assembly_today': return screenAssemblyToday();
case 'extra_act': return screenExtraAct();
case 'extra_sign': return screenExtraSign();
case 'extra_signed': return screenExtraSigned();
case 'chat': return screenChat();
case 'feedback': return screenFeedback();
case 'feedback_done': return screenFeedbackDone();
case 'profile': return screenProfile();
case 'history': return screenHistory();
default: return '<div style="padding:40px;text-align:center;color:var(--muted)">Экран в разработке</div>';
}
}
/* ─── HOME ─── */
function screenHome() {
return `<div class="page">
<div style="background:linear-gradient(135deg,var(--accent) 0%,var(--accent2) 100%);padding:24px 20px 28px">
<div style="font-size:12px;color:rgba(255,255,255,.65);font-weight:600;text-transform:uppercase;letter-spacing:.05em;margin-bottom:6px">Четверг, 21 мая 2026</div>
<div style="font-size:24px;font-weight:800;color:#fff;margin-bottom:4px">Добрый день, Дмитрий!</div>
<div style="font-size:14px;color:rgba(255,255,255,.75)">Ваша кухня уже в пути</div>
</div>
<!-- next step card -->
<div style="padding:0 16px;margin-top:-16px">
<div class="card accent-border" style="cursor:pointer" onclick="go('assembly_today')">
<div style="display:flex;align-items:center;gap:10px;margin-bottom:10px">
<div style="font-size:28px">🛠️</div>
<div>
<div style="font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.05em;color:var(--muted)">Следующий шаг</div>
<div style="font-size:17px;font-weight:800;color:var(--ink)">Сборка — сегодня</div>
</div>
<span class="badge yellow" style="margin-left:auto">Скоро</span>
</div>
<div style="font-size:14px;color:var(--muted);margin-bottom:12px">📍 ул. Ленина, 45, кв. 12 · Сегодня в <b style="color:var(--ink)">10:00</b></div>
<div style="background:var(--bg);border-radius:10px;padding:10px 12px;display:flex;align-items:center;gap:10px">
<div class="avatar" style="width:34px;height:34px;font-size:13px;background:var(--accent2);color:#fff">АК</div>
<div>
<div style="font-size:13px;font-weight:700;color:var(--ink)">Алексей Кириллов</div>
<div style="font-size:11px;color:var(--muted)">Ваш сборщик · +7 921 456-78-90</div>
</div>
<button class="btn-sm" style="margin-left:auto;padding:6px 12px;font-size:12px" onclick="event.stopPropagation()">Позвонить</button>
</div>
</div>
</div>
<div class="section-label">Статус заказа</div>
<div style="padding:0 16px">
<div class="card" style="cursor:pointer" onclick="go('order_status')">
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:12px">
<div style="font-size:14px;font-weight:700;color:var(--ink)">Заказ #З-2847</div>
<span class="badge blue">Доставлено</span>
</div>
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:6px">
<span style="font-size:12px;color:var(--muted)">Прогресс</span>
<span style="font-size:12px;font-weight:700;color:var(--ink)">Этап 3 из 4</span>
</div>
<div class="progress-bar"><div class="progress-fill" style="width:75%"></div></div>
<div style="display:flex;justify-content:space-between;margin-top:10px;font-size:11px;color:var(--muted)">
<span>Замер</span><span>Производство</span><span>Доставка</span><span style="color:var(--accent);font-weight:700">Сборка</span>
</div>
</div>
</div>
<div class="section-label">Менеджер</div>
<div style="padding:0 16px">
<div class="card" style="display:flex;align-items:center;gap:14px">
<div class="avatar" style="background:var(--accent2);color:#fff;font-size:16px">АС</div>
<div style="flex:1">
<div style="font-size:15px;font-weight:700;color:var(--ink)">Анна Соколова</div>
<div style="font-size:12px;color:var(--muted)">Ваш менеджер · ответит за 5 мин</div>
</div>
<button class="btn-sm" onclick="go('chat')">Написать</button>
</div>
</div>
<div class="section-label">Акт доп. работ</div>
<div style="padding:0 16px">
<div class="card warn-border" style="cursor:pointer" onclick="go('extra_act')">
<div style="display:flex;align-items:center;justify-content:space-between">
<div>
<div style="font-size:14px;font-weight:700;color:var(--ink)">Акт #DA-3f2a</div>
<div style="font-size:12px;color:var(--muted);margin-top:2px">Доставка на этаж · 3 позиции</div>
</div>
<div style="text-align:right">
<div style="font-size:18px;font-weight:800;color:var(--ink)">4 650 ₽</div>
<span class="badge yellow">Ожидает подписи</span>
</div>
</div>
</div>
</div>
${nav('home')}</div>`;
}
/* ─── ORDER STATUS ─── */
function screenOrderStatus() {
const steps = [
{
key:'measure', icon:'📐', title:'Замер', done:true,
desc:'Замер выполнен 05.05.2026',
detail:'Замерщик Николай Петров · 3-комнатная кухня 3,4 м',
date:'05 мая 2026',
badge:null
},
{
key:'production', icon:'🏭', title:'Производство', done:true,
desc:'Изготовление завершено',
detail:'Кухня 3м + обеденный стол, 18 фасадов, МДФ белый матовый',
date:'15 мая 2026',
badge:null
},
{
key:'delivery', icon:'🚚', title:'Доставка', done:true,
desc:'Доставлено на адрес',
detail:'Доставка выполнена · ул. Ленина, 45, кв. 12 · всё привезено',
date:'19 мая 2026',
badge:null
},
{
key:'assembly', icon:'🛠️', title:'Сборка', done:false,
desc:'Сборщик Алексей Кириллов',
detail:'Сегодня в 10:0014:00 · осталось дождаться сборки',
date:'21 мая 2026 · сегодня',
badge:'active'
},
];
return `<div class="page">
<div class="page-header">
<button class="back-btn" onclick="go('home')"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><polyline points="15 18 9 12 15 6"/></svg></button>
<h2>Мой заказ #З-2847</h2>
</div>
<div style="background:linear-gradient(135deg,var(--accent),var(--accent2));padding:16px 20px;display:flex;align-items:center;justify-content:space-between">
<div>
<div style="font-size:11px;color:rgba(255,255,255,.65);font-weight:600;text-transform:uppercase;letter-spacing:.04em">Текущий этап</div>
<div style="font-size:18px;font-weight:800;color:#fff;margin-top:2px">Сборка сегодня</div>
</div>
<div style="background:rgba(255,255,255,.18);border-radius:12px;padding:8px 14px;text-align:center">
<div style="font-size:22px;font-weight:900;color:#fff">3/4</div>
<div style="font-size:10px;color:rgba(255,255,255,.75)">этапа</div>
</div>
</div>
<div class="timeline" style="margin-top:24px">
${steps.map((s, i) => `
<div class="tl-item">
<div class="tl-left">
<div class="tl-icon ${s.done ? 'done' : s.badge === 'active' ? 'active' : 'pending'}">${s.done ? '✅' : s.icon}</div>
${i < steps.length - 1 ? `<div class="tl-line ${s.done ? 'done' : s.badge === 'active' ? 'active' : ''}"></div>` : ''}
</div>
<div class="tl-body">
<div class="tl-title ${!s.done && s.badge !== 'active' ? 'muted' : ''}">${s.title}</div>
<div class="tl-desc">${s.detail}</div>
<div class="tl-date">${s.date}</div>
${s.badge === 'active' ? `<span class="badge yellow tl-badge">Сегодня</span>` : ''}
${s.done && i === 2 ? `<span class="badge green tl-badge">Выполнено</span>` : ''}
</div>
</div>
`).join('')}
</div>
<div style="padding:0 16px 0">
<div class="card" style="background:var(--bg);border:1.5px dashed #CBD5E1">
<div style="font-size:13px;font-weight:700;color:var(--muted);margin-bottom:6px">Состав заказа</div>
<div style="font-size:13px;color:var(--ink);line-height:1.7">
🪑 Кухня 3м — нижние и верхние модули<br>
🍽️ Столешница + врезная мойка<br>
🪵 Обеденный стол 1200 (раскладной)<br>
🪑 Стулья — 4 шт<br>
📦 Пенал 2100
</div>
</div>
<button class="btn-primary" onclick="go('assembly_today')">Подробнее о сборке →</button>
</div>
${nav('order_status')}</div>`;
}
/* ─── ASSEMBLY TODAY ─── */
function screenAssemblyToday() {
return `<div class="page">
<div class="page-header">
<button class="back-btn" onclick="go('home')"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><polyline points="15 18 9 12 15 6"/></svg></button>
<h2>Сборка сегодня</h2>
</div>
<!-- time banner -->
<div style="background:linear-gradient(135deg,var(--accent),var(--accent2));padding:18px 20px;display:flex;align-items:center;gap:14px">
<div style="font-size:36px"></div>
<div>
<div style="font-size:22px;font-weight:900;color:#fff">Сегодня · 10:0014:00</div>
<div style="font-size:13px;color:rgba(255,255,255,.75)">📍 ул. Ленина, 45, кв. 12</div>
</div>
</div>
<!-- assembler card -->
<div style="padding:0 16px;margin-top:-12px">
<div class="card">
<div style="font-size:12px;font-weight:700;text-transform:uppercase;letter-spacing:.05em;color:var(--muted);margin-bottom:12px">Ваш сборщик</div>
<div style="display:flex;align-items:center;gap:16px;margin-bottom:16px">
<div class="avatar xl" style="background:var(--accent);font-size:26px;font-weight:800;color:#fff">АК</div>
<div>
<div style="font-size:20px;font-weight:800;color:var(--ink)">Алексей Кириллов</div>
<div style="font-size:13px;color:var(--muted);margin-top:2px">Опыт 5 лет · 340 сборок</div>
<div style="display:flex;gap:6px;margin-top:8px">
<span class="badge green">Рейтинг 4.9 ⭐</span>
<span class="badge blue">Кухни</span>
</div>
</div>
</div>
<a href="tel:+79214567890" style="text-decoration:none">
<button class="btn-primary" style="background:var(--success);font-size:16px;padding:16px">
📞 Позвонить · +7 921 456-78-90
</button>
</a>
<button class="btn-secondary" onclick="go('chat')" style="margin-top:8px">💬 Написать в чат</button>
</div>
</div>
<div class="section-label">Что будет собрано</div>
<div style="padding:0 16px">
<div class="card">
${[
{icon:'🔧', name:'Кухня 3м — нижние модули', sub:'6 секций, петли, доводчики'},
{icon:'🔧', name:'Кухня 3м — верхние шкафы', sub:'5 секций'},
{icon:'🔧', name:'Столешница + мойка', sub:'Врезка, силикон'},
{icon:'🔧', name:'Пенал 2100', sub:'1 шт'},
{icon:'🔧', name:'Обеденный стол 1200', sub:'Раскладной механизм'},
{icon:'🔧', name:'Стулья — 4 шт', sub:'Ножки, болты'},
].map(i => `
<div style="display:flex;align-items:center;gap:12px;padding:10px 0;border-bottom:1px solid rgba(0,0,0,.06)">
<div style="font-size:20px;width:32px;text-align:center">${i.icon}</div>
<div>
<div style="font-size:14px;font-weight:600;color:var(--ink)">${i.name}</div>
<div style="font-size:12px;color:var(--muted)">${i.sub}</div>
</div>
</div>
`).join('')}
</div>
<div class="card" style="background:rgba(16,185,129,.07);border:1.5px solid rgba(16,185,129,.25)">
<div style="font-size:13px;color:#065F46;line-height:1.6">
✅ Убедитесь, что вы дома к <b>10:00</b><br>
✅ Освободите место на кухне<br>
✅ Сборщик свяжется за 30 мин до прибытия
</div>
</div>
</div>
${nav('order_status')}</div>`;
}
/* ─── EXTRA ACT ─── */
function screenExtraAct() {
const items = [
{name:'Доставка на этаж', qty:3, unit:'этаж', price:350},
{name:'Врезка замка', qty:2, unit:'шт', price:800},
{name:'Установка доводчиков', qty:4, unit:'шт', price:250},
];
const total = items.reduce((s,i)=>s+i.qty*i.price,0);
return `<div class="page">
<div class="page-header">
<button class="back-btn" onclick="go('home')"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><polyline points="15 18 9 12 15 6"/></svg></button>
<h2>Акт доп. работ</h2>
</div>
<!-- big sum banner -->
<div style="background:var(--card);padding:20px;text-align:center;border-bottom:1px solid rgba(0,0,0,.06)">
<div style="font-size:12px;color:var(--muted);font-weight:600;text-transform:uppercase;letter-spacing:.05em;margin-bottom:6px">Сумма к оплате</div>
<div style="font-size:48px;font-weight:900;color:var(--ink);line-height:1">${total.toLocaleString('ru')} ₽</div>
<div style="font-size:13px;color:var(--muted);margin-top:6px">Акт #DA-3f2a · ${items.length} позиции</div>
</div>
<div style="padding:16px">
<div class="card">
<div style="font-size:13px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;margin-bottom:12px">Состав работ</div>
${items.map(i => `
<div class="price-item">
<div style="flex:1">
<div style="font-size:15px;font-weight:600;color:var(--ink)">${i.name}</div>
<div style="font-size:12px;color:var(--muted)">${i.qty} ${i.unit} × ${i.price} ₽</div>
</div>
<div style="font-size:16px;font-weight:800;color:var(--ink)">${(i.qty*i.price).toLocaleString('ru')} ₽</div>
</div>
`).join('')}
<div style="display:flex;justify-content:space-between;align-items:center;padding-top:14px;margin-top:4px;border-top:2px solid rgba(0,0,0,.08)">
<span style="font-size:15px;font-weight:700;color:var(--ink)">Итого</span>
<span style="font-size:22px;font-weight:900;color:var(--accent)">${total.toLocaleString('ru')} ₽</span>
</div>
</div>
<div class="card" style="background:var(--bg)">
<div style="font-size:12px;color:var(--muted);margin-bottom:4px;font-weight:600">Пояснение сборщика</div>
<div style="font-size:14px;color:var(--ink);line-height:1.5">Лифт не работал — доставка по лестнице. Дополнительно врезан замок на пенал. Установлены 4 доводчика на шкафы.</div>
</div>
<div style="display:flex;gap:10px;margin-top:4px">
<button class="btn-primary" style="background:var(--success)" onclick="go('extra_sign')">✅ Согласен</button>
<button class="btn-sm danger" style="flex:1;border-radius:12px;padding:14px;font-size:15px" onclick="go('chat')">Оспорить</button>
</div>
<div style="text-align:center;margin-top:10px;font-size:12px;color:var(--muted)">
Нажимая «Согласен», вы подтверждаете выполнение работ
</div>
</div>
${nav('order_status')}</div>`;
}
/* ─── EXTRA SIGN ─── */
function screenExtraSign() {
return `<div class="page">
<div class="page-header">
<button class="back-btn" onclick="go('extra_act')"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><polyline points="15 18 9 12 15 6"/></svg></button>
<h2>Подпись акта</h2>
</div>
<div style="padding:16px">
<div class="card" style="text-align:center;margin-bottom:16px">
<div style="font-size:32px;margin-bottom:8px">📋</div>
<div style="font-size:18px;font-weight:800;color:var(--ink);margin-bottom:4px">Акт доп. работ #DA-3f2a</div>
<div style="font-size:28px;font-weight:900;color:var(--accent);margin:8px 0">4 650 ₽</div>
<div style="font-size:13px;color:var(--muted);line-height:1.5">
Доставка на этаж, врезка замка,<br>установка доводчиков
</div>
<div style="margin-top:12px;padding-top:12px;border-top:1px solid rgba(0,0,0,.06)">
<div class="info-row" style="padding:6px 0"><span class="info-label">Клиент</span><span class="info-val">Иванов Дмитрий</span></div>
<div class="info-row" style="padding:6px 0"><span class="info-label">Дата</span><span class="info-val">21.05.2026</span></div>
<div class="info-row" style="padding:6px 0"><span class="info-label">Сборщик</span><span class="info-val">Алексей Кириллов</span></div>
</div>
</div>
<div style="font-size:14px;font-weight:700;color:var(--ink);margin-bottom:8px">Ваша подпись</div>
<canvas id="sigCanvas" width="358" height="160" style="width:100%;height:160px;background:#F8FAFC;border-radius:12px;border:2px dashed #CBD5E1;cursor:crosshair;touch-action:none;display:block"></canvas>
<div style="display:flex;justify-content:space-between;margin-top:8px">
<span style="font-size:12px;color:var(--muted)">Нарисуйте подпись пальцем</span>
<button class="btn-sm outline" style="font-size:12px;padding:5px 12px" onclick="clearSig()">Очистить</button>
</div>
<div style="margin-top:14px;padding:12px;background:rgba(0,0,0,.03);border-radius:12px;font-size:12px;color:var(--muted);line-height:1.5">
Подписывая акт, вы подтверждаете выполнение дополнительных работ и согласие с указанной стоимостью.
</div>
<div style="margin-top:16px">
<button class="btn-primary" onclick="go('extra_signed')">✅ Подписать и отправить</button>
</div>
</div>
${nav('order_status')}</div>`;
}
/* ─── EXTRA SIGNED ─── */
function screenExtraSigned() {
return `<div class="page">
<div style="padding:80px 24px 24px;text-align:center">
<div style="width:96px;height:96px;background:#DCFCE7;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:48px;margin:0 auto 24px"></div>
<div style="font-size:28px;font-weight:900;color:var(--ink);margin-bottom:10px">Акт подписан!</div>
<div style="font-size:15px;color:var(--muted);line-height:1.6;margin-bottom:32px">
Акт #DA-3f2a на сумму <b style="color:var(--ink)">4 650 ₽</b><br>успешно подписан и отправлен менеджеру
</div>
<div class="card" style="text-align:left;margin-bottom:28px">
<div class="info-row"><span class="info-label">Акт</span><span class="info-val">#DA-3f2a</span></div>
<div class="info-row"><span class="info-label">Клиент</span><span class="info-val">Иванов Дмитрий</span></div>
<div class="info-row"><span class="info-label">Сумма</span><span class="info-val" style="color:var(--success);font-weight:800">4 650 ₽</span></div>
<div class="info-row"><span class="info-label">Подписан</span><span class="info-val">21.05.2026, 11:42</span></div>
<div class="info-row"><span class="info-label">Статус</span><span class="info-val"><span class="badge green">Подписан</span></span></div>
</div>
<button class="btn-primary" onclick="go('feedback')">Оценить работу сборщика →</button>
<button class="btn-secondary" onclick="go('home')">На главную</button>
</div>
${nav('order_status')}</div>`;
}
/* ─── CHAT ─── */
function screenChat() {
const msgs = [
{me:false, name:'Анна Соколова', text:'Дмитрий, добрый день! Ваша кухня уже доставлена. Сборщик Алексей подъедет к 10:00. Всё в порядке?', time:'9:05'},
{me:true, text:'Добрый день! Да, всё хорошо. Жду. А лифт у нас не работает, он в курсе?', time:'9:12'},
{me:false, name:'Анна Соколова', text:'Предупредила Алексея. Он оформит акт на доставку по лестнице — нужно будет подписать. Ориентировочная сумма 1 050 ₽.', time:'9:14'},
{me:true, text:'Понял, спасибо за предупреждение!', time:'9:15'},
];
return `<div class="page">
<div class="page-header">
<h2>Чат с менеджером</h2>
<div style="display:flex;align-items:center;gap:8px">
<div class="avatar" style="width:30px;height:30px;font-size:12px;background:var(--accent2)">АС</div>
<div>
<div style="font-size:12px;font-weight:700;color:var(--ink)">Анна Соколова</div>
<div style="font-size:10px;color:var(--success)">● онлайн</div>
</div>
</div>
</div>
<div style="background:var(--bg);padding:8px 16px;font-size:11px;color:var(--muted);text-align:center;border-bottom:1px solid rgba(0,0,0,.05)">
Сегодня, 21 мая
</div>
<div class="chat-area">
${msgs.map(m => `
<div class="bubble-wrap ${m.me?'me':'other'}">
${!m.me ? `<div class="chat-sender">${m.name}</div>` : ''}
<div class="bubble ${m.me?'me':'other'}">${m.text}</div>
<div class="bubble-time">${m.time}</div>
</div>
`).join('')}
</div>
<div class="chat-input-bar">
<input class="chat-input" placeholder="Написать сообщение...">
<button class="chat-send">
<svg 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>
${nav('chat')}</div>`;
}
/* ─── FEEDBACK ─── */
function screenFeedback() {
return `<div class="page">
<div class="page-header">
<h2>Оценить работу</h2>
</div>
<div style="padding:24px 16px">
<!-- assembler -->
<div style="text-align:center;margin-bottom:24px">
<div class="avatar xl" style="margin:0 auto 12px;background:var(--accent);font-size:28px;font-weight:800">АК</div>
<div style="font-size:20px;font-weight:800;color:var(--ink)">Алексей Кириллов</div>
<div style="font-size:13px;color:var(--muted);margin-top:4px">Сборщик · Сборка #З-2847</div>
</div>
<div class="card" style="text-align:center">
<div style="font-size:15px;font-weight:700;color:var(--ink);margin-bottom:4px">Как оцените работу?</div>
<div style="font-size:13px;color:var(--muted);margin-bottom:12px">Нажмите на звезду</div>
<div class="star-row" id="starRow">
<span class="star active" data-v="1" onclick="rateStar(1)"></span>
<span class="star active" data-v="2" onclick="rateStar(2)"></span>
<span class="star active" data-v="3" onclick="rateStar(3)"></span>
<span class="star active" data-v="4" onclick="rateStar(4)"></span>
<span class="star" data-v="5" onclick="rateStar(5)"></span>
</div>
<div id="rateLabel" style="font-size:14px;font-weight:700;color:var(--accent);margin-bottom:4px">Хорошо</div>
</div>
<div class="form-group" style="margin-top:4px">
<label>Ваш отзыв</label>
<textarea class="form-textarea" rows="4" placeholder="Расскажите о работе сборщика...">Всё собрано аккуратно и быстро. Алексей предупредил про нюансы с петлями на пенале, объяснил как регулировать фасады. Очень доволен!</textarea>
</div>
<div class="form-group">
<label>Качество сборки</label>
<div style="display:flex;gap:8px;flex-wrap:wrap">
${['Аккуратно','Быстро','Без мусора','Вежливый','Объяснил нюансы'].map((t,i) => `
<div style="padding:7px 14px;border-radius:20px;font-size:13px;font-weight:600;cursor:pointer;border:1.5px solid ${i<3?'var(--accent)':'#E2E8F0'};background:${i<3?'var(--accent)':'transparent'};color:${i<3?'#fff':'var(--muted)'}">${t}</div>
`).join('')}
</div>
</div>
<button class="btn-primary" style="background:var(--success)" onclick="go('feedback_done')">Отправить оценку</button>
</div>
${nav('feedback')}</div>`;
}
/* ─── FEEDBACK DONE ─── */
function screenFeedbackDone() {
return `<div class="page">
<div style="padding:80px 24px 24px;text-align:center">
<div style="width:96px;height:96px;background:#FEF3C7;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:48px;margin:0 auto 24px"></div>
<div style="font-size:28px;font-weight:900;color:var(--ink);margin-bottom:10px">Спасибо за оценку!</div>
<div style="font-size:15px;color:var(--muted);line-height:1.6;margin-bottom:32px">
Ваш отзыв об Алексее Кириллове<br>уже отправлен. Рады, что вам понравилось!
</div>
<div class="card" style="display:flex;align-items:center;gap:16px;text-align:left;margin-bottom:28px">
<div class="avatar" style="background:var(--accent);font-size:18px;font-weight:800">АК</div>
<div>
<div style="font-size:15px;font-weight:700;color:var(--ink)">Алексей Кириллов</div>
<div style="font-size:24px;margin-top:2px">⭐⭐⭐⭐</div>
<div style="font-size:12px;color:var(--muted);margin-top:2px">Ваша оценка принята</div>
</div>
</div>
<div style="background:rgba(16,185,129,.08);border-radius:16px;padding:16px;margin-bottom:24px">
<div style="font-size:14px;color:#065F46;font-weight:600;margin-bottom:4px">Ваш заказ выполнен!</div>
<div style="font-size:13px;color:#047857;line-height:1.5">Кухня собрана. Если понадобится гарантийный сервис — пишите нам.</div>
</div>
<button class="btn-primary" onclick="go('home')">На главную</button>
</div>
${nav('feedback')}</div>`;
}
/* ─── PROFILE ─── */
function screenProfile() {
return `<div class="page">
<div class="page-header">
<h2>Профиль</h2>
</div>
<div style="padding:20px 16px 0">
<!-- hero -->
<div style="display:flex;align-items:center;gap:16px;margin-bottom:20px">
<div class="avatar xl" style="background:var(--accent);font-size:26px;font-weight:800">ИД</div>
<div>
<div style="font-size:22px;font-weight:900;color:var(--ink)">Иванов Дмитрий</div>
<div style="font-size:14px;color:var(--muted);margin-top:2px">+7 921 234-56-78</div>
<div style="margin-top:8px"><span class="badge green">Клиент</span></div>
</div>
</div>
<!-- contacts -->
<div class="card">
<div style="font-size:13px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;margin-bottom:12px">Контактные данные</div>
<div class="info-row"><span class="info-label">Имя</span><span class="info-val">Иванов Дмитрий Александрович</span></div>
<div class="info-row"><span class="info-label">Телефон</span><span class="info-val" style="color:var(--accent)">+7 921 234-56-78</span></div>
<div class="info-row"><span class="info-label">Email</span><span class="info-val">ivanov.d@mail.ru</span></div>
</div>
<!-- addresses -->
<div class="card">
<div style="font-size:13px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;margin-bottom:12px">Адреса</div>
<div class="info-row">
<span class="info-label">Основной</span>
<div style="flex:1">
<div class="info-val">ул. Ленина, 45, кв. 12</div>
<div style="font-size:11px;color:var(--muted);margin-top:2px">Санкт-Петербург, 190031</div>
</div>
</div>
<div class="info-row">
<span class="info-label">Дача</span>
<div style="flex:1">
<div class="info-val">пос. Заречный, ул. Садовая 7</div>
<div style="font-size:11px;color:var(--muted);margin-top:2px">Лен. область</div>
</div>
</div>
</div>
<!-- order history -->
<div style="display:flex;align-items:center;justify-content:space-between;margin:16px 0 8px">
<div style="font-size:11px;font-weight:700;letter-spacing:.06em;color:var(--muted);text-transform:uppercase">Мои заказы</div>
<button onclick="go('history')" style="background:none;border:none;font-size:13px;font-weight:700;color:var(--accent);cursor:pointer">Все</button>
</div>
${[
{id:'З-2847', name:'Кухня 3м + стол', date:'21.05.2026', badge:'yellow', label:'В процессе'},
{id:'З-2104', name:'Шкаф-купе 3-дв.', date:'14.02.2026', badge:'green', label:'Завершён'},
{id:'З-1831', name:'Детская мебель', date:'08.11.2025', badge:'green', label:'Завершён'},
].map(o => `
<div class="card" style="cursor:pointer">
<div style="display:flex;align-items:center;justify-content:space-between">
<div>
<div style="font-size:14px;font-weight:700;color:var(--ink)">${o.name}</div>
<div style="font-size:12px;color:var(--muted);margin-top:2px">Заказ #${o.id} · ${o.date}</div>
</div>
<span class="badge ${o.badge}">${o.label}</span>
</div>
</div>
`).join('')}
<button class="btn-secondary" style="margin-bottom:8px">🚪 Выйти</button>
</div>
${nav('profile')}</div>`;
}
/* ─── HISTORY ─── */
function screenHistory() {
const bk = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M15 18l-6-6 6-6"/></svg>';
const orders = [
{id:'З-2847', icon:'🍳', name:'Кухня 3м + обеденный стол', date:'21.05.2026', badge:'yellow', label:'В процессе', price:'128 500 ₽', step:'3 из 4'},
{id:'З-2104', icon:'🚪', name:'Шкаф-купе трёхдверный', date:'14.02.2026', badge:'green', label:'Завершён', price:'47 800 ₽', step:'4 из 4'},
{id:'З-1831', icon:'🧸', name:'Детская мебель — полный комплект', date:'08.11.2025', badge:'green', label:'Завершён', price:'89 200 ₽', step:'4 из 4'},
{id:'З-1245', icon:'🛏️', name:'Спальня: кровать + 2 тумбы', date:'03.06.2025', badge:'green', label:'Завершён', price:'62 400 ₽', step:'4 из 4'},
];
return `<div class="page">
<div class="page-header">
<button class="back-btn" onclick="go('profile')">${bk}</button>
<h2>История заказов</h2>
</div>
<div style="padding:12px 16px 4px">
<div style="background:var(--card);border-radius:14px;padding:14px 16px;display:flex;box-shadow:0 2px 12px rgba(0,0,0,.07)">
<div style="flex:1;text-align:center;border-right:1px solid rgba(0,0,0,.07)">
<div style="font-size:22px;font-weight:800;color:var(--ink)">4</div>
<div style="font-size:11px;color:var(--muted);margin-top:2px">Заказа</div>
</div>
<div style="flex:1;text-align:center;border-right:1px solid rgba(0,0,0,.07)">
<div style="font-size:22px;font-weight:800;color:var(--success)">3</div>
<div style="font-size:11px;color:var(--muted);margin-top:2px">Завершено</div>
</div>
<div style="flex:1;text-align:center">
<div style="font-size:20px;font-weight:800;color:var(--warn)">328 ₽тыс</div>
<div style="font-size:11px;color:var(--muted);margin-top:2px">Всего</div>
</div>
</div>
</div>
<div class="section-label">Все заказы</div>
<div style="padding:0 16px">
${orders.map(o => `
<div class="card ${o.badge==='yellow'?'accent-border':'success-border'}" onclick="go('order_status')" style="cursor:pointer">
<div style="display:flex;align-items:flex-start;gap:12px">
<div style="font-size:30px;line-height:1.1">${o.icon}</div>
<div style="flex:1">
<div style="display:flex;align-items:flex-start;justify-content:space-between;gap:6px;margin-bottom:5px">
<div style="font-size:14px;font-weight:700;color:var(--ink);flex:1">${o.name}</div>
<span class="badge ${o.badge}">${o.label}</span>
</div>
<div style="font-size:12px;color:var(--muted)">Заказ #${o.id} · ${o.date}</div>
<div style="display:flex;align-items:center;justify-content:space-between;margin-top:6px">
<span style="font-size:14px;font-weight:800;color:var(--accent)">${o.price}</span>
<span style="font-size:11px;color:var(--muted)">Этап ${o.step}</span>
</div>
</div>
</div>
</div>
`).join('')}
</div>
${nav('profile')}</div>`;
}
/* ─── Canvas signature ─── */
function initCanvas() {
const c = document.getElementById('sigCanvas');
if (!c) return;
const ctx = c.getContext('2d');
let drawing = false;
ctx.strokeStyle = '#003E7E';
ctx.lineWidth = 2.5;
ctx.lineCap = 'round';
function pos(e) {
const r = c.getBoundingClientRect();
const src = e.touches ? e.touches[0] : e;
return [(src.clientX - r.left) * (c.width / r.width), (src.clientY - r.top) * (c.height / r.height)];
}
c.addEventListener('mousedown', e => { drawing=true; ctx.beginPath(); ctx.moveTo(...pos(e)); });
c.addEventListener('mousemove', e => { if(!drawing) return; ctx.lineTo(...pos(e)); ctx.stroke(); });
c.addEventListener('mouseup', () => drawing=false);
c.addEventListener('touchstart', e => { e.preventDefault(); drawing=true; ctx.beginPath(); ctx.moveTo(...pos(e)); });
c.addEventListener('touchmove', e => { e.preventDefault(); if(!drawing) return; ctx.lineTo(...pos(e)); ctx.stroke(); });
c.addEventListener('touchend', () => drawing=false);
}
function clearSig() {
const c = document.getElementById('sigCanvas');
if (c) c.getContext('2d').clearRect(0,0,c.width,c.height);
}
function rateStar(n) {
document.querySelectorAll('.star').forEach((s,i) => s.classList.toggle('active', i < n));
const labels = ['','Плохо','Неплохо','Нормально','Хорошо','Отлично!'];
const el = document.getElementById('rateLabel');
if (el) el.textContent = labels[n];
}
// init
go('home');
</script>
</body>
</html>

3087
docs/mockup_director.html Normal file

File diff suppressed because it is too large Load Diff

3683
docs/mockup_manager.html Normal file

File diff suppressed because it is too large Load Diff

1083
docs/mockup_measurer.html Normal file

File diff suppressed because it is too large Load Diff

1785
docs/mockup_worker.html Normal file

File diff suppressed because it is too large Load Diff