mirror of
https://github.com/wasrusgen/wasrusgen1-crm.git
synced 2026-06-03 15:24:47 +00:00
2197 lines
158 KiB
HTML
2197 lines
158 KiB
HTML
<!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[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}
|
||
body[data-theme="dark-crm"] {--accent:#4A90D9;--accent2:#76BD22;--bg:#0F172A;--card:#1E293B;--ink:#F1F5F9;--muted:#94A3B8;--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}
|
||
.theme-btn[data-t="dark-crm"] {background:#0F172A;color:#4A90D9}
|
||
|
||
#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)}
|
||
.nav-fab{width:52px;height:52px;background:var(--accent);border-radius:50%;display:flex;align-items:center;justify-content:center;box-shadow:0 4px 16px rgba(0,0,0,.25);cursor:pointer;margin-top:-18px;flex-shrink:0}
|
||
.nav-fab svg{color:#fff;width:24px;height:24px}
|
||
|
||
.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:4px solid var(--warn)}
|
||
.card.success-border{border-left:4px solid var(--success)}
|
||
.card.accent-border{border-left:4px solid var(--accent)}
|
||
.card.danger-border{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}
|
||
|
||
.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:transparent;border:1.5px solid var(--danger);color:var(--danger)}
|
||
|
||
.badge{display:inline-flex;align-items:center;padding:4px 10px;border-radius:20px;font-size:12px;font-weight:600;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)}
|
||
|
||
.chip-row{display:flex;flex-wrap:wrap;gap:6px;padding:0 16px;margin-bottom:8px}
|
||
.chip{padding:5px 10px;border-radius:20px;font-size:12px;font-weight:600;cursor:pointer;white-space:nowrap;border:1.5px solid #E2E8F0;background:var(--card);color:var(--muted);transition:all .2s}
|
||
.chip.active{background:var(--accent);color:#fff;border-color:var(--accent)}
|
||
|
||
.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}
|
||
|
||
.accordion-header{display:flex;align-items:center;justify-content:space-between;padding:14px 16px;cursor:pointer;font-weight:700;font-size:14px;color:var(--ink)}
|
||
.accordion-body{padding:0 16px 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}
|
||
.price-item.selected{background:rgba(0,62,126,.06);margin:0 -16px;padding:10px 16px;border-radius:8px}
|
||
body[data-theme="radar"] .price-item.selected{background:rgba(67,56,202,.06)}
|
||
body[data-theme="dark"] .price-item.selected{background:rgba(99,102,241,.1)}
|
||
|
||
.sticky-cart{position:sticky;bottom:0;background:var(--card);border-top:1px solid #E2E8F0;padding:12px 16px;display:flex;align-items:center;justify-content:space-between;box-shadow:0 -4px 20px rgba(0,0,0,.08)}
|
||
|
||
.checklist-item{display:flex;align-items:flex-start;gap:12px;padding:12px 0;border-bottom:1px solid rgba(0,0,0,.06)}
|
||
.checklist-item:last-child{border-bottom:none}
|
||
.check-box{width:24px;height:24px;border-radius:6px;border:2px solid #CBD5E1;display:flex;align-items:center;justify-content:center;flex-shrink:0;cursor:pointer;transition:all .2s;margin-top:1px}
|
||
.check-box.done{background:var(--success);border-color:var(--success)}
|
||
.check-box.done svg{display:block}
|
||
.check-box svg{display:none;color:#fff;width:14px;height:14px}
|
||
.check-text{flex:1}
|
||
.check-text h4{font-size:14px;font-weight:600;color:var(--ink);line-height:1.3}
|
||
.check-text p{font-size:12px;color:var(--muted);margin-top:2px}
|
||
.check-text.done h4{text-decoration:line-through;color:var(--muted)}
|
||
|
||
.photo-slot{width:80px;height:80px;border-radius:12px;background:#F1F5F9;border:2px dashed #CBD5E1;display:flex;align-items:center;justify-content:center;flex-shrink:0;cursor:pointer}
|
||
.photo-slot svg{color:var(--muted);width:24px;height:24px}
|
||
.photo-slot.filled{background:linear-gradient(135deg,#E2E8F0,#CBD5E1);border:none;font-size:28px}
|
||
|
||
.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}
|
||
|
||
.earnings-card{background:linear-gradient(135deg,var(--accent),var(--accent2));border-radius:16px;padding:20px;color:#fff;margin-bottom:12px}
|
||
|
||
.chat-wrap{display:flex;flex-direction:column;gap:12px;padding:16px}
|
||
.bubble{max-width:75%;padding:10px 14px;border-radius:16px;font-size:14px;line-height:1.45}
|
||
.bubble.in{background:var(--card);color:var(--ink);align-self:flex-start;border-bottom-left-radius:4px;box-shadow:0 1px 6px rgba(0,0,0,.07)}
|
||
.bubble.out{background:var(--accent);color:#fff;align-self:flex-end;border-bottom-right-radius:4px}
|
||
.bubble-meta{font-size:10px;margin-top:4px;opacity:.6}
|
||
.chat-input-row{display:flex;gap:8px;padding:10px 16px;background:var(--card);border-top:1px solid rgba(0,0,0,.06);position:sticky;bottom:60px}
|
||
.chat-input-row input{flex:1;padding:10px 14px;border:1.5px solid #E2E8F0;border-radius:24px;font-size:14px;color:var(--ink);background:var(--bg);outline:none}
|
||
|
||
.stat-table{width:100%;border-collapse:collapse;font-size:13px}
|
||
.stat-table th{font-size:10px;text-transform:uppercase;letter-spacing:.05em;color:var(--muted);font-weight:600;padding:6px 0;border-bottom:1px solid rgba(0,0,0,.08);text-align:left}
|
||
.stat-table td{padding:8px 0;border-bottom:1px solid rgba(0,0,0,.05);color:var(--ink);font-size:13px}
|
||
.stat-table tr:last-child td{border-bottom:none}
|
||
|
||
.sign-card{border:2px solid #E2E8F0;border-radius:12px;padding:12px 14px;cursor:pointer;transition:all .2s;margin-bottom:8px;display:flex;align-items:center;gap:12px}
|
||
.sign-card:hover,.sign-card.active{border-color:var(--accent);background:rgba(0,62,126,.04)}
|
||
body[data-theme="radar"] .sign-card:hover,body[data-theme="radar"] .sign-card.active{background:rgba(67,56,202,.04)}
|
||
body[data-theme="dark"] .sign-card:hover,body[data-theme="dark"] .sign-card.active{background:rgba(99,102,241,.08)}
|
||
|
||
.qty-stepper{display:flex;align-items:center;gap:6px}
|
||
.qty-btn{width:26px;height:26px;border-radius:6px;border:1.5px solid #E2E8F0;background:transparent;font-size:16px;cursor:pointer;display:flex;align-items:center;justify-content:center;color:var(--ink);font-weight:700}
|
||
|
||
body[data-theme="dark"] .form-input,body[data-theme="dark"] .form-textarea,body[data-theme="dark-crm"] .form-input,body[data-theme="dark-crm"] .form-textarea{background:#374151;border-color:#4B5563;color:#F9FAFB}
|
||
body[data-theme="dark"] .chip,body[data-theme="dark-crm"] .chip{background:#1F2937;border-color:#374151;color:#9CA3AF}
|
||
body[data-theme="dark"] .progress-bar,body[data-theme="dark-crm"] .progress-bar{background:#374151}
|
||
body[data-theme="dark"] .page-header,body[data-theme="dark-crm"] .page-header{background:var(--card);border-bottom:1px solid rgba(255,255,255,.08)}
|
||
body[data-theme="dark"] .bottom-nav,body[data-theme="dark-crm"] .bottom-nav{background:rgba(15,23,42,.95);border-color:rgba(255,255,255,.08)}
|
||
body[data-theme="dark"] #statusBar,body[data-theme="dark-crm"] #statusBar{background:var(--card)}
|
||
body[data-theme="dark"] .back-btn,body[data-theme="dark"] .header-action{background:#374151}
|
||
.warn-amber{font-size:11px;color:#92400e;background:rgba(245,158,11,.1);border-radius:6px;padding:8px 10px;line-height:1.5}
|
||
.warn-purple{font-size:11px;color:#5b21b6;background:rgba(124,58,237,.1);border-radius:6px;padding:8px 10px;line-height:1.5}
|
||
body[data-theme="dark"] .warn-amber{color:#fbbf24;background:rgba(245,158,11,.15)}
|
||
body[data-theme="dark"] .warn-purple{color:#c4b5fd;background:rgba(124,58,237,.15)}
|
||
body[data-theme="dark-crm"] .warn-amber{color:#fbbf24;background:rgba(245,158,11,.15)}
|
||
body[data-theme="dark-crm"] .warn-purple{color:#c4b5fd;background:rgba(124,58,237,.15)}
|
||
body[data-theme="dark"] .price-item{border-color:#374151}
|
||
body[data-theme="dark"] .sticky-cart{background:#1F2937;border-color:#374151}
|
||
body[data-theme="dark"] .accordion-header{border-color:#374151}
|
||
body[data-theme="dark"] .checklist-item{border-color:#374151}
|
||
body[data-theme="dark"] .signature-canvas{background:#374151;border-color:#4B5563}
|
||
body[data-theme="dark"] .photo-slot{background:#374151;border-color:#4B5563}
|
||
body[data-theme="dark"] .info-row{border-color:#374151}
|
||
body[data-theme="dark"] .sign-card{border-color:#374151}
|
||
body[data-theme="dark"] .bubble.in{background:#374151}
|
||
body[data-theme="dark"] .chat-input-row{background:#1F2937;border-color:#374151}
|
||
body[data-theme="dark"] .chat-input-row input{background:#374151;border-color:#4B5563;color:#F9FAFB}
|
||
body[data-theme="dark"] .qty-btn{border-color:#4B5563;color:#F9FAFB}
|
||
body[data-theme="dark"] .stat-table th{border-color:#374151}
|
||
body[data-theme="dark"] .stat-table td{border-color:#374151}
|
||
|
||
@keyframes slideFade{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}
|
||
.page{animation:slideFade .18s ease-out}
|
||
|
||
/* ─── FEEDBACK WIDGET ─── */
|
||
#feedbackTrigger{
|
||
position:absolute;bottom:14px;left:50%;transform:translateX(-50%);
|
||
display:inline-flex;align-items:center;gap:10px;
|
||
background:rgba(0,0,0,.58);backdrop-filter:blur(12px);
|
||
border:1px solid rgba(255,255,255,.18);border-radius:32px;
|
||
padding:10px 20px;cursor:pointer;z-index:50;
|
||
transition:all .2s;user-select:none;white-space:nowrap;
|
||
box-shadow:0 4px 20px rgba(0,0,0,.3);
|
||
}
|
||
#feedbackTrigger:hover{background:rgba(0,0,0,.72);border-color:rgba(255,255,255,.28);transform:translateX(-50%) translateY(-1px)}
|
||
#fScreenId{display:none}
|
||
.fb-sep{width:1px;height:10px;background:rgba(255,255,255,.2);flex-shrink:0}
|
||
.fb-brand{font-family:'Montserrat',sans-serif;font-weight:700;font-size:9px;letter-spacing:1.5px;color:rgba(255,255,255,.6)}
|
||
|
||
#feedbackSheet{
|
||
display:none;position:absolute;inset:0;z-index:200;flex-direction:column;justify-content:flex-end;
|
||
}
|
||
#feedbackOverlay{position:absolute;inset:0;background:rgba(0,0,0,.5)}
|
||
#feedbackPanel{
|
||
position:relative;background:#fff;border-radius:20px 20px 0 0;
|
||
padding:20px 16px 28px;z-index:1;
|
||
animation:slideFade .2s ease-out;
|
||
}
|
||
#feedbackPanel .fb-header{text-align:center;margin-bottom:14px}
|
||
#feedbackPanel .fb-title{font-size:14px;font-weight:700;color:#1A1A2E;margin-bottom:4px}
|
||
#feedbackPanel .fb-screen{font-size:11px;color:#94A3B8;font-family:monospace;background:#F1F5F9;padding:3px 8px;border-radius:6px;display:inline-block}
|
||
#feedbackText{width:100%;border:1.5px solid #E2E8F0;border-radius:12px;padding:12px;font-size:13px;font-family:'Inter',sans-serif;resize:none;height:90px;color:#1A1A2E;outline:none}
|
||
#feedbackText:focus{border-color:#003E7E}
|
||
.fb-actions{display:flex;gap:8px;margin-top:10px}
|
||
.fb-cancel{flex:1;padding:11px;border-radius:12px;border:1.5px solid #E2E8F0;background:transparent;font-size:13px;font-weight:600;color:#64748B;cursor:pointer}
|
||
.fb-send{flex:2;padding:11px;border-radius:12px;border:none;background:#003E7E;color:#fff;font-size:13px;font-weight:700;cursor:pointer;font-family:'Inter',sans-serif}
|
||
.fb-send:hover{background:#0055B3}
|
||
</style>
|
||
</head>
|
||
<body data-theme="zov">
|
||
<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>
|
||
|
||
<!-- DEV ROLE SWITCHER -->
|
||
<div style="display:flex;align-items:center;gap:10px;background:#0F172A;padding:8px 20px;border-radius:14px;margin-bottom:14px;width:100%;max-width:600px;box-shadow:0 4px 20px rgba(0,0,0,.3)">
|
||
<span style="color:#475569;font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.1em;flex-shrink:0">Dev</span>
|
||
<div style="display:flex;gap:4px;flex:1;justify-content:center">
|
||
<a href="mockup_assembler.html" target="_blank" style="padding:5px 13px;border-radius:8px;font-size:12px;font-weight:700;background:rgba(118,189,34,.2);color:#76BD22;text-decoration:none;border:1px solid rgba(118,189,34,.4)">Сборщик</a>
|
||
<a href="../mockup_manager.html" target="_blank" style="padding:5px 13px;border-radius:8px;font-size:12px;font-weight:600;color:#64748B;text-decoration:none;transition:all .15s" onmouseover="this.style.color='#94A3B8'" onmouseout="this.style.color='#64748B'">Менеджер</a>
|
||
<a href="../mockup_measurer.html" target="_blank" style="padding:5px 13px;border-radius:8px;font-size:12px;font-weight:600;color:#64748B;text-decoration:none;transition:all .15s" onmouseover="this.style.color='#94A3B8'" onmouseout="this.style.color='#64748B'">Замерщик</a>
|
||
<a href="../mockup_client.html" target="_blank" style="padding:5px 13px;border-radius:8px;font-size:12px;font-weight:600;color:#64748B;text-decoration:none;transition:all .15s" onmouseover="this.style.color='#94A3B8'" onmouseout="this.style.color='#64748B'">Клиент</a>
|
||
<span style="padding:5px 13px;border-radius:8px;font-size:12px;font-weight:600;color:#334155;cursor:not-allowed">Директор</span>
|
||
</div>
|
||
<a href="../brandbook_crm.html" target="_blank" style="color:#475569;font-size:10px;font-weight:600;text-decoration:none;flex-shrink:0" onmouseover="this.style.color='#94A3B8'" onmouseout="this.style.color='#475569'">Brandbook</a>
|
||
</div>
|
||
|
||
<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>
|
||
<button class="theme-btn" data-t="dark-crm" onclick="setTheme('dark-crm')">CRM Dark</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="phoneWrap">
|
||
<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 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>
|
||
|
||
<!-- FEEDBACK TRIGGER -->
|
||
<span id="fScreenId" style="display:none">home</span>
|
||
<div id="feedbackTrigger" onclick="openFeedback()">
|
||
<svg width="16" height="13" viewBox="0 0 24 19" fill="none" stroke="rgba(255,255,255,.8)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="flex-shrink:0"><rect x="1" y="1" width="22" height="17" rx="3"/><polyline points="1,1 12,10.5 23,1"/></svg>
|
||
<div class="w1-slot" data-color="rgba(255,255,255,.82)" data-width="62" data-height="10" style="flex-shrink:0"></div>
|
||
<span style="color:rgba(255,255,255,.25);font-size:14px;font-weight:200;line-height:1">|</span>
|
||
<span class="fb-brand" style="font-size:10px;letter-spacing:2px">CRM</span>
|
||
</div>
|
||
|
||
<!-- FEEDBACK SHEET -->
|
||
<div id="feedbackSheet">
|
||
<div id="feedbackOverlay" onclick="closeFeedback()"></div>
|
||
<div id="feedbackPanel">
|
||
<div class="fb-header">
|
||
<div class="fb-title">Написать разработчику</div>
|
||
<span id="fSheetScreen" class="fb-screen">home</span>
|
||
</div>
|
||
<textarea id="feedbackText" placeholder="Замечание, пожелание, ошибка..."></textarea>
|
||
<div class="fb-actions">
|
||
<button class="fb-cancel" onclick="closeFeedback()">Отмена</button>
|
||
<button class="fb-send" onclick="sendFeedback()">→ Открыть Telegram</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
|
||
<script src="data.js"></script>
|
||
<script>
|
||
const SCREENS = {
|
||
home: 'Главная',
|
||
assemblies: 'Мои сборки',
|
||
asm_detail: 'Карточка сборки',
|
||
chat_asm: 'Чат по сборке',
|
||
checklist: 'Чек-лист работ',
|
||
extra_list: 'Акты и документы',
|
||
extra_create: 'Каталог позиций',
|
||
extra_basket: 'Корзина акта',
|
||
extra_sign: 'Подпись актов',
|
||
extra_payment: 'Оплата доп. работ',
|
||
extra_done: 'Акт подписан',
|
||
reclamation: 'Акт рекламации',
|
||
photo_report: 'Фото-отчёт',
|
||
profile: 'Профиль',
|
||
my_questionnaire: 'Моя анкета',
|
||
availability: 'Доступность',
|
||
notifications: 'Уведомления',
|
||
rate_team: 'Оценить команду',
|
||
};
|
||
|
||
/* ─── SVG ICON LIBRARY ─── */
|
||
function svgIcon(shape, size, color) {
|
||
size = size || 18; color = color || 'var(--accent)';
|
||
var p = {
|
||
wrench: '<path d="M14.7 6.3a1 1 0 000 1.4l1.6 1.6a1 1 0 001.4 0l3.77-3.77a6 6 0 01-7.94 7.94l-6.91 6.91a2.12 2.12 0 01-3-3l6.91-6.91a6 6 0 017.94-7.94l-3.76 3.76z"/>',
|
||
zap: '<polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/>',
|
||
droplet:'<path d="M12 2.69l5.66 5.66a8 8 0 11-11.31 0z"/>',
|
||
snow: '<line x1="12" y1="2" x2="12" y2="22"/><line x1="2" y1="12" x2="22" y2="12"/><polyline points="20 16 16 12 20 8"/><polyline points="4 8 8 12 4 16"/><polyline points="16 4 12 8 8 4"/><polyline points="8 20 12 16 16 20"/>',
|
||
flame: '<path d="M8.5 14.5A2.5 2.5 0 0011 12c0-1.38-.5-2-1-3-1.072-2.143-.224-4.054 2-6 .5 2.5 2 4.9 4 6.5 2 1.6 3 3.5 3 5.5a7 7 0 11-14 0c0-1.153.433-2.294 1-3a2.5 2.5 0 002.5 3z"/>',
|
||
wind: '<path d="M9.59 4.59A2 2 0 1111 8H2m10.59 11.41A2 2 0 1014 16H2m15.73-8.27A2.5 2.5 0 1119.5 12H2"/>',
|
||
thermo: '<path d="M14 14.76V3.5a2.5 2.5 0 00-5 0v11.26a4.5 4.5 0 105 0z"/>',
|
||
archive:'<polyline points="21 8 21 21 3 21 3 8"/><rect x="1" y="3" width="22" height="5"/><line x1="10" y1="12" x2="14" y2="12"/>',
|
||
star4: '<circle cx="12" cy="12" r="3"/><path d="M12 1v4M12 19v4M4.22 4.22l2.83 2.83M16.95 16.95l2.83 2.83M1 12h4M19 12h4M4.22 19.78l2.83-2.83M16.95 7.05l2.83-2.83"/>',
|
||
pen: '<path d="M12 20h9"/><path d="M16.5 3.5a2.121 2.121 0 013 3L7 19l-4 1 1-4L16.5 3.5z"/>',
|
||
phone: '<rect x="5" y="2" width="14" height="20" rx="2" ry="2"/><line x1="12" y1="18" x2="12.01" y2="18"/>',
|
||
check2: '<polyline points="20 6 9 17 4 12"/>',
|
||
beach: '<circle cx="12" cy="12" r="10"/><path d="M8.56 2.75c4.37 6.03 6.02 9.42 8.03 17.72m2.54-15.38c-3.72 4.35-8.94 5.66-16.88 5.85m19.5 1.9c-3.5-.93-6.63-.82-8.94 0-2.58.92-5.01 2.86-7.44 6.32"/>',
|
||
sick: '<path d="M12 2a10 10 0 100 20A10 10 0 0012 2z"/><path d="M8 15s1.5-2 4-2 4 2 4 2"/><line x1="9" y1="9" x2="9.01" y2="9"/><line x1="15" y1="9" x2="15.01" y2="9"/>',
|
||
copy: '<rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/>',
|
||
};
|
||
return '<svg width="'+size+'" height="'+size+'" viewBox="0 0 24 24" fill="none" stroke="'+color+'" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round">'+( p[shape]||'')+'</svg>';
|
||
}
|
||
|
||
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;
|
||
// Update feedback widget screen badge
|
||
var screenLabel = id + (SCREENS[id] ? ' · ' + SCREENS[id] : '');
|
||
var el = document.getElementById('fScreenId');
|
||
var el2 = document.getElementById('fSheetScreen');
|
||
if (el) el.textContent = id;
|
||
if (el2) el2.textContent = screenLabel;
|
||
}
|
||
|
||
function openFeedback() {
|
||
var screenLabel = cur + (SCREENS[cur] ? ' · ' + SCREENS[cur] : '');
|
||
document.getElementById('fSheetScreen').textContent = screenLabel;
|
||
document.getElementById('feedbackText').value = '';
|
||
document.getElementById('feedbackSheet').style.display = 'flex';
|
||
setTimeout(function(){ document.getElementById('feedbackText').focus(); }, 100);
|
||
}
|
||
function closeFeedback() {
|
||
document.getElementById('feedbackSheet').style.display = 'none';
|
||
}
|
||
function sendFeedback() {
|
||
var text = document.getElementById('feedbackText').value.trim();
|
||
if (!text) { document.getElementById('feedbackText').focus(); return; }
|
||
var screenLabel = cur + (SCREENS[cur] ? ' · ' + SCREENS[cur] : '');
|
||
var msg = '[@wasrusgen1 CRM / ' + screenLabel + ']\n' + text;
|
||
var ta = document.getElementById('feedbackText');
|
||
var btn = document.querySelector('.fb-send');
|
||
navigator.clipboard.writeText(msg).then(function() {
|
||
ta.value = ''; ta.placeholder = '✓ Сообщение скопировано';
|
||
ta.disabled = true; btn.textContent = '→ Открываю Telegram...';
|
||
setTimeout(function() {
|
||
window.open('https://t.me/wasrusgen1', '_blank');
|
||
closeFeedback();
|
||
ta.disabled = false; ta.placeholder = 'Замечание, пожелание, ошибка...';
|
||
btn.textContent = '→ Открыть Telegram';
|
||
}, 700);
|
||
}).catch(function() {
|
||
window.open('https://t.me/wasrusgen1', '_blank');
|
||
closeFeedback();
|
||
});
|
||
}
|
||
|
||
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==='assemblies'?'active':''}" onclick="go('assemblies')">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M2 18a1 1 0 001 1h18a1 1 0 001-1v-2a1 1 0 00-1-1H3a1 1 0 00-1 1v2z"/><path d="M10 10V5a1 1 0 011-1h2a1 1 0 011 1v5"/><path d="M4 15v-3a8 8 0 018-8"/><path d="M20 15v-3a8 8 0 00-8-8"/></svg>
|
||
<span>Сборки</span>
|
||
</div>
|
||
<div class="nav-item" style="flex:0">
|
||
<div class="nav-fab" onclick="go('extra_create')">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
|
||
</div>
|
||
</div>
|
||
<div class="nav-item ${active==='extra_list'?'active':''}" onclick="go('extra_list')">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="9" y1="13" x2="15" y2="13"/><line x1="9" y1="17" x2="12" y2="17"/></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 'assemblies': return screenAssemblies();
|
||
case 'asm_detail': return screenAsmDetail();
|
||
case 'chat_asm': return screenChatAsm();
|
||
case 'checklist': return screenChecklist();
|
||
case 'extra_list': return screenExtraList();
|
||
case 'extra_create': return screenExtraCreate();
|
||
case 'extra_basket': return screenExtraBasket();
|
||
case 'extra_sign': return screenExtraSign();
|
||
case 'extra_payment': return screenExtraPayment();
|
||
case 'extra_done': return screenExtraDone();
|
||
case 'reclamation': return screenReclamation();
|
||
case 'photo_report': return screenPhotoReport();
|
||
case 'profile': return screenProfile();
|
||
case 'my_questionnaire': return screenMyQuestionnaire();
|
||
case 'availability': return screenAvailability();
|
||
case 'notifications': return screenNotifications();
|
||
case 'rate_team': return screenRateTeam();
|
||
default: return '<div style="padding:40px;text-align:center;color:var(--muted)">Экран в разработке</div>';
|
||
}
|
||
}
|
||
|
||
// ── GPS: прибытие на объект сборки ────────────────────────────────────────────
|
||
var _ASM_GPS_RADIUS = 300; // метров — у клиента адрес уличный, допуск больше чем у салона
|
||
var _ASM_JOBS = {
|
||
'А-2847': {addr:'ул. Ленина, 45', lat:59.9548, lng:30.3158},
|
||
'А-2851': {addr:'пр. Победы, 88', lat:59.8695, lng:30.2867}
|
||
};
|
||
window._ASM_GPS = {}; // jobId → null | 'loading' | {ok,dist,forced}
|
||
window._ASM_GPS_DEMO = 'near'; // 'near' | 'far'
|
||
|
||
function _asmGpsDist(la1,lo1,la2,lo2){
|
||
var R=6371000,dL=(la2-la1)*Math.PI/180,dG=(lo2-lo1)*Math.PI/180;
|
||
var a=Math.sin(dL/2)*Math.sin(dL/2)+Math.cos(la1*Math.PI/180)*Math.cos(la2*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 _asmFmt(m){ return m<1000?(m+' м'):(m/1000).toFixed(1)+' км'; }
|
||
function _asmGpsResult(jobId,dist){
|
||
var ok=dist<=_ASM_GPS_RADIUS;
|
||
window._ASM_GPS[jobId]={ok:ok,dist:dist};
|
||
document.getElementById('screen').innerHTML=render('asm_detail');
|
||
}
|
||
function _asmGpsRequest(jobId){
|
||
window._ASM_GPS[jobId]='loading';
|
||
document.getElementById('screen').innerHTML=render('asm_detail');
|
||
var job=_ASM_JOBS[jobId];
|
||
function onGot(lat,lng){ _asmGpsResult(jobId,_asmGpsDist(lat,lng,job.lat,job.lng)); }
|
||
function onFail(){ _asmGpsResult(jobId, window._ASM_GPS_DEMO==='near'?62:1380); }
|
||
var tg=window.Telegram&&Telegram.WebApp;
|
||
if(tg&&tg.LocationManager&&tg.LocationManager.isInited){
|
||
tg.LocationManager.getLocation(function(r){ r&&r.latitude?onGot(r.latitude,r.longitude):onFail(); });
|
||
} else if(navigator.geolocation){
|
||
navigator.geolocation.getCurrentPosition(
|
||
function(p){ onGot(p.coords.latitude,p.coords.longitude); },
|
||
onFail,{timeout:8000,maximumAge:15000}
|
||
);
|
||
} else { onFail(); }
|
||
}
|
||
function _asmGpsForce(jobId){
|
||
var prev=window._ASM_GPS[jobId];
|
||
window._ASM_GPS[jobId]={ok:false,dist:prev&&prev.dist||0,forced:true};
|
||
document.getElementById('screen').innerHTML=render('asm_detail');
|
||
}
|
||
function _asmGpsCancelCheck(jobId){
|
||
window._ASM_GPS[jobId]=null;
|
||
document.getElementById('screen').innerHTML=render('asm_detail');
|
||
}
|
||
|
||
/* ─── BOTTOM SHEET ─── */
|
||
function showSheet(d) {
|
||
const existing = document.getElementById('bottomSheet');
|
||
if (existing) existing.remove();
|
||
const isPast = parseInt(d) < 22;
|
||
const sheet = document.createElement('div');
|
||
sheet.id = 'bottomSheet';
|
||
sheet.style.cssText = 'position:absolute;bottom:60px;left:0;right:0;background:var(--card);border-radius:20px 20px 0 0;padding:16px;box-shadow:0 -8px 32px rgba(0,0,0,.18);z-index:200;animation:slideFade .2s ease-out';
|
||
sheet.innerHTML = `
|
||
<div style="width:40px;height:4px;background:#CBD5E1;border-radius:2px;margin:0 auto 16px"></div>
|
||
<div style="font-size:15px;font-weight:700;color:var(--ink);margin-bottom:14px">${isPast?'Май, '+d:'Май, '+d+' — выбрать статус'}</div>
|
||
${isPast ? `
|
||
<div style="font-size:13px;color:var(--muted);padding:10px 0">Прошедшая дата — редактирование недоступно</div>
|
||
` : `
|
||
<div onclick="document.getElementById('bottomSheet').remove()" style="display:flex;align-items:center;gap:12px;padding:12px 0;border-bottom:1px solid rgba(0,0,0,.06);cursor:pointer">
|
||
<div style="width:36px;height:36px;border-radius:10px;background:#DCFCE7;display:flex;align-items:center;justify-content:center">${svgIcon('check2',18,'#059669')}</div>
|
||
<div><div style="font-size:14px;font-weight:600;color:var(--ink)">Доступен для сборки</div><div style="font-size:12px;color:var(--muted)">Открыт для новых назначений</div></div>
|
||
</div>
|
||
<div onclick="go('availability')" style="display:flex;align-items:center;gap:12px;padding:12px 0;border-bottom:1px solid rgba(0,0,0,.06);cursor:pointer">
|
||
<div style="width:36px;height:36px;border-radius:10px;background:#FEF3C7;display:flex;align-items:center;justify-content:center">${svgIcon('beach',18,'#D97706')}</div>
|
||
<div><div style="font-size:14px;font-weight:600;color:var(--ink)">Запросить выходной</div><div style="font-size:12px;color:var(--muted)">Минимум за 10 дней</div></div>
|
||
</div>
|
||
<div onclick="go('availability')" style="display:flex;align-items:center;gap:12px;padding:12px 0;cursor:pointer">
|
||
<div style="width:36px;height:36px;border-radius:10px;background:#FEE2E2;display:flex;align-items:center;justify-content:center">${svgIcon('sick',18,'#DC2626')}</div>
|
||
<div><div style="font-size:14px;font-weight:600;color:var(--ink)">Заболел</div><div style="font-size:12px;color:var(--muted)">Сборки уйдут на переназначение</div></div>
|
||
</div>
|
||
`}
|
||
<button onclick="document.getElementById('bottomSheet').remove()" class="btn-sm outline" style="width:100%;margin-top:10px">Закрыть</button>
|
||
`;
|
||
document.getElementById('screen').appendChild(sheet);
|
||
}
|
||
|
||
/* ─── HOME ─── */
|
||
function screenHome() {
|
||
const calDays = [
|
||
{d:'',c:null},{d:'',c:null},{d:'',c:null},{d:'',c:null},{d:'1',c:null},{d:'2',c:null},{d:'3',c:null},
|
||
{d:'4',c:null},{d:'5',c:null},{d:'6',c:null},{d:'7',c:null},{d:'8',c:null},{d:'9',c:null},{d:'10',c:null},
|
||
{d:'11',c:null},{d:'12',c:null},{d:'13',c:null},{d:'14',c:null},{d:'15',c:null},{d:'16',c:null},{d:'17',c:null},
|
||
{d:'18',c:null},{d:'19',c:'#10B981',m:true},{d:'20',c:null},{d:'21',c:'#10B981'},{d:'22',c:'#F59E0B',today:true,m:true},{d:'23',c:'#003E7E'},{d:'24',c:null},
|
||
{d:'25',c:null,m:true},{d:'26',c:null},{d:'27',c:'#003E7E',m:true},{d:'28',c:'#003E7E'},{d:'29',c:null,m:true},{d:'30',c:null},{d:'31',c:null},
|
||
];
|
||
return `<div class="page">
|
||
<!-- Баннер уведомления -->
|
||
<div style="background:var(--accent);padding:10px 16px;display:flex;align-items:center;gap:10px;cursor:pointer" onclick="go('notifications')">
|
||
<div style="width:8px;height:8px;border-radius:50%;background:#76BD22;flex-shrink:0;box-shadow:0 0 6px #76BD22"></div>
|
||
<div style="flex:1">
|
||
<div style="font-size:13px;font-weight:700;color:#fff">Назначена новая сборка</div>
|
||
<div style="font-size:12px;color:rgba(255,255,255,.75)">Сидорова Марина · 27 мая, 10:00 · Кухня 2м</div>
|
||
</div>
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="rgba(255,255,255,.7)" stroke-width="2.5"><polyline points="9 18 15 12 9 6"/></svg>
|
||
</div>
|
||
<div style="background:linear-gradient(135deg,var(--accent) 0%,var(--accent2) 100%);padding:20px 16px 24px">
|
||
<div style="font-size:12px;color:rgba(255,255,255,.65);font-weight:600;text-transform:uppercase;letter-spacing:.05em;margin-bottom:4px">Пятница, 22 мая</div>
|
||
<div style="font-size:22px;font-weight:800;color:#fff;margin-bottom:2px">Привет, Алексей 👋</div>
|
||
<div style="font-size:14px;color:rgba(255,255,255,.75)">2 сборки сегодня · 1 требует внимания</div>
|
||
</div>
|
||
|
||
<div style="padding:0 16px;margin-top:-12px">
|
||
<div class="card accent-border" onclick="go('asm_detail')" style="cursor:pointer">
|
||
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:10px">
|
||
<span class="badge yellow">В процессе</span>
|
||
<span style="font-size:12px;color:var(--muted)">Сборка #А-2847</span>
|
||
</div>
|
||
<div style="font-size:16px;font-weight:700;color:var(--ink);margin-bottom:4px">Иванов Дмитрий</div>
|
||
<div style="font-size:13px;color:var(--muted);margin-bottom:12px">📍 ул. Ленина, 45, кв. 12 · Сегодня, 10:00</div>
|
||
<div style="margin-bottom:10px">
|
||
<div style="display:flex;justify-content:space-between;margin-bottom:4px">
|
||
<span style="font-size:12px;color:var(--muted)">Прогресс сборки</span>
|
||
<span style="font-size:12px;font-weight:700;color:var(--ink)">4 / 7 позиций</span>
|
||
</div>
|
||
<div class="progress-bar"><div class="progress-fill" style="width:57%"></div></div>
|
||
</div>
|
||
<div style="display:flex;gap:8px">
|
||
<button class="btn-sm" onclick="event.stopPropagation();go('checklist')">Продолжить</button>
|
||
<button class="btn-sm outline" onclick="event.stopPropagation();go('extra_create')">+ Акт доп. работ</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="section-label">На сегодня</div>
|
||
<div style="padding:0 16px">
|
||
<div class="card" onclick="go('assemblies')" style="cursor:pointer">
|
||
<div style="display:flex;align-items:center;gap:12px">
|
||
<div class="avatar" style="background:#E0E7FF;color:var(--accent);font-size:20px">📦</div>
|
||
<div class="col" style="flex:1">
|
||
<div style="font-size:14px;font-weight:700;color:var(--ink)">Петрова Наталья</div>
|
||
<div style="font-size:12px;color:var(--muted)">Сборка #А-2851 · 14:00–16:00</div>
|
||
</div>
|
||
<span class="badge blue">Назначено</span>
|
||
</div>
|
||
<div style="margin-top:10px;padding-top:10px;border-top:1px solid rgba(0,0,0,.06)">
|
||
<div style="font-size:12px;color:var(--muted)">📍 пр. Победы, 88, кв. 34 · Шкаф-купе 2-дверный + тумба</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="section-label">Загрузка мая 2026</div>
|
||
<div style="padding:0 16px;margin-bottom:4px">
|
||
<div class="card" style="padding:10px 8px">
|
||
<div style="display:grid;grid-template-columns:repeat(7,1fr);text-align:center;margin-bottom:6px">
|
||
${['Пн','Вт','Ср','Чт','Пт','Сб','Вс'].map(d => `<span style="font-size:9px;color:var(--muted);font-weight:700">${d}</span>`).join('')}
|
||
</div>
|
||
<div style="display:grid;grid-template-columns:repeat(7,1fr);gap:2px;text-align:center">
|
||
${calDays.map(({d,c,today,m}) => `<div onclick="${d?'showSheet(\''+d+'\')':''}" style="padding:3px 2px;border-radius:6px;cursor:${d?'pointer':'default'};background:${c?c:today?'rgba(0,62,126,.08)':'transparent'};min-height:36px;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:1px;${today?(c?'box-shadow:inset 0 0 0 2px rgba(255,255,255,.7)':'box-shadow:inset 0 0 0 2px var(--accent)'):''}">
|
||
<div style="font-size:11px;font-weight:${today?800:700};color:${c?'#fff':today?'var(--accent)':d?'var(--ink)':'transparent'}">${d||''}</div>
|
||
${today&&!c?`<div style="width:4px;height:4px;border-radius:50%;background:var(--accent);margin-top:1px"></div>`:(m?`<div style="font-size:8px;font-weight:700;letter-spacing:.02em;color:${c?'rgba(255,255,255,.85)':'var(--accent2)'}">замер</div>`:'')}
|
||
</div>`).join('')}
|
||
</div>
|
||
<div style="display:flex;gap:10px;margin-top:8px;padding-top:6px;border-top:1px solid rgba(0,0,0,.06)">
|
||
<div style="display:flex;align-items:center;gap:3px"><div style="width:6px;height:6px;border-radius:50%;background:#10B981"></div><span style="font-size:10px;color:var(--muted)">Завершена</span></div>
|
||
<div style="display:flex;align-items:center;gap:3px"><div style="width:6px;height:6px;border-radius:50%;background:#F59E0B"></div><span style="font-size:10px;color:var(--muted)">В процессе</span></div>
|
||
<div style="display:flex;align-items:center;gap:3px"><div style="width:6px;height:6px;border-radius:50%;background:var(--accent)"></div><span style="font-size:10px;color:var(--muted)">Запланирована</span></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="section-label">Заработок</div>
|
||
<div style="padding:0 16px">
|
||
<div class="earnings-card">
|
||
<div style="display:flex;justify-content:space-between;align-items:flex-start">
|
||
<div>
|
||
<div style="font-size:12px;color:rgba(255,255,255,.65);margin-bottom:4px">Май 2026</div>
|
||
<div style="font-size:28px;font-weight:800">28 400 ₽</div>
|
||
<div style="font-size:13px;color:rgba(255,255,255,.7);margin-top:4px">+3 200 ₽ акты доп. работ</div>
|
||
</div>
|
||
<div style="text-align:right">
|
||
<div style="font-size:12px;color:rgba(255,255,255,.65);margin-bottom:4px">Сборок</div>
|
||
<div style="font-size:28px;font-weight:800">12</div>
|
||
<div style="font-size:13px;color:rgba(255,255,255,.7);margin-top:4px">из 15 план</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
${nav('home')}</div>`;
|
||
}
|
||
|
||
/* ─── ASSEMBLIES ─── */
|
||
function screenAssemblies() {
|
||
const list = [
|
||
{id:'А-2847',name:'Иванов Дмитрий',addr:'ул. Ленина, 45',date:'Сегодня 10:00',items:'Кухня 3м + стол',badge:'yellow',status:'В процессе',progress:57},
|
||
{id:'А-2851',name:'Петрова Наталья',addr:'пр. Победы, 88',date:'Сегодня 14:00',items:'Шкаф-купе 2-дв. + тумба',badge:'blue',status:'Назначено',progress:0},
|
||
{id:'А-2839',name:'Смирнов Андрей',addr:'ул. Садовая, 12',date:'21 мая',items:'Детская мебель 4 предм.',badge:'green',status:'Завершено',progress:100},
|
||
{id:'А-2831',name:'Козлова Елена',addr:'пр. Московский, 77',date:'19 мая',items:'Спальня: кровать + 2 тумбы',badge:'green',status:'Завершено',progress:100},
|
||
{id:'А-2820',name:'Горбунова Светлана',addr:'ул. Некрасова, 3',date:'15 мая',items:'Шкаф 3-дв. + комод',badge:'red',status:'Рекламация',progress:0},
|
||
];
|
||
return `<div class="page">
|
||
<div class="page-header">
|
||
<h2>Мои сборки</h2>
|
||
<div class="header-action"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg></div>
|
||
</div>
|
||
<div class="chip-row" style="margin-top:12px">
|
||
<div class="chip active">Все (8)</div>
|
||
<div class="chip">Сегодня (2)</div>
|
||
<div class="chip">В процессе (1)</div>
|
||
<div class="chip">Завершены (5)</div>
|
||
</div>
|
||
<div style="padding:0 16px">
|
||
${list.map(a => `
|
||
<div class="card" onclick="go('asm_detail')" style="cursor:pointer;background:${a.badge==='green'?'#F0FDF4':a.badge==='red'?'#FEF2F2':'var(--card)'}">
|
||
<div style="display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:8px">
|
||
<div>
|
||
<div style="font-size:15px;font-weight:700;color:var(--ink)">${a.name}</div>
|
||
<div style="font-size:12px;color:var(--muted);margin-top:2px">📍 ${a.addr} · ${a.date}</div>
|
||
</div>
|
||
<span class="badge ${a.badge}">${a.status}</span>
|
||
</div>
|
||
<div style="font-size:13px;color:var(--muted);margin-bottom:${a.progress>0&&a.progress<100?'10px':'0'}">
|
||
🪑 ${a.items} · #${a.id}
|
||
</div>
|
||
${a.progress>0&&a.progress<100?`
|
||
<div class="progress-bar"><div class="progress-fill" style="width:${a.progress}%"></div></div>
|
||
<div style="text-align:right;font-size:11px;color:var(--muted);margin-top:4px">${a.progress}%</div>
|
||
`:''}
|
||
</div>
|
||
`).join('')}
|
||
</div>
|
||
${nav('assemblies')}</div>`;
|
||
}
|
||
|
||
/* ─── ASSEMBLY DETAIL ─── */
|
||
function screenAsmDetail() {
|
||
const rooms = [
|
||
{room:'Кухня', items:[
|
||
{done:true, name:'Нижние модули кухни', detail:'6 шт, петли, доводчики'},
|
||
{done:true, name:'Верхние шкафы', detail:'5 шт, дюбели 8×60'},
|
||
{done:true, name:'Столешница + мойка', detail:'Силикон, врезка'},
|
||
{done:true, name:'Пенал 2100', detail:'1 шт'},
|
||
{done:false, name:'Обеденный стол', detail:'Раскладной механизм'},
|
||
]},
|
||
{room:'Гостиная', items:[
|
||
{done:false, name:'Барная стойка', detail:'Настенный крепёж'},
|
||
]},
|
||
{room:'Прихожая', items:[
|
||
{done:false, name:'Стулья — 4 шт', detail:'Ножки, болты М6'},
|
||
]},
|
||
];
|
||
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 class="header-action"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="1"/><circle cx="19" cy="12" r="1"/><circle cx="5" cy="12" r="1"/></svg></div>
|
||
</div>
|
||
|
||
${(function(){
|
||
var jobId='А-2847';
|
||
var gst=window._ASM_GPS[jobId];
|
||
if(!gst){
|
||
// Ещё не прибыл
|
||
return '<div style="background:#F0FDF4;border-bottom:1.5px solid #86EFAC;padding:12px 16px">'
|
||
+'<div style="font-size:12px;color:#059669;font-weight:700;margin-bottom:8px">📍 Подтвердите прибытие на объект</div>'
|
||
+'<div style="font-size:12px;color:var(--muted);margin-bottom:10px">ул. Ленина, 45, кв. 12 · Иванов Дмитрий</div>'
|
||
+'<div style="display:flex;gap:8px;align-items:center">'
|
||
+'<button onclick="_asmGpsRequest(\'А-2847\')" style="flex:1;padding:10px;border-radius:10px;border:none;background:#16A34A;color:#fff;font-size:13px;font-weight:700;cursor:pointer">📍 Я на месте</button>'
|
||
+'</div>'
|
||
+'<div style="margin-top:8px;font-size:10px;color:#94A3B8">Демо: '
|
||
+'<span onclick="window._ASM_GPS_DEMO=\'near\';document.getElementById(\'screen\').innerHTML=render(\'asm_detail\')" style="cursor:pointer;font-weight:700;color:'+(window._ASM_GPS_DEMO==='near'?'#16A34A':'#94A3B8')+'">Рядом</span> · '
|
||
+'<span onclick="window._ASM_GPS_DEMO=\'far\';document.getElementById(\'screen\').innerHTML=render(\'asm_detail\')" style="cursor:pointer;font-weight:700;color:'+(window._ASM_GPS_DEMO==='far'?'#DC2626':'#94A3B8')+'">Далеко</span></div>'
|
||
+'</div>';
|
||
} else if(gst==='loading'){
|
||
return '<div style="background:#F8FAFC;border-bottom:1px solid #E2E8F0;padding:14px 16px;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&&!gst.forced){
|
||
return '<div style="background:rgba(245,158,11,.1);border-bottom:1.5px solid rgba(245,158,11,.4);padding:13px 16px">'
|
||
+'<div style="font-size:13px;font-weight:700;color:var(--warn);margin-bottom:4px">⚠️ Вы в '+_asmFmt(gst.dist)+' от объекта</div>'
|
||
+'<div style="font-size:12px;color:var(--muted);margin-bottom:10px">ул. Ленина, 45 · Допустимый радиус: 300 м</div>'
|
||
+'<div style="display:flex;gap:8px">'
|
||
+'<button onclick="_asmGpsForce(\'А-2847\')" style="flex:1;padding:9px;border-radius:10px;border:none;background:#F59E0B;color:#fff;font-size:13px;font-weight:700;cursor:pointer">Отметить всё равно</button>'
|
||
+'<button onclick="_asmGpsCancelCheck(\'А-2847\')" style="padding:9px 14px;border-radius:10px;border:1px solid #E2E8F0;background:transparent;font-size:13px;font-weight:600;color:var(--muted);cursor:pointer">Отмена</button>'
|
||
+'</div></div>';
|
||
} else {
|
||
// Прибыл (ok или forced)
|
||
var chip = gst.forced
|
||
? '<span style="font-size:10px;background:rgba(245,158,11,.2);color:#D97706;border-radius:6px;padding:1px 6px;font-weight:700;margin-left:6px">⚡ вручную</span>'
|
||
: '<span style="font-size:10px;background:rgba(22,163,74,.15);color:#16A34A;border-radius:6px;padding:1px 6px;font-weight:700;margin-left:6px">GPS ✓</span>';
|
||
return '<div style="background:var(--warn);padding:10px 16px;display:flex;align-items:center;gap:8px">'
|
||
+'<span style="font-size:18px">⏱</span>'
|
||
+'<div><div style="font-size:13px;font-weight:700;color:#fff;display:flex;align-items:center">В процессе · 4 из 7 позиций'+chip+'</div>'
|
||
+'<div style="font-size:12px;color:rgba(255,255,255,.8)">Начато в 10:15 · прошло 1ч 43мин</div></div></div>';
|
||
}
|
||
})()}
|
||
|
||
<div style="padding:12px 16px 0">
|
||
<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">Время</span><span class="info-val">Сегодня, 10:00–14:00</span></div>
|
||
</div>
|
||
|
||
<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">ул. Ленина, 45, кв. 12</span></div>
|
||
<div class="info-row"><span class="info-label">Подъезд</span><span class="info-val">3</span></div>
|
||
<div class="info-row"><span class="info-label">Этаж</span><span class="info-val">7</span></div>
|
||
<div class="info-row">
|
||
<span class="info-label">GPS</span>
|
||
<span class="info-val"><a href="#" style="color:var(--accent);text-decoration:none;font-weight:600">📍 Открыть в картах</a></span>
|
||
</div>
|
||
<div style="margin-top:10px;background:#FFFBEB;border:1px solid #FDE68A;border-radius:10px;padding:10px 12px">
|
||
<div style="font-size:11px;font-weight:700;color:#D97706;text-transform:uppercase;letter-spacing:.04em;margin-bottom:4px">Примечание замерщика</div>
|
||
<div style="font-size:13px;color:#92400E;line-height:1.5">Въезд со стороны ул. Садовой через шлагбаум. Нужна заявка на охрану — позвонить за 30 мин. Домофон 45К. Лифт грузовой — справа от входа.</div>
|
||
</div>
|
||
</div>
|
||
|
||
<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 style="display:flex;align-items:center;justify-content:space-between;padding:8px 0;border-bottom:1px solid rgba(0,0,0,.06)">
|
||
<div>
|
||
<div style="font-size:12px;color:var(--muted)">Менеджер</div>
|
||
<div style="font-size:14px;font-weight:600;color:var(--ink)">Анна Соколова</div>
|
||
<div style="font-size:12px;color:var(--accent)">+7 921 100-20-30</div>
|
||
</div>
|
||
<button class="btn-sm" onclick="go('chat_asm')">Написать</button>
|
||
</div>
|
||
<div style="display:flex;align-items:center;justify-content:space-between;padding:8px 0">
|
||
<div>
|
||
<div style="font-size:12px;color:var(--muted)">Технолог</div>
|
||
<div style="font-size:14px;font-weight:600;color:var(--ink)">Игорь Волков</div>
|
||
<div style="font-size:12px;color:var(--accent)">+7 921 300-40-50</div>
|
||
</div>
|
||
<button class="btn-sm" onclick="go('chat_asm')">Написать</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="card">
|
||
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:10px">
|
||
<div style="font-size:13px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.05em">Состав мебели</div>
|
||
</div>
|
||
<!-- Проектная документация -->
|
||
<div style="display:flex;gap:7px;margin-bottom:14px;padding-bottom:12px;border-bottom:1px dashed rgba(0,0,0,.08)">
|
||
<a href="#" onclick="event.preventDefault();alert('📁 Открытие проекта фабрики…\\nВ продакшне — загрузка PDF из облака')" style="flex:1;display:flex;align-items:center;gap:6px;padding:9px 10px;background:#EFF6FF;border:1px solid #BFDBFE;border-radius:10px;text-decoration:none">
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#1D4ED8" stroke-width="2" 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"/><line x1="9" y1="13" x2="15" y2="13"/><line x1="9" y1="17" x2="13" y2="17"/></svg>
|
||
<div>
|
||
<div style="font-size:11px;font-weight:700;color:#1D4ED8">Проект (фабричка)</div>
|
||
<div style="font-size:10px;color:#93C5FD">PDF · 2.4 МБ</div>
|
||
</div>
|
||
</a>
|
||
<a href="#" onclick="event.preventDefault();alert('📐 Открытие чертежей…\\nВ продакшне — загрузка из облака')" style="flex:1;display:flex;align-items:center;gap:6px;padding:9px 10px;background:#F0FDF4;border:1px solid #BBF7D0;border-radius:10px;text-decoration:none">
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#15803D" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><line x1="9" y1="3" x2="9" y2="21"/><line x1="3" y1="9" x2="21" y2="9"/></svg>
|
||
<div>
|
||
<div style="font-size:11px;font-weight:700;color:#15803D">Чертежи</div>
|
||
<div style="font-size:10px;color:#86EFAC">PDF · 5 листов</div>
|
||
</div>
|
||
</a>
|
||
</div>
|
||
${rooms.map(r => `
|
||
<div style="font-size:11px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;margin:8px 0 4px">${r.room}</div>
|
||
${r.items.map(i => `
|
||
<div class="checklist-item">
|
||
<div class="check-box ${i.done?'done':''}">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg>
|
||
</div>
|
||
<div class="check-text ${i.done?'done':''}">
|
||
<h4>${i.name}</h4>
|
||
${i.detail?`<p>${i.detail}</p>`:''}
|
||
</div>
|
||
</div>
|
||
`).join('')}
|
||
`).join('')}
|
||
</div>
|
||
|
||
<div class="card">
|
||
<div style="font-size:13px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;margin-bottom:8px">Нюансы сборки</div>
|
||
<div style="background:#FFFBEB;border-radius:10px;padding:10px 12px;margin-bottom:10px">
|
||
<div style="font-size:10px;color:#D97706;font-weight:700;margin-bottom:4px">22.05.2026 · Анна Соколова (менеджер)</div>
|
||
<div style="font-size:13px;color:#92400E;line-height:1.5">Клиент просил оставить карнизы — установку не делать. Розетки на фасаде вырезать под заказ.</div>
|
||
</div>
|
||
<div style="font-size:12px;color:var(--muted);margin-bottom:6px;font-weight:600">Добавить замечание мастера:</div>
|
||
<textarea class="form-textarea" placeholder="Новое замечание..." style="min-height:60px"></textarea>
|
||
<button class="btn-sm" style="margin-top:8px;width:100%">+ Добавить</button>
|
||
</div>
|
||
|
||
<button class="btn-primary" onclick="go('checklist')">📋 Открыть чек-лист</button>
|
||
<button class="btn-secondary" onclick="go('extra_create')">📄 Акт доп. работ</button>
|
||
<button class="btn-secondary" style="margin-top:8px" onclick="go('photo_report')">📷 Фото-отчёт</button>
|
||
</div>
|
||
${nav('assemblies')}</div>`;
|
||
}
|
||
|
||
/* ─── CHAT ASM ─── */
|
||
function screenChatAsm() {
|
||
return `<div class="page" style="padding-bottom:0">
|
||
<div class="page-header">
|
||
<button class="back-btn" onclick="go('asm_detail')"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><polyline points="15 18 9 12 15 6"/></svg></button>
|
||
<div style="flex:1">
|
||
<div style="font-size:15px;font-weight:700;color:var(--ink)">Сборка #А-2847 · Группа</div>
|
||
<div style="font-size:11px;color:var(--muted)">Анна Соколова, Игорь Волков, Виктор Смирнов</div>
|
||
</div>
|
||
<div class="header-action"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M22 16.92v3a2 2 0 01-2.18 2A19.79 19.79 0 012 4.18 2 2 0 014 2h3a2 2 0 012 1.72c.127.96.361 1.903.7 2.81a2 2 0 01-.45 2.11L8.09 9.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></div>
|
||
</div>
|
||
|
||
<div style="padding:8px 0 4px;text-align:center;font-size:11px;color:var(--muted)">Сегодня, 10:30</div>
|
||
|
||
<div class="chat-wrap">
|
||
<div style="align-self:flex-start;max-width:80%">
|
||
<div style="font-size:10px;color:var(--muted);margin-bottom:3px">Анна Соколова · Менеджер</div>
|
||
<div class="bubble in">Алексей, привет! По сборке А-2847 — клиент просит уточнить насчёт розеток на фасаде, это в объём входит?</div>
|
||
<div class="bubble-meta" style="color:var(--muted);font-size:10px;margin-top:3px">10:31</div>
|
||
</div>
|
||
|
||
<div style="align-self:flex-end;max-width:80%">
|
||
<div class="bubble out">Да, вырежу — это стандарт. Но если заказной фасад, нужно подтверждение от технолога</div>
|
||
<div class="bubble-meta" style="color:var(--muted);font-size:10px;margin-top:3px;text-align:right">Вы · 10:34 ✓✓</div>
|
||
</div>
|
||
|
||
<div style="align-self:flex-start;max-width:80%">
|
||
<div style="font-size:10px;color:var(--muted);margin-bottom:3px">Анна Соколова · Менеджер</div>
|
||
<div class="bubble in">Фасад стандартный, МДФ. Игорь подтвердил — делай. И ещё: барную стойку пропустить, клиент сам потом установит.</div>
|
||
<div class="bubble-meta" style="color:var(--muted);font-size:10px;margin-top:3px">10:36</div>
|
||
</div>
|
||
|
||
<div style="align-self:flex-end;max-width:80%">
|
||
<div class="bubble out">Принял. Барная стойка — пропускаю, зафиксирую в чек-листе как «клиент устанавливает самостоятельно»</div>
|
||
<div class="bubble-meta" style="color:var(--muted);font-size:10px;margin-top:3px;text-align:right">Вы · 10:37 ✓✓</div>
|
||
</div>
|
||
|
||
<div style="align-self:flex-start;max-width:80%">
|
||
<div style="font-size:10px;color:var(--muted);margin-bottom:3px">Виктор Смирнов · Отдел рекламаций</div>
|
||
<div class="bubble in">Алексей, зафиксируйте дефект фасада по сборке. Акт рекламации нужен до конца дня — иначе срок по клиенту сорвётся.</div>
|
||
<div class="bubble-meta" style="color:var(--muted);font-size:10px;margin-top:3px">11:05</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="chat-input-row">
|
||
<input type="text" placeholder="Написать сообщение...">
|
||
<button class="btn-sm" style="border-radius:50%;width:40px;height:40px;padding:0;flex-shrink:0;display:flex;align-items:center;justify-content:center">
|
||
<svg width="18" height="18" 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('assemblies')}</div>`;
|
||
}
|
||
|
||
/* ─── CHECKLIST ─── */
|
||
function screenChecklist() {
|
||
return `<div class="page">
|
||
<div class="page-header">
|
||
<button class="back-btn" onclick="go('asm_detail')"><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="padding:12px 16px 0">
|
||
<div style="background:var(--card);border-radius:14px;padding:12px 16px;margin-bottom:16px;display:flex;align-items:center;gap:12px">
|
||
<div style="flex:1">
|
||
<div style="font-size:12px;color:var(--muted);margin-bottom:4px">Выполнено</div>
|
||
<div class="progress-bar"><div class="progress-fill" style="width:40%"></div></div>
|
||
</div>
|
||
<div style="font-size:22px;font-weight:800;color:var(--accent)">2/5</div>
|
||
</div>
|
||
|
||
<!-- Блок 1 — Проверка качества -->
|
||
<div style="font-size:13px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;margin-bottom:8px">Проверка качества</div>
|
||
<div class="card" style="margin-bottom:16px">
|
||
${[
|
||
{done:true, name:'Мусор вынесен', sub:''},
|
||
{done:false, name:'Наклейки с мебели удалены', sub:''},
|
||
{done:false, name:'Плёнка с фасадов снята', sub:''},
|
||
{done:false, name:'Мебель протёрта', sub:''},
|
||
{done:false, name:'Регулировка фасадов/петель проверена', sub:''},
|
||
].map(i => `
|
||
<div class="checklist-item">
|
||
<div class="check-box ${i.done?'done':''}" onclick="this.classList.toggle('done');this.querySelector('svg').style.display=this.classList.contains('done')?'block':'none'">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" style="display:${i.done?'block':'none'}"><polyline points="20 6 9 17 4 12"/></svg>
|
||
</div>
|
||
<div class="check-text">
|
||
<h4>${i.name}</h4>
|
||
</div>
|
||
</div>
|
||
`).join('')}
|
||
</div>
|
||
|
||
<!-- Блок 2 — Материалы и расходы -->
|
||
<div style="font-size:13px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;margin-bottom:8px">Материалы и расходы</div>
|
||
<div class="card" style="margin-bottom:16px">
|
||
<!-- Добавленные позиции -->
|
||
<div style="display:flex;align-items:center;padding:8px 0;border-bottom:1px solid rgba(0,0,0,.06)">
|
||
<div class="photo-slot" style="width:36px;height:36px;flex-shrink:0;margin-right:10px;cursor:pointer">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M23 19a2 2 0 01-2 2H3a2 2 0 01-2-2V8a2 2 0 012-2h4l2-3h6l2 3h4a2 2 0 012 2z"/><circle cx="12" cy="13" r="4"/></svg>
|
||
</div>
|
||
<div style="flex:1">
|
||
<div style="font-size:13px;font-weight:600;color:var(--ink)">Дюбели 8×60 (30 шт)</div>
|
||
<div style="font-size:12px;color:var(--muted)">180 ₽</div>
|
||
</div>
|
||
<button style="background:none;border:none;color:#CBD5E1;font-size:20px;cursor:pointer;padding:0 4px;line-height:1" onclick="this.closest('div.card').querySelectorAll('[data-item]')[0].remove()">×</button>
|
||
</div>
|
||
<div style="display:flex;align-items:center;padding:8px 0;border-bottom:1px solid rgba(0,0,0,.06)">
|
||
<div class="photo-slot filled" style="width:36px;height:36px;flex-shrink:0;margin-right:10px;font-size:18px">🧾</div>
|
||
<div style="flex:1">
|
||
<div style="font-size:13px;font-weight:600;color:var(--ink)">Силикон белый</div>
|
||
<div style="font-size:12px;color:var(--muted)">320 ₽ · <span style="color:var(--success)">чек прикреплён</span></div>
|
||
</div>
|
||
<button style="background:none;border:none;color:#CBD5E1;font-size:20px;cursor:pointer;padding:0 4px;line-height:1">×</button>
|
||
</div>
|
||
|
||
<!-- Распарсенный чек (состояние "после фото") -->
|
||
<div style="padding:10px 0;border-bottom:1px solid rgba(0,0,0,.06)">
|
||
<div style="background:#F0FDF4;border:1px solid #BBF7D0;border-radius:10px;padding:10px 12px;margin-bottom:10px;display:flex;align-items:center;gap:10px">
|
||
<div style="width:40px;height:50px;background:#DCFCE7;border-radius:6px;display:flex;align-items:center;justify-content:center;font-size:22px;flex-shrink:0">🧾</div>
|
||
<div>
|
||
<div style="font-size:13px;font-weight:700;color:var(--ink)">Леман Про</div>
|
||
<div style="font-size:12px;color:var(--muted)">20.04.2026 · 4 599 ₽</div>
|
||
<div style="font-size:11px;color:var(--success);font-weight:600;margin-top:2px">✓ Распознано 4 позиции</div>
|
||
</div>
|
||
</div>
|
||
${[
|
||
{name:'Дюбель-гвоздь 6×40 (100 шт)', price:'189 ₽', on:true},
|
||
{name:'Силикон сантехнический белый', price:'320 ₽', on:true},
|
||
{name:'Шуруп 3.5×35 (200 шт)', price:'210 ₽', on:false},
|
||
{name:'Клемма WAGO 5-поводковая (10 шт)', price:'380 ₽', on:true},
|
||
].map(i => `
|
||
<div style="display:flex;align-items:center;gap:10px;padding:8px 0;border-bottom:1px solid rgba(0,0,0,.04)">
|
||
<div class="check-box ${i.on?'done':''}" onclick="this.classList.toggle('done');this.querySelector('svg').style.display=this.classList.contains('done')?'block':'none'" style="flex-shrink:0;width:20px;height:20px;border-radius:5px">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" style="display:${i.on?'block':'none'}"><polyline points="20 6 9 17 4 12"/></svg>
|
||
</div>
|
||
<div style="flex:1;font-size:13px;color:var(--ink)">${i.name}</div>
|
||
<div style="font-size:13px;font-weight:700;color:var(--ink);flex-shrink:0">${i.price}</div>
|
||
</div>
|
||
`).join('')}
|
||
<button class="btn-sm" style="width:100%;margin-top:10px;background:var(--success)">Добавить выбранные (3) в расчёт</button>
|
||
</div>
|
||
|
||
<!-- Кнопка фото -->
|
||
<button class="btn-sm outline" style="width:100%;margin-top:10px">📷 Сфотографировать чек</button>
|
||
|
||
<!-- Ввод вручную -->
|
||
<div style="display:flex;align-items:center;gap:8px;padding:10px 0 4px">
|
||
<input class="form-input" placeholder="Название расхода" style="flex:2;padding:8px 10px;font-size:13px">
|
||
<input class="form-input" placeholder="₽" style="width:64px;padding:8px 10px;font-size:13px">
|
||
<button class="btn-sm" style="flex-shrink:0;padding:8px 10px">+</button>
|
||
</div>
|
||
<div style="display:flex;justify-content:space-between;align-items:center;padding:8px 0 4px;border-top:1px solid rgba(0,0,0,.06)">
|
||
<span style="font-size:13px;color:var(--muted)">Итого расходы</span>
|
||
<span style="font-size:16px;font-weight:800;color:var(--ink)">1 389 ₽</span>
|
||
</div>
|
||
<div style="display:flex;gap:8px;margin-top:8px">
|
||
<button class="btn-sm outline" style="flex:1;font-size:12px" onclick="go('extra_create')">📋 Прайс доп. работ</button>
|
||
<button class="btn-sm" style="flex:1;font-size:12px;background:var(--accent2)">Добавить в акт</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Блок 3 — Акты и документы -->
|
||
<div style="font-size:13px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;margin-bottom:8px">Акты и документы</div>
|
||
<div class="card" style="margin-bottom:16px">
|
||
<div class="checklist-item">
|
||
<div class="check-box" onclick="this.classList.toggle('done');this.querySelector('svg').style.display=this.classList.contains('done')?'block':'none'">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" style="display:none"><polyline points="20 6 9 17 4 12"/></svg>
|
||
</div>
|
||
<div class="check-text"><h4>Акт выполненных работ (компания) — подписан</h4></div>
|
||
</div>
|
||
<div class="checklist-item">
|
||
<div class="check-box" onclick="this.classList.toggle('done');this.querySelector('svg').style.display=this.classList.contains('done')?'block':'none'">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" style="display:none"><polyline points="20 6 9 17 4 12"/></svg>
|
||
</div>
|
||
<div class="check-text"><h4>Акт приёмки-передачи — подписан</h4></div>
|
||
</div>
|
||
<div class="checklist-item">
|
||
<div class="check-box" onclick="this.classList.toggle('done');this.querySelector('svg').style.display=this.classList.contains('done')?'block':'none'">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" style="display:none"><polyline points="20 6 9 17 4 12"/></svg>
|
||
</div>
|
||
<div class="check-text"><h4>Акт доп. работ — оформлен</h4></div>
|
||
</div>
|
||
<div style="margin-top:8px">
|
||
<button class="btn-sm danger" style="width:100%" onclick="go('reclamation')">⚠ Акт рекламации</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Блок 4 — Финальное фото -->
|
||
<div style="font-size:13px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;margin-bottom:8px">Финальное фото</div>
|
||
<div class="card" style="margin-bottom:16px">
|
||
<div style="display:flex;gap:12px;align-items:center">
|
||
<div>
|
||
<div style="font-size:11px;color:var(--muted);margin-bottom:4px">До</div>
|
||
<div class="photo-slot">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M23 19a2 2 0 01-2 2H3a2 2 0 01-2-2V8a2 2 0 012-2h4l2-3h6l2 3h4a2 2 0 012 2z"/><circle cx="12" cy="13" r="4"/></svg>
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<div style="font-size:11px;color:var(--muted);margin-bottom:4px">После</div>
|
||
<div class="photo-slot">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M23 19a2 2 0 01-2 2H3a2 2 0 01-2-2V8a2 2 0 012-2h4l2-3h6l2 3h4a2 2 0 012 2z"/><circle cx="12" cy="13" r="4"/></svg>
|
||
</div>
|
||
</div>
|
||
<div style="font-size:12px;color:var(--muted);flex:1">Нажмите для съёмки</div>
|
||
</div>
|
||
</div>
|
||
|
||
<button class="btn-primary" style="background:#CBD5E1;color:#64748B;cursor:not-allowed">Завершить сборку</button>
|
||
<div style="font-size:11px;color:var(--muted);text-align:center;margin-top:6px">Заполните все обязательные пункты</div>
|
||
</div>
|
||
${nav('assemblies')}</div>`;
|
||
}
|
||
|
||
/* ─── EXTRA LIST ─── */
|
||
function screenExtraList() {
|
||
return `<div class="page">
|
||
<div class="page-header">
|
||
<button class="back-btn" onclick="go('asm_detail')"><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 class="header-action" onclick="go('extra_create')"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg></div>
|
||
</div>
|
||
<div style="padding:12px 16px 0">
|
||
<div style="font-size:13px;color:var(--muted);margin-bottom:16px">Иванов Дмитрий · Сборка #А-2847</div>
|
||
|
||
<div style="background:#FEE2E2;border:1px solid #FECACA;border-radius:12px;padding:10px 14px;margin-bottom:12px;display:flex;align-items:flex-start;gap:8px">
|
||
<span style="font-size:16px;margin-top:1px">⚠️</span>
|
||
<div>
|
||
<div style="font-size:13px;font-weight:700;color:#DC2626">Открыт акт рекламации</div>
|
||
<div style="font-size:12px;color:#991B1B;margin-top:2px">Подписание регламентных актов недоступно до устранения замечаний</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div style="font-size:11px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;margin-bottom:8px">Регламентные акты компании</div>
|
||
|
||
<div class="card" style="opacity:.7">
|
||
<div style="display:flex;align-items:center;justify-content:space-between">
|
||
<div>
|
||
<div style="font-size:14px;font-weight:700;color:var(--ink)">📄 Акт выполненных работ</div>
|
||
<div style="font-size:12px;color:var(--muted);margin-top:2px">Подпись клиента</div>
|
||
</div>
|
||
<span class="badge yellow">Ожидает подписи</span>
|
||
</div>
|
||
<div style="margin-top:10px;padding-top:10px;border-top:1px solid rgba(0,0,0,.06)">
|
||
<div style="font-size:12px;color:#DC2626">🔒 Недоступно — есть открытая рекламация</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="card" style="opacity:.7">
|
||
<div style="display:flex;align-items:center;justify-content:space-between">
|
||
<div>
|
||
<div style="font-size:14px;font-weight:700;color:var(--ink)">📄 Акт приёмки-передачи</div>
|
||
<div style="font-size:12px;color:var(--muted);margin-top:2px">Подпись клиента</div>
|
||
</div>
|
||
<span class="badge yellow">Ожидает подписи</span>
|
||
</div>
|
||
<div style="margin-top:10px;padding-top:10px;border-top:1px solid rgba(0,0,0,.06)">
|
||
<div style="font-size:12px;color:#DC2626">🔒 Недоступно — есть открытая рекламация</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div style="font-size:11px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;margin:8px 0">Акт доп. работ</div>
|
||
|
||
<div class="card warn-border" style="cursor:pointer" onclick="go('extra_basket')">
|
||
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:8px">
|
||
<div style="font-size:15px;font-weight:700;color:var(--ink)">Акт #DA-3f2a</div>
|
||
<span class="badge yellow">Черновик</span>
|
||
</div>
|
||
<div style="font-size:13px;color:var(--muted);margin-bottom:8px">Подсветка ленточная · Посудомойка · Добор натяжного потолка</div>
|
||
<div style="display:flex;justify-content:space-between;align-items:center">
|
||
<span style="font-size:12px;color:var(--muted)">5 позиций</span>
|
||
<span style="font-size:16px;font-weight:800;color:var(--ink)">19 300 ₽</span>
|
||
</div>
|
||
<div style="margin-top:10px;padding-top:10px;border-top:1px solid rgba(0,0,0,.06);display:flex;gap:8px">
|
||
<button class="btn-sm" onclick="event.stopPropagation();go('extra_payment')">Перейти к оплате</button>
|
||
<button class="btn-sm outline" onclick="event.stopPropagation();go('extra_basket')">Редактировать</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div style="font-size:11px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;margin:8px 0">Акт рекламации</div>
|
||
|
||
<div class="card danger-border">
|
||
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:6px">
|
||
<div style="font-size:14px;font-weight:700;color:var(--ink)">Рекламация #R-0041</div>
|
||
<span class="badge red">Открыт</span>
|
||
</div>
|
||
<div style="font-size:13px;color:var(--muted)">Скол на фасаде верхнего шкафа. Требует замены.</div>
|
||
<div style="font-size:12px;color:var(--muted);margin-top:4px">Создан: 22.05.2026 · Срок: 30.05.2026</div>
|
||
</div>
|
||
|
||
<button class="btn-sm danger" style="width:100%;padding:10px" onclick="go('reclamation')">+ Создать акт рекламации</button>
|
||
</div>
|
||
${nav('extra_list')}</div>`;
|
||
}
|
||
|
||
/* ─── EXTRA CREATE (catalogue) ─── */
|
||
function screenExtraCreate() {
|
||
const cats = [
|
||
{icon:'wrench', title:'Общие работы', items:[
|
||
{name:'Выезд мастера в магазин по просьбе клиента',unit:'выезд',price:800,qty:0},
|
||
{name:'Доп. срочный выезд мастера в течение 24 часов',unit:'выезд',price:3000,qty:0},
|
||
{name:'Технологический выпил (один выпил в одной детали)',unit:'шт',price:100,qty:0},
|
||
{name:'Ложный выезд или ожидание заказчика более 45 мин',unit:'случай',price:1500,qty:0},
|
||
{name:'Вынос упаковки в коридор',unit:'бесплатно',price:0,qty:0},
|
||
{name:'Выезд за КАД',unit:'км',price:40,qty:0},
|
||
]},
|
||
{icon:'zap', title:'Подсветка', items:[
|
||
{name:'Подключение светодиодной ленты по наружной части мебели',unit:'пог.м',price:600,qty:4},
|
||
{name:'Подключение светодиодной ленты внутри мебели',unit:'линия',price:400,qty:2},
|
||
{name:'Монтаж декоративных планок на стену',unit:'пог.м',price:370,qty:0},
|
||
{name:'Фрезеровка канала для врезной подсветки',unit:'пог.м',price:1000,qty:0},
|
||
]},
|
||
{icon:'droplet', title:'Зона мойки', items:[
|
||
{name:'Демонтаж обесточенной розетки',unit:'шт',price:50,qty:2},
|
||
{name:'Демонтаж старой мойки',unit:'шт',price:500,qty:0},
|
||
{name:'Врезка накладной мойки Покупателя с обработкой выпила',unit:'шт',price:800,qty:0},
|
||
{name:'Установка мойки Покупателя (без подключения)',unit:'шт',price:500,qty:0},
|
||
{name:'Вырез отверстия под смеситель (металл)',unit:'шт',price:300,qty:0},
|
||
{name:'Установка встраиваемой посудомоечной машины Покупателя',unit:'шт',price:2000,qty:1},
|
||
{name:'Установка стиральной машины Покупателя',unit:'шт',price:2000,qty:0},
|
||
{name:'Установка мойки подстольного монтажа',unit:'шт',price:3000,qty:0},
|
||
]},
|
||
{icon:'snow', title:'Установка холодильника', items:[
|
||
{name:'Установка холодильника без перенавески дверей',unit:'шт',price:2500,qty:0},
|
||
{name:'Перенавеска дверей холодильника без электроники',unit:'шт',price:500,qty:0},
|
||
{name:'Перенавеска дверей холодильника с электроникой',unit:'шт',price:800,qty:0},
|
||
]},
|
||
{icon:'flame', title:'Варочная поверхность', items:[
|
||
{name:'Врезка варочной поверхности Покупателя с обработкой выпила',unit:'шт',price:800,qty:0},
|
||
{name:'Установка варочной панели Покупателя (без подключения)',unit:'шт',price:500,qty:0},
|
||
{name:'Вырез в столешнице под шахту / выступ',unit:'шт',price:500,qty:0},
|
||
]},
|
||
{icon:'wind', title:'Зона вытяжки', items:[
|
||
{name:'Установка купольной вытяжки 60см Покупателя',unit:'шт',price:1500,qty:0},
|
||
{name:'Установка купольной вытяжки 90см Покупателя',unit:'шт',price:2000,qty:0},
|
||
{name:'Установка встраиваемой вытяжки Покупателя',unit:'шт',price:1500,qty:0},
|
||
{name:'Подключение гофрированного воздуховода',unit:'шт',price:400,qty:0},
|
||
{name:'Подключение пластикового воздуховода с монтажём',unit:'пог.м',price:800,qty:0},
|
||
{name:'Установка фланца',unit:'шт',price:300,qty:0},
|
||
]},
|
||
{icon:'thermo', title:'Духовой шкаф / Микроволновка', items:[
|
||
{name:'Вырез под розетку',unit:'шт',price:200,qty:0},
|
||
{name:'Установка духового шкафа Покупателя в модуль',unit:'шт',price:600,qty:0},
|
||
{name:'Установка встраиваемой микроволновой печи',unit:'шт',price:1000,qty:0},
|
||
]},
|
||
{icon:'archive', title:'Шкаф', items:[
|
||
{name:'Монтаж доборов для установки натяжного потолка',unit:'пог.м',price:2000,qty:7},
|
||
{name:'Вырез под розетку / коммуникации',unit:'шт',price:200,qty:0},
|
||
{name:'Перепил декоративного элемента',unit:'шт',price:400,qty:0},
|
||
{name:'Переделка модуля по месту',unit:'шт',price:2000,qty:0},
|
||
{name:'Присадка ручек Покупателя',unit:'отверстие',price:40,qty:0},
|
||
{name:'Установка ручек Покупателя',unit:'шт',price:40,qty:0},
|
||
]},
|
||
{icon:'star4', title:'Доп. работы мастера (личный прайс ИП)', items:[
|
||
{name:'Установка и подключение розетки',unit:'шт',price:200,qty:0},
|
||
{name:'Подключение варочной поверхности',unit:'шт',price:800,qty:0},
|
||
{name:'Подключение посудомоечной машины',unit:'шт',price:1600,qty:0},
|
||
{name:'Подключение стиральной машины',unit:'шт',price:1600,qty:0},
|
||
{name:'Подключение смесителя',unit:'шт',price:1600,qty:0},
|
||
{name:'Подключение слива мойки',unit:'шт',price:1000,qty:0},
|
||
{name:'Установка и подключение измельчителя',unit:'шт',price:2500,qty:0},
|
||
{name:'Установка фасадной петли',unit:'шт',price:200,qty:0},
|
||
{name:'Вырез в ЛДСП с кромлением',unit:'шт',price:1000,qty:0},
|
||
{name:'Установка столешницы с заходом в подоконник',unit:'шт',price:3000,qty:0},
|
||
{name:'Присадка и монтаж полки скрытого монтажа',unit:'шт',price:700,qty:0},
|
||
]},
|
||
];
|
||
return `<div class="page">
|
||
<div class="page-header">
|
||
<button class="back-btn" onclick="go('extra_list')"><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:10px 16px 0">
|
||
<div style="position:relative">
|
||
<svg style="position:absolute;left:12px;top:50%;transform:translateY(-50%);color:var(--muted)" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
|
||
<input class="form-input" placeholder="Поиск работы..." style="padding-left:36px">
|
||
</div>
|
||
</div>
|
||
|
||
${cats.map(cat => `
|
||
<div style="background:var(--card);margin:8px 16px 0;border-radius:16px;overflow:hidden;box-shadow:0 2px 12px rgba(0,0,0,.07)">
|
||
<div class="accordion-header">
|
||
<span style="display:flex;align-items:center;gap:8px">${svgIcon(cat.icon,16,'var(--accent)')} ${cat.title}</span>
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><polyline points="6 9 12 15 18 9"/></svg>
|
||
</div>
|
||
<div class="accordion-body">
|
||
${cat.items.map(i => `
|
||
<div style="display:flex;align-items:center;justify-content:space-between;padding:9px ${i.qty>0?'8px':'0'};border-bottom:1px solid #F1F5F9;border-radius:${i.qty>0?'8px':'0'};background:${i.qty>0?'rgba(118,189,34,.08)':'transparent'};margin:${i.qty>0?'2px -4px':'0'};transition:all .2s">
|
||
<div style="flex:1;min-width:0;margin-right:8px">
|
||
<div style="font-size:13px;font-weight:600;color:var(--ink);line-height:1.35">${i.name}</div>
|
||
<div style="font-size:12px;color:var(--muted)">${i.price>0?i.price+' ₽ / '+i.unit:i.unit}</div>
|
||
</div>
|
||
<div style="flex-shrink:0;display:flex;flex-direction:column;align-items:flex-end;gap:3px">
|
||
<div class="qty-stepper">
|
||
<button class="qty-btn" style="${i.qty>0?'border-color:var(--accent2);color:var(--accent2)':''}">−</button>
|
||
<span style="font-size:14px;font-weight:700;color:${i.qty>0?'var(--accent2)':'var(--ink)'};min-width:18px;text-align:center">${i.qty}</span>
|
||
<button class="qty-btn" style="${i.qty>0?'border-color:var(--accent2);color:var(--accent2)':''}">+</button>
|
||
</div>
|
||
${i.qty>0&&i.price>0?'<div style="font-size:11px;font-weight:700;color:var(--accent2)">= '+(i.qty*i.price).toLocaleString('ru')+' ₽</div>':''}
|
||
</div>
|
||
</div>
|
||
`).join('')}
|
||
</div>
|
||
</div>
|
||
`).join('')}
|
||
|
||
<div style="height:70px"></div>
|
||
<div class="sticky-cart">
|
||
<div>
|
||
<div style="font-size:12px;color:var(--muted)">В корзине</div>
|
||
<div style="font-size:16px;font-weight:800;color:var(--ink)">5 позиций · 19 300 ₽</div>
|
||
</div>
|
||
<button class="btn-sm" onclick="go('extra_basket')" style="padding:10px 20px">Далее →</button>
|
||
</div>
|
||
${nav('extra_list')}</div>`;
|
||
}
|
||
|
||
/* ─── EXTRA BASKET ─── */
|
||
function screenExtraBasket() {
|
||
const items = [
|
||
{name:'Подключение светодиодной ленты по наружной части мебели',unit:'пог.м',qty:4,price:600},
|
||
{name:'Подключение светодиодной ленты внутри мебели',unit:'линия',qty:2,price:400},
|
||
{name:'Демонтаж обесточенной розетки',unit:'шт',qty:2,price:50},
|
||
{name:'Установка встраиваемой посудомоечной машины Покупателя',unit:'шт',qty:1,price:2000},
|
||
{name:'Монтаж доборов для установки натяжного потолка',unit:'пог.м',qty:7,price:2000},
|
||
];
|
||
return `<div class="page">
|
||
<div class="page-header">
|
||
<button class="back-btn" onclick="go('extra_create')"><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:10px 16px 0">
|
||
<div style="font-size:12px;color:var(--muted);margin-bottom:10px">Сборка #А-2847 · Иванов Дмитрий</div>
|
||
<div class="card">
|
||
${items.map(i => `
|
||
<div class="price-item">
|
||
<div style="flex:1;min-width:0;margin-right:8px">
|
||
<div style="font-size:13px;font-weight:600;color:var(--ink);line-height:1.35">${i.name}</div>
|
||
<div style="font-size:12px;color:var(--muted)">${i.qty} ${i.unit} × ${i.price} ₽</div>
|
||
</div>
|
||
<div style="text-align:right;flex-shrink:0">
|
||
<div style="font-size:14px;font-weight:700;color:var(--ink)">${i.qty*i.price} ₽</div>
|
||
<div class="qty-stepper" style="margin-top:4px;justify-content:flex-end">
|
||
<button class="qty-btn">−</button>
|
||
<span style="font-size:14px;font-weight:700;color:var(--ink);min-width:18px;text-align:center">${i.qty}</span>
|
||
<button class="qty-btn">+</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
`).join('')}
|
||
<div style="margin-top:10px;padding-top:10px;border-top:2px solid rgba(0,0,0,.08);display:flex;justify-content:space-between;align-items:center">
|
||
<span style="font-size:15px;font-weight:700;color:var(--ink)">Итого</span>
|
||
<span style="font-size:22px;font-weight:800;color:var(--accent)">19 300 ₽</span>
|
||
</div>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Примечание</label>
|
||
<textarea class="form-textarea" style="min-height:60px" placeholder="Описание выполненных работ...">Монтаж натяжного потолка — добор 7 пог.м. Подсветка ленточная наружная и внутри. Посудомойка встраиваемая установлена.</textarea>
|
||
</div>
|
||
<div style="text-align:right;margin-bottom:8px">
|
||
<span style="font-size:13px;color:var(--accent);cursor:pointer;font-weight:600" onclick="go('extra_create')">← Изменить состав</span>
|
||
</div>
|
||
<button class="btn-primary" onclick="go('extra_sign')">Подписать акт →</button>
|
||
<button class="btn-secondary" onclick="go('extra_payment')">Перейти к оплате →</button>
|
||
</div>
|
||
${nav('extra_list')}</div>`;
|
||
}
|
||
|
||
/* ─── EXTRA SIGN ─── */
|
||
function screenExtraSign() {
|
||
var signerType = window._signerType || 'client';
|
||
var contacts = window._signContacts || [
|
||
{name:'Иванов Дмитрий', role:'client', phone:'+7 921 100-00-01', tg:'@ivanov_dm', wa:true},
|
||
];
|
||
|
||
function contactRow(c, idx) {
|
||
var isContract = c.role === 'client';
|
||
return '<div style="display:flex;align-items:center;gap:10px;padding:10px 0;border-bottom:1px solid rgba(0,0,0,.05)">'
|
||
+ '<div style="width:34px;height:34px;border-radius:50%;background:' + (isContract?'rgba(0,62,126,.12)':'rgba(245,158,11,.12)') + ';display:flex;align-items:center;justify-content:center;flex-shrink:0">'
|
||
+ '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="' + (isContract?'var(--accent)':'#d97706') + '" 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>'
|
||
+ '</div>'
|
||
+ '<div style="flex:1;min-width:0">'
|
||
+ '<div style="font-size:13px;font-weight:600;color:var(--ink)">' + c.name + '</div>'
|
||
+ '<div style="font-size:11px;color:var(--muted);margin-top:1px">' + (isContract?'По договору':'Представитель') + ' · ' + c.phone + '</div>'
|
||
+ '</div>'
|
||
+ '<div style="display:flex;gap:5px;flex-shrink:0">'
|
||
+ '<button onclick="alert(\'Переслать акт в Telegram: ' + (c.tg||c.name) + '\')" style="background:rgba(37,211,102,.1);border:1px solid rgba(37,211,102,.25);color:#16a34a;border-radius:7px;padding:5px 8px;font-size:11px;font-weight:700;cursor:pointer">TG</button>'
|
||
+ (c.wa ? '<button onclick="alert(\'Переслать акт в WhatsApp: ' + c.phone + '\')" style="background:rgba(37,211,102,.1);border:1px solid rgba(37,211,102,.25);color:#16a34a;border-radius:7px;padding:5px 8px;font-size:11px;font-weight:700;cursor:pointer">WA</button>' : '')
|
||
+ '<button onclick="alert(\'Позвонить: ' + c.phone + '\')" style="background:rgba(0,62,126,.06);border:1px solid rgba(0,62,126,.15);color:var(--accent);border-radius:7px;padding:5px 8px;font-size:11px;font-weight:700;cursor:pointer">☎</button>'
|
||
+ '</div>'
|
||
+ '</div>';
|
||
}
|
||
|
||
return `<div class="page">
|
||
<div class="page-header">
|
||
<button class="back-btn" onclick="go('extra_list')"><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>
|
||
<button onclick="alert('Переслать акт целиком — выбрать получателя')" style="display:flex;align-items:center;gap:5px;background:var(--accent);border:none;color:#fff;border-radius:8px;padding:6px 12px;font-size:12px;font-weight:700;cursor:pointer">
|
||
↗ Переслать
|
||
</button>
|
||
</div>
|
||
<div style="padding:16px">
|
||
|
||
<!-- Документы -->
|
||
<div class="card" style="padding:10px 14px;margin-bottom:14px">
|
||
<div style="font-size:10px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.5px;margin-bottom:8px">Подписывается</div>
|
||
<div style="display:flex;align-items:center;gap:8px;padding:5px 0;border-bottom:1px solid rgba(0,0,0,.05)">
|
||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="var(--accent)" stroke-width="2"><path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>
|
||
<span style="font-size:13px;font-weight:600;color:var(--ink)">Акт выполненных работ · #DA-3f2a</span>
|
||
</div>
|
||
<div style="display:flex;align-items:center;gap:8px;padding:5px 0">
|
||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="var(--accent)" stroke-width="2"><path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>
|
||
<span style="font-size:13px;font-weight:600;color:var(--ink)">Акт приёмки-передачи мебели</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- КТО ПОДПИСЫВАЕТ -->
|
||
<div style="font-size:11px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.5px;margin-bottom:8px">Кто подписывает</div>
|
||
<div style="display:flex;gap:6px;margin-bottom:12px">
|
||
<div onclick="(function(){window._signerType='client';refresh()})()" style="flex:1;text-align:center;padding:8px 6px;border-radius:8px;border:2px solid ${signerType==='client'?'var(--accent)':'rgba(0,0,0,.1)'};background:${signerType==='client'?'rgba(0,62,126,.06)':'transparent'};cursor:pointer">
|
||
<div style="font-size:18px;margin-bottom:2px">👤</div>
|
||
<div style="font-size:11px;font-weight:700;color:${signerType==='client'?'var(--accent)':'var(--muted)'}">Клиент лично</div>
|
||
<div style="font-size:10px;color:var(--muted);margin-top:1px">на экране</div>
|
||
</div>
|
||
<div onclick="(function(){window._signerType='delegate';refresh()})()" style="flex:1;text-align:center;padding:8px 6px;border-radius:8px;border:2px solid ${signerType==='delegate'?'#d97706':'rgba(0,0,0,.1)'};background:${signerType==='delegate'?'rgba(245,158,11,.06)':'transparent'};cursor:pointer">
|
||
<div style="font-size:18px;margin-bottom:2px">🤝</div>
|
||
<div style="font-size:11px;font-weight:700;color:${signerType==='delegate'?'#d97706':'var(--muted)'}">Представитель</div>
|
||
<div style="font-size:10px;color:var(--muted);margin-top:1px">по паспорту</div>
|
||
</div>
|
||
<div onclick="(function(){window._signerType='remote';refresh()})()" style="flex:1;text-align:center;padding:8px 6px;border-radius:8px;border:2px solid ${signerType==='remote'?'#7c3aed':'rgba(0,0,0,.1)'};background:${signerType==='remote'?'rgba(124,58,237,.06)':'transparent'};cursor:pointer">
|
||
<div style="font-size:18px;margin-bottom:2px">📲</div>
|
||
<div style="font-size:11px;font-weight:700;color:${signerType==='remote'?'#7c3aed':'var(--muted)'}">Удалённо</div>
|
||
<div style="font-size:10px;color:var(--muted);margin-top:1px">через мессенджер</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- БЛОК ПО ТИПУ ПОДПИСИ -->
|
||
${(function(){
|
||
if (signerType === 'client') {
|
||
return '<div style="margin-bottom:14px">'
|
||
+ '<div style="font-size:13px;font-weight:600;color:var(--ink);margin-bottom:6px">Роспись клиента</div>'
|
||
+ '<div class="signature-canvas"><span>Попросите клиента расписаться здесь</span></div>'
|
||
+ '<div style="display:flex;justify-content:flex-end;margin-top:6px">'
|
||
+ '<button class="btn-sm outline" style="font-size:12px;padding:6px 12px">Очистить</button>'
|
||
+ '</div></div>';
|
||
}
|
||
if (signerType === 'delegate') {
|
||
return '<div class="card" style="margin-bottom:14px;border:1.5px solid rgba(245,158,11,.3)">'
|
||
+ '<div style="font-size:11px;font-weight:700;color:#d97706;text-transform:uppercase;letter-spacing:.5px;margin-bottom:10px">Данные представителя</div>'
|
||
+ '<input class="form-input" placeholder="ФИО представителя" style="margin-bottom:8px">'
|
||
+ '<input class="form-input" placeholder="Телефон представителя" style="margin-bottom:10px">'
|
||
+ '<div class="warn-amber" style="margin-bottom:10px">'
|
||
+ '⚖ <b>Юрий:</b> Доверенностей обычно нет. Достаточно зафиксировать ФИО и сфотографировать паспорт — это подтвердит личность подписанта при спорной ситуации.'
|
||
+ '</div>'
|
||
+ '<button class="btn-sm outline" style="font-size:12px;width:100%;margin-bottom:10px">📷 Сфотографировать паспорт</button>'
|
||
+ '<div style="font-size:13px;font-weight:600;color:var(--ink);margin-bottom:6px">Роспись представителя</div>'
|
||
+ '<div class="signature-canvas"><span>Представитель расписывается здесь</span></div>'
|
||
+ '</div>';
|
||
}
|
||
// remote
|
||
return '<div class="card" style="margin-bottom:14px;border:1.5px solid rgba(124,58,237,.25)">'
|
||
+ '<div style="font-size:11px;font-weight:700;color:#7c3aed;text-transform:uppercase;letter-spacing:.5px;margin-bottom:10px">Удалённая подпись · выбрать контакт</div>'
|
||
+ contacts.map(function(c){ return contactRow(c, 0); }).join('')
|
||
+ '<button onclick="alert(\'Добавить контакт для подписания\')" style="display:flex;align-items:center;justify-content:center;gap:6px;width:100%;background:transparent;border:1.5px dashed rgba(0,0,0,.15);border-radius:8px;padding:9px;font-size:12px;font-weight:600;color:var(--muted);cursor:pointer;margin-top:8px">+ Добавить контакт</button>'
|
||
+ '<div class="warn-purple" style="margin-top:10px">'
|
||
+ '📲 Клиент получит ссылку на акт → нарисует подпись на своём телефоне → подпись и геометка фиксируются в системе'
|
||
+ '</div>'
|
||
+ '<div class="warn-amber" style="margin-top:6px">'
|
||
+ '⚖ <b>Юрий:</b> Простая электронная подпись через мессенджер допустима для актов оказания услуг между физлицами (ст.6 63-ФЗ), если стороны заранее договорились об этом. Рекомендуется зафиксировать согласие в договоре.'
|
||
+ '</div>'
|
||
+ '</div>';
|
||
})()}
|
||
|
||
<!-- КОНТАКТЫ ДЛЯ ПЕРЕСЛАТЬ -->
|
||
<div style="font-size:11px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.5px;margin-bottom:8px">Контакты клиента</div>
|
||
<div class="card" style="padding:0 14px;margin-bottom:14px">
|
||
${contacts.map(function(c,i){ return contactRow(c,i); }).join('')}
|
||
<div style="padding:10px 0">
|
||
<button onclick="alert('Добавить телефон / мессенджер клиента')" style="display:flex;align-items:center;gap:6px;background:transparent;border:none;color:var(--accent);font-size:13px;font-weight:600;cursor:pointer;padding:0">
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
|
||
Добавить контакт
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<button class="btn-primary" onclick="go('extra_done')">✅ Подтвердить подпись</button>
|
||
|
||
<div style="margin-top:12px;padding-top:12px;border-top:1px solid rgba(0,0,0,.07)">
|
||
<div style="font-size:13px;color:var(--muted);margin-bottom:8px">Клиент отказывается подписывать?</div>
|
||
<button class="btn-sm danger" style="width:100%;padding:10px">Зафиксировать отказ</button>
|
||
</div>
|
||
</div>
|
||
${nav('extra_list')}</div>`;
|
||
}
|
||
|
||
/* ─── EXTRA PAYMENT ─── */
|
||
function screenExtraPayment() {
|
||
return `<div class="page">
|
||
<div class="page-header">
|
||
<button class="back-btn" onclick="go('extra_basket')"><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:13px;color:var(--muted);margin-bottom:4px">К оплате</div>
|
||
<div style="font-size:36px;font-weight:800;color:var(--ink);margin-bottom:12px">19 300 ₽</div>
|
||
<div style="text-align:left;border-top:1px solid rgba(0,0,0,.07);padding-top:12px">
|
||
<div style="display:flex;justify-content:space-between;font-size:13px;color:var(--muted);margin-bottom:6px">
|
||
<span>Подсветка наружная × 4 пог.м</span><span style="color:var(--ink);font-weight:600">2 400 ₽</span>
|
||
</div>
|
||
<div style="display:flex;justify-content:space-between;font-size:13px;color:var(--muted);margin-bottom:6px">
|
||
<span>Подсветка внутри × 2 линии</span><span style="color:var(--ink);font-weight:600">800 ₽</span>
|
||
</div>
|
||
<div style="display:flex;justify-content:space-between;font-size:13px;color:var(--muted);margin-bottom:6px">
|
||
<span>Демонтаж розеток × 2</span><span style="color:var(--ink);font-weight:600">100 ₽</span>
|
||
</div>
|
||
<div style="display:flex;justify-content:space-between;font-size:13px;color:var(--muted);margin-bottom:6px">
|
||
<span>Посудомойка встраиваемая × 1</span><span style="color:var(--ink);font-weight:600">2 000 ₽</span>
|
||
</div>
|
||
<div style="display:flex;justify-content:space-between;font-size:13px;color:var(--muted)">
|
||
<span>Доборы натяжного потолка × 7 пм</span><span style="color:var(--ink);font-weight:600">14 000 ₽</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div style="background:#F1F5F9;border-radius:12px;padding:14px 16px;margin-bottom:16px;text-align:center">
|
||
<div style="font-size:11px;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;margin-bottom:6px">Документ выставлен</div>
|
||
<div style="font-size:15px;font-weight:700;color:var(--ink)">ИП Кириллов А.В.</div>
|
||
<div style="font-size:12px;color:var(--muted)">ИНН 781234567890 · Акт #DA-3f2a</div>
|
||
<div style="margin-top:10px;border:2px dashed #CBD5E1;border-radius:8px;padding:8px;font-size:12px;color:var(--muted);font-style:italic">[ Факсимиле ИП Кириллов А.В. ]</div>
|
||
</div>
|
||
|
||
<div style="background:var(--card);border-radius:16px;padding:20px;text-align:center;margin-bottom:16px;box-shadow:0 2px 12px rgba(0,0,0,.07)">
|
||
<div style="width:140px;height:140px;background:#F1F5F9;border-radius:12px;margin:0 auto 12px;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px">
|
||
<svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" style="color:var(--muted)"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/><path d="M14 14h.01M18 14h.01M14 18h.01M18 18h.01"/></svg>
|
||
<span style="font-size:11px;color:var(--muted)">Сканировать для оплаты</span>
|
||
</div>
|
||
<div style="font-size:13px;color:var(--muted)">СБП · Кириллов А.В. · 19 300 ₽</div>
|
||
</div>
|
||
|
||
<button class="btn-primary" style="background:var(--success);margin-bottom:8px" onclick="this.textContent='✅ Оплата подтверждена';this.style.background='#059669'">
|
||
Открыть в приложении банка
|
||
</button>
|
||
|
||
<div style="font-size:12px;color:var(--muted);text-align:center;margin:10px 0 6px">или отправить клиенту:</div>
|
||
<div style="display:flex;gap:8px;margin-bottom:12px">
|
||
<button class="btn-sm outline" style="flex:1;font-size:12px">💬 Текстом</button>
|
||
<button class="btn-sm outline" style="flex:1;font-size:12px">📄 Файлом</button>
|
||
<button class="btn-sm outline" style="flex:1;font-size:12px">🖼 Фото</button>
|
||
</div>
|
||
|
||
<div style="text-align:center">
|
||
<span class="badge yellow">Ожидает оплаты</span>
|
||
</div>
|
||
</div>
|
||
${nav('extra_list')}</div>`;
|
||
}
|
||
|
||
/* ─── EXTRA DONE ─── */
|
||
function screenExtraDone() {
|
||
return `<div class="page">
|
||
<div style="padding:60px 24px 24px;text-align:center">
|
||
<div style="width:80px;height:80px;background:#DCFCE7;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:40px;margin:0 auto 20px">✅</div>
|
||
<div style="font-size:24px;font-weight:800;color:var(--ink);margin-bottom:8px">Акт подписан!</div>
|
||
<div style="font-size:15px;color:var(--muted);line-height:1.5;margin-bottom:32px">Акт #DA-3f2a на сумму <b style="color:var(--ink)">19 300 ₽</b> подписан Ивановым Дмитрием</div>
|
||
|
||
<div class="card" style="text-align:left;margin-bottom:24px">
|
||
<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">19 300 ₽</span></div>
|
||
<div class="info-row"><span class="info-label">Подписан</span><span class="info-val">22.05.2026, 11:58</span></div>
|
||
<div class="info-row"><span class="info-label">Способ</span><span class="info-val">Подпись canvas</span></div>
|
||
</div>
|
||
|
||
<button class="btn-primary" style="background:var(--success);margin-bottom:8px" onclick="this.textContent='✅ Отправлено';this.style.background='#059669';this.style.pointerEvents='none'">
|
||
📤 Отправить заказчику
|
||
</button>
|
||
<button class="btn-primary" onclick="go('asm_detail')">← Вернуться к сборке</button>
|
||
<button class="btn-secondary" onclick="go('extra_list')">Все акты</button>
|
||
</div>
|
||
${nav('extra_list')}</div>`;
|
||
}
|
||
|
||
/* ─── RECLAMATION ─── */
|
||
function screenReclamation() {
|
||
const actions = [
|
||
{num:1, action:'Связаться с клиентом и согласовать время замены', who:'Менеджер'},
|
||
{num:2, action:'Заказать новый фасад верхнего шкафа у технолога', who:'Технолог'},
|
||
{num:3, action:'Установить замену при повторном выезде', who:'Сборщик'},
|
||
];
|
||
return `<div class="page">
|
||
<div class="page-header">
|
||
<button class="back-btn" onclick="go('extra_list')"><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 style="background:#FEE2E2;border:1px solid #FECACA;border-radius:12px;padding:10px 14px;margin-bottom:16px;font-size:13px;color:#991B1B">
|
||
⚠️ Рекламация блокирует подписание регламентных актов до устранения
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label>Что не так</label>
|
||
<textarea class="form-textarea" placeholder="Опишите дефект подробно..." style="min-height:90px"></textarea>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label>Действия по устранению</label>
|
||
${actions.map(item => `
|
||
<div style="display:flex;align-items:flex-start;gap:10px;padding:10px 0;border-bottom:1px solid rgba(0,0,0,.06)">
|
||
<div style="width:24px;height:24px;border-radius:50%;background:var(--danger);color:#fff;display:flex;align-items:center;justify-content:center;font-size:12px;font-weight:700;flex-shrink:0;margin-top:1px">${item.num}</div>
|
||
<div style="flex:1">
|
||
<div style="font-size:13px;color:var(--ink);margin-bottom:4px">${item.action}</div>
|
||
<div style="font-size:11px;color:var(--muted)">Ответственный: <b style="color:var(--accent)">${item.who}</b></div>
|
||
</div>
|
||
</div>
|
||
`).join('')}
|
||
<button class="btn-sm outline" style="width:100%;margin-top:8px">+ Добавить действие</button>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label>Срок устранения</label>
|
||
<input type="date" class="form-input" value="2026-05-30">
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label>Фото дефекта</label>
|
||
<div style="display:flex;gap:10px">
|
||
<div class="photo-slot">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M23 19a2 2 0 01-2 2H3a2 2 0 01-2-2V8a2 2 0 012-2h4l2-3h6l2 3h4a2 2 0 012 2z"/><circle cx="12" cy="13" r="4"/></svg>
|
||
</div>
|
||
<div class="photo-slot">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M23 19a2 2 0 01-2 2H3a2 2 0 01-2-2V8a2 2 0 012-2h4l2-3h6l2 3h4a2 2 0 012 2z"/><circle cx="12" cy="13" r="4"/></svg>
|
||
</div>
|
||
<div style="font-size:12px;color:var(--muted);align-self:center">Нажмите для съёмки</div>
|
||
</div>
|
||
</div>
|
||
|
||
<button class="btn-primary" style="background:var(--danger)" onclick="
|
||
this.textContent='✅ Акт рекламации создан, менеджер уведомлён';
|
||
this.style.background='var(--success)';
|
||
this.style.pointerEvents='none';
|
||
">Отправить менеджеру</button>
|
||
</div>
|
||
${nav('extra_list')}</div>`;
|
||
}
|
||
|
||
/* ─── PHOTO REPORT ─── */
|
||
function screenPhotoReport() {
|
||
return `<div class="page">
|
||
<div class="page-header">
|
||
<button class="back-btn" onclick="go('asm_detail')"><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:12px 16px 0">
|
||
<div style="font-size:13px;color:var(--muted);margin-bottom:16px">Сборка #А-2847 · Сфотографируйте результат</div>
|
||
|
||
${[
|
||
{label:'До работ', slots:['🏚', null]},
|
||
{label:'В процессе', slots:['🔧', '🔩']},
|
||
{label:'Результат', slots:[null, null]},
|
||
].map(section => `
|
||
<div class="section-label" style="margin:0 0 8px">${section.label}</div>
|
||
<div style="display:flex;gap:10px;margin-bottom:16px">
|
||
${section.slots.map(s => `
|
||
<div class="photo-slot ${s?'filled':''}">${s||`<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M23 19a2 2 0 01-2 2H3a2 2 0 01-2-2V8a2 2 0 012-2h4l2-3h6l2 3h4a2 2 0 012 2z"/><circle cx="12" cy="13" r="4"/></svg>`}</div>
|
||
`).join('')}
|
||
<div style="font-size:12px;color:var(--muted);align-self:center;flex:1">Нажмите для съёмки</div>
|
||
</div>
|
||
`).join('')}
|
||
|
||
<div class="form-group">
|
||
<label>Комментарий</label>
|
||
<textarea class="form-textarea" placeholder="Описание выполненной работы..."></textarea>
|
||
</div>
|
||
|
||
<button class="btn-primary" style="background:var(--success)" onclick="go('rate_team')">✅ Завершить сборку</button>
|
||
<div style="text-align:center;margin-top:12px;font-size:12px;color:var(--muted)">Фото отправятся менеджеру автоматически</div>
|
||
</div>
|
||
${nav('assemblies')}</div>`;
|
||
}
|
||
|
||
/* ─── PROFILE ─── */
|
||
function screenProfile() {
|
||
const weekDays = [
|
||
{d:'18',label:'Пн',c:null,today:false,m:false},
|
||
{d:'19',label:'Вт',c:'#10B981',today:false,m:true},
|
||
{d:'20',label:'Ср',c:null,today:false,m:false},
|
||
{d:'21',label:'Чт',c:'#10B981',today:false,m:false},
|
||
{d:'22',label:'Пт',c:'#F59E0B',today:true,m:true},
|
||
{d:'23',label:'Сб',c:'#003E7E',today:false,m:false},
|
||
{d:'24',label:'Вс',c:null,today:false,m:false},
|
||
];
|
||
return `<div class="page">
|
||
<div class="page-header">
|
||
<h2>Профиль</h2>
|
||
<div class="header-action"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M11 4H4a2 2 0 00-2 2v14a2 2 0 002 2h14a2 2 0 002-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 013 3L12 15l-4 1 1-4 9.5-9.5z"/></svg></div>
|
||
</div>
|
||
<div style="padding:20px 16px 0">
|
||
|
||
<div style="display:flex;align-items:center;gap:16px;margin-bottom:20px">
|
||
<div class="avatar lg" style="font-size:28px;background:var(--accent2);color:var(--ink)">АК</div>
|
||
<div>
|
||
<div style="font-size:20px;font-weight:800;color:var(--ink)">Алексей Кириллов</div>
|
||
<div style="font-size:14px;color:var(--muted)">Сборщик · Старший</div>
|
||
<div style="margin-top:6px;display:flex;gap:6px">
|
||
<span class="badge green">Активен</span>
|
||
<span class="badge blue">ИП</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Категория и загрузка -->
|
||
<div class="card" style="padding:14px 16px;margin-bottom:12px">
|
||
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:10px">
|
||
<div style="display:flex;align-items:center;gap:8px">
|
||
<span style="background:#4338CA;color:#fff;font-size:11px;font-weight:800;padding:3px 10px;border-radius:20px;letter-spacing:.03em">Кат.А</span>
|
||
<span style="font-size:12px;color:var(--muted)">Присвоена 12.01.2026</span>
|
||
</div>
|
||
<button onclick="go('my_questionnaire')" style="font-size:12px;font-weight:700;color:var(--accent);background:rgba(0,62,126,.07);border:none;border-radius:8px;padding:5px 10px;cursor:pointer">Анкета →</button>
|
||
</div>
|
||
<div style="display:flex;gap:8px;margin-bottom:10px">
|
||
<div style="flex:1;background:rgba(67,56,202,.07);border-radius:8px;padding:8px 10px;text-align:center">
|
||
<div style="font-size:18px;font-weight:800;color:#4338CA">85%</div>
|
||
<div style="font-size:10px;color:var(--muted);font-weight:600;margin-top:1px">загрузка</div>
|
||
</div>
|
||
<div style="flex:1;background:rgba(0,62,126,.07);border-radius:8px;padding:8px 10px;text-align:center">
|
||
<div style="font-size:18px;font-weight:800;color:var(--accent)">2 500 ₽</div>
|
||
<div style="font-size:10px;color:var(--muted);font-weight:600;margin-top:1px">ставка</div>
|
||
</div>
|
||
<div style="flex:1;background:rgba(16,185,129,.07);border-radius:8px;padding:8px 10px;text-align:center">
|
||
<div style="font-size:18px;font-weight:800;color:#059669">3/д</div>
|
||
<div style="font-size:10px;color:var(--muted);font-weight:600;margin-top:1px">макс. заказов</div>
|
||
</div>
|
||
</div>
|
||
<div style="height:6px;border-radius:99px;background:rgba(0,0,0,.07);overflow:hidden">
|
||
<div style="height:100%;width:85%;border-radius:99px;background:linear-gradient(90deg,#4338CA,#6366F1)"></div>
|
||
</div>
|
||
<div style="font-size:10px;color:var(--muted);margin-top:4px">Кат.А ≥ Кат.Б · Средняя Кат.Б: 54% ✓</div>
|
||
</div>
|
||
|
||
<div class="earnings-card">
|
||
<div style="display:flex;justify-content:space-between">
|
||
<div>
|
||
<div style="font-size:12px;color:rgba(255,255,255,.65);margin-bottom:4px">Заработок за май</div>
|
||
<div style="font-size:28px;font-weight:800">28 400 ₽</div>
|
||
</div>
|
||
<div style="text-align:right">
|
||
<div style="font-size:12px;color:rgba(255,255,255,.65);margin-bottom:4px">Рейтинг</div>
|
||
<div style="font-size:28px;font-weight:800">4.9 ⭐</div>
|
||
</div>
|
||
</div>
|
||
<div style="display:flex;gap:16px;margin-top:14px;padding-top:14px;border-top:1px solid rgba(255,255,255,.2)">
|
||
<div><div style="font-size:11px;color:rgba(255,255,255,.6)">Сборок</div><div style="font-size:16px;font-weight:700">12</div></div>
|
||
<div><div style="font-size:11px;color:rgba(255,255,255,.6)">Актов доп.</div><div style="font-size:16px;font-weight:700">7</div></div>
|
||
<div><div style="font-size:11px;color:rgba(255,255,255,.6)">Доп. выручка</div><div style="font-size:16px;font-weight:700">3 200 ₽</div></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Тикер текущей недели -->
|
||
<div class="card" style="padding:12px;margin-bottom:12px">
|
||
<div style="font-size:11px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;margin-bottom:10px">Текущая неделя</div>
|
||
<div style="display:grid;grid-template-columns:repeat(7,1fr);text-align:center;gap:4px">
|
||
${weekDays.map(({d,label,c,today,m}) => `
|
||
<div style="display:flex;flex-direction:column;align-items:center;gap:3px">
|
||
<div style="font-size:10px;color:var(--muted);font-weight:600">${label}</div>
|
||
<div style="width:34px;border-radius:8px;padding:4px 2px;display:flex;flex-direction:column;align-items:center;background:${today?'var(--accent)':c?c:'transparent'};border:${today||c?'none':'1.5px solid #E2E8F0'}">
|
||
<div style="font-size:13px;font-weight:${today?800:700};color:${today||c?'#fff':'var(--ink)'}">${d}</div>
|
||
${m?`<div style="font-size:8px;font-weight:700;color:${today||c?'rgba(255,255,255,.8)':'var(--accent2)'}">замер</div>`:'<div style="height:10px"></div>'}
|
||
</div>
|
||
</div>
|
||
`).join('')}
|
||
</div>
|
||
<div style="display:flex;gap:12px;margin-top:10px;padding-top:8px;border-top:1px solid rgba(0,0,0,.06);flex-wrap:wrap">
|
||
${[
|
||
{c:'#10B981',label:'Завершена'},
|
||
{c:'#F59E0B',label:'В процессе'},
|
||
{c:'#003E7E',label:'Запланирована'},
|
||
{c:null,label:'Замер',accent:true},
|
||
].map(l => `
|
||
<div style="display:flex;align-items:center;gap:5px">
|
||
${l.accent
|
||
? `<div style="width:8px;height:8px;border-radius:2px;background:var(--accent2)"></div>`
|
||
: `<div style="width:10px;height:10px;border-radius:3px;background:${l.c}"></div>`
|
||
}
|
||
<span style="font-size:11px;color:var(--muted)">${l.label}</span>
|
||
</div>
|
||
`).join('')}
|
||
</div>
|
||
</div>
|
||
|
||
<div class="card">
|
||
<div style="font-size:13px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;margin-bottom:12px">Загрузка по месяцам</div>
|
||
<table class="stat-table">
|
||
<thead><tr>
|
||
<th>Месяц</th><th>Раб. дней</th><th>Факт</th><th>Коэф.</th>
|
||
</tr></thead>
|
||
<tbody>
|
||
<tr><td>Май</td><td>22</td><td>18</td><td style="color:var(--success);font-weight:700">82%</td></tr>
|
||
<tr><td>Апрель</td><td>21</td><td>19</td><td style="color:var(--success);font-weight:700">90%</td></tr>
|
||
<tr><td>Март</td><td>21</td><td>15</td><td style="color:var(--warn);font-weight:700">71%</td></tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<div class="card">
|
||
<div style="font-size:13px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;margin-bottom:12px">Доходы по месяцам</div>
|
||
<table class="stat-table">
|
||
<thead><tr>
|
||
<th>Месяц</th><th>Компания</th><th>Клиент</th><th>Итого</th>
|
||
</tr></thead>
|
||
<tbody>
|
||
<tr><td>Май</td><td>25 200 ₽</td><td>3 200 ₽</td><td style="font-weight:700">28 400 ₽</td></tr>
|
||
<tr><td>Апрель</td><td>28 000 ₽</td><td>5 100 ₽</td><td style="font-weight:700">33 100 ₽</td></tr>
|
||
<tr><td>Март</td><td>21 000 ₽</td><td>2 800 ₽</td><td style="font-weight:700">23 800 ₽</td></tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<!-- Подпись / Факсимиле -->
|
||
<div class="card" style="margin-bottom:12px">
|
||
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:14px">
|
||
<div>
|
||
<div style="font-size:11px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.05em">Подпись</div>
|
||
<div style="font-size:11px;color:var(--muted);margin-top:2px">Используется в актах дополнительных работ</div>
|
||
</div>
|
||
<div style="display:flex;gap:6px">
|
||
<button onclick="alert('Загрузить изображение подписи (PNG/JPG)')" style="background:rgba(0,0,0,.05);border:none;border-radius:8px;padding:6px 10px;font-size:12px;font-weight:600;color:var(--muted);cursor:pointer">↑ Загрузить</button>
|
||
<button onclick="go('extra_sign')" style="background:var(--accent);border:none;border-radius:8px;padding:6px 10px;font-size:12px;font-weight:600;color:#fff;cursor:pointer">✏ Нарисовать</button>
|
||
</div>
|
||
</div>
|
||
<div style="border:1.5px dashed rgba(0,0,0,.15);border-radius:10px;padding:16px 20px;background:rgba(0,0,0,.02);min-height:80px;display:flex;flex-direction:column;justify-content:space-between">
|
||
<div style="display:flex;justify-content:center;align-items:center;flex:1;padding:4px 0 10px">
|
||
<svg width="180" height="48" viewBox="0 0 180 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||
<path d="M8 36 C16 14, 28 42, 36 24 C42 10, 46 34, 56 28 C66 22, 62 40, 72 30 C80 22, 84 44, 96 28 C106 14, 110 38, 122 26 C132 16, 138 36, 150 28 C158 22, 164 38, 172 30" stroke="#1e3a5f" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
|
||
<path d="M20 42 C28 36, 38 44, 50 40" stroke="#1e3a5f" stroke-width="1.5" stroke-linecap="round" fill="none"/>
|
||
</svg>
|
||
</div>
|
||
<div style="border-top:1px solid rgba(0,0,0,.2);padding-top:6px;display:flex;align-items:center;justify-content:space-between">
|
||
<span style="font-size:10px;color:var(--muted)">Кириллов А.В.</span>
|
||
<span style="font-size:10px;color:var(--muted)">ИП · Сборщик</span>
|
||
</div>
|
||
</div>
|
||
<div style="display:flex;align-items:center;gap:6px;margin-top:10px">
|
||
<div style="width:7px;height:7px;border-radius:50%;background:#10b981;flex-shrink:0"></div>
|
||
<span style="font-size:11px;color:#065f46">Подпись сохранена · обновлена 10.01.2025</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="card">
|
||
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:12px">
|
||
<div style="font-size:13px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.05em">Реквизиты ИП</div>
|
||
<div style="display:flex;gap:6px">
|
||
<button onclick="navigator.clipboard.writeText('ИП Кириллов А.В.\nИНН: 781234567890\nОГРНИП: 312784612345678\nБанк: Сбербанк').then(()=>{this.textContent='✓ Скопировано';this.style.color='var(--success)';setTimeout(()=>{this.textContent='⎘ Копировать';this.style.color='var(--accent)'},1500)})" style="display:flex;align-items:center;gap:4px;border:1px solid rgba(0,62,126,.2);border-radius:8px;padding:4px 10px;font-size:12px;font-weight:600;color:var(--accent);background:rgba(0,62,126,.04);cursor:pointer">⎘ Копировать</button>
|
||
<button onclick="if(navigator.share){navigator.share({title:'Реквизиты ИП',text:'ИП Кириллов А.В.\nИНН: 781234567890\nОГРНИП: 312784612345678\nБанк: Сбербанк'})}else{navigator.clipboard.writeText('ИП Кириллов А.В.\nИНН: 781234567890\nОГРНИП: 312784612345678');this.textContent='✓ Скопировано';this.style.color='var(--success)';setTimeout(()=>{this.textContent='↗ Поделиться';this.style.color='var(--accent)'},1500)}" style="display:flex;align-items:center;gap:4px;border:1px solid rgba(0,62,126,.2);border-radius:8px;padding:4px 10px;font-size:12px;font-weight:600;color:var(--accent);background:rgba(0,62,126,.04);cursor:pointer">↗ Поделиться</button>
|
||
</div>
|
||
</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="display:flex;align-items:center;gap:6px">781234567890
|
||
<button onclick="navigator.clipboard.writeText('781234567890').then(()=>{this.textContent='✓';this.style.color='var(--success)';setTimeout(()=>{this.textContent='⎘';this.style.color='var(--muted)'},1200)})" style="border:none;background:none;color:var(--muted);font-size:13px;cursor:pointer;padding:0;line-height:1">⎘</button>
|
||
</span>
|
||
</div>
|
||
<div class="info-row"><span class="info-label">ОГРНИП</span>
|
||
<span class="info-val" style="display:flex;align-items:center;gap:6px">312784612345678
|
||
<button onclick="navigator.clipboard.writeText('312784612345678').then(()=>{this.textContent='✓';this.style.color='var(--success)';setTimeout(()=>{this.textContent='⎘';this.style.color='var(--muted)'},1200)})" style="border:none;background:none;color:var(--muted);font-size:13px;cursor:pointer;padding:0;line-height:1">⎘</button>
|
||
</span>
|
||
</div>
|
||
<div class="info-row"><span class="info-label">Банк</span><span class="info-val">Сбербанк · •••• 4421</span></div>
|
||
</div>
|
||
|
||
<div class="card">
|
||
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:12px">
|
||
<div style="font-size:13px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.05em">Контакт</div>
|
||
<div style="display:flex;gap:6px">
|
||
<button onclick="navigator.clipboard.writeText('Алексей Кириллов\nТел: +7 921 456-78-90\nTelegram: @kirillov_asm').then(()=>{this.textContent='✓ Скопировано';this.style.color='var(--success)';setTimeout(()=>{this.textContent='⎘ Копировать';this.style.color='var(--accent)'},1500)})" style="display:flex;align-items:center;gap:4px;border:1px solid rgba(0,62,126,.2);border-radius:8px;padding:4px 10px;font-size:12px;font-weight:600;color:var(--accent);background:rgba(0,62,126,.04);cursor:pointer">⎘ Копировать</button>
|
||
<button onclick="if(navigator.share){navigator.share({title:'Контакт',text:'Тел: +7 921 456-78-90\nTelegram: @kirillov_asm'})}else{navigator.clipboard.writeText('+7 921 456-78-90\n@kirillov_asm');this.textContent='✓ Скопировано';this.style.color='var(--success)';setTimeout(()=>{this.textContent='↗ Поделиться';this.style.color='var(--accent)'},1500)}" style="display:flex;align-items:center;gap:4px;border:1px solid rgba(0,62,126,.2);border-radius:8px;padding:4px 10px;font-size:12px;font-weight:600;color:var(--accent);background:rgba(0,62,126,.04);cursor:pointer">↗ Поделиться</button>
|
||
</div>
|
||
</div>
|
||
<div class="info-row"><span class="info-label">Телефон</span>
|
||
<span class="info-val" style="display:flex;align-items:center;gap:6px">+7 921 456-78-90
|
||
<button onclick="navigator.clipboard.writeText('+79214567890').then(()=>{this.textContent='✓';this.style.color='var(--success)';setTimeout(()=>{this.textContent='⎘';this.style.color='var(--muted)'},1200)})" style="border:none;background:none;color:var(--muted);font-size:13px;cursor:pointer;padding:0;line-height:1">⎘</button>
|
||
</span>
|
||
</div>
|
||
<div class="info-row"><span class="info-label">Telegram</span>
|
||
<span class="info-val" style="display:flex;align-items:center;gap:6px">@kirillov_asm
|
||
<button onclick="navigator.clipboard.writeText('@kirillov_asm').then(()=>{this.textContent='✓';this.style.color='var(--success)';setTimeout(()=>{this.textContent='⎘';this.style.color='var(--muted)'},1200)})" style="border:none;background:none;color:var(--muted);font-size:13px;cursor:pointer;padding:0;line-height:1">⎘</button>
|
||
</span>
|
||
</div>
|
||
<div class="info-row"><span class="info-label">Опыт</span><span class="info-val">3 года · 340 сборок</span></div>
|
||
</div>
|
||
|
||
<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" style="margin-bottom:10px">
|
||
<span class="info-label">Категория</span>
|
||
<span class="info-val" style="display:flex;align-items:center;gap:6px"><span class="badge blue" style="font-size:13px;font-weight:800">А</span> <span style="font-size:12px;color:var(--muted)">Старший мастер</span></span>
|
||
</div>
|
||
<div style="border-top:1px solid rgba(0,0,0,.06);padding-top:10px">
|
||
<div style="font-size:11px;color:var(--muted);font-weight:700;text-transform:uppercase;letter-spacing:.05em;margin-bottom:8px">Виды работ</div>
|
||
${[
|
||
{label:'Корпусная мебель', on:true, accent:false},
|
||
{label:'Кухни', on:true, accent:false},
|
||
{label:'Шкафы-купе', on:true, accent:false},
|
||
{label:'Мебель для ванной', on:true, accent:false},
|
||
{label:'Подключение электроприборов', on:true, accent:true},
|
||
{label:'Подключение сантехники', on:true, accent:true},
|
||
{label:'Работы на высоте (стремянка)', on:false, accent:false},
|
||
].map(s => `
|
||
<div style="display:flex;align-items:center;gap:10px;padding:5px 0">
|
||
<div style="width:18px;height:18px;border-radius:5px;flex-shrink:0;background:${s.on?(s.accent?'var(--accent2)':'var(--accent)'):'#E2E8F0'};display:flex;align-items:center;justify-content:center">
|
||
${s.on?'<svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="3.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>':''}
|
||
</div>
|
||
<span style="font-size:13px;color:${s.on?'var(--ink)':'var(--muted)'}${s.accent?';font-weight:600':''}">${s.label}</span>
|
||
${s.accent&&s.on?'<span style="font-size:10px;background:rgba(118,189,34,.12);color:var(--accent2);font-weight:700;padding:2px 6px;border-radius:4px;flex-shrink:0">доп. оплата</span>':''}
|
||
</div>
|
||
`).join('')}
|
||
</div>
|
||
<div style="margin-top:10px;padding:8px 10px;background:rgba(0,62,126,.05);border-radius:8px;border-left:3px solid var(--accent);font-size:12px;color:var(--ink);line-height:1.4">
|
||
Электро + сантехника учитываются при авто-назначении (Smart Dispatch)
|
||
</div>
|
||
</div>
|
||
|
||
<div class="card">
|
||
<div style="font-size:13px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;margin-bottom:12px">История актов</div>
|
||
<table class="stat-table">
|
||
<thead><tr><th>Акт</th><th>Сборка</th><th>Сумма</th><th>Статус</th></tr></thead>
|
||
<tbody>
|
||
<tr><td>#DA-3f2a</td><td>А-2847</td><td style="font-weight:600">19 300 ₽</td><td><span class="badge yellow" style="font-size:10px">Черновик</span></td></tr>
|
||
<tr><td>#DA-1c8b</td><td>А-2839</td><td style="font-weight:600">5 100 ₽</td><td><span class="badge green" style="font-size:10px">Оплачен</span></td></tr>
|
||
<tr><td>#DA-0f4e</td><td>А-2831</td><td style="font-weight:600">3 200 ₽</td><td><span class="badge green" style="font-size:10px">Оплачен</span></td></tr>
|
||
<tr><td>#DA-0a21</td><td>А-2810</td><td style="font-weight:600">8 800 ₽</td><td><span class="badge green" style="font-size:10px">Оплачен</span></td></tr>
|
||
<tr><td>#DA-0918</td><td>А-2795</td><td style="font-weight:600">2 400 ₽</td><td><span class="badge green" style="font-size:10px">Оплачен</span></td></tr>
|
||
</tbody>
|
||
</table>
|
||
<div style="text-align:center;margin-top:10px">
|
||
<span style="font-size:12px;color:var(--accent);font-weight:600;cursor:pointer">Показать все 47 актов →</span>
|
||
</div>
|
||
</div>
|
||
|
||
<button class="btn-secondary" onclick="go('availability')" style="margin-bottom:8px">📅 Управление доступностью</button>
|
||
<button class="btn-secondary" onclick="go('notifications')" style="margin-bottom:8px">🔔 Уведомления</button>
|
||
<button class="btn-secondary" style="margin-bottom:8px">🚪 Выйти</button>
|
||
|
||
<div style="text-align:center;padding:20px 0 4px;border-top:1px solid rgba(0,0,0,.06);margin-top:8px">
|
||
<div style="display:flex;align-items:center;justify-content:center;gap:8px;margin-bottom:6px">
|
||
<div class="w1-slot" data-color="var(--muted)" data-width="110" data-height="17"></div>
|
||
<span style="color:var(--muted);font-size:13px;font-weight:300">|</span>
|
||
<span style="font-family:'Montserrat',sans-serif;font-weight:700;font-size:13px;color:var(--muted);letter-spacing:2px">CRM</span>
|
||
</div>
|
||
<div style="font-size:11px;color:var(--muted);opacity:.6">v0.1 · Прототип</div>
|
||
</div>
|
||
</div>
|
||
${nav('profile')}</div>`;
|
||
}
|
||
|
||
/* ─── RATE TEAM ─── */
|
||
function screenRateTeam() {
|
||
function stars(id, val) {
|
||
return [1,2,3,4,5].map(n => `
|
||
<span data-star="${n}" data-group="${id}" onclick="
|
||
this.parentNode.querySelectorAll('[data-star]').forEach((s,i)=>{
|
||
s.style.color = i < ${n} ? '#F59E0B' : '#E2E8F0';
|
||
});
|
||
" style="font-size:26px;cursor:pointer;color:${n<=val?'#F59E0B':'#E2E8F0'};transition:color .15s">★</span>
|
||
`).join('');
|
||
}
|
||
|
||
const people = [
|
||
{
|
||
role:'Менеджер', name:'Анна Соколова', init:'АС', val:4,
|
||
criteria:[
|
||
{id:'m1', label:'Точность информации по сборке', val:4},
|
||
{id:'m2', label:'Скорость ответа в чате', val:5},
|
||
{id:'m3', label:'Качество документов и актов', val:4},
|
||
{id:'m4', label:'Решение проблем на месте', val:3},
|
||
]
|
||
},
|
||
{
|
||
role:'Технолог', name:'Игорь Волков', init:'ИВ', val:5,
|
||
criteria:[
|
||
{id:'t1', label:'Точность замеров (совпало с реальностью)', val:5},
|
||
{id:'t2', label:'Качество примечаний к объекту', val:4},
|
||
{id:'t3', label:'Полнота нюансов в карточке сборки', val:5},
|
||
]
|
||
},
|
||
];
|
||
|
||
return `<div class="page">
|
||
<div style="background:linear-gradient(135deg,var(--success) 0%,#059669 100%);padding:20px 16px 24px;text-align:center">
|
||
<div style="font-size:32px;margin-bottom:8px">🎉</div>
|
||
<div style="font-size:20px;font-weight:800;color:#fff">Сборка завершена!</div>
|
||
<div style="font-size:13px;color:rgba(255,255,255,.75);margin-top:4px">А-2847 · Иванов Дмитрий · 22 мая</div>
|
||
</div>
|
||
|
||
<div style="padding:16px">
|
||
<div style="background:#FFFBEB;border:1px solid #FDE68A;border-radius:12px;padding:10px 14px;margin-bottom:16px;display:flex;gap:10px;align-items:flex-start">
|
||
<span style="font-size:16px">🔒</span>
|
||
<div style="font-size:12px;color:#92400E;line-height:1.5">Оценки видны только директору и вам. Менеджер и технолог видят только усреднённый балл по всем своим сборкам — без привязки к конкретному мастеру.</div>
|
||
</div>
|
||
|
||
${people.map(p => `
|
||
<div class="card" style="margin-bottom:12px">
|
||
<div style="display:flex;align-items:center;gap:12px;margin-bottom:14px">
|
||
<div class="avatar" style="background:var(--accent);color:#fff;font-size:14px">${p.init}</div>
|
||
<div>
|
||
<div style="font-size:15px;font-weight:700;color:var(--ink)">${p.name}</div>
|
||
<div style="font-size:12px;color:var(--muted)">${p.role}</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div style="font-size:12px;color:var(--muted);margin-bottom:6px;font-weight:600">Общая оценка</div>
|
||
<div style="margin-bottom:14px">${stars(p.role, p.val)}</div>
|
||
|
||
${p.criteria.map(c => `
|
||
<div style="margin-bottom:12px">
|
||
<div style="font-size:12px;color:var(--ink);margin-bottom:4px;font-weight:500">${c.label}</div>
|
||
<div>${stars(c.id, c.val)}</div>
|
||
</div>
|
||
`).join('')}
|
||
</div>
|
||
`).join('')}
|
||
|
||
<div class="form-group">
|
||
<label>Комментарий (необязательно)</label>
|
||
<textarea class="form-textarea" placeholder="Что было хорошо или что можно улучшить..." style="min-height:70px"></textarea>
|
||
</div>
|
||
|
||
<button class="btn-primary" style="background:var(--success)" onclick="go('home')">Отправить оценку</button>
|
||
<button class="btn-secondary" onclick="go('home')" style="margin-top:8px;color:var(--muted);border-color:#E2E8F0">Пропустить</button>
|
||
<div style="text-align:center;font-size:11px;color:var(--muted);margin-top:8px">Оценку можно изменить в течение 24 часов</div>
|
||
</div>
|
||
${nav('home')}</div>`;
|
||
}
|
||
|
||
/* ─── MY QUESTIONNAIRE (Assembler) ─── */
|
||
function screenMyQuestionnaire() {
|
||
if (!window._qSaved) window._qSaved = false;
|
||
if (!window._qCar) window._qCar = 'yes';
|
||
if (!window._qTools) window._qTools = {drill:true,jigsaw:true,miter:false,plunge:false,hammer:false,grinder:false,router:false,vacuum:true};
|
||
if (!window._qFurn) window._qFurn = {corpus:true,upholstered:false,kitchen:true,office:true,premium:false};
|
||
if (!window._qZones) window._qZones = {center:true,north:true,south:false,east:false,west:false};
|
||
|
||
function chk(key, store, label, note) {
|
||
var on = window[store][key];
|
||
return '<label style="display:flex;align-items:flex-start;gap:10px;padding:9px 0;border-bottom:1px solid rgba(0,0,0,.05);cursor:pointer">'
|
||
+'<div onclick="(function(){window[\''+store+'\'][\''+key+'\']=!window[\''+store+'\'][\''+key+'\'];'
|
||
+'document.getElementById(\'screen\').innerHTML=renderScreen(\'my_questionnaire\')})()"'
|
||
+' style="width:20px;height:20px;border-radius:5px;border:2px solid '+(on?'var(--accent)':'#D1D5DB')+';background:'+(on?'var(--accent)':'transparent')+';flex-shrink:0;margin-top:1px;display:flex;align-items:center;justify-content:center">'
|
||
+(on?'<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg>':'')
|
||
+'</div>'
|
||
+'<div><div style="font-size:13px;color:var(--ink);font-weight:500">'+label+'</div>'
|
||
+(note?'<div style="font-size:11px;color:var(--muted);margin-top:1px">'+note+'</div>':'')
|
||
+'</div></label>';
|
||
}
|
||
|
||
var saved = window._qSaved;
|
||
|
||
return '<div class="page">'
|
||
+'<div class="page-header">'
|
||
+'<button class="back-btn" onclick="go(\'profile\')"><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:0 16px 20px">'
|
||
|
||
// Info banner
|
||
+'<div style="background:rgba(0,62,126,.06);border:1px solid rgba(0,62,126,.15);border-radius:10px;padding:10px 14px;margin:12px 0;display:flex;gap:8px;align-items:flex-start">'
|
||
+'<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="var(--accent)" stroke-width="2" style="flex-shrink:0;margin-top:1px"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>'
|
||
+'<div style="font-size:12px;color:var(--ink);line-height:1.5">Данные видны директору. При изменениях (новый инструмент, зона, тип мебели) — директор получит уведомление.</div>'
|
||
+'</div>'
|
||
|
||
// Instruments
|
||
+'<div class="card" style="padding:14px 16px;margin-bottom:10px">'
|
||
+'<div style="font-size:11px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;margin-bottom:4px">Инструменты</div>'
|
||
+'<div style="font-size:11px;color:var(--muted);margin-bottom:10px">Отметьте что есть в наличии</div>'
|
||
+ chk('drill', '_qTools', 'Дрель / шуруповёрт', null)
|
||
+ chk('jigsaw', '_qTools', 'Лобзик', null)
|
||
+ chk('miter', '_qTools', 'Торцовочная пила', 'для точных угловых срезов')
|
||
+ chk('plunge', '_qTools', 'Погружная пила', 'для раскроя панелей и столешниц')
|
||
+ chk('hammer', '_qTools', 'Перфоратор', null)
|
||
+ chk('grinder', '_qTools', 'Болгарка', null)
|
||
+ chk('router', '_qTools', 'Фрезер', 'для кромки и пазов')
|
||
+ chk('vacuum', '_qTools', 'Строительный пылесос', null)
|
||
+'</div>'
|
||
|
||
// Furniture types
|
||
+'<div class="card" style="padding:14px 16px;margin-bottom:10px">'
|
||
+'<div style="font-size:11px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;margin-bottom:10px">Типы мебели</div>'
|
||
+ chk('corpus', '_qFurn', 'Корпусная (шкафы, стеллажи, комоды)', null)
|
||
+ chk('upholstered', '_qFurn', 'Мягкая (диваны, кресла)', null)
|
||
+ chk('kitchen', '_qFurn', 'Кухонные гарнитуры', null)
|
||
+ chk('office', '_qFurn', 'Офисная мебель', null)
|
||
+ chk('premium', '_qFurn', 'Премиальная / итальянская', 'влияет на категорию Кат.А')
|
||
+'</div>'
|
||
|
||
// Transport
|
||
+'<div class="card" style="padding:14px 16px;margin-bottom:10px">'
|
||
+'<div style="font-size:11px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;margin-bottom:10px">Личный автомобиль</div>'
|
||
+'<div style="display:flex;gap:8px">'
|
||
+'<button onclick="(function(){window._qCar=\'yes\';document.getElementById(\'screen\').innerHTML=renderScreen(\'my_questionnaire\')})()"'
|
||
+' style="flex:1;padding:12px;border-radius:10px;border:2px solid '+(window._qCar==='yes'?'var(--accent)':'rgba(0,0,0,.1)')+';background:'+(window._qCar==='yes'?'rgba(0,62,126,.07)':'transparent')+';cursor:pointer;font-size:13px;font-weight:700;color:'+(window._qCar==='yes'?'var(--accent)':'var(--muted)')+'">'
|
||
+'🚗 Есть авто</button>'
|
||
+'<button onclick="(function(){window._qCar=\'no\';document.getElementById(\'screen\').innerHTML=renderScreen(\'my_questionnaire\')})()"'
|
||
+' style="flex:1;padding:12px;border-radius:10px;border:2px solid '+(window._qCar==='no'?'var(--accent)':'rgba(0,0,0,.1)')+';background:'+(window._qCar==='no'?'rgba(0,62,126,.07)':'transparent')+';cursor:pointer;font-size:13px;font-weight:700;color:'+(window._qCar==='no'?'var(--accent)':'var(--muted)')+'">'
|
||
+'🚌 Без авто</button>'
|
||
+'</div>'
|
||
+'</div>'
|
||
|
||
// Work zones
|
||
+'<div class="card" style="padding:14px 16px;margin-bottom:16px">'
|
||
+'<div style="font-size:11px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;margin-bottom:4px">Зона работы</div>'
|
||
+'<div style="font-size:11px;color:var(--muted);margin-bottom:10px">Комфортные районы</div>'
|
||
+ chk('center', '_qZones', 'Центр города', null)
|
||
+ chk('north', '_qZones', 'Север', null)
|
||
+ chk('south', '_qZones', 'Юг', null)
|
||
+ chk('east', '_qZones', 'Восток', null)
|
||
+ chk('west', '_qZones', 'Запад / ЗАД', null)
|
||
+'</div>'
|
||
|
||
// Save button
|
||
+(saved
|
||
? '<div style="background:rgba(16,185,129,.1);border:1px solid rgba(16,185,129,.3);border-radius:12px;padding:14px;text-align:center;font-size:14px;font-weight:700;color:#065f46">✓ Сохранено · Директор уведомлён</div>'
|
||
: '<button onclick="(function(){window._qSaved=true;document.getElementById(\'screen\').innerHTML=renderScreen(\'my_questionnaire\')})()"'
|
||
+' style="width:100%;padding:16px;background:var(--accent);color:#fff;border:none;border-radius:12px;font-size:15px;font-weight:700;cursor:pointer">Сохранить изменения</button>'
|
||
)
|
||
+'<div style="text-align:center;font-size:11px;color:var(--muted);margin-top:8px">Последнее обновление: 22.05.2026 · 10:14</div>'
|
||
+'</div>'
|
||
+ nav('profile')
|
||
+'</div>';
|
||
}
|
||
|
||
/* ─── AVAILABILITY ─── */
|
||
function screenAvailability() {
|
||
const requests = [
|
||
{dates:'26–27 мая', type:'выходной', status:'green', label:'Одобрен'},
|
||
{dates:'1–2 июня', type:'выходной', status:'yellow', label:'На рассмотрении'},
|
||
{dates:'15–20 июня', type:'отпуск', status:'yellow', label:'На рассмотрении'},
|
||
];
|
||
return `<div class="page">
|
||
<div class="page-header">
|
||
<button class="back-btn" onclick="go('profile')"><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:12px 16px 0">
|
||
|
||
<!-- Текущий статус -->
|
||
<div class="card" style="margin-bottom:12px">
|
||
<div style="font-size:13px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;margin-bottom:12px">Мой статус сейчас</div>
|
||
${[
|
||
{icon:'check2', iconBg:'#DCFCE7', iconColor:'#059669', label:'Доступен для сборки', desc:'Открыт для новых назначений', active:true},
|
||
{icon:'beach', iconBg:'#FEF3C7', iconColor:'#D97706', label:'Выходной', desc:'Недоступен на выбранные даты', active:false},
|
||
{icon:'sick', iconBg:'#FEE2E2', iconColor:'#DC2626', label:'Заболел', desc:'Сборки уходят на переназначение', active:false},
|
||
].map(s => `
|
||
<div onclick="this.parentNode.querySelectorAll('.status-opt').forEach(e=>e.style.borderColor='#E2E8F0');this.style.borderColor='var(--accent)'" class="status-opt" style="display:flex;align-items:center;gap:12px;padding:10px 12px;border:2px solid ${s.active?'var(--accent)':'#E2E8F0'};border-radius:12px;margin-bottom:8px;cursor:pointer;transition:all .2s">
|
||
<div style="width:36px;height:36px;border-radius:10px;background:${s.iconBg};display:flex;align-items:center;justify-content:center;flex-shrink:0">${svgIcon(s.icon,18,s.iconColor)}</div>
|
||
<div>
|
||
<div style="font-size:14px;font-weight:600;color:var(--ink)">${s.label}</div>
|
||
<div style="font-size:12px;color:var(--muted)">${s.desc}</div>
|
||
</div>
|
||
</div>
|
||
`).join('')}
|
||
</div>
|
||
|
||
<!-- Больничный -->
|
||
<div class="card" style="margin-bottom:12px">
|
||
<div style="font-size:13px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;margin-bottom:12px">Больничный</div>
|
||
<div style="display:flex;gap:8px;margin-bottom:8px">
|
||
<div style="flex:1">
|
||
<div style="font-size:11px;color:var(--muted);margin-bottom:4px">Начало</div>
|
||
<input type="date" class="form-input" value="2026-05-22" style="padding:8px 10px">
|
||
</div>
|
||
<div style="flex:1">
|
||
<div style="font-size:11px;color:var(--muted);margin-bottom:4px">Ориентировочно вернусь</div>
|
||
<input type="date" class="form-input" style="padding:8px 10px">
|
||
</div>
|
||
</div>
|
||
<div style="background:#FEF3C7;border-radius:10px;padding:8px 12px;font-size:12px;color:#D97706;margin-bottom:8px">
|
||
⚠️ Активные сборки в эти даты уйдут на переназначение. Менеджер получит уведомление.
|
||
</div>
|
||
<button class="btn-sm" style="width:100%;background:var(--danger)">🤒 Открыть больничный</button>
|
||
</div>
|
||
|
||
<!-- Запрос выходных -->
|
||
<div class="card" style="margin-bottom:12px">
|
||
<div style="font-size:13px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;margin-bottom:4px">Запросить выходной / отпуск</div>
|
||
<div style="font-size:12px;color:var(--muted);margin-bottom:12px">Нерегламентированный выходной — не менее чем за 10 дней</div>
|
||
<div style="display:flex;gap:8px;margin-bottom:8px">
|
||
<div style="flex:1">
|
||
<div style="font-size:11px;color:var(--muted);margin-bottom:4px">С</div>
|
||
<input type="date" class="form-input" value="2026-06-01" style="padding:8px 10px">
|
||
</div>
|
||
<div style="flex:1">
|
||
<div style="font-size:11px;color:var(--muted);margin-bottom:4px">По</div>
|
||
<input type="date" class="form-input" value="2026-06-02" style="padding:8px 10px">
|
||
</div>
|
||
</div>
|
||
<button class="btn-sm" style="width:100%">Отправить запрос менеджеру</button>
|
||
</div>
|
||
|
||
<!-- История запросов -->
|
||
<div style="font-size:13px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;margin-bottom:8px">История запросов</div>
|
||
${requests.map(r => `
|
||
<div class="card" style="padding:12px 16px;margin-bottom:8px">
|
||
<div style="display:flex;align-items:center;justify-content:space-between">
|
||
<div>
|
||
<div style="font-size:14px;font-weight:600;color:var(--ink)">${r.dates}</div>
|
||
<div style="font-size:12px;color:var(--muted);margin-top:2px;text-transform:capitalize">${r.type}</div>
|
||
</div>
|
||
<span class="badge ${r.status}">${r.label}</span>
|
||
</div>
|
||
</div>
|
||
`).join('')}
|
||
</div>
|
||
${nav('profile')}</div>`;
|
||
}
|
||
|
||
/* ─── NOTIFICATIONS ─── */
|
||
function screenNotifications() {
|
||
const items = [
|
||
{icon:'📦', color:'#DBEAFE', time:'5 мин назад', title:'Назначена новая сборка', body:'Сидорова Марина · 27 мая, 10:00 · Кухня 2м · пр. Просвещения, 40', action:'asm_detail', actionLabel:'Открыть', unread:true},
|
||
{icon:'✅', color:'#DCFCE7', time:'1 час назад', title:'Акт подписан', body:'Акт #DA-1c8b · Смирнов Андрей · 5 100 ₽ — клиент подписал', action:'extra_done', actionLabel:'Просмотреть', unread:true},
|
||
{icon:'⚠️', color:'#FEE2E2', time:'3 часа назад', title:'Рекламация требует действия', body:'#R-0041 · Скол на фасаде · Срок: 30.05.2026', action:'reclamation', actionLabel:'К рекламации', unread:false},
|
||
{icon:'💰', color:'#DCFCE7', time:'Вчера, 18:30', title:'Оплата получена', body:'Акт #DA-0f4e · 3 200 ₽ зачислено на счёт', action:null, actionLabel:null, unread:false},
|
||
{icon:'📋', color:'#FEF3C7', time:'Вчера, 14:00', title:'Выходной одобрен', body:'26–27 мая · Менеджер Анна Соколова одобрила запрос', action:null, actionLabel:null, unread:false},
|
||
{icon:'📦', color:'#DBEAFE', time:'21 мая', title:'Назначена сборка', body:'Смирнов Андрей · А-2839 · Детская мебель 4 предм.', action:'asm_detail', actionLabel:'Открыть', unread:false},
|
||
];
|
||
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 class="header-action"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="20 6 9 17 4 12"/></svg></div>
|
||
</div>
|
||
<div style="padding:8px 0 0">
|
||
${items.map(n => `
|
||
<div style="display:flex;gap:12px;padding:12px 16px;border-bottom:1px solid rgba(0,0,0,.06);background:${n.unread?'rgba(0,62,126,.03)':'transparent'};cursor:${n.action?'pointer':'default'}" onclick="${n.action?'go(\''+n.action+'\')':''}">
|
||
<div style="width:40px;height:40px;border-radius:12px;background:${n.color};display:flex;align-items:center;justify-content:center;font-size:20px;flex-shrink:0">${n.icon}</div>
|
||
<div style="flex:1;min-width:0">
|
||
<div style="display:flex;align-items:flex-start;justify-content:space-between;gap:8px">
|
||
<div style="font-size:13px;font-weight:${n.unread?700:600};color:var(--ink)">${n.title}</div>
|
||
${n.unread?'<div style="width:8px;height:8px;border-radius:50%;background:var(--accent);flex-shrink:0;margin-top:4px"></div>':''}
|
||
</div>
|
||
<div style="font-size:12px;color:var(--muted);margin-top:3px;line-height:1.4">${n.body}</div>
|
||
<div style="display:flex;align-items:center;justify-content:space-between;margin-top:6px">
|
||
<span style="font-size:11px;color:var(--muted)">${n.time}</span>
|
||
${n.actionLabel?`<span style="font-size:12px;color:var(--accent);font-weight:600">${n.actionLabel} →</span>`:''}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
`).join('')}
|
||
<div style="text-align:center;padding:20px;font-size:13px;color:var(--muted)">Все уведомления загружены</div>
|
||
</div>
|
||
${nav('home')}</div>`;
|
||
}
|
||
|
||
// init
|
||
go('home');
|
||
</script>
|
||
</body>
|
||
</html>
|
||
|
||
|